1/*
2Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
3
4Permission to use, copy, modify, and/or distribute this software for any purpose
5with or without fee is hereby granted, provided that the above copyright notice
6and this permission notice appear in all copies.
7
8THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
12OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
14THIS SOFTWARE.
15*/
16
17package resize
18
19import (
20	"image"
21	"image/color"
22)
23
24// ycc is an in memory YCbCr image.  The Y, Cb and Cr samples are held in a
25// single slice to increase resizing performance.
26type ycc struct {
27	// Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at
28	// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
29	Pix []uint8
30	// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
31	Stride int
32	// Rect is the image's bounds.
33	Rect image.Rectangle
34	// SubsampleRatio is the subsample ratio of the original YCbCr image.
35	SubsampleRatio image.YCbCrSubsampleRatio
36}
37
38// PixOffset returns the index of the first element of Pix that corresponds to
39// the pixel at (x, y).
40func (p *ycc) PixOffset(x, y int) int {
41	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3
42}
43
44func (p *ycc) Bounds() image.Rectangle {
45	return p.Rect
46}
47
48func (p *ycc) ColorModel() color.Model {
49	return color.YCbCrModel
50}
51
52func (p *ycc) At(x, y int) color.Color {
53	if !(image.Point{x, y}.In(p.Rect)) {
54		return color.YCbCr{}
55	}
56	i := p.PixOffset(x, y)
57	return color.YCbCr{
58		p.Pix[i+0],
59		p.Pix[i+1],
60		p.Pix[i+2],
61	}
62}
63
64func (p *ycc) Opaque() bool {
65	return true
66}
67
68// SubImage returns an image representing the portion of the image p visible
69// through r. The returned value shares pixels with the original image.
70func (p *ycc) SubImage(r image.Rectangle) image.Image {
71	r = r.Intersect(p.Rect)
72	if r.Empty() {
73		return &ycc{SubsampleRatio: p.SubsampleRatio}
74	}
75	i := p.PixOffset(r.Min.X, r.Min.Y)
76	return &ycc{
77		Pix:            p.Pix[i:],
78		Stride:         p.Stride,
79		Rect:           r,
80		SubsampleRatio: p.SubsampleRatio,
81	}
82}
83
84// newYCC returns a new ycc with the given bounds and subsample ratio.
85func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc {
86	w, h := r.Dx(), r.Dy()
87	buf := make([]uint8, 3*w*h)
88	return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s}
89}
90
91// Copy of image.YCbCrSubsampleRatio constants - this allows us to support
92// older versions of Go where these constants are not defined (i.e. Go 1.4)
93const (
94	ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota
95	ycbcrSubsampleRatio422
96	ycbcrSubsampleRatio420
97	ycbcrSubsampleRatio440
98	ycbcrSubsampleRatio411
99	ycbcrSubsampleRatio410
100)
101
102// YCbCr converts ycc to a YCbCr image with the same subsample ratio
103// as the YCbCr image that ycc was generated from.
104func (p *ycc) YCbCr() *image.YCbCr {
105	ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
106	switch ycbcr.SubsampleRatio {
107	case ycbcrSubsampleRatio422:
108		return p.ycbcr422(ycbcr)
109	case ycbcrSubsampleRatio420:
110		return p.ycbcr420(ycbcr)
111	case ycbcrSubsampleRatio440:
112		return p.ycbcr440(ycbcr)
113	case ycbcrSubsampleRatio444:
114		return p.ycbcr444(ycbcr)
115	case ycbcrSubsampleRatio411:
116		return p.ycbcr411(ycbcr)
117	case ycbcrSubsampleRatio410:
118		return p.ycbcr410(ycbcr)
119	}
120	return ycbcr
121}
122
123// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
124func imageYCbCrToYCC(in *image.YCbCr) *ycc {
125	w, h := in.Rect.Dx(), in.Rect.Dy()
126	p := ycc{
127		Pix:            make([]uint8, 3*w*h),
128		Stride:         3 * w,
129		Rect:           image.Rect(0, 0, w, h),
130		SubsampleRatio: in.SubsampleRatio,
131	}
132	switch in.SubsampleRatio {
133	case ycbcrSubsampleRatio422:
134		return convertToYCC422(in, &p)
135	case ycbcrSubsampleRatio420:
136		return convertToYCC420(in, &p)
137	case ycbcrSubsampleRatio440:
138		return convertToYCC440(in, &p)
139	case ycbcrSubsampleRatio444:
140		return convertToYCC444(in, &p)
141	case ycbcrSubsampleRatio411:
142		return convertToYCC411(in, &p)
143	case ycbcrSubsampleRatio410:
144		return convertToYCC410(in, &p)
145	}
146	return &p
147}
148
149func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr {
150	var off int
151	Pix := p.Pix
152	Y := ycbcr.Y
153	Cb := ycbcr.Cb
154	Cr := ycbcr.Cr
155	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
156		yy := y * ycbcr.YStride
157		cy := y * ycbcr.CStride
158		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
159			ci := cy + x/2
160			Y[yy+x] = Pix[off+0]
161			Cb[ci] = Pix[off+1]
162			Cr[ci] = Pix[off+2]
163			off += 3
164		}
165	}
166	return ycbcr
167}
168
169func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr {
170	var off int
171	Pix := p.Pix
172	Y := ycbcr.Y
173	Cb := ycbcr.Cb
174	Cr := ycbcr.Cr
175	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
176		yy := y * ycbcr.YStride
177		cy := (y / 2) * ycbcr.CStride
178		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
179			ci := cy + x/2
180			Y[yy+x] = Pix[off+0]
181			Cb[ci] = Pix[off+1]
182			Cr[ci] = Pix[off+2]
183			off += 3
184		}
185	}
186	return ycbcr
187}
188
189func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr {
190	var off int
191	Pix := p.Pix
192	Y := ycbcr.Y
193	Cb := ycbcr.Cb
194	Cr := ycbcr.Cr
195	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
196		yy := y * ycbcr.YStride
197		cy := (y / 2) * ycbcr.CStride
198		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
199			ci := cy + x
200			Y[yy+x] = Pix[off+0]
201			Cb[ci] = Pix[off+1]
202			Cr[ci] = Pix[off+2]
203			off += 3
204		}
205	}
206	return ycbcr
207}
208
209func (p *ycc) ycbcr444(ycbcr *image.YCbCr) *image.YCbCr {
210	var off int
211	Pix := p.Pix
212	Y := ycbcr.Y
213	Cb := ycbcr.Cb
214	Cr := ycbcr.Cr
215	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
216		yy := y * ycbcr.YStride
217		cy := y * ycbcr.CStride
218		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
219			ci := cy + x
220			Y[yy+x] = Pix[off+0]
221			Cb[ci] = Pix[off+1]
222			Cr[ci] = Pix[off+2]
223			off += 3
224		}
225	}
226	return ycbcr
227}
228
229func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr {
230	var off int
231	Pix := p.Pix
232	Y := ycbcr.Y
233	Cb := ycbcr.Cb
234	Cr := ycbcr.Cr
235	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
236		yy := y * ycbcr.YStride
237		cy := y * ycbcr.CStride
238		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
239			ci := cy + x/4
240			Y[yy+x] = Pix[off+0]
241			Cb[ci] = Pix[off+1]
242			Cr[ci] = Pix[off+2]
243			off += 3
244		}
245	}
246	return ycbcr
247}
248
249func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr {
250	var off int
251	Pix := p.Pix
252	Y := ycbcr.Y
253	Cb := ycbcr.Cb
254	Cr := ycbcr.Cr
255	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
256		yy := y * ycbcr.YStride
257		cy := (y / 2) * ycbcr.CStride
258		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
259			ci := cy + x/4
260			Y[yy+x] = Pix[off+0]
261			Cb[ci] = Pix[off+1]
262			Cr[ci] = Pix[off+2]
263			off += 3
264		}
265	}
266	return ycbcr
267}
268
269func convertToYCC422(in *image.YCbCr, p *ycc) *ycc {
270	var off int
271	Pix := p.Pix
272	Y := in.Y
273	Cb := in.Cb
274	Cr := in.Cr
275	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
276		yy := y * in.YStride
277		cy := y * in.CStride
278		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
279			ci := cy + x/2
280			Pix[off+0] = Y[yy+x]
281			Pix[off+1] = Cb[ci]
282			Pix[off+2] = Cr[ci]
283			off += 3
284		}
285	}
286	return p
287}
288
289func convertToYCC420(in *image.YCbCr, p *ycc) *ycc {
290	var off int
291	Pix := p.Pix
292	Y := in.Y
293	Cb := in.Cb
294	Cr := in.Cr
295	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
296		yy := y * in.YStride
297		cy := (y / 2) * in.CStride
298		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
299			ci := cy + x/2
300			Pix[off+0] = Y[yy+x]
301			Pix[off+1] = Cb[ci]
302			Pix[off+2] = Cr[ci]
303			off += 3
304		}
305	}
306	return p
307}
308
309func convertToYCC440(in *image.YCbCr, p *ycc) *ycc {
310	var off int
311	Pix := p.Pix
312	Y := in.Y
313	Cb := in.Cb
314	Cr := in.Cr
315	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
316		yy := y * in.YStride
317		cy := (y / 2) * in.CStride
318		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
319			ci := cy + x
320			Pix[off+0] = Y[yy+x]
321			Pix[off+1] = Cb[ci]
322			Pix[off+2] = Cr[ci]
323			off += 3
324		}
325	}
326	return p
327}
328
329func convertToYCC444(in *image.YCbCr, p *ycc) *ycc {
330	var off int
331	Pix := p.Pix
332	Y := in.Y
333	Cb := in.Cb
334	Cr := in.Cr
335	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
336		yy := y * in.YStride
337		cy := y * in.CStride
338		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
339			ci := cy + x
340			Pix[off+0] = Y[yy+x]
341			Pix[off+1] = Cb[ci]
342			Pix[off+2] = Cr[ci]
343			off += 3
344		}
345	}
346	return p
347}
348
349func convertToYCC411(in *image.YCbCr, p *ycc) *ycc {
350	var off int
351	Pix := p.Pix
352	Y := in.Y
353	Cb := in.Cb
354	Cr := in.Cr
355	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
356		yy := y * in.YStride
357		cy := y * in.CStride
358		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
359			ci := cy + x/4
360			Pix[off+0] = Y[yy+x]
361			Pix[off+1] = Cb[ci]
362			Pix[off+2] = Cr[ci]
363			off += 3
364		}
365	}
366	return p
367}
368
369func convertToYCC410(in *image.YCbCr, p *ycc) *ycc {
370	var off int
371	Pix := p.Pix
372	Y := in.Y
373	Cb := in.Cb
374	Cr := in.Cr
375	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
376		yy := y * in.YStride
377		cy := (y / 2) * in.CStride
378		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
379			ci := cy + x/4
380			Pix[off+0] = Y[yy+x]
381			Pix[off+1] = Cb[ci]
382			Pix[off+2] = Cr[ci]
383			off += 3
384		}
385	}
386	return p
387}
388