1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build ignore 6// +build ignore 7 8package main 9 10import ( 11 "bytes" 12 "flag" 13 "fmt" 14 "go/format" 15 "io/ioutil" 16 "log" 17 "os" 18 "strings" 19) 20 21var debug = flag.Bool("debug", false, "") 22 23func main() { 24 flag.Parse() 25 26 w := new(bytes.Buffer) 27 w.WriteString("// generated by \"go run gen.go\". DO NOT EDIT.\n\n" + 28 "package draw\n\nimport (\n" + 29 "\"image\"\n" + 30 "\"image/color\"\n" + 31 "\"math\"\n" + 32 "\n" + 33 "\"golang.org/x/image/math/f64\"\n" + 34 ")\n") 35 36 gen(w, "nnInterpolator", codeNNScaleLeaf, codeNNTransformLeaf) 37 gen(w, "ablInterpolator", codeABLScaleLeaf, codeABLTransformLeaf) 38 genKernel(w) 39 40 if *debug { 41 os.Stdout.Write(w.Bytes()) 42 return 43 } 44 out, err := format.Source(w.Bytes()) 45 if err != nil { 46 log.Fatal(err) 47 } 48 if err := ioutil.WriteFile("impl.go", out, 0660); err != nil { 49 log.Fatal(err) 50 } 51} 52 53var ( 54 // dsTypes are the (dst image type, src image type) pairs to generate 55 // scale_DType_SType implementations for. The last element in the slice 56 // should be the fallback pair ("Image", "image.Image"). 57 // 58 // TODO: add *image.CMYK src type after Go 1.5 is released. 59 // An *image.CMYK is also alwaysOpaque. 60 dsTypes = []struct{ dType, sType string }{ 61 {"*image.RGBA", "*image.Gray"}, 62 {"*image.RGBA", "*image.NRGBA"}, 63 {"*image.RGBA", "*image.RGBA"}, 64 {"*image.RGBA", "*image.YCbCr"}, 65 {"*image.RGBA", "image.Image"}, 66 {"Image", "image.Image"}, 67 } 68 dTypes, sTypes []string 69 sTypesForDType = map[string][]string{} 70 subsampleRatios = []string{ 71 "444", 72 "422", 73 "420", 74 "440", 75 } 76 ops = []string{"Over", "Src"} 77 // alwaysOpaque are those image.Image implementations that are always 78 // opaque. For these types, Over is equivalent to the faster Src, in the 79 // absence of a source mask. 80 alwaysOpaque = map[string]bool{ 81 "*image.Gray": true, 82 "*image.YCbCr": true, 83 } 84) 85 86func init() { 87 dTypesSeen := map[string]bool{} 88 sTypesSeen := map[string]bool{} 89 for _, t := range dsTypes { 90 if !sTypesSeen[t.sType] { 91 sTypesSeen[t.sType] = true 92 sTypes = append(sTypes, t.sType) 93 } 94 if !dTypesSeen[t.dType] { 95 dTypesSeen[t.dType] = true 96 dTypes = append(dTypes, t.dType) 97 } 98 sTypesForDType[t.dType] = append(sTypesForDType[t.dType], t.sType) 99 } 100 sTypesForDType["anyDType"] = sTypes 101} 102 103type data struct { 104 dType string 105 sType string 106 sratio string 107 receiver string 108 op string 109} 110 111func gen(w *bytes.Buffer, receiver string, codes ...string) { 112 expn(w, codeRoot, &data{receiver: receiver}) 113 for _, code := range codes { 114 for _, t := range dsTypes { 115 for _, op := range ops { 116 if op == "Over" && alwaysOpaque[t.sType] { 117 continue 118 } 119 expn(w, code, &data{ 120 dType: t.dType, 121 sType: t.sType, 122 receiver: receiver, 123 op: op, 124 }) 125 } 126 } 127 } 128} 129 130func genKernel(w *bytes.Buffer) { 131 expn(w, codeKernelRoot, &data{}) 132 for _, sType := range sTypes { 133 expn(w, codeKernelScaleLeafX, &data{ 134 sType: sType, 135 }) 136 } 137 for _, dType := range dTypes { 138 for _, op := range ops { 139 expn(w, codeKernelScaleLeafY, &data{ 140 dType: dType, 141 op: op, 142 }) 143 } 144 } 145 for _, t := range dsTypes { 146 for _, op := range ops { 147 if op == "Over" && alwaysOpaque[t.sType] { 148 continue 149 } 150 expn(w, codeKernelTransformLeaf, &data{ 151 dType: t.dType, 152 sType: t.sType, 153 op: op, 154 }) 155 } 156 } 157} 158 159func expn(w *bytes.Buffer, code string, d *data) { 160 if d.sType == "*image.YCbCr" && d.sratio == "" { 161 for _, sratio := range subsampleRatios { 162 e := *d 163 e.sratio = sratio 164 expn(w, code, &e) 165 } 166 return 167 } 168 169 for _, line := range strings.Split(code, "\n") { 170 line = expnLine(line, d) 171 if line == ";" { 172 continue 173 } 174 fmt.Fprintln(w, line) 175 } 176} 177 178func expnLine(line string, d *data) string { 179 for { 180 i := strings.IndexByte(line, '$') 181 if i < 0 { 182 break 183 } 184 prefix, s := line[:i], line[i+1:] 185 186 i = len(s) 187 for j, c := range s { 188 if !('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z') { 189 i = j 190 break 191 } 192 } 193 dollar, suffix := s[:i], s[i:] 194 195 e := expnDollar(prefix, dollar, suffix, d) 196 if e == "" { 197 log.Fatalf("couldn't expand %q", line) 198 } 199 line = e 200 } 201 return line 202} 203 204// expnDollar expands a "$foo" fragment in a line of generated code. It returns 205// the empty string if there was a problem. It returns ";" if the generated 206// code is a no-op. 207func expnDollar(prefix, dollar, suffix string, d *data) string { 208 switch dollar { 209 case "dType": 210 return prefix + d.dType + suffix 211 case "dTypeRN": 212 return prefix + relName(d.dType) + suffix 213 case "sratio": 214 return prefix + d.sratio + suffix 215 case "sType": 216 return prefix + d.sType + suffix 217 case "sTypeRN": 218 return prefix + relName(d.sType) + suffix 219 case "receiver": 220 return prefix + d.receiver + suffix 221 case "op": 222 return prefix + d.op + suffix 223 224 case "switch": 225 return expnSwitch("", "", true, suffix) 226 case "switchD": 227 return expnSwitch("", "", false, suffix) 228 case "switchS": 229 return expnSwitch("", "anyDType", false, suffix) 230 231 case "preOuter": 232 switch d.dType { 233 default: 234 return ";" 235 case "Image": 236 s := "" 237 if d.sType == "image.Image" { 238 s = "srcMask, smp := opts.SrcMask, opts.SrcMaskP\n" 239 } 240 return s + 241 "dstMask, dmp := opts.DstMask, opts.DstMaskP\n" + 242 "dstColorRGBA64 := &color.RGBA64{}\n" + 243 "dstColor := color.Color(dstColorRGBA64)" 244 } 245 246 case "preInner": 247 switch d.dType { 248 default: 249 return ";" 250 case "*image.RGBA": 251 return "d := " + pixOffset("dst", "dr.Min.X+adr.Min.X", "dr.Min.Y+int(dy)", "*4", "*dst.Stride") 252 } 253 254 case "preKernelOuter": 255 switch d.sType { 256 default: 257 return ";" 258 case "image.Image": 259 return "srcMask, smp := opts.SrcMask, opts.SrcMaskP" 260 } 261 262 case "preKernelInner": 263 switch d.dType { 264 default: 265 return ";" 266 case "*image.RGBA": 267 return "d := " + pixOffset("dst", "dr.Min.X+int(dx)", "dr.Min.Y+adr.Min.Y", "*4", "*dst.Stride") 268 } 269 270 case "blend": 271 args, _ := splitArgs(suffix) 272 if len(args) != 4 { 273 return "" 274 } 275 switch d.sType { 276 default: 277 return argf(args, ""+ 278 "$3r = $0*$1r + $2*$3r\n"+ 279 "$3g = $0*$1g + $2*$3g\n"+ 280 "$3b = $0*$1b + $2*$3b\n"+ 281 "$3a = $0*$1a + $2*$3a", 282 ) 283 case "*image.Gray": 284 return argf(args, ""+ 285 "$3r = $0*$1r + $2*$3r", 286 ) 287 case "*image.YCbCr": 288 return argf(args, ""+ 289 "$3r = $0*$1r + $2*$3r\n"+ 290 "$3g = $0*$1g + $2*$3g\n"+ 291 "$3b = $0*$1b + $2*$3b", 292 ) 293 } 294 295 case "clampToAlpha": 296 if alwaysOpaque[d.sType] { 297 return ";" 298 } 299 // Go uses alpha-premultiplied color. The naive computation can lead to 300 // invalid colors, e.g. red > alpha, when some weights are negative. 301 return ` 302 if pr > pa { 303 pr = pa 304 } 305 if pg > pa { 306 pg = pa 307 } 308 if pb > pa { 309 pb = pa 310 } 311 ` 312 313 case "convFtou": 314 args, _ := splitArgs(suffix) 315 if len(args) != 2 { 316 return "" 317 } 318 319 switch d.sType { 320 default: 321 return argf(args, ""+ 322 "$0r := uint32($1r)\n"+ 323 "$0g := uint32($1g)\n"+ 324 "$0b := uint32($1b)\n"+ 325 "$0a := uint32($1a)", 326 ) 327 case "*image.Gray": 328 return argf(args, ""+ 329 "$0r := uint32($1r)", 330 ) 331 case "*image.YCbCr": 332 return argf(args, ""+ 333 "$0r := uint32($1r)\n"+ 334 "$0g := uint32($1g)\n"+ 335 "$0b := uint32($1b)", 336 ) 337 } 338 339 case "outputu": 340 args, _ := splitArgs(suffix) 341 if len(args) != 3 { 342 return "" 343 } 344 345 switch d.op { 346 case "Over": 347 switch d.dType { 348 default: 349 log.Fatalf("bad dType %q", d.dType) 350 case "Image": 351 return argf(args, ""+ 352 "qr, qg, qb, qa := dst.At($0, $1).RGBA()\n"+ 353 "if dstMask != nil {\n"+ 354 " _, _, _, ma := dstMask.At(dmp.X + $0, dmp.Y + $1).RGBA()\n"+ 355 " $2r = $2r * ma / 0xffff\n"+ 356 " $2g = $2g * ma / 0xffff\n"+ 357 " $2b = $2b * ma / 0xffff\n"+ 358 " $2a = $2a * ma / 0xffff\n"+ 359 "}\n"+ 360 "$2a1 := 0xffff - $2a\n"+ 361 "dstColorRGBA64.R = uint16(qr*$2a1/0xffff + $2r)\n"+ 362 "dstColorRGBA64.G = uint16(qg*$2a1/0xffff + $2g)\n"+ 363 "dstColorRGBA64.B = uint16(qb*$2a1/0xffff + $2b)\n"+ 364 "dstColorRGBA64.A = uint16(qa*$2a1/0xffff + $2a)\n"+ 365 "dst.Set($0, $1, dstColor)", 366 ) 367 case "*image.RGBA": 368 return argf(args, ""+ 369 "$2a1 := (0xffff - $2a) * 0x101\n"+ 370 "dst.Pix[d+0] = uint8((uint32(dst.Pix[d+0])*$2a1/0xffff + $2r) >> 8)\n"+ 371 "dst.Pix[d+1] = uint8((uint32(dst.Pix[d+1])*$2a1/0xffff + $2g) >> 8)\n"+ 372 "dst.Pix[d+2] = uint8((uint32(dst.Pix[d+2])*$2a1/0xffff + $2b) >> 8)\n"+ 373 "dst.Pix[d+3] = uint8((uint32(dst.Pix[d+3])*$2a1/0xffff + $2a) >> 8)", 374 ) 375 } 376 377 case "Src": 378 switch d.dType { 379 default: 380 log.Fatalf("bad dType %q", d.dType) 381 case "Image": 382 return argf(args, ""+ 383 "if dstMask != nil {\n"+ 384 " qr, qg, qb, qa := dst.At($0, $1).RGBA()\n"+ 385 " _, _, _, ma := dstMask.At(dmp.X + $0, dmp.Y + $1).RGBA()\n"+ 386 " pr = pr * ma / 0xffff\n"+ 387 " pg = pg * ma / 0xffff\n"+ 388 " pb = pb * ma / 0xffff\n"+ 389 " pa = pa * ma / 0xffff\n"+ 390 " $2a1 := 0xffff - ma\n"+ // Note that this is ma, not $2a. 391 " dstColorRGBA64.R = uint16(qr*$2a1/0xffff + $2r)\n"+ 392 " dstColorRGBA64.G = uint16(qg*$2a1/0xffff + $2g)\n"+ 393 " dstColorRGBA64.B = uint16(qb*$2a1/0xffff + $2b)\n"+ 394 " dstColorRGBA64.A = uint16(qa*$2a1/0xffff + $2a)\n"+ 395 " dst.Set($0, $1, dstColor)\n"+ 396 "} else {\n"+ 397 " dstColorRGBA64.R = uint16($2r)\n"+ 398 " dstColorRGBA64.G = uint16($2g)\n"+ 399 " dstColorRGBA64.B = uint16($2b)\n"+ 400 " dstColorRGBA64.A = uint16($2a)\n"+ 401 " dst.Set($0, $1, dstColor)\n"+ 402 "}", 403 ) 404 case "*image.RGBA": 405 switch d.sType { 406 default: 407 return argf(args, ""+ 408 "dst.Pix[d+0] = uint8($2r >> 8)\n"+ 409 "dst.Pix[d+1] = uint8($2g >> 8)\n"+ 410 "dst.Pix[d+2] = uint8($2b >> 8)\n"+ 411 "dst.Pix[d+3] = uint8($2a >> 8)", 412 ) 413 case "*image.Gray": 414 return argf(args, ""+ 415 "out := uint8($2r >> 8)\n"+ 416 "dst.Pix[d+0] = out\n"+ 417 "dst.Pix[d+1] = out\n"+ 418 "dst.Pix[d+2] = out\n"+ 419 "dst.Pix[d+3] = 0xff", 420 ) 421 case "*image.YCbCr": 422 return argf(args, ""+ 423 "dst.Pix[d+0] = uint8($2r >> 8)\n"+ 424 "dst.Pix[d+1] = uint8($2g >> 8)\n"+ 425 "dst.Pix[d+2] = uint8($2b >> 8)\n"+ 426 "dst.Pix[d+3] = 0xff", 427 ) 428 } 429 } 430 } 431 432 case "outputf": 433 args, _ := splitArgs(suffix) 434 if len(args) != 5 { 435 return "" 436 } 437 ret := "" 438 439 switch d.op { 440 case "Over": 441 switch d.dType { 442 default: 443 log.Fatalf("bad dType %q", d.dType) 444 case "Image": 445 ret = argf(args, ""+ 446 "qr, qg, qb, qa := dst.At($0, $1).RGBA()\n"+ 447 "$3r0 := uint32($2($3r * $4))\n"+ 448 "$3g0 := uint32($2($3g * $4))\n"+ 449 "$3b0 := uint32($2($3b * $4))\n"+ 450 "$3a0 := uint32($2($3a * $4))\n"+ 451 "if dstMask != nil {\n"+ 452 " _, _, _, ma := dstMask.At(dmp.X + $0, dmp.Y + $1).RGBA()\n"+ 453 " $3r0 = $3r0 * ma / 0xffff\n"+ 454 " $3g0 = $3g0 * ma / 0xffff\n"+ 455 " $3b0 = $3b0 * ma / 0xffff\n"+ 456 " $3a0 = $3a0 * ma / 0xffff\n"+ 457 "}\n"+ 458 "$3a1 := 0xffff - $3a0\n"+ 459 "dstColorRGBA64.R = uint16(qr*$3a1/0xffff + $3r0)\n"+ 460 "dstColorRGBA64.G = uint16(qg*$3a1/0xffff + $3g0)\n"+ 461 "dstColorRGBA64.B = uint16(qb*$3a1/0xffff + $3b0)\n"+ 462 "dstColorRGBA64.A = uint16(qa*$3a1/0xffff + $3a0)\n"+ 463 "dst.Set($0, $1, dstColor)", 464 ) 465 case "*image.RGBA": 466 ret = argf(args, ""+ 467 "$3r0 := uint32($2($3r * $4))\n"+ 468 "$3g0 := uint32($2($3g * $4))\n"+ 469 "$3b0 := uint32($2($3b * $4))\n"+ 470 "$3a0 := uint32($2($3a * $4))\n"+ 471 "$3a1 := (0xffff - uint32($3a0)) * 0x101\n"+ 472 "dst.Pix[d+0] = uint8((uint32(dst.Pix[d+0])*$3a1/0xffff + $3r0) >> 8)\n"+ 473 "dst.Pix[d+1] = uint8((uint32(dst.Pix[d+1])*$3a1/0xffff + $3g0) >> 8)\n"+ 474 "dst.Pix[d+2] = uint8((uint32(dst.Pix[d+2])*$3a1/0xffff + $3b0) >> 8)\n"+ 475 "dst.Pix[d+3] = uint8((uint32(dst.Pix[d+3])*$3a1/0xffff + $3a0) >> 8)", 476 ) 477 } 478 479 case "Src": 480 switch d.dType { 481 default: 482 log.Fatalf("bad dType %q", d.dType) 483 case "Image": 484 ret = argf(args, ""+ 485 "if dstMask != nil {\n"+ 486 " qr, qg, qb, qa := dst.At($0, $1).RGBA()\n"+ 487 " _, _, _, ma := dstMask.At(dmp.X + $0, dmp.Y + $1).RGBA()\n"+ 488 " pr := uint32($2($3r * $4)) * ma / 0xffff\n"+ 489 " pg := uint32($2($3g * $4)) * ma / 0xffff\n"+ 490 " pb := uint32($2($3b * $4)) * ma / 0xffff\n"+ 491 " pa := uint32($2($3a * $4)) * ma / 0xffff\n"+ 492 " pa1 := 0xffff - ma\n"+ // Note that this is ma, not pa. 493 " dstColorRGBA64.R = uint16(qr*pa1/0xffff + pr)\n"+ 494 " dstColorRGBA64.G = uint16(qg*pa1/0xffff + pg)\n"+ 495 " dstColorRGBA64.B = uint16(qb*pa1/0xffff + pb)\n"+ 496 " dstColorRGBA64.A = uint16(qa*pa1/0xffff + pa)\n"+ 497 " dst.Set($0, $1, dstColor)\n"+ 498 "} else {\n"+ 499 " dstColorRGBA64.R = $2($3r * $4)\n"+ 500 " dstColorRGBA64.G = $2($3g * $4)\n"+ 501 " dstColorRGBA64.B = $2($3b * $4)\n"+ 502 " dstColorRGBA64.A = $2($3a * $4)\n"+ 503 " dst.Set($0, $1, dstColor)\n"+ 504 "}", 505 ) 506 case "*image.RGBA": 507 switch d.sType { 508 default: 509 ret = argf(args, ""+ 510 "dst.Pix[d+0] = uint8($2($3r * $4) >> 8)\n"+ 511 "dst.Pix[d+1] = uint8($2($3g * $4) >> 8)\n"+ 512 "dst.Pix[d+2] = uint8($2($3b * $4) >> 8)\n"+ 513 "dst.Pix[d+3] = uint8($2($3a * $4) >> 8)", 514 ) 515 case "*image.Gray": 516 ret = argf(args, ""+ 517 "out := uint8($2($3r * $4) >> 8)\n"+ 518 "dst.Pix[d+0] = out\n"+ 519 "dst.Pix[d+1] = out\n"+ 520 "dst.Pix[d+2] = out\n"+ 521 "dst.Pix[d+3] = 0xff", 522 ) 523 case "*image.YCbCr": 524 ret = argf(args, ""+ 525 "dst.Pix[d+0] = uint8($2($3r * $4) >> 8)\n"+ 526 "dst.Pix[d+1] = uint8($2($3g * $4) >> 8)\n"+ 527 "dst.Pix[d+2] = uint8($2($3b * $4) >> 8)\n"+ 528 "dst.Pix[d+3] = 0xff", 529 ) 530 } 531 } 532 } 533 534 return strings.Replace(ret, " * 1)", ")", -1) 535 536 case "srcf", "srcu": 537 lhs, eqOp := splitEq(prefix) 538 if lhs == "" { 539 return "" 540 } 541 args, extra := splitArgs(suffix) 542 if len(args) != 2 { 543 return "" 544 } 545 546 tmp := "" 547 if dollar == "srcf" { 548 tmp = "u" 549 } 550 551 // TODO: there's no need to multiply by 0x101 in the switch below if 552 // the next thing we're going to do is shift right by 8. 553 554 buf := new(bytes.Buffer) 555 switch d.sType { 556 default: 557 log.Fatalf("bad sType %q", d.sType) 558 case "image.Image": 559 fmt.Fprintf(buf, ""+ 560 "%sr%s, %sg%s, %sb%s, %sa%s := src.At(%s, %s).RGBA()\n", 561 lhs, tmp, lhs, tmp, lhs, tmp, lhs, tmp, args[0], args[1], 562 ) 563 if d.dType == "" || d.dType == "Image" { 564 fmt.Fprintf(buf, ""+ 565 "if srcMask != nil {\n"+ 566 " _, _, _, ma := srcMask.At(smp.X+%s, smp.Y+%s).RGBA()\n"+ 567 " %sr%s = %sr%s * ma / 0xffff\n"+ 568 " %sg%s = %sg%s * ma / 0xffff\n"+ 569 " %sb%s = %sb%s * ma / 0xffff\n"+ 570 " %sa%s = %sa%s * ma / 0xffff\n"+ 571 "}\n", 572 args[0], args[1], 573 lhs, tmp, lhs, tmp, 574 lhs, tmp, lhs, tmp, 575 lhs, tmp, lhs, tmp, 576 lhs, tmp, lhs, tmp, 577 ) 578 } 579 case "*image.Gray": 580 fmt.Fprintf(buf, ""+ 581 "%si := %s\n"+ 582 "%sr%s := uint32(src.Pix[%si]) * 0x101\n", 583 lhs, pixOffset("src", args[0], args[1], "", "*src.Stride"), 584 lhs, tmp, lhs, 585 ) 586 case "*image.NRGBA": 587 fmt.Fprintf(buf, ""+ 588 "%si := %s\n"+ 589 "%sa%s := uint32(src.Pix[%si+3]) * 0x101\n"+ 590 "%sr%s := uint32(src.Pix[%si+0]) * %sa%s / 0xff\n"+ 591 "%sg%s := uint32(src.Pix[%si+1]) * %sa%s / 0xff\n"+ 592 "%sb%s := uint32(src.Pix[%si+2]) * %sa%s / 0xff\n", 593 lhs, pixOffset("src", args[0], args[1], "*4", "*src.Stride"), 594 lhs, tmp, lhs, 595 lhs, tmp, lhs, lhs, tmp, 596 lhs, tmp, lhs, lhs, tmp, 597 lhs, tmp, lhs, lhs, tmp, 598 ) 599 case "*image.RGBA": 600 fmt.Fprintf(buf, ""+ 601 "%si := %s\n"+ 602 "%sr%s := uint32(src.Pix[%si+0]) * 0x101\n"+ 603 "%sg%s := uint32(src.Pix[%si+1]) * 0x101\n"+ 604 "%sb%s := uint32(src.Pix[%si+2]) * 0x101\n"+ 605 "%sa%s := uint32(src.Pix[%si+3]) * 0x101\n", 606 lhs, pixOffset("src", args[0], args[1], "*4", "*src.Stride"), 607 lhs, tmp, lhs, 608 lhs, tmp, lhs, 609 lhs, tmp, lhs, 610 lhs, tmp, lhs, 611 ) 612 case "*image.YCbCr": 613 fmt.Fprintf(buf, ""+ 614 "%si := %s\n"+ 615 "%sj := %s\n"+ 616 "%s\n", 617 lhs, pixOffset("src", args[0], args[1], "", "*src.YStride"), 618 lhs, cOffset(args[0], args[1], d.sratio), 619 ycbcrToRGB(lhs, tmp), 620 ) 621 } 622 623 if dollar == "srcf" { 624 switch d.sType { 625 default: 626 fmt.Fprintf(buf, ""+ 627 "%sr %s float64(%sru)%s\n"+ 628 "%sg %s float64(%sgu)%s\n"+ 629 "%sb %s float64(%sbu)%s\n"+ 630 "%sa %s float64(%sau)%s\n", 631 lhs, eqOp, lhs, extra, 632 lhs, eqOp, lhs, extra, 633 lhs, eqOp, lhs, extra, 634 lhs, eqOp, lhs, extra, 635 ) 636 case "*image.Gray": 637 fmt.Fprintf(buf, ""+ 638 "%sr %s float64(%sru)%s\n", 639 lhs, eqOp, lhs, extra, 640 ) 641 case "*image.YCbCr": 642 fmt.Fprintf(buf, ""+ 643 "%sr %s float64(%sru)%s\n"+ 644 "%sg %s float64(%sgu)%s\n"+ 645 "%sb %s float64(%sbu)%s\n", 646 lhs, eqOp, lhs, extra, 647 lhs, eqOp, lhs, extra, 648 lhs, eqOp, lhs, extra, 649 ) 650 } 651 } 652 653 return strings.TrimSpace(buf.String()) 654 655 case "tweakD": 656 if d.dType == "*image.RGBA" { 657 return "d += dst.Stride" 658 } 659 return ";" 660 661 case "tweakDx": 662 if d.dType == "*image.RGBA" { 663 return strings.Replace(prefix, "dx++", "dx, d = dx+1, d+4", 1) 664 } 665 return prefix 666 667 case "tweakDy": 668 if d.dType == "*image.RGBA" { 669 return strings.Replace(prefix, "for dy, s", "for _, s", 1) 670 } 671 return prefix 672 673 case "tweakP": 674 switch d.sType { 675 case "*image.Gray": 676 if strings.HasPrefix(strings.TrimSpace(prefix), "pa * ") { 677 return "1," 678 } 679 return "pr," 680 case "*image.YCbCr": 681 if strings.HasPrefix(strings.TrimSpace(prefix), "pa * ") { 682 return "1," 683 } 684 } 685 return prefix 686 687 case "tweakPr": 688 if d.sType == "*image.Gray" { 689 return "pr *= s.invTotalWeightFFFF" 690 } 691 return ";" 692 693 case "tweakVarP": 694 switch d.sType { 695 case "*image.Gray": 696 return strings.Replace(prefix, "var pr, pg, pb, pa", "var pr", 1) 697 case "*image.YCbCr": 698 return strings.Replace(prefix, "var pr, pg, pb, pa", "var pr, pg, pb", 1) 699 } 700 return prefix 701 } 702 return "" 703} 704 705func expnSwitch(op, dType string, expandBoth bool, template string) string { 706 if op == "" && dType != "anyDType" { 707 lines := []string{"switch op {"} 708 for _, op = range ops { 709 lines = append(lines, 710 fmt.Sprintf("case %s:", op), 711 expnSwitch(op, dType, expandBoth, template), 712 ) 713 } 714 lines = append(lines, "}") 715 return strings.Join(lines, "\n") 716 } 717 718 switchVar := "dst" 719 if dType != "" { 720 switchVar = "src" 721 } 722 lines := []string{fmt.Sprintf("switch %s := %s.(type) {", switchVar, switchVar)} 723 724 fallback, values := "Image", dTypes 725 if dType != "" { 726 fallback, values = "image.Image", sTypesForDType[dType] 727 } 728 for _, v := range values { 729 if dType != "" { 730 // v is the sType. Skip those always-opaque sTypes, where Over is 731 // equivalent to Src. 732 if op == "Over" && alwaysOpaque[v] { 733 continue 734 } 735 } 736 737 if v == fallback { 738 lines = append(lines, "default:") 739 } else { 740 lines = append(lines, fmt.Sprintf("case %s:", v)) 741 } 742 743 if dType != "" { 744 if v == "*image.YCbCr" { 745 lines = append(lines, expnSwitchYCbCr(op, dType, template)) 746 } else { 747 lines = append(lines, expnLine(template, &data{dType: dType, sType: v, op: op})) 748 } 749 } else if !expandBoth { 750 lines = append(lines, expnLine(template, &data{dType: v, op: op})) 751 } else { 752 lines = append(lines, expnSwitch(op, v, false, template)) 753 } 754 } 755 756 lines = append(lines, "}") 757 return strings.Join(lines, "\n") 758} 759 760func expnSwitchYCbCr(op, dType, template string) string { 761 lines := []string{ 762 "switch src.SubsampleRatio {", 763 "default:", 764 expnLine(template, &data{dType: dType, sType: "image.Image", op: op}), 765 } 766 for _, sratio := range subsampleRatios { 767 lines = append(lines, 768 fmt.Sprintf("case image.YCbCrSubsampleRatio%s:", sratio), 769 expnLine(template, &data{dType: dType, sType: "*image.YCbCr", sratio: sratio, op: op}), 770 ) 771 } 772 lines = append(lines, "}") 773 return strings.Join(lines, "\n") 774} 775 776func argf(args []string, s string) string { 777 if len(args) > 9 { 778 panic("too many args") 779 } 780 for i, a := range args { 781 old := fmt.Sprintf("$%d", i) 782 s = strings.Replace(s, old, a, -1) 783 } 784 return s 785} 786 787func pixOffset(m, x, y, xstride, ystride string) string { 788 return fmt.Sprintf("(%s-%s.Rect.Min.Y)%s + (%s-%s.Rect.Min.X)%s", y, m, ystride, x, m, xstride) 789} 790 791func cOffset(x, y, sratio string) string { 792 switch sratio { 793 case "444": 794 return fmt.Sprintf("( %s - src.Rect.Min.Y )*src.CStride + ( %s - src.Rect.Min.X )", y, x) 795 case "422": 796 return fmt.Sprintf("( %s - src.Rect.Min.Y )*src.CStride + ((%s)/2 - src.Rect.Min.X/2)", y, x) 797 case "420": 798 return fmt.Sprintf("((%s)/2 - src.Rect.Min.Y/2)*src.CStride + ((%s)/2 - src.Rect.Min.X/2)", y, x) 799 case "440": 800 return fmt.Sprintf("((%s)/2 - src.Rect.Min.Y/2)*src.CStride + ( %s - src.Rect.Min.X )", y, x) 801 } 802 return fmt.Sprintf("unsupported sratio %q", sratio) 803} 804 805func ycbcrToRGB(lhs, tmp string) string { 806 s := ` 807 // This is an inline version of image/color/ycbcr.go's YCbCr.RGBA method. 808 $yy1 := int(src.Y[$i]) * 0x10101 809 $cb1 := int(src.Cb[$j]) - 128 810 $cr1 := int(src.Cr[$j]) - 128 811 $r@ := ($yy1 + 91881*$cr1) >> 8 812 $g@ := ($yy1 - 22554*$cb1 - 46802*$cr1) >> 8 813 $b@ := ($yy1 + 116130*$cb1) >> 8 814 if $r@ < 0 { 815 $r@ = 0 816 } else if $r@ > 0xffff { 817 $r@ = 0xffff 818 } 819 if $g@ < 0 { 820 $g@ = 0 821 } else if $g@ > 0xffff { 822 $g@ = 0xffff 823 } 824 if $b@ < 0 { 825 $b@ = 0 826 } else if $b@ > 0xffff { 827 $b@ = 0xffff 828 } 829 ` 830 s = strings.Replace(s, "$", lhs, -1) 831 s = strings.Replace(s, "@", tmp, -1) 832 return s 833} 834 835func split(s, sep string) (string, string) { 836 if i := strings.Index(s, sep); i >= 0 { 837 return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):]) 838 } 839 return "", "" 840} 841 842func splitEq(s string) (lhs, eqOp string) { 843 s = strings.TrimSpace(s) 844 if lhs, _ = split(s, ":="); lhs != "" { 845 return lhs, ":=" 846 } 847 if lhs, _ = split(s, "+="); lhs != "" { 848 return lhs, "+=" 849 } 850 return "", "" 851} 852 853func splitArgs(s string) (args []string, extra string) { 854 s = strings.TrimSpace(s) 855 if s == "" || s[0] != '[' { 856 return nil, "" 857 } 858 s = s[1:] 859 860 i := strings.IndexByte(s, ']') 861 if i < 0 { 862 return nil, "" 863 } 864 args, extra = strings.Split(s[:i], ","), s[i+1:] 865 for i := range args { 866 args[i] = strings.TrimSpace(args[i]) 867 } 868 return args, extra 869} 870 871func relName(s string) string { 872 if i := strings.LastIndex(s, "."); i >= 0 { 873 return s[i+1:] 874 } 875 return s 876} 877 878const ( 879 codeRoot = ` 880 func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) { 881 // Try to simplify a Scale to a Copy when DstMask is not specified. 882 // If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow. 883 if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) { 884 Copy(dst, dr.Min, src, sr, op, opts) 885 return 886 } 887 888 var o Options 889 if opts != nil { 890 o = *opts 891 } 892 893 // adr is the affected destination pixels. 894 adr := dst.Bounds().Intersect(dr) 895 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP) 896 if adr.Empty() || sr.Empty() { 897 return 898 } 899 // Make adr relative to dr.Min. 900 adr = adr.Sub(dr.Min) 901 if op == Over && o.SrcMask == nil && opaque(src) { 902 op = Src 903 } 904 905 // sr is the source pixels. If it extends beyond the src bounds, 906 // we cannot use the type-specific fast paths, as they access 907 // the Pix fields directly without bounds checking. 908 // 909 // Similarly, the fast paths assume that the masks are nil. 910 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { 911 switch op { 912 case Over: 913 z.scale_Image_Image_Over(dst, dr, adr, src, sr, &o) 914 case Src: 915 z.scale_Image_Image_Src(dst, dr, adr, src, sr, &o) 916 } 917 } else if _, ok := src.(*image.Uniform); ok { 918 Draw(dst, dr, src, src.Bounds().Min, op) 919 } else { 920 $switch z.scale_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, src, sr, &o) 921 } 922 } 923 924 func (z $receiver) Transform(dst Image, s2d f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options) { 925 // Try to simplify a Transform to a Copy. 926 if s2d[0] == 1 && s2d[1] == 0 && s2d[3] == 0 && s2d[4] == 1 { 927 dx := int(s2d[2]) 928 dy := int(s2d[5]) 929 if float64(dx) == s2d[2] && float64(dy) == s2d[5] { 930 Copy(dst, image.Point{X: sr.Min.X + dx, Y: sr.Min.X + dy}, src, sr, op, opts) 931 return 932 } 933 } 934 935 var o Options 936 if opts != nil { 937 o = *opts 938 } 939 940 dr := transformRect(&s2d, &sr) 941 // adr is the affected destination pixels. 942 adr := dst.Bounds().Intersect(dr) 943 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP) 944 if adr.Empty() || sr.Empty() { 945 return 946 } 947 if op == Over && o.SrcMask == nil && opaque(src) { 948 op = Src 949 } 950 951 d2s := invert(&s2d) 952 // bias is a translation of the mapping from dst coordinates to src 953 // coordinates such that the latter temporarily have non-negative X 954 // and Y coordinates. This allows us to write int(f) instead of 955 // int(math.Floor(f)), since "round to zero" and "round down" are 956 // equivalent when f >= 0, but the former is much cheaper. The X-- 957 // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" 958 // adjustment. 959 bias := transformRect(&d2s, &adr).Min 960 bias.X-- 961 bias.Y-- 962 d2s[2] -= float64(bias.X) 963 d2s[5] -= float64(bias.Y) 964 // Make adr relative to dr.Min. 965 adr = adr.Sub(dr.Min) 966 // sr is the source pixels. If it extends beyond the src bounds, 967 // we cannot use the type-specific fast paths, as they access 968 // the Pix fields directly without bounds checking. 969 // 970 // Similarly, the fast paths assume that the masks are nil. 971 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { 972 switch op { 973 case Over: 974 z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, &o) 975 case Src: 976 z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, &o) 977 } 978 } else if u, ok := src.(*image.Uniform); ok { 979 transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) 980 } else { 981 $switch z.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias, &o) 982 } 983 } 984 ` 985 986 codeNNScaleLeaf = ` 987 func (nnInterpolator) scale_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle, opts *Options) { 988 dw2 := uint64(dr.Dx()) * 2 989 dh2 := uint64(dr.Dy()) * 2 990 sw := uint64(sr.Dx()) 991 sh := uint64(sr.Dy()) 992 $preOuter 993 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ { 994 sy := (2*uint64(dy) + 1) * sh / dh2 995 $preInner 996 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx 997 sx := (2*uint64(dx) + 1) * sw / dw2 998 p := $srcu[sr.Min.X + int(sx), sr.Min.Y + int(sy)] 999 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p] 1000 } 1001 } 1002 } 1003 ` 1004 1005 codeNNTransformLeaf = ` 1006 func (nnInterpolator) transform_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, d2s *f64.Aff3, src $sType, sr image.Rectangle, bias image.Point, opts *Options) { 1007 $preOuter 1008 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ { 1009 dyf := float64(dr.Min.Y + int(dy)) + 0.5 1010 $preInner 1011 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx 1012 dxf := float64(dr.Min.X + int(dx)) + 0.5 1013 sx0 := int(d2s[0]*dxf + d2s[1]*dyf + d2s[2]) + bias.X 1014 sy0 := int(d2s[3]*dxf + d2s[4]*dyf + d2s[5]) + bias.Y 1015 if !(image.Point{sx0, sy0}).In(sr) { 1016 continue 1017 } 1018 p := $srcu[sx0, sy0] 1019 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p] 1020 } 1021 } 1022 } 1023 ` 1024 1025 codeABLScaleLeaf = ` 1026 func (ablInterpolator) scale_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle, opts *Options) { 1027 sw := int32(sr.Dx()) 1028 sh := int32(sr.Dy()) 1029 yscale := float64(sh) / float64(dr.Dy()) 1030 xscale := float64(sw) / float64(dr.Dx()) 1031 swMinus1, shMinus1 := sw - 1, sh - 1 1032 $preOuter 1033 1034 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ { 1035 sy := (float64(dy)+0.5)*yscale - 0.5 1036 // If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if 1037 // we say int32(sy) instead of int32(math.Floor(sy)). Similarly for 1038 // sx, below. 1039 sy0 := int32(sy) 1040 yFrac0 := sy - float64(sy0) 1041 yFrac1 := 1 - yFrac0 1042 sy1 := sy0 + 1 1043 if sy < 0 { 1044 sy0, sy1 = 0, 0 1045 yFrac0, yFrac1 = 0, 1 1046 } else if sy1 > shMinus1 { 1047 sy0, sy1 = shMinus1, shMinus1 1048 yFrac0, yFrac1 = 1, 0 1049 } 1050 $preInner 1051 1052 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx 1053 sx := (float64(dx)+0.5)*xscale - 0.5 1054 sx0 := int32(sx) 1055 xFrac0 := sx - float64(sx0) 1056 xFrac1 := 1 - xFrac0 1057 sx1 := sx0 + 1 1058 if sx < 0 { 1059 sx0, sx1 = 0, 0 1060 xFrac0, xFrac1 = 0, 1 1061 } else if sx1 > swMinus1 { 1062 sx0, sx1 = swMinus1, swMinus1 1063 xFrac0, xFrac1 = 1, 0 1064 } 1065 1066 s00 := $srcf[sr.Min.X + int(sx0), sr.Min.Y + int(sy0)] 1067 s10 := $srcf[sr.Min.X + int(sx1), sr.Min.Y + int(sy0)] 1068 $blend[xFrac1, s00, xFrac0, s10] 1069 s01 := $srcf[sr.Min.X + int(sx0), sr.Min.Y + int(sy1)] 1070 s11 := $srcf[sr.Min.X + int(sx1), sr.Min.Y + int(sy1)] 1071 $blend[xFrac1, s01, xFrac0, s11] 1072 $blend[yFrac1, s10, yFrac0, s11] 1073 $convFtou[p, s11] 1074 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p] 1075 } 1076 } 1077 } 1078 ` 1079 1080 codeABLTransformLeaf = ` 1081 func (ablInterpolator) transform_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, d2s *f64.Aff3, src $sType, sr image.Rectangle, bias image.Point, opts *Options) { 1082 $preOuter 1083 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ { 1084 dyf := float64(dr.Min.Y + int(dy)) + 0.5 1085 $preInner 1086 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx 1087 dxf := float64(dr.Min.X + int(dx)) + 0.5 1088 sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2] 1089 sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5] 1090 if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) { 1091 continue 1092 } 1093 1094 sx -= 0.5 1095 sx0 := int(sx) 1096 xFrac0 := sx - float64(sx0) 1097 xFrac1 := 1 - xFrac0 1098 sx0 += bias.X 1099 sx1 := sx0 + 1 1100 if sx0 < sr.Min.X { 1101 sx0, sx1 = sr.Min.X, sr.Min.X 1102 xFrac0, xFrac1 = 0, 1 1103 } else if sx1 >= sr.Max.X { 1104 sx0, sx1 = sr.Max.X-1, sr.Max.X-1 1105 xFrac0, xFrac1 = 1, 0 1106 } 1107 1108 sy -= 0.5 1109 sy0 := int(sy) 1110 yFrac0 := sy - float64(sy0) 1111 yFrac1 := 1 - yFrac0 1112 sy0 += bias.Y 1113 sy1 := sy0 + 1 1114 if sy0 < sr.Min.Y { 1115 sy0, sy1 = sr.Min.Y, sr.Min.Y 1116 yFrac0, yFrac1 = 0, 1 1117 } else if sy1 >= sr.Max.Y { 1118 sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1 1119 yFrac0, yFrac1 = 1, 0 1120 } 1121 1122 s00 := $srcf[sx0, sy0] 1123 s10 := $srcf[sx1, sy0] 1124 $blend[xFrac1, s00, xFrac0, s10] 1125 s01 := $srcf[sx0, sy1] 1126 s11 := $srcf[sx1, sy1] 1127 $blend[xFrac1, s01, xFrac0, s11] 1128 $blend[yFrac1, s10, yFrac0, s11] 1129 $convFtou[p, s11] 1130 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p] 1131 } 1132 } 1133 } 1134 ` 1135 1136 codeKernelRoot = ` 1137 func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) { 1138 if z.dw != int32(dr.Dx()) || z.dh != int32(dr.Dy()) || z.sw != int32(sr.Dx()) || z.sh != int32(sr.Dy()) { 1139 z.kernel.Scale(dst, dr, src, sr, op, opts) 1140 return 1141 } 1142 1143 var o Options 1144 if opts != nil { 1145 o = *opts 1146 } 1147 1148 // adr is the affected destination pixels. 1149 adr := dst.Bounds().Intersect(dr) 1150 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP) 1151 if adr.Empty() || sr.Empty() { 1152 return 1153 } 1154 // Make adr relative to dr.Min. 1155 adr = adr.Sub(dr.Min) 1156 if op == Over && o.SrcMask == nil && opaque(src) { 1157 op = Src 1158 } 1159 1160 if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) { 1161 Draw(dst, dr, src, src.Bounds().Min, op) 1162 return 1163 } 1164 1165 // Create a temporary buffer: 1166 // scaleX distributes the source image's columns over the temporary image. 1167 // scaleY distributes the temporary image's rows over the destination image. 1168 var tmp [][4]float64 1169 if z.pool.New != nil { 1170 tmpp := z.pool.Get().(*[][4]float64) 1171 defer z.pool.Put(tmpp) 1172 tmp = *tmpp 1173 } else { 1174 tmp = z.makeTmpBuf() 1175 } 1176 1177 // sr is the source pixels. If it extends beyond the src bounds, 1178 // we cannot use the type-specific fast paths, as they access 1179 // the Pix fields directly without bounds checking. 1180 // 1181 // Similarly, the fast paths assume that the masks are nil. 1182 if o.SrcMask != nil || !sr.In(src.Bounds()) { 1183 z.scaleX_Image(tmp, src, sr, &o) 1184 } else { 1185 $switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr, &o) 1186 } 1187 1188 if o.DstMask != nil { 1189 switch op { 1190 case Over: 1191 z.scaleY_Image_Over(dst, dr, adr, tmp, &o) 1192 case Src: 1193 z.scaleY_Image_Src(dst, dr, adr, tmp, &o) 1194 } 1195 } else { 1196 $switchD z.scaleY_$dTypeRN_$op(dst, dr, adr, tmp, &o) 1197 } 1198 } 1199 1200 func (q *Kernel) Transform(dst Image, s2d f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options) { 1201 var o Options 1202 if opts != nil { 1203 o = *opts 1204 } 1205 1206 dr := transformRect(&s2d, &sr) 1207 // adr is the affected destination pixels. 1208 adr := dst.Bounds().Intersect(dr) 1209 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP) 1210 if adr.Empty() || sr.Empty() { 1211 return 1212 } 1213 if op == Over && o.SrcMask == nil && opaque(src) { 1214 op = Src 1215 } 1216 d2s := invert(&s2d) 1217 // bias is a translation of the mapping from dst coordinates to src 1218 // coordinates such that the latter temporarily have non-negative X 1219 // and Y coordinates. This allows us to write int(f) instead of 1220 // int(math.Floor(f)), since "round to zero" and "round down" are 1221 // equivalent when f >= 0, but the former is much cheaper. The X-- 1222 // and Y-- are because the TransformLeaf methods have a "sx -= 0.5" 1223 // adjustment. 1224 bias := transformRect(&d2s, &adr).Min 1225 bias.X-- 1226 bias.Y-- 1227 d2s[2] -= float64(bias.X) 1228 d2s[5] -= float64(bias.Y) 1229 // Make adr relative to dr.Min. 1230 adr = adr.Sub(dr.Min) 1231 1232 if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) { 1233 transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op) 1234 return 1235 } 1236 1237 xscale := abs(d2s[0]) 1238 if s := abs(d2s[1]); xscale < s { 1239 xscale = s 1240 } 1241 yscale := abs(d2s[3]) 1242 if s := abs(d2s[4]); yscale < s { 1243 yscale = s 1244 } 1245 1246 // sr is the source pixels. If it extends beyond the src bounds, 1247 // we cannot use the type-specific fast paths, as they access 1248 // the Pix fields directly without bounds checking. 1249 // 1250 // Similarly, the fast paths assume that the masks are nil. 1251 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) { 1252 switch op { 1253 case Over: 1254 q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o) 1255 case Src: 1256 q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o) 1257 } 1258 } else { 1259 $switch q.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o) 1260 } 1261 } 1262 ` 1263 1264 codeKernelScaleLeafX = ` 1265 func (z *kernelScaler) scaleX_$sTypeRN$sratio(tmp [][4]float64, src $sType, sr image.Rectangle, opts *Options) { 1266 t := 0 1267 $preKernelOuter 1268 for y := int32(0); y < z.sh; y++ { 1269 for _, s := range z.horizontal.sources { 1270 var pr, pg, pb, pa float64 $tweakVarP 1271 for _, c := range z.horizontal.contribs[s.i:s.j] { 1272 p += $srcf[sr.Min.X + int(c.coord), sr.Min.Y + int(y)] * c.weight 1273 } 1274 $tweakPr 1275 tmp[t] = [4]float64{ 1276 pr * s.invTotalWeightFFFF, $tweakP 1277 pg * s.invTotalWeightFFFF, $tweakP 1278 pb * s.invTotalWeightFFFF, $tweakP 1279 pa * s.invTotalWeightFFFF, $tweakP 1280 } 1281 t++ 1282 } 1283 } 1284 } 1285 ` 1286 1287 codeKernelScaleLeafY = ` 1288 func (z *kernelScaler) scaleY_$dTypeRN_$op(dst $dType, dr, adr image.Rectangle, tmp [][4]float64, opts *Options) { 1289 $preOuter 1290 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { 1291 $preKernelInner 1292 for dy, s := range z.vertical.sources[adr.Min.Y:adr.Max.Y] { $tweakDy 1293 var pr, pg, pb, pa float64 1294 for _, c := range z.vertical.contribs[s.i:s.j] { 1295 p := &tmp[c.coord*z.dw+dx] 1296 pr += p[0] * c.weight 1297 pg += p[1] * c.weight 1298 pb += p[2] * c.weight 1299 pa += p[3] * c.weight 1300 } 1301 $clampToAlpha 1302 $outputf[dr.Min.X + int(dx), dr.Min.Y + int(adr.Min.Y + dy), ftou, p, s.invTotalWeight] 1303 $tweakD 1304 } 1305 } 1306 } 1307 ` 1308 1309 codeKernelTransformLeaf = ` 1310 func (q *Kernel) transform_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, d2s *f64.Aff3, src $sType, sr image.Rectangle, bias image.Point, xscale, yscale float64, opts *Options) { 1311 // When shrinking, broaden the effective kernel support so that we still 1312 // visit every source pixel. 1313 xHalfWidth, xKernelArgScale := q.Support, 1.0 1314 if xscale > 1 { 1315 xHalfWidth *= xscale 1316 xKernelArgScale = 1 / xscale 1317 } 1318 yHalfWidth, yKernelArgScale := q.Support, 1.0 1319 if yscale > 1 { 1320 yHalfWidth *= yscale 1321 yKernelArgScale = 1 / yscale 1322 } 1323 1324 xWeights := make([]float64, 1 + 2*int(math.Ceil(xHalfWidth))) 1325 yWeights := make([]float64, 1 + 2*int(math.Ceil(yHalfWidth))) 1326 1327 $preOuter 1328 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ { 1329 dyf := float64(dr.Min.Y + int(dy)) + 0.5 1330 $preInner 1331 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx 1332 dxf := float64(dr.Min.X + int(dx)) + 0.5 1333 sx := d2s[0]*dxf + d2s[1]*dyf + d2s[2] 1334 sy := d2s[3]*dxf + d2s[4]*dyf + d2s[5] 1335 if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) { 1336 continue 1337 } 1338 1339 // TODO: adjust the bias so that we can use int(f) instead 1340 // of math.Floor(f) and math.Ceil(f). 1341 sx += float64(bias.X) 1342 sx -= 0.5 1343 ix := int(math.Floor(sx - xHalfWidth)) 1344 if ix < sr.Min.X { 1345 ix = sr.Min.X 1346 } 1347 jx := int(math.Ceil(sx + xHalfWidth)) 1348 if jx > sr.Max.X { 1349 jx = sr.Max.X 1350 } 1351 1352 totalXWeight := 0.0 1353 for kx := ix; kx < jx; kx++ { 1354 xWeight := 0.0 1355 if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support { 1356 xWeight = q.At(t) 1357 } 1358 xWeights[kx - ix] = xWeight 1359 totalXWeight += xWeight 1360 } 1361 for x := range xWeights[:jx-ix] { 1362 xWeights[x] /= totalXWeight 1363 } 1364 1365 sy += float64(bias.Y) 1366 sy -= 0.5 1367 iy := int(math.Floor(sy - yHalfWidth)) 1368 if iy < sr.Min.Y { 1369 iy = sr.Min.Y 1370 } 1371 jy := int(math.Ceil(sy + yHalfWidth)) 1372 if jy > sr.Max.Y { 1373 jy = sr.Max.Y 1374 } 1375 1376 totalYWeight := 0.0 1377 for ky := iy; ky < jy; ky++ { 1378 yWeight := 0.0 1379 if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support { 1380 yWeight = q.At(t) 1381 } 1382 yWeights[ky - iy] = yWeight 1383 totalYWeight += yWeight 1384 } 1385 for y := range yWeights[:jy-iy] { 1386 yWeights[y] /= totalYWeight 1387 } 1388 1389 var pr, pg, pb, pa float64 $tweakVarP 1390 for ky := iy; ky < jy; ky++ { 1391 if yWeight := yWeights[ky - iy]; yWeight != 0 { 1392 for kx := ix; kx < jx; kx++ { 1393 if w := xWeights[kx - ix] * yWeight; w != 0 { 1394 p += $srcf[kx, ky] * w 1395 } 1396 } 1397 } 1398 } 1399 $clampToAlpha 1400 $outputf[dr.Min.X + int(dx), dr.Min.Y + int(dy), fffftou, p, 1] 1401 } 1402 } 1403 } 1404 ` 1405) 1406