ires 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,57 @@
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 "testing"
20
+
21
+ func Test_FloatToUint8(t *testing.T) {
22
+ var testData = []struct {
23
+ in float32
24
+ expected uint8
25
+ }{
26
+ {0, 0},
27
+ {255, 255},
28
+ {128, 128},
29
+ {1, 1},
30
+ {256, 255},
31
+ }
32
+ for _, test := range testData {
33
+ actual := floatToUint8(test.in)
34
+ if actual != test.expected {
35
+ t.Fail()
36
+ }
37
+ }
38
+ }
39
+
40
+ func Test_FloatToUint16(t *testing.T) {
41
+ var testData = []struct {
42
+ in float32
43
+ expected uint16
44
+ }{
45
+ {0, 0},
46
+ {65535, 65535},
47
+ {128, 128},
48
+ {1, 1},
49
+ {65536, 65535},
50
+ }
51
+ for _, test := range testData {
52
+ actual := floatToUint16(test.in)
53
+ if actual != test.expected {
54
+ t.Fail()
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,614 @@
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 implements various image resizing methods.
18
+ //
19
+ // The package works with the Image interface described in the image package.
20
+ // Various interpolation methods are provided and multiple processors may be
21
+ // utilized in the computations.
22
+ //
23
+ // Example:
24
+ // imgResized := resize.Resize(1000, 0, imgOld, resize.MitchellNetravali)
25
+ package resize
26
+
27
+ import (
28
+ "image"
29
+ "runtime"
30
+ "sync"
31
+ )
32
+
33
+ // An InterpolationFunction provides the parameters that describe an
34
+ // interpolation kernel. It returns the number of samples to take
35
+ // and the kernel function to use for sampling.
36
+ type InterpolationFunction int
37
+
38
+ // InterpolationFunction constants
39
+ const (
40
+ // Nearest-neighbor interpolation
41
+ NearestNeighbor InterpolationFunction = iota
42
+ // Bilinear interpolation
43
+ Bilinear
44
+ // Bicubic interpolation (with cubic hermite spline)
45
+ Bicubic
46
+ // Mitchell-Netravali interpolation
47
+ MitchellNetravali
48
+ // Lanczos interpolation (a=2)
49
+ Lanczos2
50
+ // Lanczos interpolation (a=3)
51
+ Lanczos3
52
+ )
53
+
54
+ // kernal, returns an InterpolationFunctions taps and kernel.
55
+ func (i InterpolationFunction) kernel() (int, func(float64) float64) {
56
+ switch i {
57
+ case Bilinear:
58
+ return 2, linear
59
+ case Bicubic:
60
+ return 4, cubic
61
+ case MitchellNetravali:
62
+ return 4, mitchellnetravali
63
+ case Lanczos2:
64
+ return 4, lanczos2
65
+ case Lanczos3:
66
+ return 6, lanczos3
67
+ default:
68
+ // Default to NearestNeighbor.
69
+ return 2, nearest
70
+ }
71
+ }
72
+
73
+ // values <1 will sharpen the image
74
+ var blur = 1.0
75
+
76
+ // Resize scales an image to new width and height using the interpolation function interp.
77
+ // A new image with the given dimensions will be returned.
78
+ // If one of the parameters width or height is set to 0, its size will be calculated so that
79
+ // the aspect ratio is that of the originating image.
80
+ // The resizing algorithm uses channels for parallel computation.
81
+ func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image {
82
+ scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
83
+ if width == 0 {
84
+ width = uint(0.7 + float64(img.Bounds().Dx())/scaleX)
85
+ }
86
+ if height == 0 {
87
+ height = uint(0.7 + float64(img.Bounds().Dy())/scaleY)
88
+ }
89
+
90
+ // Trivial case: return input image
91
+ if int(width) == img.Bounds().Dx() && int(height) == img.Bounds().Dy() {
92
+ return img
93
+ }
94
+
95
+ if interp == NearestNeighbor {
96
+ return resizeNearest(width, height, scaleX, scaleY, img, interp)
97
+ }
98
+
99
+ taps, kernel := interp.kernel()
100
+ cpus := runtime.GOMAXPROCS(0)
101
+ wg := sync.WaitGroup{}
102
+
103
+ // Generic access to image.Image is slow in tight loops.
104
+ // The optimal access has to be determined from the concrete image type.
105
+ switch input := img.(type) {
106
+ case *image.RGBA:
107
+ // 8-bit precision
108
+ temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
109
+ result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
110
+
111
+ // horizontal filter, results in transposed temporary image
112
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
113
+ wg.Add(cpus)
114
+ for i := 0; i < cpus; i++ {
115
+ slice := makeSlice(temp, i, cpus).(*image.RGBA)
116
+ go func() {
117
+ defer wg.Done()
118
+ resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength)
119
+ }()
120
+ }
121
+ wg.Wait()
122
+
123
+ // horizontal filter on transposed image, result is not transposed
124
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
125
+ wg.Add(cpus)
126
+ for i := 0; i < cpus; i++ {
127
+ slice := makeSlice(result, i, cpus).(*image.RGBA)
128
+ go func() {
129
+ defer wg.Done()
130
+ resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
131
+ }()
132
+ }
133
+ wg.Wait()
134
+ return result
135
+ case *image.NRGBA:
136
+ // 8-bit precision
137
+ temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
138
+ result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
139
+
140
+ // horizontal filter, results in transposed temporary image
141
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
142
+ wg.Add(cpus)
143
+ for i := 0; i < cpus; i++ {
144
+ slice := makeSlice(temp, i, cpus).(*image.RGBA)
145
+ go func() {
146
+ defer wg.Done()
147
+ resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
148
+ }()
149
+ }
150
+ wg.Wait()
151
+
152
+ // horizontal filter on transposed image, result is not transposed
153
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
154
+ wg.Add(cpus)
155
+ for i := 0; i < cpus; i++ {
156
+ slice := makeSlice(result, i, cpus).(*image.RGBA)
157
+ go func() {
158
+ defer wg.Done()
159
+ resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
160
+ }()
161
+ }
162
+ wg.Wait()
163
+ return result
164
+
165
+ case *image.YCbCr:
166
+ // 8-bit precision
167
+ // accessing the YCbCr arrays in a tight loop is slow.
168
+ // converting the image to ycc increases performance by 2x.
169
+ temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
170
+ result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
171
+
172
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
173
+ in := imageYCbCrToYCC(input)
174
+ wg.Add(cpus)
175
+ for i := 0; i < cpus; i++ {
176
+ slice := makeSlice(temp, i, cpus).(*ycc)
177
+ go func() {
178
+ defer wg.Done()
179
+ resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
180
+ }()
181
+ }
182
+ wg.Wait()
183
+
184
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
185
+ wg.Add(cpus)
186
+ for i := 0; i < cpus; i++ {
187
+ slice := makeSlice(result, i, cpus).(*ycc)
188
+ go func() {
189
+ defer wg.Done()
190
+ resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
191
+ }()
192
+ }
193
+ wg.Wait()
194
+ return result.YCbCr()
195
+ case *image.RGBA64:
196
+ // 16-bit precision
197
+ temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
198
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
199
+
200
+ // horizontal filter, results in transposed temporary image
201
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
202
+ wg.Add(cpus)
203
+ for i := 0; i < cpus; i++ {
204
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
205
+ go func() {
206
+ defer wg.Done()
207
+ resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
208
+ }()
209
+ }
210
+ wg.Wait()
211
+
212
+ // horizontal filter on transposed image, result is not transposed
213
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
214
+ wg.Add(cpus)
215
+ for i := 0; i < cpus; i++ {
216
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
217
+ go func() {
218
+ defer wg.Done()
219
+ resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
220
+ }()
221
+ }
222
+ wg.Wait()
223
+ return result
224
+ case *image.NRGBA64:
225
+ // 16-bit precision
226
+ temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
227
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
228
+
229
+ // horizontal filter, results in transposed temporary image
230
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
231
+ wg.Add(cpus)
232
+ for i := 0; i < cpus; i++ {
233
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
234
+ go func() {
235
+ defer wg.Done()
236
+ resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
237
+ }()
238
+ }
239
+ wg.Wait()
240
+
241
+ // horizontal filter on transposed image, result is not transposed
242
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
243
+ wg.Add(cpus)
244
+ for i := 0; i < cpus; i++ {
245
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
246
+ go func() {
247
+ defer wg.Done()
248
+ resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
249
+ }()
250
+ }
251
+ wg.Wait()
252
+ return result
253
+ case *image.Gray:
254
+ // 8-bit precision
255
+ temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
256
+ result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
257
+
258
+ // horizontal filter, results in transposed temporary image
259
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
260
+ wg.Add(cpus)
261
+ for i := 0; i < cpus; i++ {
262
+ slice := makeSlice(temp, i, cpus).(*image.Gray)
263
+ go func() {
264
+ defer wg.Done()
265
+ resizeGray(input, slice, scaleX, coeffs, offset, filterLength)
266
+ }()
267
+ }
268
+ wg.Wait()
269
+
270
+ // horizontal filter on transposed image, result is not transposed
271
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
272
+ wg.Add(cpus)
273
+ for i := 0; i < cpus; i++ {
274
+ slice := makeSlice(result, i, cpus).(*image.Gray)
275
+ go func() {
276
+ defer wg.Done()
277
+ resizeGray(temp, slice, scaleY, coeffs, offset, filterLength)
278
+ }()
279
+ }
280
+ wg.Wait()
281
+ return result
282
+ case *image.Gray16:
283
+ // 16-bit precision
284
+ temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
285
+ result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
286
+
287
+ // horizontal filter, results in transposed temporary image
288
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
289
+ wg.Add(cpus)
290
+ for i := 0; i < cpus; i++ {
291
+ slice := makeSlice(temp, i, cpus).(*image.Gray16)
292
+ go func() {
293
+ defer wg.Done()
294
+ resizeGray16(input, slice, scaleX, coeffs, offset, filterLength)
295
+ }()
296
+ }
297
+ wg.Wait()
298
+
299
+ // horizontal filter on transposed image, result is not transposed
300
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
301
+ wg.Add(cpus)
302
+ for i := 0; i < cpus; i++ {
303
+ slice := makeSlice(result, i, cpus).(*image.Gray16)
304
+ go func() {
305
+ defer wg.Done()
306
+ resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength)
307
+ }()
308
+ }
309
+ wg.Wait()
310
+ return result
311
+ default:
312
+ // 16-bit precision
313
+ temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
314
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
315
+
316
+ // horizontal filter, results in transposed temporary image
317
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
318
+ wg.Add(cpus)
319
+ for i := 0; i < cpus; i++ {
320
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
321
+ go func() {
322
+ defer wg.Done()
323
+ resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength)
324
+ }()
325
+ }
326
+ wg.Wait()
327
+
328
+ // horizontal filter on transposed image, result is not transposed
329
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
330
+ wg.Add(cpus)
331
+ for i := 0; i < cpus; i++ {
332
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
333
+ go func() {
334
+ defer wg.Done()
335
+ resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
336
+ }()
337
+ }
338
+ wg.Wait()
339
+ return result
340
+ }
341
+ }
342
+
343
+ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image {
344
+ taps, _ := interp.kernel()
345
+ cpus := runtime.GOMAXPROCS(0)
346
+ wg := sync.WaitGroup{}
347
+
348
+ switch input := img.(type) {
349
+ case *image.RGBA:
350
+ // 8-bit precision
351
+ temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
352
+ result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
353
+
354
+ // horizontal filter, results in transposed temporary image
355
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
356
+ wg.Add(cpus)
357
+ for i := 0; i < cpus; i++ {
358
+ slice := makeSlice(temp, i, cpus).(*image.RGBA)
359
+ go func() {
360
+ defer wg.Done()
361
+ nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength)
362
+ }()
363
+ }
364
+ wg.Wait()
365
+
366
+ // horizontal filter on transposed image, result is not transposed
367
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
368
+ wg.Add(cpus)
369
+ for i := 0; i < cpus; i++ {
370
+ slice := makeSlice(result, i, cpus).(*image.RGBA)
371
+ go func() {
372
+ defer wg.Done()
373
+ nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
374
+ }()
375
+ }
376
+ wg.Wait()
377
+ return result
378
+ case *image.NRGBA:
379
+ // 8-bit precision
380
+ temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
381
+ result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
382
+
383
+ // horizontal filter, results in transposed temporary image
384
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
385
+ wg.Add(cpus)
386
+ for i := 0; i < cpus; i++ {
387
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA)
388
+ go func() {
389
+ defer wg.Done()
390
+ nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
391
+ }()
392
+ }
393
+ wg.Wait()
394
+
395
+ // horizontal filter on transposed image, result is not transposed
396
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
397
+ wg.Add(cpus)
398
+ for i := 0; i < cpus; i++ {
399
+ slice := makeSlice(result, i, cpus).(*image.NRGBA)
400
+ go func() {
401
+ defer wg.Done()
402
+ nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
403
+ }()
404
+ }
405
+ wg.Wait()
406
+ return result
407
+ case *image.YCbCr:
408
+ // 8-bit precision
409
+ // accessing the YCbCr arrays in a tight loop is slow.
410
+ // converting the image to ycc increases performance by 2x.
411
+ temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
412
+ result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
413
+
414
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
415
+ in := imageYCbCrToYCC(input)
416
+ wg.Add(cpus)
417
+ for i := 0; i < cpus; i++ {
418
+ slice := makeSlice(temp, i, cpus).(*ycc)
419
+ go func() {
420
+ defer wg.Done()
421
+ nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
422
+ }()
423
+ }
424
+ wg.Wait()
425
+
426
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
427
+ wg.Add(cpus)
428
+ for i := 0; i < cpus; i++ {
429
+ slice := makeSlice(result, i, cpus).(*ycc)
430
+ go func() {
431
+ defer wg.Done()
432
+ nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
433
+ }()
434
+ }
435
+ wg.Wait()
436
+ return result.YCbCr()
437
+ case *image.RGBA64:
438
+ // 16-bit precision
439
+ temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
440
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
441
+
442
+ // horizontal filter, results in transposed temporary image
443
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
444
+ wg.Add(cpus)
445
+ for i := 0; i < cpus; i++ {
446
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
447
+ go func() {
448
+ defer wg.Done()
449
+ nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
450
+ }()
451
+ }
452
+ wg.Wait()
453
+
454
+ // horizontal filter on transposed image, result is not transposed
455
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
456
+ wg.Add(cpus)
457
+ for i := 0; i < cpus; i++ {
458
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
459
+ go func() {
460
+ defer wg.Done()
461
+ nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
462
+ }()
463
+ }
464
+ wg.Wait()
465
+ return result
466
+ case *image.NRGBA64:
467
+ // 16-bit precision
468
+ temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
469
+ result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
470
+
471
+ // horizontal filter, results in transposed temporary image
472
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
473
+ wg.Add(cpus)
474
+ for i := 0; i < cpus; i++ {
475
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
476
+ go func() {
477
+ defer wg.Done()
478
+ nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
479
+ }()
480
+ }
481
+ wg.Wait()
482
+
483
+ // horizontal filter on transposed image, result is not transposed
484
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
485
+ wg.Add(cpus)
486
+ for i := 0; i < cpus; i++ {
487
+ slice := makeSlice(result, i, cpus).(*image.NRGBA64)
488
+ go func() {
489
+ defer wg.Done()
490
+ nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
491
+ }()
492
+ }
493
+ wg.Wait()
494
+ return result
495
+ case *image.Gray:
496
+ // 8-bit precision
497
+ temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
498
+ result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
499
+
500
+ // horizontal filter, results in transposed temporary image
501
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
502
+ wg.Add(cpus)
503
+ for i := 0; i < cpus; i++ {
504
+ slice := makeSlice(temp, i, cpus).(*image.Gray)
505
+ go func() {
506
+ defer wg.Done()
507
+ nearestGray(input, slice, scaleX, coeffs, offset, filterLength)
508
+ }()
509
+ }
510
+ wg.Wait()
511
+
512
+ // horizontal filter on transposed image, result is not transposed
513
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
514
+ wg.Add(cpus)
515
+ for i := 0; i < cpus; i++ {
516
+ slice := makeSlice(result, i, cpus).(*image.Gray)
517
+ go func() {
518
+ defer wg.Done()
519
+ nearestGray(temp, slice, scaleY, coeffs, offset, filterLength)
520
+ }()
521
+ }
522
+ wg.Wait()
523
+ return result
524
+ case *image.Gray16:
525
+ // 16-bit precision
526
+ temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
527
+ result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
528
+
529
+ // horizontal filter, results in transposed temporary image
530
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
531
+ wg.Add(cpus)
532
+ for i := 0; i < cpus; i++ {
533
+ slice := makeSlice(temp, i, cpus).(*image.Gray16)
534
+ go func() {
535
+ defer wg.Done()
536
+ nearestGray16(input, slice, scaleX, coeffs, offset, filterLength)
537
+ }()
538
+ }
539
+ wg.Wait()
540
+
541
+ // horizontal filter on transposed image, result is not transposed
542
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
543
+ wg.Add(cpus)
544
+ for i := 0; i < cpus; i++ {
545
+ slice := makeSlice(result, i, cpus).(*image.Gray16)
546
+ go func() {
547
+ defer wg.Done()
548
+ nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength)
549
+ }()
550
+ }
551
+ wg.Wait()
552
+ return result
553
+ default:
554
+ // 16-bit precision
555
+ temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
556
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
557
+
558
+ // horizontal filter, results in transposed temporary image
559
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
560
+ wg.Add(cpus)
561
+ for i := 0; i < cpus; i++ {
562
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
563
+ go func() {
564
+ defer wg.Done()
565
+ nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength)
566
+ }()
567
+ }
568
+ wg.Wait()
569
+
570
+ // horizontal filter on transposed image, result is not transposed
571
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
572
+ wg.Add(cpus)
573
+ for i := 0; i < cpus; i++ {
574
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
575
+ go func() {
576
+ defer wg.Done()
577
+ nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
578
+ }()
579
+ }
580
+ wg.Wait()
581
+ return result
582
+ }
583
+
584
+ }
585
+
586
+ // Calculates scaling factors using old and new image dimensions.
587
+ func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) {
588
+ if width == 0 {
589
+ if height == 0 {
590
+ scaleX = 1.0
591
+ scaleY = 1.0
592
+ } else {
593
+ scaleY = oldHeight / float64(height)
594
+ scaleX = scaleY
595
+ }
596
+ } else {
597
+ scaleX = oldWidth / float64(width)
598
+ if height == 0 {
599
+ scaleY = scaleX
600
+ } else {
601
+ scaleY = oldHeight / float64(height)
602
+ }
603
+ }
604
+ return
605
+ }
606
+
607
+ type imageWithSubImage interface {
608
+ image.Image
609
+ SubImage(image.Rectangle) image.Image
610
+ }
611
+
612
+ func makeSlice(img imageWithSubImage, i, n int) image.Image {
613
+ return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n))
614
+ }