ires 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +111 -0
  4. data/Rakefile +30 -0
  5. data/ext/Gopkg.lock +21 -0
  6. data/ext/Gopkg.toml +30 -0
  7. data/ext/main.go +90 -0
  8. data/ext/operate/image.go +139 -0
  9. data/ext/util/uri/uri.go +96 -0
  10. data/ext/vendor/github.com/nfnt/resize/LICENSE +13 -0
  11. data/ext/vendor/github.com/nfnt/resize/README.md +149 -0
  12. data/ext/vendor/github.com/nfnt/resize/converter.go +438 -0
  13. data/ext/vendor/github.com/nfnt/resize/converter_test.go +43 -0
  14. data/ext/vendor/github.com/nfnt/resize/filters.go +143 -0
  15. data/ext/vendor/github.com/nfnt/resize/nearest.go +318 -0
  16. data/ext/vendor/github.com/nfnt/resize/nearest_test.go +57 -0
  17. data/ext/vendor/github.com/nfnt/resize/resize.go +614 -0
  18. data/ext/vendor/github.com/nfnt/resize/resize_test.go +330 -0
  19. data/ext/vendor/github.com/nfnt/resize/thumbnail.go +55 -0
  20. data/ext/vendor/github.com/nfnt/resize/thumbnail_test.go +47 -0
  21. data/ext/vendor/github.com/nfnt/resize/ycc.go +227 -0
  22. data/ext/vendor/github.com/nfnt/resize/ycc_test.go +214 -0
  23. data/ext/vendor/github.com/oliamb/cutter/LICENSE +20 -0
  24. data/ext/vendor/github.com/oliamb/cutter/README.md +88 -0
  25. data/ext/vendor/github.com/oliamb/cutter/benchmark_test.go +54 -0
  26. data/ext/vendor/github.com/oliamb/cutter/cutter.go +183 -0
  27. data/ext/vendor/github.com/oliamb/cutter/cutter/main.go +68 -0
  28. data/ext/vendor/github.com/oliamb/cutter/cutter_test.go +267 -0
  29. data/ext/vendor/github.com/oliamb/cutter/example_test.go +35 -0
  30. data/ext/vendor/github.com/oliamb/cutter/fixtures/gopher.jpg +0 -0
  31. data/lib/ires.rb +4 -0
  32. data/lib/ires/engine.rb +7 -0
  33. data/lib/ires/service.rb +19 -0
  34. data/lib/ires/util.rb +39 -0
  35. data/lib/ires/version.rb +3 -0
  36. data/lib/ires/view_helper.rb +42 -0
  37. data/lib/tasks/ires.rake +11 -0
  38. data/shared/darwin/ires.h +64 -0
  39. data/shared/darwin/ires.so +0 -0
  40. data/shared/linux/ires.h +64 -0
  41. data/shared/linux/ires.so +0 -0
  42. metadata +154 -0
@@ -0,0 +1,330 @@
1
+ package resize
2
+
3
+ import (
4
+ "image"
5
+ "image/color"
6
+ "runtime"
7
+ "testing"
8
+ )
9
+
10
+ var img = image.NewGray16(image.Rect(0, 0, 3, 3))
11
+
12
+ func init() {
13
+ runtime.GOMAXPROCS(runtime.NumCPU())
14
+ img.Set(1, 1, color.White)
15
+ }
16
+
17
+ func Test_Param1(t *testing.T) {
18
+ m := Resize(0, 0, img, NearestNeighbor)
19
+ if m.Bounds() != img.Bounds() {
20
+ t.Fail()
21
+ }
22
+ }
23
+
24
+ func Test_Param2(t *testing.T) {
25
+ m := Resize(100, 0, img, NearestNeighbor)
26
+ if m.Bounds() != image.Rect(0, 0, 100, 100) {
27
+ t.Fail()
28
+ }
29
+ }
30
+
31
+ func Test_ZeroImg(t *testing.T) {
32
+ zeroImg := image.NewGray16(image.Rect(0, 0, 0, 0))
33
+
34
+ m := Resize(0, 0, zeroImg, NearestNeighbor)
35
+ if m.Bounds() != zeroImg.Bounds() {
36
+ t.Fail()
37
+ }
38
+ }
39
+
40
+ func Test_CorrectResize(t *testing.T) {
41
+ zeroImg := image.NewGray16(image.Rect(0, 0, 256, 256))
42
+
43
+ m := Resize(60, 0, zeroImg, NearestNeighbor)
44
+ if m.Bounds() != image.Rect(0, 0, 60, 60) {
45
+ t.Fail()
46
+ }
47
+ }
48
+
49
+ func Test_SameColorWithRGBA(t *testing.T) {
50
+ img := image.NewRGBA(image.Rect(0, 0, 20, 20))
51
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
52
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
53
+ img.SetRGBA(x, y, color.RGBA{0x80, 0x80, 0x80, 0xFF})
54
+ }
55
+ }
56
+ out := Resize(10, 10, img, Lanczos3)
57
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
58
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
59
+ color := out.At(x, y).(color.RGBA)
60
+ if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
61
+ t.Errorf("%+v", color)
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ func Test_SameColorWithNRGBA(t *testing.T) {
68
+ img := image.NewNRGBA(image.Rect(0, 0, 20, 20))
69
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
70
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
71
+ img.SetNRGBA(x, y, color.NRGBA{0x80, 0x80, 0x80, 0xFF})
72
+ }
73
+ }
74
+ out := Resize(10, 10, img, Lanczos3)
75
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
76
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
77
+ color := out.At(x, y).(color.RGBA)
78
+ if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
79
+ t.Errorf("%+v", color)
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ func Test_SameColorWithRGBA64(t *testing.T) {
86
+ img := image.NewRGBA64(image.Rect(0, 0, 20, 20))
87
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
88
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
89
+ img.SetRGBA64(x, y, color.RGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
90
+ }
91
+ }
92
+ out := Resize(10, 10, img, Lanczos3)
93
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
94
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
95
+ color := out.At(x, y).(color.RGBA64)
96
+ if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
97
+ t.Errorf("%+v", color)
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ func Test_SameColorWithNRGBA64(t *testing.T) {
104
+ img := image.NewNRGBA64(image.Rect(0, 0, 20, 20))
105
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
106
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
107
+ img.SetNRGBA64(x, y, color.NRGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
108
+ }
109
+ }
110
+ out := Resize(10, 10, img, Lanczos3)
111
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
112
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
113
+ color := out.At(x, y).(color.RGBA64)
114
+ if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
115
+ t.Errorf("%+v", color)
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ func Test_SameColorWithGray(t *testing.T) {
122
+ img := image.NewGray(image.Rect(0, 0, 20, 20))
123
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
124
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
125
+ img.SetGray(x, y, color.Gray{0x80})
126
+ }
127
+ }
128
+ out := Resize(10, 10, img, Lanczos3)
129
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
130
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
131
+ color := out.At(x, y).(color.Gray)
132
+ if color.Y != 0x80 {
133
+ t.Errorf("%+v", color)
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ func Test_SameColorWithGray16(t *testing.T) {
140
+ img := image.NewGray16(image.Rect(0, 0, 20, 20))
141
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
142
+ for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
143
+ img.SetGray16(x, y, color.Gray16{0x8000})
144
+ }
145
+ }
146
+ out := Resize(10, 10, img, Lanczos3)
147
+ for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
148
+ for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
149
+ color := out.At(x, y).(color.Gray16)
150
+ if color.Y != 0x8000 {
151
+ t.Errorf("%+v", color)
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ func Test_Bounds(t *testing.T) {
158
+ img := image.NewRGBA(image.Rect(20, 10, 200, 99))
159
+ out := Resize(80, 80, img, Lanczos2)
160
+ out.At(0, 0)
161
+ }
162
+
163
+ func Test_SameSizeReturnsOriginal(t *testing.T) {
164
+ img := image.NewRGBA(image.Rect(0, 0, 10, 10))
165
+ out := Resize(0, 0, img, Lanczos2)
166
+
167
+ if img != out {
168
+ t.Fail()
169
+ }
170
+
171
+ out = Resize(10, 10, img, Lanczos2)
172
+
173
+ if img != out {
174
+ t.Fail()
175
+ }
176
+ }
177
+
178
+ func Test_PixelCoordinates(t *testing.T) {
179
+ checkers := image.NewGray(image.Rect(0, 0, 4, 4))
180
+ checkers.Pix = []uint8{
181
+ 255, 0, 255, 0,
182
+ 0, 255, 0, 255,
183
+ 255, 0, 255, 0,
184
+ 0, 255, 0, 255,
185
+ }
186
+
187
+ resized := Resize(12, 12, checkers, NearestNeighbor).(*image.Gray)
188
+
189
+ if resized.Pix[0] != 255 || resized.Pix[1] != 255 || resized.Pix[2] != 255 {
190
+ t.Fail()
191
+ }
192
+
193
+ if resized.Pix[3] != 0 || resized.Pix[4] != 0 || resized.Pix[5] != 0 {
194
+ t.Fail()
195
+ }
196
+ }
197
+
198
+ func Test_ResizeWithPremultipliedAlpha(t *testing.T) {
199
+ img := image.NewRGBA(image.Rect(0, 0, 1, 4))
200
+ for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
201
+ // 0x80 = 0.5 * 0xFF.
202
+ img.SetRGBA(0, y, color.RGBA{0x80, 0x80, 0x80, 0x80})
203
+ }
204
+
205
+ out := Resize(1, 2, img, MitchellNetravali)
206
+
207
+ outputColor := out.At(0, 0).(color.RGBA)
208
+ if outputColor.R != 0x80 {
209
+ t.Fail()
210
+ }
211
+ }
212
+
213
+ func Test_ResizeWithTranslucentColor(t *testing.T) {
214
+ img := image.NewNRGBA(image.Rect(0, 0, 1, 2))
215
+
216
+ // Set the pixel colors to an "invisible green" and white.
217
+ // After resizing, the green shouldn't be visible.
218
+ img.SetNRGBA(0, 0, color.NRGBA{0x00, 0xFF, 0x00, 0x00})
219
+ img.SetNRGBA(0, 1, color.NRGBA{0x00, 0x00, 0x00, 0xFF})
220
+
221
+ out := Resize(1, 1, img, Bilinear)
222
+
223
+ _, g, _, _ := out.At(0, 0).RGBA()
224
+ if g != 0x00 {
225
+ t.Errorf("%+v", g)
226
+ }
227
+ }
228
+
229
+ const (
230
+ // Use a small image size for benchmarks. We don't want memory performance
231
+ // to affect the benchmark results.
232
+ benchMaxX = 250
233
+ benchMaxY = 250
234
+
235
+ // Resize values near the original size require increase the amount of time
236
+ // resize spends converting the image.
237
+ benchWidth = 200
238
+ benchHeight = 200
239
+ )
240
+
241
+ func benchRGBA(b *testing.B, interp InterpolationFunction) {
242
+ m := image.NewRGBA(image.Rect(0, 0, benchMaxX, benchMaxY))
243
+ // Initialize m's pixels to create a non-uniform image.
244
+ for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
245
+ for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
246
+ i := m.PixOffset(x, y)
247
+ m.Pix[i+0] = uint8(y + 4*x)
248
+ m.Pix[i+1] = uint8(y + 4*x)
249
+ m.Pix[i+2] = uint8(y + 4*x)
250
+ m.Pix[i+3] = uint8(4*y + x)
251
+ }
252
+ }
253
+
254
+ var out image.Image
255
+ b.ResetTimer()
256
+ for i := 0; i < b.N; i++ {
257
+ out = Resize(benchWidth, benchHeight, m, interp)
258
+ }
259
+ out.At(0, 0)
260
+ }
261
+
262
+ // The names of some interpolation functions are truncated so that the columns
263
+ // of 'go test -bench' line up.
264
+ func Benchmark_Nearest_RGBA(b *testing.B) {
265
+ benchRGBA(b, NearestNeighbor)
266
+ }
267
+
268
+ func Benchmark_Bilinear_RGBA(b *testing.B) {
269
+ benchRGBA(b, Bilinear)
270
+ }
271
+
272
+ func Benchmark_Bicubic_RGBA(b *testing.B) {
273
+ benchRGBA(b, Bicubic)
274
+ }
275
+
276
+ func Benchmark_Mitchell_RGBA(b *testing.B) {
277
+ benchRGBA(b, MitchellNetravali)
278
+ }
279
+
280
+ func Benchmark_Lanczos2_RGBA(b *testing.B) {
281
+ benchRGBA(b, Lanczos2)
282
+ }
283
+
284
+ func Benchmark_Lanczos3_RGBA(b *testing.B) {
285
+ benchRGBA(b, Lanczos3)
286
+ }
287
+
288
+ func benchYCbCr(b *testing.B, interp InterpolationFunction) {
289
+ m := image.NewYCbCr(image.Rect(0, 0, benchMaxX, benchMaxY), image.YCbCrSubsampleRatio422)
290
+ // Initialize m's pixels to create a non-uniform image.
291
+ for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
292
+ for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
293
+ yi := m.YOffset(x, y)
294
+ ci := m.COffset(x, y)
295
+ m.Y[yi] = uint8(16*y + x)
296
+ m.Cb[ci] = uint8(y + 16*x)
297
+ m.Cr[ci] = uint8(y + 16*x)
298
+ }
299
+ }
300
+ var out image.Image
301
+ b.ResetTimer()
302
+ for i := 0; i < b.N; i++ {
303
+ out = Resize(benchWidth, benchHeight, m, interp)
304
+ }
305
+ out.At(0, 0)
306
+ }
307
+
308
+ func Benchmark_Nearest_YCC(b *testing.B) {
309
+ benchYCbCr(b, NearestNeighbor)
310
+ }
311
+
312
+ func Benchmark_Bilinear_YCC(b *testing.B) {
313
+ benchYCbCr(b, Bilinear)
314
+ }
315
+
316
+ func Benchmark_Bicubic_YCC(b *testing.B) {
317
+ benchYCbCr(b, Bicubic)
318
+ }
319
+
320
+ func Benchmark_Mitchell_YCC(b *testing.B) {
321
+ benchYCbCr(b, MitchellNetravali)
322
+ }
323
+
324
+ func Benchmark_Lanczos2_YCC(b *testing.B) {
325
+ benchYCbCr(b, Lanczos2)
326
+ }
327
+
328
+ func Benchmark_Lanczos3_YCC(b *testing.B) {
329
+ benchYCbCr(b, Lanczos3)
330
+ }
@@ -0,0 +1,55 @@
1
+ /*
2
+ Copyright (c) 2012, Jan Schlicht <jan.schlicht@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
+ )
22
+
23
+ // Thumbnail will downscale provided image to max width and height preserving
24
+ // original aspect ratio and using the interpolation function interp.
25
+ // It will return original image, without processing it, if original sizes
26
+ // are already smaller than provided constraints.
27
+ func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image {
28
+ origBounds := img.Bounds()
29
+ origWidth := uint(origBounds.Dx())
30
+ origHeight := uint(origBounds.Dy())
31
+ newWidth, newHeight := origWidth, origHeight
32
+
33
+ // Return original image if it have same or smaller size as constraints
34
+ if maxWidth >= origWidth && maxHeight >= origHeight {
35
+ return img
36
+ }
37
+
38
+ // Preserve aspect ratio
39
+ if origWidth > maxWidth {
40
+ newHeight = uint(origHeight * maxWidth / origWidth)
41
+ if newHeight < 1 {
42
+ newHeight = 1
43
+ }
44
+ newWidth = maxWidth
45
+ }
46
+
47
+ if newHeight > maxHeight {
48
+ newWidth = uint(newWidth * maxHeight / newHeight)
49
+ if newWidth < 1 {
50
+ newWidth = 1
51
+ }
52
+ newHeight = maxHeight
53
+ }
54
+ return Resize(newWidth, newHeight, img, interp)
55
+ }
@@ -0,0 +1,47 @@
1
+ package resize
2
+
3
+ import (
4
+ "image"
5
+ "runtime"
6
+ "testing"
7
+ )
8
+
9
+ func init() {
10
+ runtime.GOMAXPROCS(runtime.NumCPU())
11
+ }
12
+
13
+ var thumbnailTests = []struct {
14
+ origWidth int
15
+ origHeight int
16
+ maxWidth uint
17
+ maxHeight uint
18
+ expectedWidth uint
19
+ expectedHeight uint
20
+ }{
21
+ {5, 5, 10, 10, 5, 5},
22
+ {10, 10, 5, 5, 5, 5},
23
+ {10, 50, 10, 10, 2, 10},
24
+ {50, 10, 10, 10, 10, 2},
25
+ {50, 100, 60, 90, 45, 90},
26
+ {120, 100, 60, 90, 60, 50},
27
+ {200, 250, 200, 150, 120, 150},
28
+ }
29
+
30
+ func TestThumbnail(t *testing.T) {
31
+ for i, tt := range thumbnailTests {
32
+ img := image.NewGray16(image.Rect(0, 0, tt.origWidth, tt.origHeight))
33
+
34
+ outImg := Thumbnail(tt.maxWidth, tt.maxHeight, img, NearestNeighbor)
35
+
36
+ newWidth := uint(outImg.Bounds().Dx())
37
+ newHeight := uint(outImg.Bounds().Dy())
38
+ if newWidth != tt.expectedWidth ||
39
+ newHeight != tt.expectedHeight {
40
+ t.Errorf("%d. Thumbnail(%v, %v, img, NearestNeighbor) => "+
41
+ "width: %v, height: %v, want width: %v, height: %v",
42
+ i, tt.maxWidth, tt.maxHeight,
43
+ newWidth, newHeight, tt.expectedWidth, tt.expectedHeight,
44
+ )
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,227 @@
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.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.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
+ r := image.Rect(0, 0, w, h)
163
+ buf := make([]uint8, 3*w*h)
164
+ p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio}
165
+ var off int
166
+
167
+ switch in.SubsampleRatio {
168
+ case image.YCbCrSubsampleRatio422:
169
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
170
+ yy := (y - in.Rect.Min.Y) * in.YStride
171
+ cy := (y - in.Rect.Min.Y) * in.CStride
172
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
173
+ xx := (x - in.Rect.Min.X)
174
+ yi := yy + xx
175
+ ci := cy + xx/2
176
+ p.Pix[off+0] = in.Y[yi]
177
+ p.Pix[off+1] = in.Cb[ci]
178
+ p.Pix[off+2] = in.Cr[ci]
179
+ off += 3
180
+ }
181
+ }
182
+ case image.YCbCrSubsampleRatio420:
183
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
184
+ yy := (y - in.Rect.Min.Y) * in.YStride
185
+ cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
186
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
187
+ xx := (x - in.Rect.Min.X)
188
+ yi := yy + xx
189
+ ci := cy + xx/2
190
+ p.Pix[off+0] = in.Y[yi]
191
+ p.Pix[off+1] = in.Cb[ci]
192
+ p.Pix[off+2] = in.Cr[ci]
193
+ off += 3
194
+ }
195
+ }
196
+ case image.YCbCrSubsampleRatio440:
197
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
198
+ yy := (y - in.Rect.Min.Y) * in.YStride
199
+ cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
200
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
201
+ xx := (x - in.Rect.Min.X)
202
+ yi := yy + xx
203
+ ci := cy + xx
204
+ p.Pix[off+0] = in.Y[yi]
205
+ p.Pix[off+1] = in.Cb[ci]
206
+ p.Pix[off+2] = in.Cr[ci]
207
+ off += 3
208
+ }
209
+ }
210
+ default:
211
+ // Default to 4:4:4 subsampling.
212
+ for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
213
+ yy := (y - in.Rect.Min.Y) * in.YStride
214
+ cy := (y - in.Rect.Min.Y) * in.CStride
215
+ for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
216
+ xx := (x - in.Rect.Min.X)
217
+ yi := yy + xx
218
+ ci := cy + xx
219
+ p.Pix[off+0] = in.Y[yi]
220
+ p.Pix[off+1] = in.Cb[ci]
221
+ p.Pix[off+2] = in.Cr[ci]
222
+ off += 3
223
+ }
224
+ }
225
+ }
226
+ return &p
227
+ }