|
@@ -0,0 +1,226 @@
|
|
1
|
+/*
|
|
2
|
+Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
|
|
3
|
+
|
|
4
|
+Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
5
|
+with or without fee is hereby granted, provided that the above copyright notice
|
|
6
|
+and this permission notice appear in all copies.
|
|
7
|
+
|
|
8
|
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
10
|
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
12
|
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
13
|
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
14
|
+THIS SOFTWARE.
|
|
15
|
+*/
|
|
16
|
+
|
|
17
|
+package resize
|
|
18
|
+
|
|
19
|
+import (
|
|
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.
|
|
26
|
+type 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).
|
|
40
|
+func (p *ycc) PixOffset(x, y int) int {
|
|
41
|
+ return (y * p.Stride) + (x * 3)
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+func (p *ycc) Bounds() image.Rectangle {
|
|
45
|
+ return p.Rect
|
|
46
|
+}
|
|
47
|
+
|
|
48
|
+func (p *ycc) ColorModel() color.Model {
|
|
49
|
+ return color.YCbCrModel
|
|
50
|
+}
|
|
51
|
+
|
|
52
|
+func (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
|
+
|
|
64
|
+func (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.
|
|
70
|
+func (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.
|
|
85
|
+func 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
|
+// YCbCr converts ycc to a YCbCr image with the same subsample ratio
|
|
92
|
+// as the YCbCr image that ycc was generated from.
|
|
93
|
+func (p *ycc) YCbCr() *image.YCbCr {
|
|
94
|
+ ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
|
|
95
|
+ var off int
|
|
96
|
+
|
|
97
|
+ switch ycbcr.SubsampleRatio {
|
|
98
|
+ case image.YCbCrSubsampleRatio422:
|
|
99
|
+ for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
100
|
+ yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
101
|
+ cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
|
|
102
|
+ for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
103
|
+ xx := (x - ycbcr.Rect.Min.X)
|
|
104
|
+ yi := yy + xx
|
|
105
|
+ ci := cy + xx/2
|
|
106
|
+ ycbcr.Y[yi] = p.Pix[off+0]
|
|
107
|
+ ycbcr.Cb[ci] = p.Pix[off+1]
|
|
108
|
+ ycbcr.Cr[ci] = p.Pix[off+2]
|
|
109
|
+ off += 3
|
|
110
|
+ }
|
|
111
|
+ }
|
|
112
|
+ case image.YCbCrSubsampleRatio420:
|
|
113
|
+ for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
114
|
+ yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
115
|
+ cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
|
|
116
|
+ for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
117
|
+ xx := (x - ycbcr.Rect.Min.X)
|
|
118
|
+ yi := yy + xx
|
|
119
|
+ ci := cy + xx/2
|
|
120
|
+ ycbcr.Y[yi] = p.Pix[off+0]
|
|
121
|
+ ycbcr.Cb[ci] = p.Pix[off+1]
|
|
122
|
+ ycbcr.Cr[ci] = p.Pix[off+2]
|
|
123
|
+ off += 3
|
|
124
|
+ }
|
|
125
|
+ }
|
|
126
|
+ case image.YCbCrSubsampleRatio440:
|
|
127
|
+ for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
128
|
+ yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
129
|
+ cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
|
|
130
|
+ for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
131
|
+ xx := (x - ycbcr.Rect.Min.X)
|
|
132
|
+ yi := yy + xx
|
|
133
|
+ ci := cy + xx
|
|
134
|
+ ycbcr.Y[yi] = p.Pix[off+0]
|
|
135
|
+ ycbcr.Cb[ci] = p.Pix[off+1]
|
|
136
|
+ ycbcr.Cr[ci] = p.Pix[off+2]
|
|
137
|
+ off += 3
|
|
138
|
+ }
|
|
139
|
+ }
|
|
140
|
+ default:
|
|
141
|
+ // Default to 4:4:4 subsampling.
|
|
142
|
+ for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
|
|
143
|
+ yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
|
|
144
|
+ cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
|
|
145
|
+ for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
|
|
146
|
+ xx := (x - ycbcr.Rect.Min.X)
|
|
147
|
+ yi := yy + xx
|
|
148
|
+ ci := cy + xx
|
|
149
|
+ ycbcr.Y[yi] = p.Pix[off+0]
|
|
150
|
+ ycbcr.Cb[ci] = p.Pix[off+1]
|
|
151
|
+ ycbcr.Cr[ci] = p.Pix[off+2]
|
|
152
|
+ off += 3
|
|
153
|
+ }
|
|
154
|
+ }
|
|
155
|
+ }
|
|
156
|
+ return ycbcr
|
|
157
|
+}
|
|
158
|
+
|
|
159
|
+// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
|
|
160
|
+func imageYCbCrToYCC(in *image.YCbCr) *ycc {
|
|
161
|
+ w, h := in.Rect.Dx(), in.Rect.Dy()
|
|
162
|
+ buf := make([]uint8, 3*w*h)
|
|
163
|
+ p := ycc{Pix: buf, Stride: 3 * w, Rect: in.Rect, SubsampleRatio: in.SubsampleRatio}
|
|
164
|
+ var off int
|
|
165
|
+
|
|
166
|
+ switch in.SubsampleRatio {
|
|
167
|
+ case image.YCbCrSubsampleRatio422:
|
|
168
|
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
169
|
+ yy := (y - in.Rect.Min.Y) * in.YStride
|
|
170
|
+ cy := (y - in.Rect.Min.Y) * in.CStride
|
|
171
|
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
172
|
+ xx := (x - in.Rect.Min.X)
|
|
173
|
+ yi := yy + xx
|
|
174
|
+ ci := cy + xx/2
|
|
175
|
+ p.Pix[off+0] = in.Y[yi]
|
|
176
|
+ p.Pix[off+1] = in.Cb[ci]
|
|
177
|
+ p.Pix[off+2] = in.Cr[ci]
|
|
178
|
+ off += 3
|
|
179
|
+ }
|
|
180
|
+ }
|
|
181
|
+ case image.YCbCrSubsampleRatio420:
|
|
182
|
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
183
|
+ yy := (y - in.Rect.Min.Y) * in.YStride
|
|
184
|
+ cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
|
|
185
|
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
186
|
+ xx := (x - in.Rect.Min.X)
|
|
187
|
+ yi := yy + xx
|
|
188
|
+ ci := cy + xx/2
|
|
189
|
+ p.Pix[off+0] = in.Y[yi]
|
|
190
|
+ p.Pix[off+1] = in.Cb[ci]
|
|
191
|
+ p.Pix[off+2] = in.Cr[ci]
|
|
192
|
+ off += 3
|
|
193
|
+ }
|
|
194
|
+ }
|
|
195
|
+ case image.YCbCrSubsampleRatio440:
|
|
196
|
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
197
|
+ yy := (y - in.Rect.Min.Y) * in.YStride
|
|
198
|
+ cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
|
|
199
|
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
200
|
+ xx := (x - in.Rect.Min.X)
|
|
201
|
+ yi := yy + xx
|
|
202
|
+ ci := cy + xx
|
|
203
|
+ p.Pix[off+0] = in.Y[yi]
|
|
204
|
+ p.Pix[off+1] = in.Cb[ci]
|
|
205
|
+ p.Pix[off+2] = in.Cr[ci]
|
|
206
|
+ off += 3
|
|
207
|
+ }
|
|
208
|
+ }
|
|
209
|
+ default:
|
|
210
|
+ // Default to 4:4:4 subsampling.
|
|
211
|
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
|
|
212
|
+ yy := (y - in.Rect.Min.Y) * in.YStride
|
|
213
|
+ cy := (y - in.Rect.Min.Y) * in.CStride
|
|
214
|
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
|
|
215
|
+ xx := (x - in.Rect.Min.X)
|
|
216
|
+ yi := yy + xx
|
|
217
|
+ ci := cy + xx
|
|
218
|
+ p.Pix[off+0] = in.Y[yi]
|
|
219
|
+ p.Pix[off+1] = in.Cb[ci]
|
|
220
|
+ p.Pix[off+2] = in.Cr[ci]
|
|
221
|
+ off += 3
|
|
222
|
+ }
|
|
223
|
+ }
|
|
224
|
+ }
|
|
225
|
+ return &p
|
|
226
|
+}
|