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
5package image
6
7import (
8	"image/color"
9)
10
11// YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image.
12type YCbCrSubsampleRatio int
13
14const (
15	YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
16	YCbCrSubsampleRatio422
17	YCbCrSubsampleRatio420
18	YCbCrSubsampleRatio440
19	YCbCrSubsampleRatio411
20	YCbCrSubsampleRatio410
21)
22
23func (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.
54type YCbCr struct {
55	Y, Cb, Cr      []uint8
56	YStride        int
57	CStride        int
58	SubsampleRatio YCbCrSubsampleRatio
59	Rect           Rectangle
60}
61
62func (p *YCbCr) ColorModel() color.Model {
63	return color.YCbCrModel
64}
65
66func (p *YCbCr) Bounds() Rectangle {
67	return p.Rect
68}
69
70func (p *YCbCr) At(x, y int) color.Color {
71	return p.YCbCrAt(x, y)
72}
73
74func (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).
89func (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).
95func (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.
114func (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
137func (p *YCbCr) Opaque() bool {
138	return true
139}
140
141func 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.
169func 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.
189type NYCbCrA struct {
190	YCbCr
191	A       []uint8
192	AStride int
193}
194
195func (p *NYCbCrA) ColorModel() color.Model {
196	return color.NYCbCrAModel
197}
198
199func (p *NYCbCrA) At(x, y int) color.Color {
200	return p.NYCbCrAAt(x, y)
201}
202
203func (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).
222func (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.
228func (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.
259func (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.
278func 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}
299