1package transform
2
3import (
4	"image"
5	"testing"
6
7	"github.com/anthonynsimon/bild/util"
8)
9
10func TestResize(t *testing.T) {
11	cases := []struct {
12		name     string
13		width    int
14		height   int
15		img      *image.RGBA
16		expected *image.RGBA
17	}{
18		{
19			name:   "empty",
20			width:  2,
21			height: 2,
22			img: &image.RGBA{
23				Stride: 0,
24				Rect:   image.Rect(0, 0, 0, 0),
25				Pix:    []uint8{},
26			},
27			expected: &image.RGBA{
28				Stride: 0,
29				Rect:   image.Rect(0, 0, 0, 0),
30				Pix:    []uint8{},
31			},
32		},
33		{
34			name:   "x1",
35			width:  1,
36			height: 1,
37			img: &image.RGBA{
38				Stride: 1 * 4,
39				Rect:   image.Rect(0, 0, 1, 1),
40				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
41			},
42			expected: &image.RGBA{
43				Stride: 1 * 4,
44				Rect:   image.Rect(0, 0, 1, 1),
45				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
46			},
47		},
48		{
49			name:   "x2",
50			width:  2,
51			height: 2,
52			img: &image.RGBA{
53				Stride: 1 * 4,
54				Rect:   image.Rect(0, 0, 1, 1),
55				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
56			},
57			expected: &image.RGBA{
58				Stride: 2 * 4,
59				Rect:   image.Rect(0, 0, 2, 2),
60				Pix: []uint8{
61					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
62					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
63				},
64			},
65		},
66		{
67			name:   "only horizontal x2",
68			width:  2,
69			height: 1,
70			img: &image.RGBA{
71				Stride: 1 * 4,
72				Rect:   image.Rect(0, 0, 1, 1),
73				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
74			},
75			expected: &image.RGBA{
76				Stride: 2 * 4,
77				Rect:   image.Rect(0, 0, 2, 1),
78				Pix: []uint8{
79					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
80				},
81			},
82		},
83		{
84			name:   "only vertical x2",
85			width:  1,
86			height: 2,
87			img: &image.RGBA{
88				Stride: 1 * 4,
89				Rect:   image.Rect(0, 0, 1, 1),
90				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
91			},
92			expected: &image.RGBA{
93				Stride: 1 * 4,
94				Rect:   image.Rect(0, 0, 1, 2),
95				Pix: []uint8{
96					0xFF, 0xFF, 0xFF, 0xFF,
97					0xFF, 0xFF, 0xFF, 0xFF,
98				},
99			},
100		},
101		{
102			name:   "x0.5",
103			width:  1,
104			height: 1,
105			img: &image.RGBA{
106				Stride: 1 * 4,
107				Rect:   image.Rect(0, 0, 1, 2),
108				Pix: []uint8{
109					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
110					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
111				},
112			},
113			expected: &image.RGBA{
114				Stride: 1 * 4,
115				Rect:   image.Rect(0, 0, 1, 1),
116				Pix:    []uint8{0xFF, 0xFF, 0xFF, 0xFF},
117			},
118		},
119		{
120			name:   "only horizontal x0.5",
121			width:  1,
122			height: 2,
123			img: &image.RGBA{
124				Stride: 2 * 4,
125				Rect:   image.Rect(0, 0, 2, 2),
126				Pix: []uint8{
127					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
128					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
129				},
130			},
131			expected: &image.RGBA{
132				Stride: 1 * 4,
133				Rect:   image.Rect(0, 0, 1, 2),
134				Pix: []uint8{
135					0xFF, 0xFF, 0xFF, 0xFF,
136					0xFF, 0xFF, 0xFF, 0xFF,
137				},
138			},
139		},
140		{
141			name:   "only vertical x0.5",
142			width:  2,
143			height: 1,
144			img: &image.RGBA{
145				Stride: 2 * 4,
146				Rect:   image.Rect(0, 0, 2, 2),
147				Pix: []uint8{
148					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
149					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
150				},
151			},
152			expected: &image.RGBA{
153				Stride: 2 * 4,
154				Rect:   image.Rect(0, 0, 2, 1),
155				Pix: []uint8{
156					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
157				},
158			},
159		},
160		{
161			name:   "x0",
162			width:  0,
163			height: 0,
164			img: &image.RGBA{
165				Stride: 1 * 4,
166				Rect:   image.Rect(0, 0, 1, 2),
167				Pix: []uint8{
168					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
169					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
170				},
171			},
172			expected: &image.RGBA{
173				Stride: 0,
174				Rect:   image.Rect(0, 0, 0, 0),
175				Pix:    []uint8{},
176			},
177		},
178	}
179
180	for _, c := range cases {
181		result := Resize(c.img, c.width, c.height, NearestNeighbor)
182		if !util.RGBAImageEqual(result, c.expected) {
183			t.Errorf("%s: expected: %#v, actual: %#v", "Resize no filter "+c.name, c.expected, result)
184		}
185
186		result = Resize(c.img, c.width, c.height, Linear)
187		if !util.RGBAImageEqual(result, c.expected) {
188			t.Errorf("%s: expected: %#v, actual: %#v", "Resize with filter "+c.name, c.expected, result)
189		}
190	}
191}
192
193func TestResizeNearestNeighbor(t *testing.T) {
194	cases := []struct {
195		name     string
196		width    int
197		height   int
198		img      *image.RGBA
199		expected *image.RGBA
200	}{
201		{
202			name:   "x2",
203			width:  4,
204			height: 4,
205			img: &image.RGBA{
206				Stride: 2 * 4,
207				Rect:   image.Rect(0, 0, 2, 2),
208				Pix: []uint8{
209					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
210					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
211				},
212			},
213			expected: &image.RGBA{
214				Stride: 4 * 4,
215				Rect:   image.Rect(0, 0, 4, 4),
216				Pix: []uint8{
217					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
218					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
219					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
220					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
221				},
222			},
223		},
224		{
225			name:   "x0.5",
226			width:  2,
227			height: 2,
228			img: &image.RGBA{
229				Stride: 4 * 4,
230				Rect:   image.Rect(0, 0, 4, 4),
231				Pix: []uint8{
232					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
233					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
234					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
235					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
236				},
237			},
238			expected: &image.RGBA{
239				Stride: 2 * 4,
240				Rect:   image.Rect(0, 0, 2, 2),
241				Pix: []uint8{
242					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
243					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
244				},
245			},
246		},
247	}
248
249	for _, c := range cases {
250		actual := Resize(c.img, c.width, c.height, NearestNeighbor)
251		if !util.RGBAImageEqual(actual, c.expected) {
252			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeNearestNeighbor "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
253		}
254	}
255}
256
257func TestResizeBox(t *testing.T) {
258	cases := []struct {
259		name     string
260		width    int
261		height   int
262		img      *image.RGBA
263		expected *image.RGBA
264	}{
265		{
266			name:   "x2",
267			width:  4,
268			height: 4,
269			img: &image.RGBA{
270				Stride: 2 * 4,
271				Rect:   image.Rect(0, 0, 2, 2),
272				Pix: []uint8{
273					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
274					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
275				},
276			},
277			expected: &image.RGBA{
278				Stride: 4 * 4,
279				Rect:   image.Rect(0, 0, 4, 4),
280				Pix: []uint8{
281					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
282					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
283					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
284					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
285				},
286			},
287		},
288		{
289			name:   "x0.5",
290			width:  2,
291			height: 2,
292			img: &image.RGBA{
293				Stride: 4 * 4,
294				Rect:   image.Rect(0, 0, 4, 4),
295				Pix: []uint8{
296					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
297					0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
298					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
299					0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
300				},
301			},
302			expected: &image.RGBA{
303				Stride: 2 * 4,
304				Rect:   image.Rect(0, 0, 2, 2),
305				Pix: []uint8{
306					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
307					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
308				},
309			},
310		},
311	}
312
313	for _, c := range cases {
314		actual := Resize(c.img, c.width, c.height, Box)
315		if !util.RGBAImageEqual(actual, c.expected) {
316			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeNearestNeighbor "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
317		}
318	}
319}
320
321func TestResizeLinear(t *testing.T) {
322	cases := []struct {
323		name     string
324		width    int
325		height   int
326		img      *image.RGBA
327		expected *image.RGBA
328	}{
329		{
330			name:   "x2",
331			width:  4,
332			height: 4,
333			img: &image.RGBA{
334				Stride: 2 * 4,
335				Rect:   image.Rect(0, 0, 2, 2),
336				Pix: []uint8{
337					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
338					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
339				},
340			},
341			expected: &image.RGBA{
342				Stride: 4 * 4,
343				Rect:   image.Rect(0, 0, 4, 4),
344				Pix: []uint8{
345					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
346					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
347					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
348					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
349				},
350			},
351		},
352		{
353			name:   "x0.5",
354			width:  2,
355			height: 2,
356			img: &image.RGBA{
357				Stride: 4 * 4,
358				Rect:   image.Rect(0, 0, 4, 4),
359				Pix: []uint8{
360					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
361					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
362					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
363					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
364				},
365			},
366			expected: &image.RGBA{
367				Stride: 2 * 4,
368				Rect:   image.Rect(0, 0, 2, 2),
369				Pix: []uint8{
370					0xA3, 0x31, 0x31, 0xF9, 0x41, 0xB3, 0x21, 0xEA,
371					0x40, 0x21, 0xB3, 0xEA, 0x5B, 0x7A, 0x7A, 0xB0,
372				},
373			},
374		},
375	}
376
377	for _, c := range cases {
378		actual := Resize(c.img, c.width, c.height, Linear)
379		if !util.RGBAImageEqual(actual, c.expected) {
380			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeLinear "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
381		}
382	}
383}
384
385func TestResizeGaussian(t *testing.T) {
386	cases := []struct {
387		name     string
388		width    int
389		height   int
390		img      *image.RGBA
391		expected *image.RGBA
392	}{
393		{
394			name:   "x2",
395			width:  4,
396			height: 4,
397			img: &image.RGBA{
398				Stride: 2 * 4,
399				Rect:   image.Rect(0, 0, 2, 2),
400				Pix: []uint8{
401					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
402					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
403				},
404			},
405			expected: &image.RGBA{
406				Stride: 4 * 4,
407				Rect:   image.Rect(0, 0, 4, 4),
408				Pix: []uint8{
409					0xFF, 0x0, 0x0, 0xFF, 0xD6, 0x29, 0x0, 0xFF, 0x29, 0xD6, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
410					0xD6, 0x0, 0x29, 0xFF, 0xB7, 0x26, 0x25, 0xFC, 0x34, 0xC5, 0x18, 0xEE, 0x14, 0xEB, 0x14, 0xEB,
411					0x29, 0x0, 0xD6, 0xFF, 0x33, 0x17, 0xC6, 0xEE, 0x61, 0x7D, 0x7C, 0xA5, 0x6C, 0x94, 0x6C, 0x94,
412					0x0, 0x0, 0xFF, 0xFF, 0x14, 0x14, 0xEB, 0xEB, 0x6C, 0x6C, 0x94, 0x94, 0x80, 0x80, 0x80, 0x80,
413				},
414			},
415		},
416		{
417			name:   "x0.5",
418			width:  2,
419			height: 2,
420			img: &image.RGBA{
421				Stride: 4 * 4,
422				Rect:   image.Rect(0, 0, 4, 4),
423				Pix: []uint8{
424					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
425					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
426					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
427					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
428				},
429			},
430			expected: &image.RGBA{
431				Stride: 2 * 4,
432				Rect:   image.Rect(0, 0, 2, 2),
433				Pix: []uint8{
434					0xAF, 0x2A, 0x2A, 0xFB, 0x39, 0xBE, 0x1B, 0xEC,
435					0x39, 0x1B, 0xBE, 0xEC, 0x5E, 0x7C, 0x7C, 0xAA,
436				},
437			},
438		},
439	}
440
441	for _, c := range cases {
442		actual := Resize(c.img, c.width, c.height, Gaussian)
443		if !util.RGBAImageEqual(actual, c.expected) {
444			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeGaussian "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
445		}
446	}
447}
448
449func TestResizeCatmullRom(t *testing.T) {
450	cases := []struct {
451		name     string
452		width    int
453		height   int
454		img      *image.RGBA
455		expected *image.RGBA
456	}{
457		{
458			name:   "x2",
459			width:  4,
460			height: 4,
461			img: &image.RGBA{
462				Stride: 2 * 4,
463				Rect:   image.Rect(0, 0, 2, 2),
464				Pix: []uint8{
465					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
466					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
467				},
468			},
469			expected: &image.RGBA{
470				Stride: 4 * 4,
471				Rect:   image.Rect(0, 0, 4, 4),
472				Pix: []uint8{
473					0xFF, 0x0, 0x0, 0xFF, 0xD9, 0x37, 0x0, 0xFF, 0x31, 0xD3, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
474					0xCA, 0x0, 0x35, 0xFF, 0xA6, 0x30, 0x2F, 0xFA, 0x3F, 0xB5, 0x20, 0xEA, 0x1D, 0xE7, 0x18, 0xE2,
475					0x35, 0x0, 0xCA, 0xFF, 0x3F, 0x20, 0xB6, 0xEA, 0x5B, 0x7A, 0x7A, 0xAF, 0x6E, 0xA3, 0x5D, 0x92,
476					0x0, 0x0, 0xFF, 0xFF, 0xC, 0x19, 0xF9, 0xE3, 0x69, 0x5C, 0xA8, 0x91, 0x97, 0x81, 0x7F, 0x69,
477				},
478			},
479		},
480		{
481			name:   "x0.5",
482			width:  2,
483			height: 2,
484			img: &image.RGBA{
485				Stride: 4 * 4,
486				Rect:   image.Rect(0, 0, 4, 4),
487				Pix: []uint8{
488					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
489					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
490					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
491					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
492				},
493			},
494			expected: &image.RGBA{
495				Stride: 2 * 4,
496				Rect:   image.Rect(0, 0, 2, 2),
497				Pix: []uint8{
498					0xB4, 0x27, 0x27, 0xFC, 0x36, 0xC3, 0x19, 0xED,
499					0x35, 0x19, 0xC3, 0xED, 0x60, 0x7C, 0x7C, 0xA7,
500				},
501			},
502		},
503	}
504
505	for _, c := range cases {
506		actual := Resize(c.img, c.width, c.height, CatmullRom)
507		if !util.RGBAImageEqual(actual, c.expected) {
508			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeCatmullRom "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
509		}
510	}
511}
512
513func TestResizeMitchell(t *testing.T) {
514	cases := []struct {
515		name     string
516		width    int
517		height   int
518		img      *image.RGBA
519		expected *image.RGBA
520	}{
521		{
522			name:   "x2",
523			width:  4,
524			height: 4,
525			img: &image.RGBA{
526				Stride: 2 * 4,
527				Rect:   image.Rect(0, 0, 2, 2),
528				Pix: []uint8{
529					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
530					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
531				},
532			},
533			expected: &image.RGBA{
534				Stride: 4 * 4,
535				Rect:   image.Rect(0, 0, 4, 4),
536				Pix: []uint8{
537					0xFF, 0x0, 0x0, 0xFF, 0xC5, 0x40, 0x0, 0xFF, 0x3E, 0xC3, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
538					0xC0, 0x0, 0x3F, 0xFF, 0x99, 0x37, 0x37, 0xF7, 0x47, 0xA8, 0x27, 0xE7, 0x21, 0xE1, 0x1F, 0xDF,
539					0x3F, 0x0, 0xC0, 0xFF, 0x47, 0x28, 0xA9, 0xE8, 0x58, 0x78, 0x78, 0xB7, 0x63, 0xA2, 0x5D, 0x9C,
540					0x0, 0x0, 0xFF, 0xFF, 0x1B, 0x1F, 0xE7, 0xDF, 0x61, 0x5D, 0xA4, 0x9C, 0x88, 0x80, 0x80, 0x78,
541				},
542			},
543		},
544		{
545			name:   "x0.5",
546			width:  2,
547			height: 2,
548			img: &image.RGBA{
549				Stride: 4 * 4,
550				Rect:   image.Rect(0, 0, 4, 4),
551				Pix: []uint8{
552					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
553					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
554					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
555					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
556				},
557			},
558			expected: &image.RGBA{
559				Stride: 2 * 4,
560				Rect:   image.Rect(0, 0, 2, 2),
561				Pix: []uint8{
562					0xA7, 0x2E, 0x2F, 0xFA, 0x3E, 0xB7, 0x1F, 0xEA,
563					0x3E, 0x1F, 0xB7, 0xEB, 0x5C, 0x7B, 0x7B, 0xAE,
564				},
565			},
566		},
567	}
568
569	for _, c := range cases {
570		actual := Resize(c.img, c.width, c.height, MitchellNetravali)
571		if !util.RGBAImageEqual(actual, c.expected) {
572			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeMitchell "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
573		}
574	}
575}
576
577func TestResizeLanczos(t *testing.T) {
578	cases := []struct {
579		name     string
580		width    int
581		height   int
582		img      *image.RGBA
583		expected *image.RGBA
584	}{
585		{
586			name:   "x2",
587			width:  4,
588			height: 4,
589			img: &image.RGBA{
590				Stride: 2 * 4,
591				Rect:   image.Rect(0, 0, 2, 2),
592				Pix: []uint8{
593					0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
594					0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
595				},
596			},
597			expected: &image.RGBA{
598				Stride: 4 * 4,
599				Rect:   image.Rect(0, 0, 4, 4),
600				Pix: []uint8{
601					0xFF, 0x0, 0x0, 0xFF, 0xE1, 0x40, 0x0, 0xFF, 0x34, 0xD5, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
602					0xC4, 0x0, 0x3B, 0xFF, 0x9D, 0x34, 0x34, 0xF8, 0x44, 0xAD, 0x25, 0xE8, 0x23, 0xE7, 0x19, 0xDC,
603					0x3B, 0x0, 0xC4, 0xFF, 0x45, 0x25, 0xAD, 0xE8, 0x59, 0x79, 0x79, 0xB5, 0x73, 0xAE, 0x51, 0x8D,
604					0x0, 0x0, 0xFF, 0xFF, 0x1, 0x19, 0xFF, 0xDC, 0x69, 0x51, 0xBA, 0x8D, 0xB0, 0x84, 0x7D, 0x50,
605				},
606			},
607		},
608		{
609			name:   "x0.5",
610			width:  2,
611			height: 2,
612			img: &image.RGBA{
613				Stride: 4 * 4,
614				Rect:   image.Rect(0, 0, 4, 4),
615				Pix: []uint8{
616					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
617					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
618					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
619					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
620				},
621			},
622			expected: &image.RGBA{
623				Stride: 2 * 4,
624				Rect:   image.Rect(0, 0, 2, 2),
625				Pix: []uint8{
626					0xBA, 0x24, 0x24, 0xFC, 0x31, 0xC8, 0x17, 0xEF,
627					0x32, 0x17, 0xC8, 0xEF, 0x62, 0x7D, 0x7D, 0xA4,
628				},
629			},
630		},
631	}
632
633	for _, c := range cases {
634		actual := Resize(c.img, c.width, c.height, Lanczos)
635		if !util.RGBAImageEqual(actual, c.expected) {
636			t.Errorf("%s: expected: %#v, actual: %#v", "ResizeLanczos "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
637		}
638	}
639}
640
641func TestCrop(t *testing.T) {
642	cases := []struct {
643		name     string
644		rect     image.Rectangle
645		img      *image.RGBA
646		expected *image.RGBA
647	}{
648		{
649			name: "center",
650			rect: image.Rect(1, 1, 3, 3),
651			img: &image.RGBA{
652				Stride: 4 * 4,
653				Rect:   image.Rect(0, 0, 4, 4),
654				Pix: []uint8{
655					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
656					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
657					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
658					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
659				},
660			},
661			expected: &image.RGBA{
662				Stride: 2 * 4,
663				Rect:   image.Rect(1, 1, 3, 3),
664				Pix: []uint8{
665					0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7,
666					0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8,
667				},
668			},
669		},
670		{
671			name: "top left",
672			rect: image.Rect(0, 0, 1, 1),
673			img: &image.RGBA{
674				Stride: 4 * 4,
675				Rect:   image.Rect(0, 0, 4, 4),
676				Pix: []uint8{
677					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
678					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
679					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
680					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
681				},
682			},
683			expected: &image.RGBA{
684				Stride: 1 * 4,
685				Rect:   image.Rect(0, 0, 1, 1),
686				Pix: []uint8{
687					0xFF, 0x0, 0x0, 0xFF,
688				},
689			},
690		},
691		{
692			name: "no change",
693			rect: image.Rect(0, 0, 4, 4),
694			img: &image.RGBA{
695				Stride: 4 * 4,
696				Rect:   image.Rect(0, 0, 4, 4),
697				Pix: []uint8{
698					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
699					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
700					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
701					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
702				},
703			},
704			expected: &image.RGBA{
705				Stride: 4 * 4,
706				Rect:   image.Rect(0, 0, 4, 4),
707				Pix: []uint8{
708					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
709					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
710					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
711					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
712				},
713			},
714		},
715		{
716			name: "larger intersect",
717			rect: image.Rect(-50, -50, 50, 50),
718			img: &image.RGBA{
719				Stride: 4 * 4,
720				Rect:   image.Rect(0, 0, 4, 4),
721				Pix: []uint8{
722					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
723					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
724					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
725					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
726				},
727			},
728			expected: &image.RGBA{
729				Stride: 4 * 4,
730				Rect:   image.Rect(0, 0, 4, 4),
731				Pix: []uint8{
732					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
733					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
734					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
735					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
736				},
737			},
738		},
739		{
740			name: "horizontal only",
741			rect: image.Rect(2, 0, 4, 4),
742			img: &image.RGBA{
743				Stride: 4 * 4,
744				Rect:   image.Rect(0, 0, 4, 4),
745				Pix: []uint8{
746					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
747					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
748					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
749					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
750				},
751			},
752			expected: &image.RGBA{
753				Stride: 2 * 4,
754				Rect:   image.Rect(2, 0, 4, 4),
755				Pix: []uint8{
756					0x40, 0xBF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
757					0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
758					0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
759					0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
760				},
761			},
762		},
763		{
764			name: "vertical only",
765			rect: image.Rect(0, 2, 4, 4),
766			img: &image.RGBA{
767				Stride: 4 * 4,
768				Rect:   image.Rect(0, 0, 4, 4),
769				Pix: []uint8{
770					0xFF, 0x0, 0x0, 0xFF, 0xBF, 0x40, 0x0, 0xFF, 0x40, 0xBF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
771					0xBF, 0x0, 0x40, 0xFF, 0x97, 0x38, 0x38, 0xF7, 0x48, 0xA7, 0x28, 0xE7, 0x20, 0xDF, 0x20, 0xDF,
772					0x40, 0x0, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
773					0x0, 0x0, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
774				},
775			},
776			expected: &image.RGBA{
777				Stride: 4 * 4,
778				Rect:   image.Rect(0, 2, 4, 4),
779				Pix: []uint8{
780					0x40, 0x00, 0xBF, 0xFF, 0x48, 0x28, 0xA7, 0xE7, 0x58, 0x78, 0x78, 0xB8, 0x60, 0xA0, 0x60, 0xA0,
781					0x00, 0x00, 0xFF, 0xFF, 0x20, 0x20, 0xDF, 0xDF, 0x60, 0x60, 0xA0, 0xA0, 0x80, 0x80, 0x80, 0x80,
782				},
783			},
784		},
785	}
786
787	for _, c := range cases {
788		actual := Crop(c.img, c.rect)
789		if !util.RGBAImageEqual(actual, c.expected) {
790			t.Errorf("%s: expected: %#v, actual: %#v", "Crop "+c.name, util.RGBAToString(c.expected), util.RGBAToString(actual))
791		}
792	}
793}
794
795func BenchmarkResizeTenth(b *testing.B) {
796	benchResize(b, 4096, 4096, 0.1, Linear)
797}
798
799func BenchmarkResizeQuarter(b *testing.B) {
800	benchResize(b, 4096, 4096, 0.25, Linear)
801}
802
803func BenchmarkResizeHalf(b *testing.B) {
804	benchResize(b, 4096, 4096, 0.5, Linear)
805}
806
807func BenchmarkResize1x(b *testing.B) {
808	benchResize(b, 1024, 1024, 1.0, Linear)
809}
810
811func BenchmarkResize2x(b *testing.B) {
812	benchResize(b, 1024, 1024, 2.0, Linear)
813}
814
815func BenchmarkResize4x(b *testing.B) {
816	benchResize(b, 1024, 1024, 4.0, Linear)
817}
818
819func BenchmarkResize8x(b *testing.B) {
820	benchResize(b, 1024, 1024, 8.0, Linear)
821}
822
823func BenchmarkResize16x(b *testing.B) {
824	benchResize(b, 1024, 1024, 16.0, Linear)
825}
826
827func benchResize(b *testing.B, w, h int, scale float64, f ResampleFilter) {
828	newW := int(float64(w) * scale)
829	newH := int(float64(h) * scale)
830	img := image.NewRGBA(image.Rect(0, 0, w, h))
831	b.ResetTimer()
832	for n := 0; n < b.N; n++ {
833		benchResult = Resize(img, newW, newH, f)
834	}
835}
836