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