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