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