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,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
+ }