1
2
3
4
5
6
7 package main
8
9 import (
10 "bytes"
11 "flag"
12 "fmt"
13 "go/format"
14 "log"
15 "os"
16 )
17
18 var debug = flag.Bool("debug", false, "")
19
20 func 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
43 const pre = `// Code generated by go run gen.go; DO NOT EDIT.
44
45 package imageutil
46
47 import (
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.
58 func 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
81 const post = `
82 default:
83 return false
84 }
85 return true
86 }
87 `
88
89 const 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
146 var subsampleRatios = []string{
147 "444",
148 "422",
149 "420",
150 "440",
151 }
152
153 var 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
View as plain text