111
|
1 // Copyright 2011 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 package image
|
|
6
|
|
7 import (
|
|
8 "image/color"
|
|
9 )
|
|
10
|
|
11 // YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image.
|
|
12 type YCbCrSubsampleRatio int
|
|
13
|
|
14 const (
|
|
15 YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
|
|
16 YCbCrSubsampleRatio422
|
|
17 YCbCrSubsampleRatio420
|
|
18 YCbCrSubsampleRatio440
|
|
19 YCbCrSubsampleRatio411
|
|
20 YCbCrSubsampleRatio410
|
|
21 )
|
|
22
|
|
23 func (s YCbCrSubsampleRatio) String() string {
|
|
24 switch s {
|
|
25 case YCbCrSubsampleRatio444:
|
|
26 return "YCbCrSubsampleRatio444"
|
|
27 case YCbCrSubsampleRatio422:
|
|
28 return "YCbCrSubsampleRatio422"
|
|
29 case YCbCrSubsampleRatio420:
|
|
30 return "YCbCrSubsampleRatio420"
|
|
31 case YCbCrSubsampleRatio440:
|
|
32 return "YCbCrSubsampleRatio440"
|
|
33 case YCbCrSubsampleRatio411:
|
|
34 return "YCbCrSubsampleRatio411"
|
|
35 case YCbCrSubsampleRatio410:
|
|
36 return "YCbCrSubsampleRatio410"
|
|
37 }
|
|
38 return "YCbCrSubsampleRatioUnknown"
|
|
39 }
|
|
40
|
|
41 // YCbCr is an in-memory image of Y'CbCr colors. There is one Y sample per
|
|
42 // pixel, but each Cb and Cr sample can span one or more pixels.
|
|
43 // YStride is the Y slice index delta between vertically adjacent pixels.
|
|
44 // CStride is the Cb and Cr slice index delta between vertically adjacent pixels
|
|
45 // that map to separate chroma samples.
|
|
46 // It is not an absolute requirement, but YStride and len(Y) are typically
|
|
47 // multiples of 8, and:
|
|
48 // For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
|
|
49 // For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
|
|
50 // For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
|
|
51 // For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2.
|
|
52 // For 4:1:1, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/4.
|
|
53 // For 4:1:0, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/8.
|
|
54 type YCbCr struct {
|
|
55 Y, Cb, Cr []uint8
|
|
56 YStride int
|
|
57 CStride int
|
|
58 SubsampleRatio YCbCrSubsampleRatio
|
|
59 Rect Rectangle
|
|
60 }
|
|
61
|
|
62 func (p *YCbCr) ColorModel() color.Model {
|
|
63 return color.YCbCrModel
|
|
64 }
|
|
65
|
|
66 func (p *YCbCr) Bounds() Rectangle {
|
|
67 return p.Rect
|
|
68 }
|
|
69
|
|
70 func (p *YCbCr) At(x, y int) color.Color {
|
|
71 return p.YCbCrAt(x, y)
|
|
72 }
|
|
73
|
|
74 func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr {
|
|
75 if !(Point{x, y}.In(p.Rect)) {
|
|
76 return color.YCbCr{}
|
|
77 }
|
|
78 yi := p.YOffset(x, y)
|
|
79 ci := p.COffset(x, y)
|
|
80 return color.YCbCr{
|
|
81 p.Y[yi],
|
|
82 p.Cb[ci],
|
|
83 p.Cr[ci],
|
|
84 }
|
|
85 }
|
|
86
|
|
87 // YOffset returns the index of the first element of Y that corresponds to
|
|
88 // the pixel at (x, y).
|
|
89 func (p *YCbCr) YOffset(x, y int) int {
|
|
90 return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X)
|
|
91 }
|
|
92
|
|
93 // COffset returns the index of the first element of Cb or Cr that corresponds
|
|
94 // to the pixel at (x, y).
|
|
95 func (p *YCbCr) COffset(x, y int) int {
|
|
96 switch p.SubsampleRatio {
|
|
97 case YCbCrSubsampleRatio422:
|
|
98 return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
|
|
99 case YCbCrSubsampleRatio420:
|
|
100 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2)
|
|
101 case YCbCrSubsampleRatio440:
|
|
102 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X)
|
|
103 case YCbCrSubsampleRatio411:
|
|
104 return (y-p.Rect.Min.Y)*p.CStride + (x/4 - p.Rect.Min.X/4)
|
|
105 case YCbCrSubsampleRatio410:
|
|
106 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/4 - p.Rect.Min.X/4)
|
|
107 }
|
|
108 // Default to 4:4:4 subsampling.
|
|
109 return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X)
|
|
110 }
|
|
111
|
|
112 // SubImage returns an image representing the portion of the image p visible
|
|
113 // through r. The returned value shares pixels with the original image.
|
|
114 func (p *YCbCr) SubImage(r Rectangle) Image {
|
|
115 r = r.Intersect(p.Rect)
|
|
116 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
|
|
117 // either r1 or r2 if the intersection is empty. Without explicitly checking for
|
|
118 // this, the Pix[i:] expression below can panic.
|
|
119 if r.Empty() {
|
|
120 return &YCbCr{
|
|
121 SubsampleRatio: p.SubsampleRatio,
|
|
122 }
|
|
123 }
|
|
124 yi := p.YOffset(r.Min.X, r.Min.Y)
|
|
125 ci := p.COffset(r.Min.X, r.Min.Y)
|
|
126 return &YCbCr{
|
|
127 Y: p.Y[yi:],
|
|
128 Cb: p.Cb[ci:],
|
|
129 Cr: p.Cr[ci:],
|
|
130 SubsampleRatio: p.SubsampleRatio,
|
|
131 YStride: p.YStride,
|
|
132 CStride: p.CStride,
|
|
133 Rect: r,
|
|
134 }
|
|
135 }
|
|
136
|
|
137 func (p *YCbCr) Opaque() bool {
|
|
138 return true
|
|
139 }
|
|
140
|
|
141 func yCbCrSize(r Rectangle, subsampleRatio YCbCrSubsampleRatio) (w, h, cw, ch int) {
|
|
142 w, h = r.Dx(), r.Dy()
|
|
143 switch subsampleRatio {
|
|
144 case YCbCrSubsampleRatio422:
|
|
145 cw = (r.Max.X+1)/2 - r.Min.X/2
|
|
146 ch = h
|
|
147 case YCbCrSubsampleRatio420:
|
|
148 cw = (r.Max.X+1)/2 - r.Min.X/2
|
|
149 ch = (r.Max.Y+1)/2 - r.Min.Y/2
|
|
150 case YCbCrSubsampleRatio440:
|
|
151 cw = w
|
|
152 ch = (r.Max.Y+1)/2 - r.Min.Y/2
|
|
153 case YCbCrSubsampleRatio411:
|
|
154 cw = (r.Max.X+3)/4 - r.Min.X/4
|
|
155 ch = h
|
|
156 case YCbCrSubsampleRatio410:
|
|
157 cw = (r.Max.X+3)/4 - r.Min.X/4
|
|
158 ch = (r.Max.Y+1)/2 - r.Min.Y/2
|
|
159 default:
|
|
160 // Default to 4:4:4 subsampling.
|
|
161 cw = w
|
|
162 ch = h
|
|
163 }
|
|
164 return
|
|
165 }
|
|
166
|
|
167 // NewYCbCr returns a new YCbCr image with the given bounds and subsample
|
|
168 // ratio.
|
|
169 func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr {
|
|
170 w, h, cw, ch := yCbCrSize(r, subsampleRatio)
|
|
171 i0 := w*h + 0*cw*ch
|
|
172 i1 := w*h + 1*cw*ch
|
|
173 i2 := w*h + 2*cw*ch
|
|
174 b := make([]byte, i2)
|
|
175 return &YCbCr{
|
|
176 Y: b[:i0:i0],
|
|
177 Cb: b[i0:i1:i1],
|
|
178 Cr: b[i1:i2:i2],
|
|
179 SubsampleRatio: subsampleRatio,
|
|
180 YStride: w,
|
|
181 CStride: cw,
|
|
182 Rect: r,
|
|
183 }
|
|
184 }
|
|
185
|
|
186 // NYCbCrA is an in-memory image of non-alpha-premultiplied Y'CbCr-with-alpha
|
|
187 // colors. A and AStride are analogous to the Y and YStride fields of the
|
|
188 // embedded YCbCr.
|
|
189 type NYCbCrA struct {
|
|
190 YCbCr
|
|
191 A []uint8
|
|
192 AStride int
|
|
193 }
|
|
194
|
|
195 func (p *NYCbCrA) ColorModel() color.Model {
|
|
196 return color.NYCbCrAModel
|
|
197 }
|
|
198
|
|
199 func (p *NYCbCrA) At(x, y int) color.Color {
|
|
200 return p.NYCbCrAAt(x, y)
|
|
201 }
|
|
202
|
|
203 func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA {
|
|
204 if !(Point{X: x, Y: y}.In(p.Rect)) {
|
|
205 return color.NYCbCrA{}
|
|
206 }
|
|
207 yi := p.YOffset(x, y)
|
|
208 ci := p.COffset(x, y)
|
|
209 ai := p.AOffset(x, y)
|
|
210 return color.NYCbCrA{
|
|
211 color.YCbCr{
|
|
212 Y: p.Y[yi],
|
|
213 Cb: p.Cb[ci],
|
|
214 Cr: p.Cr[ci],
|
|
215 },
|
|
216 p.A[ai],
|
|
217 }
|
|
218 }
|
|
219
|
|
220 // AOffset returns the index of the first element of A that corresponds to the
|
|
221 // pixel at (x, y).
|
|
222 func (p *NYCbCrA) AOffset(x, y int) int {
|
|
223 return (y-p.Rect.Min.Y)*p.AStride + (x - p.Rect.Min.X)
|
|
224 }
|
|
225
|
|
226 // SubImage returns an image representing the portion of the image p visible
|
|
227 // through r. The returned value shares pixels with the original image.
|
|
228 func (p *NYCbCrA) SubImage(r Rectangle) Image {
|
|
229 r = r.Intersect(p.Rect)
|
|
230 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
|
|
231 // either r1 or r2 if the intersection is empty. Without explicitly checking for
|
|
232 // this, the Pix[i:] expression below can panic.
|
|
233 if r.Empty() {
|
|
234 return &NYCbCrA{
|
|
235 YCbCr: YCbCr{
|
|
236 SubsampleRatio: p.SubsampleRatio,
|
|
237 },
|
|
238 }
|
|
239 }
|
|
240 yi := p.YOffset(r.Min.X, r.Min.Y)
|
|
241 ci := p.COffset(r.Min.X, r.Min.Y)
|
|
242 ai := p.AOffset(r.Min.X, r.Min.Y)
|
|
243 return &NYCbCrA{
|
|
244 YCbCr: YCbCr{
|
|
245 Y: p.Y[yi:],
|
|
246 Cb: p.Cb[ci:],
|
|
247 Cr: p.Cr[ci:],
|
|
248 SubsampleRatio: p.SubsampleRatio,
|
|
249 YStride: p.YStride,
|
|
250 CStride: p.CStride,
|
|
251 Rect: r,
|
|
252 },
|
|
253 A: p.A[ai:],
|
|
254 AStride: p.AStride,
|
|
255 }
|
|
256 }
|
|
257
|
|
258 // Opaque scans the entire image and reports whether it is fully opaque.
|
|
259 func (p *NYCbCrA) Opaque() bool {
|
|
260 if p.Rect.Empty() {
|
|
261 return true
|
|
262 }
|
|
263 i0, i1 := 0, p.Rect.Dx()
|
|
264 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
|
|
265 for _, a := range p.A[i0:i1] {
|
|
266 if a != 0xff {
|
|
267 return false
|
|
268 }
|
|
269 }
|
|
270 i0 += p.AStride
|
|
271 i1 += p.AStride
|
|
272 }
|
|
273 return true
|
|
274 }
|
|
275
|
|
276 // NewNYCbCrA returns a new NYCbCrA image with the given bounds and subsample
|
|
277 // ratio.
|
|
278 func NewNYCbCrA(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *NYCbCrA {
|
|
279 w, h, cw, ch := yCbCrSize(r, subsampleRatio)
|
|
280 i0 := 1*w*h + 0*cw*ch
|
|
281 i1 := 1*w*h + 1*cw*ch
|
|
282 i2 := 1*w*h + 2*cw*ch
|
|
283 i3 := 2*w*h + 2*cw*ch
|
|
284 b := make([]byte, i3)
|
|
285 return &NYCbCrA{
|
|
286 YCbCr: YCbCr{
|
|
287 Y: b[:i0:i0],
|
|
288 Cb: b[i0:i1:i1],
|
|
289 Cr: b[i1:i2:i2],
|
|
290 SubsampleRatio: subsampleRatio,
|
|
291 YStride: w,
|
|
292 CStride: cw,
|
|
293 Rect: r,
|
|
294 },
|
|
295 A: b[i2:],
|
|
296 AStride: w,
|
|
297 }
|
|
298 }
|