git clone https://git.lucas.co/go_mono.git
draw/gen.go (45.7K)
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
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 // dsTypes are the (dst image type, src image type) pairs to generate
53 // scale_DType_SType implementations for. The last element in the slice
54 // should be the fallback pair ("Image", "image.Image").
55 //
56 // TODO: add *image.CMYK src type after Go 1.5 is released.
57 // An *image.CMYK is also alwaysOpaque.
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 // 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
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 // 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.
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 = float64($0*$1r) + float64($2*$3r)\n"+
287 "$3g = float64($0*$1g) + float64($2*$3g)\n"+
288 "$3b = float64($0*$1b) + float64($2*$3b)\n"+
289 "$3a = float64($0*$1a) + float64($2*$3a)",
290 )
291 case "*image.Gray":
292 return argf(args, ""+
293 "$3r = float64($0*$1r) + float64($2*$3r)",
294 )
295 case "*image.YCbCr":
296 return argf(args, ""+
297 "$3r = float64($0*$1r) + float64($2*$3r)\n"+
298 "$3g = float64($0*$1g) + float64($2*$3g)\n"+
299 "$3b = float64($0*$1b) + float64($2*$3b)",
300 )
301 }
302
303 case "clampToAlpha":
304 if alwaysOpaque[d.sType] {
305 return ";"
306 }
307 // Go uses alpha-premultiplied color. The naive computation can lead to
308 // invalid colors, e.g. red > alpha, when some weights are negative.
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"+ // Note that this is ma, not $2a.
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"+ // Note that this is ma, not $2a.
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"+ // Note that this is ma, not $2a.
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"+ // Note that this is ma, not pa.
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"+ // Note that this is ma, not pa.
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 // TODO: there's no need to multiply by 0x101 in the switch below if
708 // the next thing we're going to do is shift right by 8.
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 avoidFMA0, avoidFMA1 := "", "" // FMA is Fused Multiply Add.
787 if extra != "" {
788 avoidFMA0, avoidFMA1 = "float64(", ")"
789 }
790
791 switch d.sType {
792 default:
793 fmt.Fprintf(buf, ""+
794 "%[1]sr %[2]s %[4]sfloat64(%[1]sru)%[3]s%[5]s\n"+
795 "%[1]sg %[2]s %[4]sfloat64(%[1]sgu)%[3]s%[5]s\n"+
796 "%[1]sb %[2]s %[4]sfloat64(%[1]sbu)%[3]s%[5]s\n"+
797 "%[1]sa %[2]s %[4]sfloat64(%[1]sau)%[3]s%[5]s\n",
798 lhs, eqOp, extra, avoidFMA0, avoidFMA1,
799 )
800 case "*image.Gray":
801 fmt.Fprintf(buf, ""+
802 "%[1]sr %[2]s %[4]sfloat64(%[1]sru)%[3]s%[5]s\n",
803 lhs, eqOp, extra, avoidFMA0, avoidFMA1,
804 )
805 case "*image.YCbCr":
806 fmt.Fprintf(buf, ""+
807 "%[1]sr %[2]s %[4]sfloat64(%[1]sru)%[3]s%[5]s\n"+
808 "%[1]sg %[2]s %[4]sfloat64(%[1]sgu)%[3]s%[5]s\n"+
809 "%[1]sb %[2]s %[4]sfloat64(%[1]sbu)%[3]s%[5]s\n",
810 lhs, eqOp, extra, avoidFMA0, avoidFMA1,
811 )
812 case "image.RGBA64Image":
813 fmt.Fprintf(buf, ""+
814 "%[1]sr %[2]s %[4]sfloat64(%[1]su.R)%[3]s%[5]s\n"+
815 "%[1]sg %[2]s %[4]sfloat64(%[1]su.G)%[3]s%[5]s\n"+
816 "%[1]sb %[2]s %[4]sfloat64(%[1]su.B)%[3]s%[5]s\n"+
817 "%[1]sa %[2]s %[4]sfloat64(%[1]su.A)%[3]s%[5]s\n",
818 lhs, eqOp, extra, avoidFMA0, avoidFMA1,
819 )
820 }
821 }
822
823 return strings.TrimSpace(buf.String())
824
825 case "tweakD":
826 if d.dType == "*image.RGBA" {
827 return "d += dst.Stride"
828 }
829 return ";"
830
831 case "tweakDx":
832 if d.dType == "*image.RGBA" {
833 return strings.Replace(prefix, "dx++", "dx, d = dx+1, d+4", 1)
834 }
835 return prefix
836
837 case "tweakDy":
838 if d.dType == "*image.RGBA" {
839 return strings.Replace(prefix, "for dy, s", "for _, s", 1)
840 }
841 return prefix
842
843 case "tweakP":
844 switch d.sType {
845 case "*image.Gray":
846 if strings.HasPrefix(strings.TrimSpace(prefix), "pa * ") {
847 return "1,"
848 }
849 return "pr,"
850 case "*image.YCbCr":
851 if strings.HasPrefix(strings.TrimSpace(prefix), "pa * ") {
852 return "1,"
853 }
854 }
855 return prefix
856
857 case "tweakPr":
858 if d.sType == "*image.Gray" {
859 return "pr *= s.invTotalWeightFFFF"
860 }
861 return ";"
862
863 case "tweakVarP":
864 switch d.sType {
865 case "*image.Gray":
866 return strings.Replace(prefix, "var pr, pg, pb, pa", "var pr", 1)
867 case "*image.YCbCr":
868 return strings.Replace(prefix, "var pr, pg, pb, pa", "var pr, pg, pb", 1)
869 }
870 return prefix
871 }
872 return ""
873 }
874
875 func expnSwitch(op, dType string, expandBoth bool, template string) string {
876 if op == "" && dType != "anyDType" {
877 lines := []string{"switch op {"}
878 for _, op = range ops {
879 lines = append(lines,
880 fmt.Sprintf("case %s:", op),
881 expnSwitch(op, dType, expandBoth, template),
882 )
883 }
884 lines = append(lines, "}")
885 return strings.Join(lines, "\n")
886 }
887
888 switchVar := "dst"
889 if dType != "" {
890 switchVar = "src"
891 }
892 lines := []string{fmt.Sprintf("switch %s := %s.(type) {", switchVar, switchVar)}
893
894 fallback, values := "Image", dTypes
895 if dType != "" {
896 fallback, values = "image.Image", sTypesForDType[dType]
897 }
898 for _, v := range values {
899 if dType != "" {
900 // v is the sType. Skip those always-opaque sTypes, where Over is
901 // equivalent to Src.
902 if op == "Over" && alwaysOpaque[v] {
903 continue
904 }
905 }
906
907 if v == fallback {
908 lines = append(lines, "default:")
909 } else {
910 lines = append(lines, fmt.Sprintf("case %s:", v))
911 }
912
913 if dType != "" {
914 if v == "*image.YCbCr" {
915 lines = append(lines, expnSwitchYCbCr(op, dType, template))
916 } else {
917 lines = append(lines, expnLine(template, &data{dType: dType, sType: v, op: op}))
918 }
919 } else if !expandBoth {
920 lines = append(lines, expnLine(template, &data{dType: v, op: op}))
921 } else {
922 lines = append(lines, expnSwitch(op, v, false, template))
923 }
924 }
925
926 lines = append(lines, "}")
927 return strings.Join(lines, "\n")
928 }
929
930 func expnSwitchYCbCr(op, dType, template string) string {
931 lines := []string{
932 "switch src.SubsampleRatio {",
933 "default:",
934 expnLine(template, &data{dType: dType, sType: "image.Image", op: op}),
935 }
936 for _, sratio := range subsampleRatios {
937 lines = append(lines,
938 fmt.Sprintf("case image.YCbCrSubsampleRatio%s:", sratio),
939 expnLine(template, &data{dType: dType, sType: "*image.YCbCr", sratio: sratio, op: op}),
940 )
941 }
942 lines = append(lines, "}")
943 return strings.Join(lines, "\n")
944 }
945
946 func argf(args []string, s string) string {
947 if len(args) > 9 {
948 panic("too many args")
949 }
950 for i, a := range args {
951 old := fmt.Sprintf("$%d", i)
952 s = strings.Replace(s, old, a, -1)
953 }
954 return s
955 }
956
957 func pixOffset(m, x, y, xstride, ystride string) string {
958 return fmt.Sprintf("(%s-%s.Rect.Min.Y)%s + (%s-%s.Rect.Min.X)%s", y, m, ystride, x, m, xstride)
959 }
960
961 func cOffset(x, y, sratio string) string {
962 switch sratio {
963 case "444":
964 return fmt.Sprintf("( %s - src.Rect.Min.Y )*src.CStride + ( %s - src.Rect.Min.X )", y, x)
965 case "422":
966 return fmt.Sprintf("( %s - src.Rect.Min.Y )*src.CStride + ((%s)/2 - src.Rect.Min.X/2)", y, x)
967 case "420":
968 return fmt.Sprintf("((%s)/2 - src.Rect.Min.Y/2)*src.CStride + ((%s)/2 - src.Rect.Min.X/2)", y, x)
969 case "440":
970 return fmt.Sprintf("((%s)/2 - src.Rect.Min.Y/2)*src.CStride + ( %s - src.Rect.Min.X )", y, x)
971 }
972 return fmt.Sprintf("unsupported sratio %q", sratio)
973 }
974
975 func ycbcrToRGB(lhs, tmp string) string {
976 s := `
977 // This is an inline version of image/color/ycbcr.go's YCbCr.RGBA method.
978 $yy1 := int(src.Y[$i]) * 0x10101
979 $cb1 := int(src.Cb[$j]) - 128
980 $cr1 := int(src.Cr[$j]) - 128
981 $r@ := ($yy1 + 91881*$cr1) >> 8
982 $g@ := ($yy1 - 22554*$cb1 - 46802*$cr1) >> 8
983 $b@ := ($yy1 + 116130*$cb1) >> 8
984 if $r@ < 0 {
985 $r@ = 0
986 } else if $r@ > 0xffff {
987 $r@ = 0xffff
988 }
989 if $g@ < 0 {
990 $g@ = 0
991 } else if $g@ > 0xffff {
992 $g@ = 0xffff
993 }
994 if $b@ < 0 {
995 $b@ = 0
996 } else if $b@ > 0xffff {
997 $b@ = 0xffff
998 }
999 `
1000 s = strings.Replace(s, "$", lhs, -1)
1001 s = strings.Replace(s, "@", tmp, -1)
1002 return s
1003 }
1004
1005 func split(s, sep string) (string, string) {
1006 if i := strings.Index(s, sep); i >= 0 {
1007 return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
1008 }
1009 return "", ""
1010 }
1011
1012 func splitEq(s string) (lhs, eqOp string) {
1013 s = strings.TrimSpace(s)
1014 if lhs, _ = split(s, ":="); lhs != "" {
1015 return lhs, ":="
1016 }
1017 if lhs, _ = split(s, "+="); lhs != "" {
1018 return lhs, "+="
1019 }
1020 return "", ""
1021 }
1022
1023 func splitArgs(s string) (args []string, extra string) {
1024 s = strings.TrimSpace(s)
1025 if s == "" || s[0] != '[' {
1026 return nil, ""
1027 }
1028 s = s[1:]
1029
1030 i := strings.IndexByte(s, ']')
1031 if i < 0 {
1032 return nil, ""
1033 }
1034 args, extra = strings.Split(s[:i], ","), s[i+1:]
1035 for i := range args {
1036 args[i] = strings.TrimSpace(args[i])
1037 }
1038 return args, extra
1039 }
1040
1041 func relName(s string) string {
1042 if i := strings.LastIndex(s, "."); i >= 0 {
1043 return s[i+1:]
1044 }
1045 return s
1046 }
1047
1048 const (
1049 codeRoot = `
1050 func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
1051 // Try to simplify a Scale to a Copy when DstMask is not specified.
1052 // If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
1053 if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
1054 Copy(dst, dr.Min, src, sr, op, opts)
1055 return
1056 }
1057
1058 var o Options
1059 if opts != nil {
1060 o = *opts
1061 }
1062
1063 // adr is the affected destination pixels.
1064 adr := dst.Bounds().Intersect(dr)
1065 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
1066 if adr.Empty() || sr.Empty() {
1067 return
1068 }
1069 // Make adr relative to dr.Min.
1070 adr = adr.Sub(dr.Min)
1071 if op == Over && o.SrcMask == nil && opaque(src) {
1072 op = Src
1073 }
1074
1075 // sr is the source pixels. If it extends beyond the src bounds,
1076 // we cannot use the type-specific fast paths, as they access
1077 // the Pix fields directly without bounds checking.
1078 //
1079 // Similarly, the fast paths assume that the masks are nil.
1080 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
1081 switch op {
1082 case Over:
1083 z.scale_Image_Image_Over(dst, dr, adr, src, sr, &o)
1084 case Src:
1085 z.scale_Image_Image_Src(dst, dr, adr, src, sr, &o)
1086 }
1087 } else if _, ok := src.(*image.Uniform); ok {
1088 Draw(dst, dr, src, src.Bounds().Min, op)
1089 } else {
1090 $switch z.scale_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, src, sr, &o)
1091 }
1092 }
1093
1094 func (z $receiver) Transform(dst Image, s2d f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options) {
1095 // Try to simplify a Transform to a Copy.
1096 if s2d[0] == 1 && s2d[1] == 0 && s2d[3] == 0 && s2d[4] == 1 {
1097 dx := int(s2d[2])
1098 dy := int(s2d[5])
1099 if float64(dx) == s2d[2] && float64(dy) == s2d[5] {
1100 Copy(dst, image.Point{X: sr.Min.X + dx, Y: sr.Min.X + dy}, src, sr, op, opts)
1101 return
1102 }
1103 }
1104
1105 var o Options
1106 if opts != nil {
1107 o = *opts
1108 }
1109
1110 dr := transformRect(&s2d, &sr)
1111 // adr is the affected destination pixels.
1112 adr := dst.Bounds().Intersect(dr)
1113 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
1114 if adr.Empty() || sr.Empty() {
1115 return
1116 }
1117 if op == Over && o.SrcMask == nil && opaque(src) {
1118 op = Src
1119 }
1120
1121 d2s := invert(&s2d)
1122 // bias is a translation of the mapping from dst coordinates to src
1123 // coordinates such that the latter temporarily have non-negative X
1124 // and Y coordinates. This allows us to write int(f) instead of
1125 // int(math.Floor(f)), since "round to zero" and "round down" are
1126 // equivalent when f >= 0, but the former is much cheaper. The X--
1127 // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
1128 // adjustment.
1129 bias := transformRect(&d2s, &adr).Min
1130 bias.X--
1131 bias.Y--
1132 d2s[2] -= float64(bias.X)
1133 d2s[5] -= float64(bias.Y)
1134 // Make adr relative to dr.Min.
1135 adr = adr.Sub(dr.Min)
1136 // sr is the source pixels. If it extends beyond the src bounds,
1137 // we cannot use the type-specific fast paths, as they access
1138 // the Pix fields directly without bounds checking.
1139 //
1140 // Similarly, the fast paths assume that the masks are nil.
1141 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
1142 switch op {
1143 case Over:
1144 z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, &o)
1145 case Src:
1146 z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, &o)
1147 }
1148 } else if u, ok := src.(*image.Uniform); ok {
1149 transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
1150 } else {
1151 $switch z.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias, &o)
1152 }
1153 }
1154 `
1155
1156 codeNNScaleLeaf = `
1157 func (nnInterpolator) scale_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle, opts *Options) {
1158 dw2 := uint64(dr.Dx()) * 2
1159 dh2 := uint64(dr.Dy()) * 2
1160 sw := uint64(sr.Dx())
1161 sh := uint64(sr.Dy())
1162 $preOuter
1163 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
1164 sy := (2*uint64(dy) + 1) * sh / dh2
1165 $preInner
1166 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx
1167 sx := (2*uint64(dx) + 1) * sw / dw2
1168 p := $srcu[sr.Min.X + int(sx), sr.Min.Y + int(sy)]
1169 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p]
1170 }
1171 }
1172 }
1173 `
1174
1175 codeNNTransformLeaf = `
1176 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) {
1177 $preOuter
1178 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
1179 dyf := float64(dr.Min.Y + int(dy)) + 0.5
1180 $preInner
1181 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx
1182 dxf := float64(dr.Min.X + int(dx)) + 0.5
1183 sx0 := int(float64(d2s[0]*dxf) + float64(d2s[1]*dyf) + d2s[2]) + bias.X
1184 sy0 := int(float64(d2s[3]*dxf) + float64(d2s[4]*dyf) + d2s[5]) + bias.Y
1185 if !(image.Point{sx0, sy0}).In(sr) {
1186 continue
1187 }
1188 p := $srcu[sx0, sy0]
1189 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p]
1190 }
1191 }
1192 }
1193 `
1194
1195 codeABLScaleLeaf = `
1196 func (ablInterpolator) scale_$dTypeRN_$sTypeRN$sratio_$op(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle, opts *Options) {
1197 sw := int32(sr.Dx())
1198 sh := int32(sr.Dy())
1199 yscale := float64(sh) / float64(dr.Dy())
1200 xscale := float64(sw) / float64(dr.Dx())
1201 swMinus1, shMinus1 := sw - 1, sh - 1
1202 $preOuter
1203
1204 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
1205 sy := float64((float64(dy)+0.5)*yscale) - 0.5
1206 // If sy < 0, we will clamp sy0 to 0 anyway, so it doesn't matter if
1207 // we say int32(sy) instead of int32(math.Floor(sy)). Similarly for
1208 // sx, below.
1209 sy0 := int32(sy)
1210 yFrac0 := sy - float64(sy0)
1211 yFrac1 := 1 - yFrac0
1212 sy1 := sy0 + 1
1213 if sy < 0 {
1214 sy0, sy1 = 0, 0
1215 yFrac0, yFrac1 = 0, 1
1216 } else if sy1 > shMinus1 {
1217 sy0, sy1 = shMinus1, shMinus1
1218 yFrac0, yFrac1 = 1, 0
1219 }
1220 $preInner
1221
1222 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx
1223 sx := float64((float64(dx)+0.5)*xscale) - 0.5
1224 sx0 := int32(sx)
1225 xFrac0 := sx - float64(sx0)
1226 xFrac1 := 1 - xFrac0
1227 sx1 := sx0 + 1
1228 if sx < 0 {
1229 sx0, sx1 = 0, 0
1230 xFrac0, xFrac1 = 0, 1
1231 } else if sx1 > swMinus1 {
1232 sx0, sx1 = swMinus1, swMinus1
1233 xFrac0, xFrac1 = 1, 0
1234 }
1235
1236 s00 := $srcf[sr.Min.X + int(sx0), sr.Min.Y + int(sy0)]
1237 s10 := $srcf[sr.Min.X + int(sx1), sr.Min.Y + int(sy0)]
1238 $blend[xFrac1, s00, xFrac0, s10]
1239 s01 := $srcf[sr.Min.X + int(sx0), sr.Min.Y + int(sy1)]
1240 s11 := $srcf[sr.Min.X + int(sx1), sr.Min.Y + int(sy1)]
1241 $blend[xFrac1, s01, xFrac0, s11]
1242 $blend[yFrac1, s10, yFrac0, s11]
1243 $convFtou[p, s11]
1244 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p]
1245 }
1246 }
1247 }
1248 `
1249
1250 codeABLTransformLeaf = `
1251 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) {
1252 $preOuter
1253 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
1254 dyf := float64(dr.Min.Y + int(dy)) + 0.5
1255 $preInner
1256 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx
1257 dxf := float64(dr.Min.X + int(dx)) + 0.5
1258 sx := float64(d2s[0]*dxf) + float64(d2s[1]*dyf) + d2s[2]
1259 sy := float64(d2s[3]*dxf) + float64(d2s[4]*dyf) + d2s[5]
1260 if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
1261 continue
1262 }
1263
1264 sx -= 0.5
1265 sx0 := int(sx)
1266 xFrac0 := sx - float64(sx0)
1267 xFrac1 := 1 - xFrac0
1268 sx0 += bias.X
1269 sx1 := sx0 + 1
1270 if sx0 < sr.Min.X {
1271 sx0, sx1 = sr.Min.X, sr.Min.X
1272 xFrac0, xFrac1 = 0, 1
1273 } else if sx1 >= sr.Max.X {
1274 sx0, sx1 = sr.Max.X-1, sr.Max.X-1
1275 xFrac0, xFrac1 = 1, 0
1276 }
1277
1278 sy -= 0.5
1279 sy0 := int(sy)
1280 yFrac0 := sy - float64(sy0)
1281 yFrac1 := 1 - yFrac0
1282 sy0 += bias.Y
1283 sy1 := sy0 + 1
1284 if sy0 < sr.Min.Y {
1285 sy0, sy1 = sr.Min.Y, sr.Min.Y
1286 yFrac0, yFrac1 = 0, 1
1287 } else if sy1 >= sr.Max.Y {
1288 sy0, sy1 = sr.Max.Y-1, sr.Max.Y-1
1289 yFrac0, yFrac1 = 1, 0
1290 }
1291
1292 s00 := $srcf[sx0, sy0]
1293 s10 := $srcf[sx1, sy0]
1294 $blend[xFrac1, s00, xFrac0, s10]
1295 s01 := $srcf[sx0, sy1]
1296 s11 := $srcf[sx1, sy1]
1297 $blend[xFrac1, s01, xFrac0, s11]
1298 $blend[yFrac1, s10, yFrac0, s11]
1299 $convFtou[p, s11]
1300 $outputu[dr.Min.X + int(dx), dr.Min.Y + int(dy), p]
1301 }
1302 }
1303 }
1304 `
1305
1306 codeKernelRoot = `
1307 func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
1308 if z.dw != int32(dr.Dx()) || z.dh != int32(dr.Dy()) || z.sw != int32(sr.Dx()) || z.sh != int32(sr.Dy()) {
1309 z.kernel.Scale(dst, dr, src, sr, op, opts)
1310 return
1311 }
1312
1313 var o Options
1314 if opts != nil {
1315 o = *opts
1316 }
1317
1318 // adr is the affected destination pixels.
1319 adr := dst.Bounds().Intersect(dr)
1320 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
1321 if adr.Empty() || sr.Empty() {
1322 return
1323 }
1324 // Make adr relative to dr.Min.
1325 adr = adr.Sub(dr.Min)
1326 if op == Over && o.SrcMask == nil && opaque(src) {
1327 op = Src
1328 }
1329
1330 if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) {
1331 Draw(dst, dr, src, src.Bounds().Min, op)
1332 return
1333 }
1334
1335 // Create a temporary buffer:
1336 // scaleX distributes the source image's columns over the temporary image.
1337 // scaleY distributes the temporary image's rows over the destination image.
1338 var tmp [][4]float64
1339 if z.pool.New != nil {
1340 tmpp := z.pool.Get().(*[][4]float64)
1341 defer z.pool.Put(tmpp)
1342 tmp = *tmpp
1343 } else {
1344 tmp = z.makeTmpBuf()
1345 }
1346
1347 // sr is the source pixels. If it extends beyond the src bounds,
1348 // we cannot use the type-specific fast paths, as they access
1349 // the Pix fields directly without bounds checking.
1350 //
1351 // Similarly, the fast paths assume that the masks are nil.
1352 if o.SrcMask != nil || !sr.In(src.Bounds()) {
1353 z.scaleX_Image(tmp, src, sr, &o)
1354 } else {
1355 $switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr, &o)
1356 }
1357
1358 if o.DstMask != nil {
1359 switch op {
1360 case Over:
1361 z.scaleY_Image_Over(dst, dr, adr, tmp, &o)
1362 case Src:
1363 z.scaleY_Image_Src(dst, dr, adr, tmp, &o)
1364 }
1365 } else {
1366 $switchD z.scaleY_$dTypeRN_$op(dst, dr, adr, tmp, &o)
1367 }
1368 }
1369
1370 func (q *Kernel) Transform(dst Image, s2d f64.Aff3, src image.Image, sr image.Rectangle, op Op, opts *Options) {
1371 var o Options
1372 if opts != nil {
1373 o = *opts
1374 }
1375
1376 dr := transformRect(&s2d, &sr)
1377 // adr is the affected destination pixels.
1378 adr := dst.Bounds().Intersect(dr)
1379 adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
1380 if adr.Empty() || sr.Empty() {
1381 return
1382 }
1383 if op == Over && o.SrcMask == nil && opaque(src) {
1384 op = Src
1385 }
1386 d2s := invert(&s2d)
1387 // bias is a translation of the mapping from dst coordinates to src
1388 // coordinates such that the latter temporarily have non-negative X
1389 // and Y coordinates. This allows us to write int(f) instead of
1390 // int(math.Floor(f)), since "round to zero" and "round down" are
1391 // equivalent when f >= 0, but the former is much cheaper. The X--
1392 // and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
1393 // adjustment.
1394 bias := transformRect(&d2s, &adr).Min
1395 bias.X--
1396 bias.Y--
1397 d2s[2] -= float64(bias.X)
1398 d2s[5] -= float64(bias.Y)
1399 // Make adr relative to dr.Min.
1400 adr = adr.Sub(dr.Min)
1401
1402 if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) {
1403 transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
1404 return
1405 }
1406
1407 xscale := abs(d2s[0])
1408 if s := abs(d2s[1]); xscale < s {
1409 xscale = s
1410 }
1411 yscale := abs(d2s[3])
1412 if s := abs(d2s[4]); yscale < s {
1413 yscale = s
1414 }
1415
1416 // sr is the source pixels. If it extends beyond the src bounds,
1417 // we cannot use the type-specific fast paths, as they access
1418 // the Pix fields directly without bounds checking.
1419 //
1420 // Similarly, the fast paths assume that the masks are nil.
1421 if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
1422 switch op {
1423 case Over:
1424 q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o)
1425 case Src:
1426 q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o)
1427 }
1428 } else {
1429 $switch q.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale, &o)
1430 }
1431 }
1432 `
1433
1434 codeKernelScaleLeafX = `
1435 func (z *kernelScaler) scaleX_$sTypeRN$sratio(tmp [][4]float64, src $sType, sr image.Rectangle, opts *Options) {
1436 t := 0
1437 $preKernelOuter
1438 for y := int32(0); y < z.sh; y++ {
1439 for _, s := range z.horizontal.sources {
1440 var pr, pg, pb, pa float64 $tweakVarP
1441 for _, c := range z.horizontal.contribs[s.i:s.j] {
1442 p += $srcf[sr.Min.X + int(c.coord), sr.Min.Y + int(y)] * c.weight
1443 }
1444 $tweakPr
1445 tmp[t] = [4]float64{
1446 pr * s.invTotalWeightFFFF, $tweakP
1447 pg * s.invTotalWeightFFFF, $tweakP
1448 pb * s.invTotalWeightFFFF, $tweakP
1449 pa * s.invTotalWeightFFFF, $tweakP
1450 }
1451 t++
1452 }
1453 }
1454 }
1455 `
1456
1457 codeKernelScaleLeafY = `
1458 func (z *kernelScaler) scaleY_$dTypeRN_$op(dst $dType, dr, adr image.Rectangle, tmp [][4]float64, opts *Options) {
1459 $preOuter
1460 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
1461 $preKernelInner
1462 for dy, s := range z.vertical.sources[adr.Min.Y:adr.Max.Y] { $tweakDy
1463 var pr, pg, pb, pa float64
1464 for _, c := range z.vertical.contribs[s.i:s.j] {
1465 p := &tmp[c.coord*z.dw+dx]
1466 pr += float64(p[0] * c.weight)
1467 pg += float64(p[1] * c.weight)
1468 pb += float64(p[2] * c.weight)
1469 pa += float64(p[3] * c.weight)
1470 }
1471 $clampToAlpha
1472 $outputf[dr.Min.X + int(dx), dr.Min.Y + int(adr.Min.Y + dy), ftou, p, s.invTotalWeight]
1473 $tweakD
1474 }
1475 }
1476 }
1477 `
1478
1479 codeKernelTransformLeaf = `
1480 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) {
1481 // When shrinking, broaden the effective kernel support so that we still
1482 // visit every source pixel.
1483 xHalfWidth, xKernelArgScale := q.Support, 1.0
1484 if xscale > 1 {
1485 xHalfWidth *= xscale
1486 xKernelArgScale = 1 / xscale
1487 }
1488 yHalfWidth, yKernelArgScale := q.Support, 1.0
1489 if yscale > 1 {
1490 yHalfWidth *= yscale
1491 yKernelArgScale = 1 / yscale
1492 }
1493
1494 xWeights := make([]float64, 1 + 2*int(math.Ceil(xHalfWidth)))
1495 yWeights := make([]float64, 1 + 2*int(math.Ceil(yHalfWidth)))
1496
1497 $preOuter
1498 for dy := int32(adr.Min.Y); dy < int32(adr.Max.Y); dy++ {
1499 dyf := float64(dr.Min.Y + int(dy)) + 0.5
1500 $preInner
1501 for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { $tweakDx
1502 dxf := float64(dr.Min.X + int(dx)) + 0.5
1503 sx := float64(d2s[0]*dxf) + float64(d2s[1]*dyf) + d2s[2]
1504 sy := float64(d2s[3]*dxf) + float64(d2s[4]*dyf) + d2s[5]
1505 if !(image.Point{int(sx) + bias.X, int(sy) + bias.Y}).In(sr) {
1506 continue
1507 }
1508
1509 // TODO: adjust the bias so that we can use int(f) instead
1510 // of math.Floor(f) and math.Ceil(f).
1511 sx += float64(bias.X)
1512 sx -= 0.5
1513 ix := int(math.Floor(sx - xHalfWidth))
1514 if ix < sr.Min.X {
1515 ix = sr.Min.X
1516 }
1517 jx := int(math.Ceil(sx + xHalfWidth))
1518 if jx > sr.Max.X {
1519 jx = sr.Max.X
1520 }
1521
1522 totalXWeight := 0.0
1523 for kx := ix; kx < jx; kx++ {
1524 xWeight := 0.0
1525 if t := abs((sx - float64(kx)) * xKernelArgScale); t < q.Support {
1526 xWeight = q.At(t)
1527 }
1528 xWeights[kx - ix] = xWeight
1529 totalXWeight += xWeight
1530 }
1531 for x := range xWeights[:jx-ix] {
1532 xWeights[x] /= totalXWeight
1533 }
1534
1535 sy += float64(bias.Y)
1536 sy -= 0.5
1537 iy := int(math.Floor(sy - yHalfWidth))
1538 if iy < sr.Min.Y {
1539 iy = sr.Min.Y
1540 }
1541 jy := int(math.Ceil(sy + yHalfWidth))
1542 if jy > sr.Max.Y {
1543 jy = sr.Max.Y
1544 }
1545
1546 totalYWeight := 0.0
1547 for ky := iy; ky < jy; ky++ {
1548 yWeight := 0.0
1549 if t := abs((sy - float64(ky)) * yKernelArgScale); t < q.Support {
1550 yWeight = q.At(t)
1551 }
1552 yWeights[ky - iy] = yWeight
1553 totalYWeight += yWeight
1554 }
1555 for y := range yWeights[:jy-iy] {
1556 yWeights[y] /= totalYWeight
1557 }
1558
1559 var pr, pg, pb, pa float64 $tweakVarP
1560 for ky := iy; ky < jy; ky++ {
1561 if yWeight := yWeights[ky - iy]; yWeight != 0 {
1562 for kx := ix; kx < jx; kx++ {
1563 if w := xWeights[kx - ix] * yWeight; w != 0 {
1564 p += $srcf[kx, ky] * w
1565 }
1566 }
1567 }
1568 }
1569 $clampToAlpha
1570 $outputf[dr.Min.X + int(dx), dr.Min.Y + int(dy), fffftou, p, 1]
1571 }
1572 }
1573 }
1574 `
1575 )