1package transform
2
3import (
4	"image"
5	"testing"
6
7	"github.com/anthonynsimon/bild/util"
8)
9
10func TestRotate(t *testing.T) {
11	cases := []struct {
12		description string
13		angle       float64
14		options     *RotationOptions
15		value       image.Image
16		expected    *image.RGBA
17	}{
18		{
19			description: "angle 0.0 at center",
20			angle:       0.0,
21			options:     &RotationOptions{ResizeBounds: false},
22			value: &image.RGBA{
23				Rect:   image.Rect(0, 0, 2, 2),
24				Stride: 8,
25				Pix: []uint8{
26					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
27					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
28				},
29			},
30			expected: &image.RGBA{
31				Rect:   image.Rect(0, 0, 2, 2),
32				Stride: 8,
33				Pix: []uint8{
34					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
35					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
36				},
37			},
38		},
39		{
40			description: "angle 90.0 at center",
41			angle:       90.0,
42			options:     &RotationOptions{ResizeBounds: false},
43			value: &image.RGBA{
44				Rect:   image.Rect(0, 0, 2, 2),
45				Stride: 8,
46				Pix: []uint8{
47					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
48					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF,
49				},
50			},
51			expected: &image.RGBA{
52				Rect:   image.Rect(0, 0, 2, 2),
53				Stride: 8,
54				Pix: []uint8{
55					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF,
56					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
57				},
58			},
59		},
60		{
61			description: "angle 180.0 at center",
62			angle:       180.0,
63			options:     &RotationOptions{ResizeBounds: false},
64			value: &image.RGBA{
65				Rect:   image.Rect(0, 0, 2, 2),
66				Stride: 8,
67				Pix: []uint8{
68					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
69					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
70				},
71			},
72			expected: &image.RGBA{
73				Rect:   image.Rect(0, 0, 2, 2),
74				Stride: 8,
75				Pix: []uint8{
76					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
78				},
79			},
80		},
81		{
82			description: "angle 360.0 at center",
83			angle:       360.0,
84			options:     &RotationOptions{ResizeBounds: false},
85			value: &image.RGBA{
86				Rect:   image.Rect(0, 0, 2, 2),
87				Stride: 8,
88				Pix: []uint8{
89					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
90					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
91				},
92			},
93			expected: &image.RGBA{
94				Rect:   image.Rect(0, 0, 2, 2),
95				Stride: 8,
96				Pix: []uint8{
97					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
98					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
99				},
100			},
101		},
102		{
103			description: "angle -90.0 at center",
104			angle:       -90.0,
105			options:     &RotationOptions{ResizeBounds: false},
106			value: &image.RGBA{
107				Rect:   image.Rect(0, 0, 2, 2),
108				Stride: 8,
109				Pix: []uint8{
110					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
111					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
112				},
113			},
114			expected: &image.RGBA{
115				Rect:   image.Rect(0, 0, 2, 2),
116				Stride: 8,
117				Pix: []uint8{
118					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
119					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
120				},
121			},
122		},
123		{
124			description: "angle -90.0 at middle bottom",
125			angle:       -90.0,
126			options:     &RotationOptions{ResizeBounds: false, Pivot: &image.Point{1, 2}},
127			value: &image.RGBA{
128				Rect:   image.Rect(0, 0, 2, 2),
129				Stride: 8,
130				Pix: []uint8{
131					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF,
132					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
133				},
134			},
135			expected: &image.RGBA{
136				Rect:   image.Rect(0, 0, 2, 2),
137				Stride: 8,
138				Pix: []uint8{
139					0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
140					0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0,
141				},
142			},
143		},
144		{
145			description: "angle 45.0 at center, don't preserve bounds",
146			angle:       45.0,
147			options:     &RotationOptions{ResizeBounds: true},
148			value: &image.RGBA{
149				Rect:   image.Rect(0, 0, 4, 4),
150				Stride: 4 * 4,
151				Pix: []uint8{
152					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
153					0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
154					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80,
155					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF, 0x80, 0x80, 0x80,
156				},
157			},
158			expected: &image.RGBA{
159				Rect:   image.Rect(0, 0, 5, 5),
160				Stride: 5 * 4,
161				Pix: []uint8{
162					0x5C, 0x5C, 0x5C, 0x87, 0x85, 0x85, 0x85, 0xF7, 0x81, 0x81, 0x81, 0xFF, 0x8C, 0x8C, 0x8C, 0xD1, 0x33, 0x33, 0x33, 0x33,
163					0xF0, 0xF0, 0xF0, 0xF7, 0xD3, 0xD3, 0xD3, 0xFF, 0xAD, 0xAD, 0xAD, 0xFF, 0xEF, 0xEF, 0xEF, 0xFD, 0x95, 0x95, 0x95, 0x95,
164					0xDF, 0xDE, 0xDE, 0xDE, 0xFD, 0xDA, 0xDA, 0xDB, 0xFC, 0xB3, 0xB3, 0xB7, 0xF6, 0xEC, 0xEC, 0xEC, 0x7E, 0x7E, 0x7E, 0x7E,
165					0x35, 0x2B, 0x2B, 0x2B, 0xC9, 0x6F, 0x6F, 0x6F, 0xF5, 0x7C, 0x7C, 0x7C, 0x82, 0x54, 0x54, 0x54, 0xA, 0xA, 0xA, 0xA,
166					0x00, 0x00, 0x00, 0x00, 0x35, 0x1B, 0x1B, 0x1B, 0x79, 0x3D, 0x3D, 0x3D, 0xA, 0x5, 0x5, 0x5, 0x00, 0x00, 0x00, 0x00,
167				},
168			},
169		},
170	}
171
172	for _, c := range cases {
173		actual := Rotate(c.value, c.angle, c.options)
174		if !util.RGBAImageEqual(actual, c.expected) {
175			t.Errorf("%s:\nexpected:%v\nactual:%v", "Rotate "+c.description, util.RGBAToString(c.expected), util.RGBAToString(actual))
176		}
177	}
178}
179
180func TestFlipH(t *testing.T) {
181	cases := []struct {
182		value    image.Image
183		expected *image.RGBA
184	}{
185		{
186			value: &image.RGBA{
187				Rect:   image.Rect(0, 0, 2, 2),
188				Stride: 8,
189				Pix: []uint8{
190					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
191					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
192				},
193			},
194			expected: &image.RGBA{
195				Rect:   image.Rect(0, 0, 2, 2),
196				Stride: 8,
197				Pix: []uint8{
198					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF,
199					0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
200				},
201			},
202		},
203		{
204			value: &image.RGBA{
205				Rect:   image.Rect(0, 0, 3, 2),
206				Stride: 12,
207				Pix: []uint8{
208					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
209					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
210				},
211			},
212			expected: &image.RGBA{
213				Rect:   image.Rect(0, 0, 3, 2),
214				Stride: 12,
215				Pix: []uint8{
216					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF,
217					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
218				},
219			},
220		},
221		{
222			value: &image.RGBA{
223				Rect:   image.Rect(0, 0, 2, 3),
224				Stride: 8,
225				Pix: []uint8{
226					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
227					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
228					0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
229				},
230			},
231			expected: &image.RGBA{
232				Rect:   image.Rect(0, 0, 2, 3),
233				Stride: 8,
234				Pix: []uint8{
235					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0xFF,
236					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
237					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
238				},
239			},
240		},
241	}
242
243	for _, c := range cases {
244		actual := FlipH(c.value)
245		if !util.RGBAImageEqual(actual, c.expected) {
246			t.Errorf("%s: expected: %#v, actual: %#v", "FlipH", util.RGBAToString(c.expected), util.RGBAToString(actual))
247		}
248	}
249}
250
251func TestFlipV(t *testing.T) {
252	cases := []struct {
253		value    image.Image
254		expected *image.RGBA
255	}{
256		{
257			value: &image.RGBA{
258				Rect:   image.Rect(0, 0, 2, 2),
259				Stride: 8,
260				Pix: []uint8{
261					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
262					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
263				},
264			},
265			expected: &image.RGBA{
266				Rect:   image.Rect(0, 0, 2, 2),
267				Stride: 8,
268				Pix: []uint8{
269					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80,
270					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
271				},
272			},
273		},
274		{
275			value: &image.RGBA{
276				Rect:   image.Rect(0, 0, 3, 2),
277				Stride: 12,
278				Pix: []uint8{
279					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
280					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
281				},
282			},
283			expected: &image.RGBA{
284				Rect:   image.Rect(0, 0, 3, 2),
285				Stride: 12,
286				Pix: []uint8{
287					0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
288					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
289				},
290			},
291		},
292		{
293			value: &image.RGBA{
294				Rect:   image.Rect(0, 0, 2, 3),
295				Stride: 8,
296				Pix: []uint8{
297					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
298					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
299					0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
300				},
301			},
302			expected: &image.RGBA{
303				Rect:   image.Rect(0, 0, 2, 3),
304				Stride: 8,
305				Pix: []uint8{
306					0x80, 0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
307					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
308					0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
309				},
310			},
311		},
312	}
313
314	for _, c := range cases {
315		actual := FlipV(c.value)
316		if !util.RGBAImageEqual(actual, c.expected) {
317			t.Errorf("%s: expected: %#v, actual: %#v", "FlipV", util.RGBAToString(c.expected), util.RGBAToString(actual))
318		}
319	}
320}
321
322func BenchmarkRotation256(b *testing.B) {
323	benchRotate(256, 256, 90.0, b)
324}
325
326func BenchmarkRotation512(b *testing.B) {
327	benchRotate(512, 512, 90.0, b)
328}
329
330func BenchmarkRotation1024(b *testing.B) {
331	benchRotate(1024, 1024, 90.0, b)
332}
333
334func BenchmarkRotation2048(b *testing.B) {
335	benchRotate(2048, 2048, 90.0, b)
336}
337
338func BenchmarkRotation4096(b *testing.B) {
339	benchRotate(4096, 4096, 90.0, b)
340}
341
342func BenchmarkRotation8192(b *testing.B) {
343	benchRotate(8192, 8192, 90.0, b)
344}
345
346func benchRotate(w, h int, rot float64, bench *testing.B) {
347	img := image.NewRGBA(image.Rect(0, 0, w, h))
348	bench.ResetTimer()
349	for i := 0; i < bench.N; i++ {
350		benchResult = Rotate(img, rot, nil)
351	}
352}
353