github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/image/gif/writer_test.go (about) 1 // Copyright 2013 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 gif 6 7 import ( 8 "bytes" 9 "image" 10 "image/color" 11 "image/color/palette" 12 "image/draw" 13 _ "image/png" 14 "io" 15 "math/rand" 16 "os" 17 "reflect" 18 "testing" 19 ) 20 21 func readImg(filename string) (image.Image, error) { 22 f, err := os.Open(filename) 23 if err != nil { 24 return nil, err 25 } 26 defer f.Close() 27 m, _, err := image.Decode(f) 28 return m, err 29 } 30 31 func readGIF(filename string) (*GIF, error) { 32 f, err := os.Open(filename) 33 if err != nil { 34 return nil, err 35 } 36 defer f.Close() 37 return DecodeAll(f) 38 } 39 40 func delta(u0, u1 uint32) int64 { 41 d := int64(u0) - int64(u1) 42 if d < 0 { 43 return -d 44 } 45 return d 46 } 47 48 // averageDelta returns the average delta in RGB space. The two images must 49 // have the same bounds. 50 func averageDelta(m0, m1 image.Image) int64 { 51 b := m0.Bounds() 52 return averageDeltaBound(m0, m1, b, b) 53 } 54 55 // averageDeltaBounds returns the average delta in RGB space. The average delta is 56 // calculated in the specified bounds. 57 func averageDeltaBound(m0, m1 image.Image, b0, b1 image.Rectangle) int64 { 58 var sum, n int64 59 for y := b0.Min.Y; y < b0.Max.Y; y++ { 60 for x := b0.Min.X; x < b0.Max.X; x++ { 61 c0 := m0.At(x, y) 62 c1 := m1.At(x-b0.Min.X+b1.Min.X, y-b0.Min.Y+b1.Min.Y) 63 r0, g0, b0, _ := c0.RGBA() 64 r1, g1, b1, _ := c1.RGBA() 65 sum += delta(r0, r1) 66 sum += delta(g0, g1) 67 sum += delta(b0, b1) 68 n += 3 69 } 70 } 71 return sum / n 72 } 73 74 // lzw.NewWriter wants an interface which is basically the same thing as gif's 75 // writer interface. This ensures we're compatible. 76 var _ writer = blockWriter{} 77 78 var testCase = []struct { 79 filename string 80 tolerance int64 81 }{ 82 {"../testdata/video-001.png", 1 << 12}, 83 {"../testdata/video-001.gif", 0}, 84 {"../testdata/video-001.interlaced.gif", 0}, 85 } 86 87 func TestWriter(t *testing.T) { 88 for _, tc := range testCase { 89 m0, err := readImg(tc.filename) 90 if err != nil { 91 t.Error(tc.filename, err) 92 continue 93 } 94 var buf bytes.Buffer 95 err = Encode(&buf, m0, nil) 96 if err != nil { 97 t.Error(tc.filename, err) 98 continue 99 } 100 m1, err := Decode(&buf) 101 if err != nil { 102 t.Error(tc.filename, err) 103 continue 104 } 105 if m0.Bounds() != m1.Bounds() { 106 t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds()) 107 continue 108 } 109 // Compare the average delta to the tolerance level. 110 avgDelta := averageDelta(m0, m1) 111 if avgDelta > tc.tolerance { 112 t.Errorf("%s: average delta is too high. expected: %d, got %d", tc.filename, tc.tolerance, avgDelta) 113 continue 114 } 115 } 116 } 117 118 func TestSubImage(t *testing.T) { 119 m0, err := readImg("../testdata/video-001.gif") 120 if err != nil { 121 t.Fatalf("readImg: %v", err) 122 } 123 m0 = m0.(*image.Paletted).SubImage(image.Rect(0, 0, 50, 30)) 124 var buf bytes.Buffer 125 err = Encode(&buf, m0, nil) 126 if err != nil { 127 t.Fatalf("Encode: %v", err) 128 } 129 m1, err := Decode(&buf) 130 if err != nil { 131 t.Fatalf("Decode: %v", err) 132 } 133 if m0.Bounds() != m1.Bounds() { 134 t.Fatalf("bounds differ: %v and %v", m0.Bounds(), m1.Bounds()) 135 } 136 if averageDelta(m0, m1) != 0 { 137 t.Fatalf("images differ") 138 } 139 } 140 141 // palettesEqual reports whether two color.Palette values are equal, ignoring 142 // any trailing opaque-black palette entries. 143 func palettesEqual(p, q color.Palette) bool { 144 n := len(p) 145 if n > len(q) { 146 n = len(q) 147 } 148 for i := 0; i < n; i++ { 149 if p[i] != q[i] { 150 return false 151 } 152 } 153 for i := n; i < len(p); i++ { 154 r, g, b, a := p[i].RGBA() 155 if r != 0 || g != 0 || b != 0 || a != 0xffff { 156 return false 157 } 158 } 159 for i := n; i < len(q); i++ { 160 r, g, b, a := q[i].RGBA() 161 if r != 0 || g != 0 || b != 0 || a != 0xffff { 162 return false 163 } 164 } 165 return true 166 } 167 168 var frames = []string{ 169 "../testdata/video-001.gif", 170 "../testdata/video-005.gray.gif", 171 } 172 173 func testEncodeAll(t *testing.T, go1Dot5Fields bool, useGlobalColorModel bool) { 174 const width, height = 150, 103 175 176 g0 := &GIF{ 177 Image: make([]*image.Paletted, len(frames)), 178 Delay: make([]int, len(frames)), 179 LoopCount: 5, 180 } 181 for i, f := range frames { 182 g, err := readGIF(f) 183 if err != nil { 184 t.Fatal(f, err) 185 } 186 m := g.Image[0] 187 if m.Bounds().Dx() != width || m.Bounds().Dy() != height { 188 t.Fatalf("frame %d had unexpected bounds: got %v, want width/height = %d/%d", 189 i, m.Bounds(), width, height) 190 } 191 g0.Image[i] = m 192 } 193 // The GIF.Disposal, GIF.Config and GIF.BackgroundIndex fields were added 194 // in Go 1.5. Valid Go 1.4 or earlier code should still produce valid GIFs. 195 // 196 // On the following line, color.Model is an interface type, and 197 // color.Palette is a concrete (slice) type. 198 globalColorModel, backgroundIndex := color.Model(color.Palette(nil)), uint8(0) 199 if useGlobalColorModel { 200 globalColorModel, backgroundIndex = color.Palette(palette.WebSafe), uint8(1) 201 } 202 if go1Dot5Fields { 203 g0.Disposal = make([]byte, len(g0.Image)) 204 for i := range g0.Disposal { 205 g0.Disposal[i] = DisposalNone 206 } 207 g0.Config = image.Config{ 208 ColorModel: globalColorModel, 209 Width: width, 210 Height: height, 211 } 212 g0.BackgroundIndex = backgroundIndex 213 } 214 215 var buf bytes.Buffer 216 if err := EncodeAll(&buf, g0); err != nil { 217 t.Fatal("EncodeAll:", err) 218 } 219 encoded := buf.Bytes() 220 config, err := DecodeConfig(bytes.NewReader(encoded)) 221 if err != nil { 222 t.Fatal("DecodeConfig:", err) 223 } 224 g1, err := DecodeAll(bytes.NewReader(encoded)) 225 if err != nil { 226 t.Fatal("DecodeAll:", err) 227 } 228 229 if !reflect.DeepEqual(config, g1.Config) { 230 t.Errorf("DecodeConfig inconsistent with DecodeAll") 231 } 232 if !palettesEqual(g1.Config.ColorModel.(color.Palette), globalColorModel.(color.Palette)) { 233 t.Errorf("unexpected global color model") 234 } 235 if w, h := g1.Config.Width, g1.Config.Height; w != width || h != height { 236 t.Errorf("got config width * height = %d * %d, want %d * %d", w, h, width, height) 237 } 238 239 if g0.LoopCount != g1.LoopCount { 240 t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount) 241 } 242 if backgroundIndex != g1.BackgroundIndex { 243 t.Errorf("background indexes differ: %d and %d", backgroundIndex, g1.BackgroundIndex) 244 } 245 if len(g0.Image) != len(g1.Image) { 246 t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image)) 247 } 248 if len(g1.Image) != len(g1.Delay) { 249 t.Fatalf("image and delay lengths differ: %d and %d", len(g1.Image), len(g1.Delay)) 250 } 251 if len(g1.Image) != len(g1.Disposal) { 252 t.Fatalf("image and disposal lengths differ: %d and %d", len(g1.Image), len(g1.Disposal)) 253 } 254 255 for i := range g0.Image { 256 m0, m1 := g0.Image[i], g1.Image[i] 257 if m0.Bounds() != m1.Bounds() { 258 t.Errorf("frame %d: bounds differ: %v and %v", i, m0.Bounds(), m1.Bounds()) 259 } 260 d0, d1 := g0.Delay[i], g1.Delay[i] 261 if d0 != d1 { 262 t.Errorf("frame %d: delay values differ: %d and %d", i, d0, d1) 263 } 264 p0, p1 := uint8(0), g1.Disposal[i] 265 if go1Dot5Fields { 266 p0 = DisposalNone 267 } 268 if p0 != p1 { 269 t.Errorf("frame %d: disposal values differ: %d and %d", i, p0, p1) 270 } 271 } 272 } 273 274 func TestEncodeAllGo1Dot4(t *testing.T) { testEncodeAll(t, false, false) } 275 func TestEncodeAllGo1Dot5(t *testing.T) { testEncodeAll(t, true, false) } 276 func TestEncodeAllGo1Dot5GlobalColorModel(t *testing.T) { testEncodeAll(t, true, true) } 277 278 func TestEncodeMismatchDelay(t *testing.T) { 279 images := make([]*image.Paletted, 2) 280 for i := range images { 281 images[i] = image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9) 282 } 283 284 g0 := &GIF{ 285 Image: images, 286 Delay: make([]int, 1), 287 } 288 if err := EncodeAll(io.Discard, g0); err == nil { 289 t.Error("expected error from mismatched delay and image slice lengths") 290 } 291 292 g1 := &GIF{ 293 Image: images, 294 Delay: make([]int, len(images)), 295 Disposal: make([]byte, 1), 296 } 297 for i := range g1.Disposal { 298 g1.Disposal[i] = DisposalNone 299 } 300 if err := EncodeAll(io.Discard, g1); err == nil { 301 t.Error("expected error from mismatched disposal and image slice lengths") 302 } 303 } 304 305 func TestEncodeZeroGIF(t *testing.T) { 306 if err := EncodeAll(io.Discard, &GIF{}); err == nil { 307 t.Error("expected error from providing empty gif") 308 } 309 } 310 311 func TestEncodeAllFramesOutOfBounds(t *testing.T) { 312 images := []*image.Paletted{ 313 image.NewPaletted(image.Rect(0, 0, 5, 5), palette.Plan9), 314 image.NewPaletted(image.Rect(2, 2, 8, 8), palette.Plan9), 315 image.NewPaletted(image.Rect(3, 3, 4, 4), palette.Plan9), 316 } 317 for _, upperBound := range []int{6, 10} { 318 g := &GIF{ 319 Image: images, 320 Delay: make([]int, len(images)), 321 Disposal: make([]byte, len(images)), 322 Config: image.Config{ 323 Width: upperBound, 324 Height: upperBound, 325 }, 326 } 327 err := EncodeAll(io.Discard, g) 328 if upperBound >= 8 { 329 if err != nil { 330 t.Errorf("upperBound=%d: %v", upperBound, err) 331 } 332 } else { 333 if err == nil { 334 t.Errorf("upperBound=%d: got nil error, want non-nil", upperBound) 335 } 336 } 337 } 338 } 339 340 func TestEncodeNonZeroMinPoint(t *testing.T) { 341 points := []image.Point{ 342 {-8, -9}, 343 {-4, -4}, 344 {-3, +3}, 345 {+0, +0}, 346 {+2, +2}, 347 } 348 for _, p := range points { 349 src := image.NewPaletted(image.Rectangle{ 350 Min: p, 351 Max: p.Add(image.Point{6, 6}), 352 }, palette.Plan9) 353 var buf bytes.Buffer 354 if err := Encode(&buf, src, nil); err != nil { 355 t.Errorf("p=%v: Encode: %v", p, err) 356 continue 357 } 358 m, err := Decode(&buf) 359 if err != nil { 360 t.Errorf("p=%v: Decode: %v", p, err) 361 continue 362 } 363 if got, want := m.Bounds(), image.Rect(0, 0, 6, 6); got != want { 364 t.Errorf("p=%v: got %v, want %v", p, got, want) 365 } 366 } 367 368 // Also test having a source image (gray on the diagonal) that has a 369 // non-zero Bounds().Min, but isn't an image.Paletted. 370 { 371 p := image.Point{+2, +2} 372 src := image.NewRGBA(image.Rectangle{ 373 Min: p, 374 Max: p.Add(image.Point{6, 6}), 375 }) 376 src.SetRGBA(2, 2, color.RGBA{0x22, 0x22, 0x22, 0xFF}) 377 src.SetRGBA(3, 3, color.RGBA{0x33, 0x33, 0x33, 0xFF}) 378 src.SetRGBA(4, 4, color.RGBA{0x44, 0x44, 0x44, 0xFF}) 379 src.SetRGBA(5, 5, color.RGBA{0x55, 0x55, 0x55, 0xFF}) 380 src.SetRGBA(6, 6, color.RGBA{0x66, 0x66, 0x66, 0xFF}) 381 src.SetRGBA(7, 7, color.RGBA{0x77, 0x77, 0x77, 0xFF}) 382 383 var buf bytes.Buffer 384 if err := Encode(&buf, src, nil); err != nil { 385 t.Errorf("gray-diagonal: Encode: %v", err) 386 return 387 } 388 m, err := Decode(&buf) 389 if err != nil { 390 t.Errorf("gray-diagonal: Decode: %v", err) 391 return 392 } 393 if got, want := m.Bounds(), image.Rect(0, 0, 6, 6); got != want { 394 t.Errorf("gray-diagonal: got %v, want %v", got, want) 395 return 396 } 397 398 rednessAt := func(x int, y int) uint32 { 399 r, _, _, _ := m.At(x, y).RGBA() 400 // Shift by 8 to convert from 16 bit color to 8 bit color. 401 return r >> 8 402 } 403 404 // Round-tripping a still (non-animated) image.Image through 405 // Encode+Decode should shift the origin to (0, 0). 406 if got, want := rednessAt(0, 0), uint32(0x22); got != want { 407 t.Errorf("gray-diagonal: rednessAt(0, 0): got 0x%02x, want 0x%02x", got, want) 408 } 409 if got, want := rednessAt(5, 5), uint32(0x77); got != want { 410 t.Errorf("gray-diagonal: rednessAt(5, 5): got 0x%02x, want 0x%02x", got, want) 411 } 412 } 413 } 414 415 func TestEncodeImplicitConfigSize(t *testing.T) { 416 // For backwards compatibility for Go 1.4 and earlier code, the Config 417 // field is optional, and if zero, the width and height is implied by the 418 // first (and in this case only) frame's width and height. 419 // 420 // A Config only specifies a width and height (two integers) while an 421 // image.Image's Bounds method returns an image.Rectangle (four integers). 422 // For a gif.GIF, the overall bounds' top-left point is always implicitly 423 // (0, 0), and any frame whose bounds have a negative X or Y will be 424 // outside those overall bounds, so encoding should fail. 425 for _, lowerBound := range []int{-1, 0, 1} { 426 images := []*image.Paletted{ 427 image.NewPaletted(image.Rect(lowerBound, lowerBound, 4, 4), palette.Plan9), 428 } 429 g := &GIF{ 430 Image: images, 431 Delay: make([]int, len(images)), 432 } 433 err := EncodeAll(io.Discard, g) 434 if lowerBound >= 0 { 435 if err != nil { 436 t.Errorf("lowerBound=%d: %v", lowerBound, err) 437 } 438 } else { 439 if err == nil { 440 t.Errorf("lowerBound=%d: got nil error, want non-nil", lowerBound) 441 } 442 } 443 } 444 } 445 446 func TestEncodePalettes(t *testing.T) { 447 const w, h = 5, 5 448 pals := []color.Palette{{ 449 color.RGBA{0x00, 0x00, 0x00, 0xff}, 450 color.RGBA{0x01, 0x00, 0x00, 0xff}, 451 color.RGBA{0x02, 0x00, 0x00, 0xff}, 452 }, { 453 color.RGBA{0x00, 0x00, 0x00, 0xff}, 454 color.RGBA{0x00, 0x01, 0x00, 0xff}, 455 }, { 456 color.RGBA{0x00, 0x00, 0x03, 0xff}, 457 color.RGBA{0x00, 0x00, 0x02, 0xff}, 458 color.RGBA{0x00, 0x00, 0x01, 0xff}, 459 color.RGBA{0x00, 0x00, 0x00, 0xff}, 460 }, { 461 color.RGBA{0x10, 0x07, 0xf0, 0xff}, 462 color.RGBA{0x20, 0x07, 0xf0, 0xff}, 463 color.RGBA{0x30, 0x07, 0xf0, 0xff}, 464 color.RGBA{0x40, 0x07, 0xf0, 0xff}, 465 color.RGBA{0x50, 0x07, 0xf0, 0xff}, 466 }} 467 g0 := &GIF{ 468 Image: []*image.Paletted{ 469 image.NewPaletted(image.Rect(0, 0, w, h), pals[0]), 470 image.NewPaletted(image.Rect(0, 0, w, h), pals[1]), 471 image.NewPaletted(image.Rect(0, 0, w, h), pals[2]), 472 image.NewPaletted(image.Rect(0, 0, w, h), pals[3]), 473 }, 474 Delay: make([]int, len(pals)), 475 Disposal: make([]byte, len(pals)), 476 Config: image.Config{ 477 ColorModel: pals[2], 478 Width: w, 479 Height: h, 480 }, 481 } 482 483 var buf bytes.Buffer 484 if err := EncodeAll(&buf, g0); err != nil { 485 t.Fatalf("EncodeAll: %v", err) 486 } 487 g1, err := DecodeAll(&buf) 488 if err != nil { 489 t.Fatalf("DecodeAll: %v", err) 490 } 491 if len(g0.Image) != len(g1.Image) { 492 t.Fatalf("image lengths differ: %d and %d", len(g0.Image), len(g1.Image)) 493 } 494 for i, m := range g1.Image { 495 if got, want := m.Palette, pals[i]; !palettesEqual(got, want) { 496 t.Errorf("frame %d:\ngot %v\nwant %v", i, got, want) 497 } 498 } 499 } 500 501 func TestEncodeBadPalettes(t *testing.T) { 502 const w, h = 5, 5 503 for _, n := range []int{256, 257} { 504 for _, nilColors := range []bool{false, true} { 505 pal := make(color.Palette, n) 506 if !nilColors { 507 for i := range pal { 508 pal[i] = color.Black 509 } 510 } 511 512 err := EncodeAll(io.Discard, &GIF{ 513 Image: []*image.Paletted{ 514 image.NewPaletted(image.Rect(0, 0, w, h), pal), 515 }, 516 Delay: make([]int, 1), 517 Disposal: make([]byte, 1), 518 Config: image.Config{ 519 ColorModel: pal, 520 Width: w, 521 Height: h, 522 }, 523 }) 524 525 got := err != nil 526 want := n > 256 || nilColors 527 if got != want { 528 t.Errorf("n=%d, nilColors=%t: err != nil: got %t, want %t", n, nilColors, got, want) 529 } 530 } 531 } 532 } 533 534 func TestColorTablesMatch(t *testing.T) { 535 const trIdx = 100 536 global := color.Palette(palette.Plan9) 537 if rgb := global[trIdx].(color.RGBA); rgb.R == 0 && rgb.G == 0 && rgb.B == 0 { 538 t.Fatalf("trIdx (%d) is already black", trIdx) 539 } 540 541 // Make a copy of the palette, substituting trIdx's slot with transparent, 542 // just like decoder.decode. 543 local := append(color.Palette(nil), global...) 544 local[trIdx] = color.RGBA{} 545 546 const testLen = 3 * 256 547 const padded = 7 548 e := new(encoder) 549 if l, err := encodeColorTable(e.globalColorTable[:], global, padded); err != nil || l != testLen { 550 t.Fatalf("Failed to encode global color table: got %d, %v; want nil, %d", l, err, testLen) 551 } 552 if l, err := encodeColorTable(e.localColorTable[:], local, padded); err != nil || l != testLen { 553 t.Fatalf("Failed to encode local color table: got %d, %v; want nil, %d", l, err, testLen) 554 } 555 if bytes.Equal(e.globalColorTable[:testLen], e.localColorTable[:testLen]) { 556 t.Fatal("Encoded color tables are equal, expected mismatch") 557 } 558 if !e.colorTablesMatch(len(local), trIdx) { 559 t.Fatal("colorTablesMatch() == false, expected true") 560 } 561 } 562 563 func TestEncodeCroppedSubImages(t *testing.T) { 564 // This test means to ensure that Encode honors the Bounds and Strides of 565 // images correctly when encoding. 566 whole := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9) 567 subImages := []image.Rectangle{ 568 image.Rect(0, 0, 50, 50), 569 image.Rect(50, 0, 100, 50), 570 image.Rect(0, 50, 50, 50), 571 image.Rect(50, 50, 100, 100), 572 image.Rect(25, 25, 75, 75), 573 image.Rect(0, 0, 100, 50), 574 image.Rect(0, 50, 100, 100), 575 image.Rect(0, 0, 50, 100), 576 image.Rect(50, 0, 100, 100), 577 } 578 for _, sr := range subImages { 579 si := whole.SubImage(sr) 580 buf := bytes.NewBuffer(nil) 581 if err := Encode(buf, si, nil); err != nil { 582 t.Errorf("Encode: sr=%v: %v", sr, err) 583 continue 584 } 585 if _, err := Decode(buf); err != nil { 586 t.Errorf("Decode: sr=%v: %v", sr, err) 587 } 588 } 589 } 590 591 type offsetImage struct { 592 image.Image 593 Rect image.Rectangle 594 } 595 596 func (i offsetImage) Bounds() image.Rectangle { 597 return i.Rect 598 } 599 600 func TestEncodeWrappedImage(t *testing.T) { 601 m0, err := readImg("../testdata/video-001.gif") 602 if err != nil { 603 t.Fatalf("readImg: %v", err) 604 } 605 606 // Case 1: Encode a wrapped image.Image 607 buf := new(bytes.Buffer) 608 w0 := offsetImage{m0, m0.Bounds()} 609 err = Encode(buf, w0, nil) 610 if err != nil { 611 t.Fatalf("Encode: %v", err) 612 } 613 w1, err := Decode(buf) 614 if err != nil { 615 t.Fatalf("Dencode: %v", err) 616 } 617 avgDelta := averageDelta(m0, w1) 618 if avgDelta > 0 { 619 t.Fatalf("Wrapped: average delta is too high. expected: 0, got %d", avgDelta) 620 } 621 622 // Case 2: Encode a wrapped image.Image with offset 623 b0 := image.Rectangle{ 624 Min: image.Point{ 625 X: 128, 626 Y: 64, 627 }, 628 Max: image.Point{ 629 X: 256, 630 Y: 128, 631 }, 632 } 633 w0 = offsetImage{m0, b0} 634 buf = new(bytes.Buffer) 635 err = Encode(buf, w0, nil) 636 if err != nil { 637 t.Fatalf("Encode: %v", err) 638 } 639 w1, err = Decode(buf) 640 if err != nil { 641 t.Fatalf("Dencode: %v", err) 642 } 643 644 b1 := image.Rectangle{ 645 Min: image.Point{ 646 X: 0, 647 Y: 0, 648 }, 649 Max: image.Point{ 650 X: 128, 651 Y: 64, 652 }, 653 } 654 avgDelta = averageDeltaBound(m0, w1, b0, b1) 655 if avgDelta > 0 { 656 t.Fatalf("Wrapped and offset: average delta is too high. expected: 0, got %d", avgDelta) 657 } 658 } 659 660 func BenchmarkEncodeRandomPaletted(b *testing.B) { 661 paletted := image.NewPaletted(image.Rect(0, 0, 640, 480), palette.Plan9) 662 rnd := rand.New(rand.NewSource(123)) 663 for i := range paletted.Pix { 664 paletted.Pix[i] = uint8(rnd.Intn(256)) 665 } 666 667 b.SetBytes(640 * 480 * 1) 668 b.ReportAllocs() 669 b.ResetTimer() 670 for i := 0; i < b.N; i++ { 671 Encode(io.Discard, paletted, nil) 672 } 673 } 674 675 func BenchmarkEncodeRandomRGBA(b *testing.B) { 676 rgba := image.NewRGBA(image.Rect(0, 0, 640, 480)) 677 bo := rgba.Bounds() 678 rnd := rand.New(rand.NewSource(123)) 679 for y := bo.Min.Y; y < bo.Max.Y; y++ { 680 for x := bo.Min.X; x < bo.Max.X; x++ { 681 rgba.SetRGBA(x, y, color.RGBA{ 682 uint8(rnd.Intn(256)), 683 uint8(rnd.Intn(256)), 684 uint8(rnd.Intn(256)), 685 255, 686 }) 687 } 688 } 689 690 b.SetBytes(640 * 480 * 4) 691 b.ReportAllocs() 692 b.ResetTimer() 693 for i := 0; i < b.N; i++ { 694 Encode(io.Discard, rgba, nil) 695 } 696 } 697 698 func BenchmarkEncodeRealisticPaletted(b *testing.B) { 699 img, err := readImg("../testdata/video-001.png") 700 if err != nil { 701 b.Fatalf("readImg: %v", err) 702 } 703 bo := img.Bounds() 704 paletted := image.NewPaletted(bo, palette.Plan9) 705 draw.Draw(paletted, bo, img, bo.Min, draw.Src) 706 707 b.SetBytes(int64(bo.Dx() * bo.Dy() * 1)) 708 b.ReportAllocs() 709 b.ResetTimer() 710 for i := 0; i < b.N; i++ { 711 Encode(io.Discard, paletted, nil) 712 } 713 } 714 715 func BenchmarkEncodeRealisticRGBA(b *testing.B) { 716 img, err := readImg("../testdata/video-001.png") 717 if err != nil { 718 b.Fatalf("readImg: %v", err) 719 } 720 bo := img.Bounds() 721 // Converting img to rgba is redundant for video-001.png, which is already 722 // in the RGBA format, but for those copy/pasting this benchmark (but 723 // changing the source image), the conversion ensures that we're still 724 // benchmarking encoding an RGBA image. 725 rgba := image.NewRGBA(bo) 726 draw.Draw(rgba, bo, img, bo.Min, draw.Src) 727 728 b.SetBytes(int64(bo.Dx() * bo.Dy() * 4)) 729 b.ReportAllocs() 730 b.ResetTimer() 731 for i := 0; i < b.N; i++ { 732 Encode(io.Discard, rgba, nil) 733 } 734 }