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