github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/image/image_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package image 6 7 import ( 8 "image/color" 9 "image/color/palette" 10 "testing" 11 ) 12 13 type image interface { 14 Image 15 Opaque() bool 16 Set(int, int, color.Color) 17 SubImage(Rectangle) Image 18 } 19 20 func cmp(cm color.Model, c0, c1 color.Color) bool { 21 r0, g0, b0, a0 := cm.Convert(c0).RGBA() 22 r1, g1, b1, a1 := cm.Convert(c1).RGBA() 23 return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1 24 } 25 26 var testImages = []struct { 27 name string 28 image func() image 29 }{ 30 {"rgba", func() image { return NewRGBA(Rect(0, 0, 10, 10)) }}, 31 {"rgba64", func() image { return NewRGBA64(Rect(0, 0, 10, 10)) }}, 32 {"nrgba", func() image { return NewNRGBA(Rect(0, 0, 10, 10)) }}, 33 {"nrgba64", func() image { return NewNRGBA64(Rect(0, 0, 10, 10)) }}, 34 {"alpha", func() image { return NewAlpha(Rect(0, 0, 10, 10)) }}, 35 {"alpha16", func() image { return NewAlpha16(Rect(0, 0, 10, 10)) }}, 36 {"gray", func() image { return NewGray(Rect(0, 0, 10, 10)) }}, 37 {"gray16", func() image { return NewGray16(Rect(0, 0, 10, 10)) }}, 38 {"paletted", func() image { 39 return NewPaletted(Rect(0, 0, 10, 10), color.Palette{ 40 Transparent, 41 Opaque, 42 }) 43 }}, 44 } 45 46 func TestImage(t *testing.T) { 47 for _, tc := range testImages { 48 m := tc.image() 49 if !Rect(0, 0, 10, 10).Eq(m.Bounds()) { 50 t.Errorf("%T: want bounds %v, got %v", m, Rect(0, 0, 10, 10), m.Bounds()) 51 continue 52 } 53 if !cmp(m.ColorModel(), Transparent, m.At(6, 3)) { 54 t.Errorf("%T: at (6, 3), want a zero color, got %v", m, m.At(6, 3)) 55 continue 56 } 57 m.Set(6, 3, Opaque) 58 if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) { 59 t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3)) 60 continue 61 } 62 if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() { 63 t.Errorf("%T: at (6, 3) was not opaque", m) 64 continue 65 } 66 m = m.SubImage(Rect(3, 2, 9, 8)).(image) 67 if !Rect(3, 2, 9, 8).Eq(m.Bounds()) { 68 t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds()) 69 continue 70 } 71 if !cmp(m.ColorModel(), Opaque, m.At(6, 3)) { 72 t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", m, m.At(6, 3)) 73 continue 74 } 75 if !cmp(m.ColorModel(), Transparent, m.At(3, 3)) { 76 t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", m, m.At(3, 3)) 77 continue 78 } 79 m.Set(3, 3, Opaque) 80 if !cmp(m.ColorModel(), Opaque, m.At(3, 3)) { 81 t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3)) 82 continue 83 } 84 // Test that taking an empty sub-image starting at a corner does not panic. 85 m.SubImage(Rect(0, 0, 0, 0)) 86 m.SubImage(Rect(10, 0, 10, 0)) 87 m.SubImage(Rect(0, 10, 0, 10)) 88 m.SubImage(Rect(10, 10, 10, 10)) 89 } 90 } 91 92 func TestNewXxxBadRectangle(t *testing.T) { 93 // call calls f(r) and reports whether it ran without panicking. 94 call := func(f func(Rectangle), r Rectangle) (ok bool) { 95 defer func() { 96 if recover() != nil { 97 ok = false 98 } 99 }() 100 f(r) 101 return true 102 } 103 104 testCases := []struct { 105 name string 106 f func(Rectangle) 107 }{ 108 {"RGBA", func(r Rectangle) { NewRGBA(r) }}, 109 {"RGBA64", func(r Rectangle) { NewRGBA64(r) }}, 110 {"NRGBA", func(r Rectangle) { NewNRGBA(r) }}, 111 {"NRGBA64", func(r Rectangle) { NewNRGBA64(r) }}, 112 {"Alpha", func(r Rectangle) { NewAlpha(r) }}, 113 {"Alpha16", func(r Rectangle) { NewAlpha16(r) }}, 114 {"Gray", func(r Rectangle) { NewGray(r) }}, 115 {"Gray16", func(r Rectangle) { NewGray16(r) }}, 116 {"CMYK", func(r Rectangle) { NewCMYK(r) }}, 117 {"Paletted", func(r Rectangle) { NewPaletted(r, color.Palette{color.Black, color.White}) }}, 118 {"YCbCr", func(r Rectangle) { NewYCbCr(r, YCbCrSubsampleRatio422) }}, 119 {"NYCbCrA", func(r Rectangle) { NewNYCbCrA(r, YCbCrSubsampleRatio444) }}, 120 } 121 122 for _, tc := range testCases { 123 // Calling NewXxx(r) should fail (panic, since NewXxx doesn't return an 124 // error) unless r's width and height are both non-negative. 125 for _, negDx := range []bool{false, true} { 126 for _, negDy := range []bool{false, true} { 127 r := Rectangle{ 128 Min: Point{15, 28}, 129 Max: Point{16, 29}, 130 } 131 if negDx { 132 r.Max.X = 14 133 } 134 if negDy { 135 r.Max.Y = 27 136 } 137 138 got := call(tc.f, r) 139 want := !negDx && !negDy 140 if got != want { 141 t.Errorf("New%s: negDx=%t, negDy=%t: got %t, want %t", 142 tc.name, negDx, negDy, got, want) 143 } 144 } 145 } 146 147 // Passing a Rectangle whose width and height is MaxInt should also fail 148 // (panic), due to overflow. 149 { 150 zeroAsUint := uint(0) 151 maxUint := zeroAsUint - 1 152 maxInt := int(maxUint / 2) 153 got := call(tc.f, Rectangle{ 154 Min: Point{0, 0}, 155 Max: Point{maxInt, maxInt}, 156 }) 157 if got { 158 t.Errorf("New%s: overflow: got ok, want !ok", tc.name) 159 } 160 } 161 } 162 } 163 164 func Test16BitsPerColorChannel(t *testing.T) { 165 testColorModel := []color.Model{ 166 color.RGBA64Model, 167 color.NRGBA64Model, 168 color.Alpha16Model, 169 color.Gray16Model, 170 } 171 for _, cm := range testColorModel { 172 c := cm.Convert(color.RGBA64{0x1234, 0x1234, 0x1234, 0x1234}) // Premultiplied alpha. 173 r, _, _, _ := c.RGBA() 174 if r != 0x1234 { 175 t.Errorf("%T: want red value 0x%04x got 0x%04x", c, 0x1234, r) 176 continue 177 } 178 } 179 testImage := []image{ 180 NewRGBA64(Rect(0, 0, 10, 10)), 181 NewNRGBA64(Rect(0, 0, 10, 10)), 182 NewAlpha16(Rect(0, 0, 10, 10)), 183 NewGray16(Rect(0, 0, 10, 10)), 184 } 185 for _, m := range testImage { 186 m.Set(1, 2, color.NRGBA64{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha. 187 r, _, _, _ := m.At(1, 2).RGBA() 188 if r != 0x1357 { 189 t.Errorf("%T: want red value 0x%04x got 0x%04x", m, 0x1357, r) 190 continue 191 } 192 } 193 } 194 195 func TestRGBA64Image(t *testing.T) { 196 // memset sets every element of s to v. 197 memset := func(s []byte, v byte) { 198 for i := range s { 199 s[i] = v 200 } 201 } 202 203 r := Rect(0, 0, 3, 2) 204 testCases := []Image{ 205 NewAlpha(r), 206 NewAlpha16(r), 207 NewCMYK(r), 208 NewGray(r), 209 NewGray16(r), 210 NewNRGBA(r), 211 NewNRGBA64(r), 212 NewNYCbCrA(r, YCbCrSubsampleRatio444), 213 NewPaletted(r, palette.Plan9), 214 NewRGBA(r), 215 NewRGBA64(r), 216 NewUniform(color.RGBA64{}), 217 NewYCbCr(r, YCbCrSubsampleRatio444), 218 r, 219 } 220 for _, tc := range testCases { 221 switch tc := tc.(type) { 222 // Most of the concrete image types in the testCases implement the 223 // draw.RGBA64Image interface: they have a SetRGBA64 method. We use an 224 // interface literal here, instead of importing "image/draw", to avoid 225 // an import cycle. 226 // 227 // The YCbCr and NYCbCrA types are special-cased. Chroma subsampling 228 // means that setting one pixel can modify neighboring pixels. They 229 // don't have Set or SetRGBA64 methods because that side effect could 230 // be surprising. Here, we just memset the channel buffers instead. 231 // 232 // The Uniform and Rectangle types are also special-cased, as they 233 // don't have a Set or SetRGBA64 method. 234 case interface { 235 SetRGBA64(x, y int, c color.RGBA64) 236 }: 237 tc.SetRGBA64(1, 1, color.RGBA64{0x7FFF, 0x3FFF, 0x0000, 0x7FFF}) 238 239 case *NYCbCrA: 240 memset(tc.YCbCr.Y, 0x77) 241 memset(tc.YCbCr.Cb, 0x88) 242 memset(tc.YCbCr.Cr, 0x99) 243 memset(tc.A, 0xAA) 244 245 case *Uniform: 246 tc.C = color.RGBA64{0x7FFF, 0x3FFF, 0x0000, 0x7FFF} 247 248 case *YCbCr: 249 memset(tc.Y, 0x77) 250 memset(tc.Cb, 0x88) 251 memset(tc.Cr, 0x99) 252 253 case Rectangle: 254 // No-op. Rectangle pixels' colors are immutable. They're always 255 // color.Opaque. 256 257 default: 258 t.Errorf("could not initialize pixels for %T", tc) 259 continue 260 } 261 262 // Check that RGBA64At(x, y) is equivalent to At(x, y).RGBA(). 263 rgba64Image, ok := tc.(RGBA64Image) 264 if !ok { 265 t.Errorf("%T is not an RGBA64Image", tc) 266 continue 267 } 268 got := rgba64Image.RGBA64At(1, 1) 269 wantR, wantG, wantB, wantA := tc.At(1, 1).RGBA() 270 if (uint32(got.R) != wantR) || (uint32(got.G) != wantG) || 271 (uint32(got.B) != wantB) || (uint32(got.A) != wantA) { 272 t.Errorf("%T:\ngot (0x%04X, 0x%04X, 0x%04X, 0x%04X)\n"+ 273 "want (0x%04X, 0x%04X, 0x%04X, 0x%04X)", tc, 274 got.R, got.G, got.B, got.A, 275 wantR, wantG, wantB, wantA) 276 continue 277 } 278 } 279 } 280 281 func BenchmarkAt(b *testing.B) { 282 for _, tc := range testImages { 283 b.Run(tc.name, func(b *testing.B) { 284 m := tc.image() 285 b.ReportAllocs() 286 b.ResetTimer() 287 for i := 0; i < b.N; i++ { 288 m.At(4, 5) 289 } 290 }) 291 } 292 } 293 294 func BenchmarkSet(b *testing.B) { 295 c := color.Gray{0xff} 296 for _, tc := range testImages { 297 b.Run(tc.name, func(b *testing.B) { 298 m := tc.image() 299 b.ReportAllocs() 300 b.ResetTimer() 301 for i := 0; i < b.N; i++ { 302 m.Set(4, 5, c) 303 } 304 }) 305 } 306 } 307 308 func BenchmarkRGBAAt(b *testing.B) { 309 m := NewRGBA(Rect(0, 0, 10, 10)) 310 b.ResetTimer() 311 312 for i := 0; i < b.N; i++ { 313 m.RGBAAt(4, 5) 314 } 315 } 316 317 func BenchmarkRGBASetRGBA(b *testing.B) { 318 m := NewRGBA(Rect(0, 0, 10, 10)) 319 c := color.RGBA{0xff, 0xff, 0xff, 0x13} 320 b.ResetTimer() 321 322 for i := 0; i < b.N; i++ { 323 m.SetRGBA(4, 5, c) 324 } 325 } 326 327 func BenchmarkRGBA64At(b *testing.B) { 328 m := NewRGBA64(Rect(0, 0, 10, 10)) 329 b.ResetTimer() 330 331 for i := 0; i < b.N; i++ { 332 m.RGBA64At(4, 5) 333 } 334 } 335 336 func BenchmarkRGBA64SetRGBA64(b *testing.B) { 337 m := NewRGBA64(Rect(0, 0, 10, 10)) 338 c := color.RGBA64{0xffff, 0xffff, 0xffff, 0x1357} 339 b.ResetTimer() 340 341 for i := 0; i < b.N; i++ { 342 m.SetRGBA64(4, 5, c) 343 } 344 } 345 346 func BenchmarkNRGBAAt(b *testing.B) { 347 m := NewNRGBA(Rect(0, 0, 10, 10)) 348 b.ResetTimer() 349 350 for i := 0; i < b.N; i++ { 351 m.NRGBAAt(4, 5) 352 } 353 } 354 355 func BenchmarkNRGBASetNRGBA(b *testing.B) { 356 m := NewNRGBA(Rect(0, 0, 10, 10)) 357 c := color.NRGBA{0xff, 0xff, 0xff, 0x13} 358 b.ResetTimer() 359 360 for i := 0; i < b.N; i++ { 361 m.SetNRGBA(4, 5, c) 362 } 363 } 364 365 func BenchmarkNRGBA64At(b *testing.B) { 366 m := NewNRGBA64(Rect(0, 0, 10, 10)) 367 b.ResetTimer() 368 369 for i := 0; i < b.N; i++ { 370 m.NRGBA64At(4, 5) 371 } 372 } 373 374 func BenchmarkNRGBA64SetNRGBA64(b *testing.B) { 375 m := NewNRGBA64(Rect(0, 0, 10, 10)) 376 c := color.NRGBA64{0xffff, 0xffff, 0xffff, 0x1357} 377 b.ResetTimer() 378 379 for i := 0; i < b.N; i++ { 380 m.SetNRGBA64(4, 5, c) 381 } 382 } 383 384 func BenchmarkAlphaAt(b *testing.B) { 385 m := NewAlpha(Rect(0, 0, 10, 10)) 386 b.ResetTimer() 387 388 for i := 0; i < b.N; i++ { 389 m.AlphaAt(4, 5) 390 } 391 } 392 393 func BenchmarkAlphaSetAlpha(b *testing.B) { 394 m := NewAlpha(Rect(0, 0, 10, 10)) 395 c := color.Alpha{0x13} 396 b.ResetTimer() 397 398 for i := 0; i < b.N; i++ { 399 m.SetAlpha(4, 5, c) 400 } 401 } 402 403 func BenchmarkAlpha16At(b *testing.B) { 404 m := NewAlpha16(Rect(0, 0, 10, 10)) 405 b.ResetTimer() 406 407 for i := 0; i < b.N; i++ { 408 m.Alpha16At(4, 5) 409 } 410 } 411 412 func BenchmarkAlphaSetAlpha16(b *testing.B) { 413 m := NewAlpha16(Rect(0, 0, 10, 10)) 414 c := color.Alpha16{0x13} 415 b.ResetTimer() 416 417 for i := 0; i < b.N; i++ { 418 m.SetAlpha16(4, 5, c) 419 } 420 } 421 422 func BenchmarkGrayAt(b *testing.B) { 423 m := NewGray(Rect(0, 0, 10, 10)) 424 b.ResetTimer() 425 426 for i := 0; i < b.N; i++ { 427 m.GrayAt(4, 5) 428 } 429 } 430 431 func BenchmarkGraySetGray(b *testing.B) { 432 m := NewGray(Rect(0, 0, 10, 10)) 433 c := color.Gray{0x13} 434 b.ResetTimer() 435 436 for i := 0; i < b.N; i++ { 437 m.SetGray(4, 5, c) 438 } 439 } 440 441 func BenchmarkGray16At(b *testing.B) { 442 m := NewGray16(Rect(0, 0, 10, 10)) 443 b.ResetTimer() 444 445 for i := 0; i < b.N; i++ { 446 m.Gray16At(4, 5) 447 } 448 } 449 450 func BenchmarkGraySetGray16(b *testing.B) { 451 m := NewGray16(Rect(0, 0, 10, 10)) 452 c := color.Gray16{0x13} 453 b.ResetTimer() 454 455 for i := 0; i < b.N; i++ { 456 m.SetGray16(4, 5, c) 457 } 458 }