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
7package main
8
9import (
10	"bytes"
11	"flag"
12	"fmt"
13	"go/format"
14	"log"
15	"os"
16)
17
18var debug = flag.Bool("debug", false, "")
19
20func main() {
21	flag.Parse()
22
23	w := new(bytes.Buffer)
24	w.WriteString(pre)
25	for _, sratio := range subsampleRatios {
26		fmt.Fprintf(w, sratioCase, sratio, sratioLines[sratio])
27	}
28	w.WriteString(post)
29
30	if *debug {
31		os.Stdout.Write(w.Bytes())
32		return
33	}
34	out, err := format.Source(w.Bytes())
35	if err != nil {
36		log.Fatal(err)
37	}
38	if err := os.WriteFile("impl.go", out, 0660); err != nil {
39		log.Fatal(err)
40	}
41}
42
43const pre = `// Code generated by go run gen.go; DO NOT EDIT.
44
45package imageutil
46
47import (
48	"image"
49)
50
51// DrawYCbCr draws the YCbCr source image on the RGBA destination image with
52// r.Min in dst aligned with sp in src. It reports whether the draw was
53// successful. If it returns false, no dst pixels were changed.
54//
55// This function assumes that r is entirely within dst's bounds and the
56// translation of r from dst coordinate space to src coordinate space is
57// entirely within src's bounds.
58func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
59	// This function exists in the image/internal/imageutil package because it
60	// is needed by both the image/draw and image/jpeg packages, but it doesn't
61	// seem right for one of those two to depend on the other.
62	//
63	// Another option is to have this code be exported in the image package,
64	// but we'd need to make sure we're totally happy with the API (for the
65	// rest of Go 1 compatibility), and decide if we want to have a more
66	// general purpose DrawToRGBA method for other image types. One possibility
67	// is:
68	//
69	// func (src *YCbCr) CopyToRGBA(dst *RGBA, dr, sr Rectangle) (effectiveDr, effectiveSr Rectangle)
70	//
71	// in the spirit of the built-in copy function for 1-dimensional slices,
72	// that also allowed a CopyFromRGBA method if needed.
73
74	x0 := (r.Min.X - dst.Rect.Min.X) * 4
75	x1 := (r.Max.X - dst.Rect.Min.X) * 4
76	y0 := r.Min.Y - dst.Rect.Min.Y
77	y1 := r.Max.Y - dst.Rect.Min.Y
78	switch src.SubsampleRatio {
79`
80
81const post = `
82	default:
83		return false
84	}
85	return true
86}
87`
88
89const sratioCase = `
90	case image.YCbCrSubsampleRatio%s:
91		for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
92			dpix := dst.Pix[y*dst.Stride:]
93			yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
94			%s
95
96				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
97				yy1 := int32(src.Y[yi]) * 0x10101
98				cb1 := int32(src.Cb[ci]) - 128
99				cr1 := int32(src.Cr[ci]) - 128
100
101				// The bit twiddling below is equivalent to
102				//
103				// r := (yy1 + 91881*cr1) >> 16
104				// if r < 0 {
105				//     r = 0
106				// } else if r > 0xff {
107				//     r = ^int32(0)
108				// }
109				//
110				// but uses fewer branches and is faster.
111				// Note that the uint8 type conversion in the return
112				// statement will convert ^int32(0) to 0xff.
113				// The code below to compute g and b uses a similar pattern.
114				r := yy1 + 91881*cr1
115				if uint32(r)&0xff000000 == 0 {
116					r >>= 16
117				} else {
118					r = ^(r >> 31)
119				}
120
121				g := yy1 - 22554*cb1 - 46802*cr1
122				if uint32(g)&0xff000000 == 0 {
123					g >>= 16
124				} else {
125					g = ^(g >> 31)
126				}
127
128				b := yy1 + 116130*cb1
129				if uint32(b)&0xff000000 == 0 {
130					b >>= 16
131				} else {
132					b = ^(b >> 31)
133				}
134
135
136				// use a temp slice to hint to the compiler that a single bounds check suffices
137				rgba := dpix[x : x+4 : len(dpix)]
138				rgba[0] = uint8(r)
139				rgba[1] = uint8(g)
140				rgba[2] = uint8(b)
141				rgba[3] = 255
142			}
143		}
144`
145
146var subsampleRatios = []string{
147	"444",
148	"422",
149	"420",
150	"440",
151}
152
153var sratioLines = map[string]string{
154	"444": `
155		ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
156		for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
157	`,
158	"422": `
159		ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
160		for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
161			ci := ciBase + sx/2
162	`,
163	"420": `
164		ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2
165		for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
166			ci := ciBase + sx/2
167	`,
168	"440": `
169		ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
170		for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
171	`,
172}
173