github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/image/draw/gen.go (about)

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