Source file
src/image/gif/writer_test.go
1
2
3
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
49
50 func averageDelta(m0, m1 image.Image) int64 {
51 b := m0.Bounds()
52 return averageDeltaBound(m0, m1, b, b)
53 }
54
55
56
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
75
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
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
142
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
194
195
196
197
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
369
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
401 return r >> 8
402 }
403
404
405
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
417
418
419
420
421
422
423
424
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
542
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
565
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
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
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
722
723
724
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 }
735
View as plain text