git.lucas.co / go_mono
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 )