github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/src/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/png" 12 "io/ioutil" 13 "math/rand" 14 "os" 15 "testing" 16 ) 17 18 func readImg(filename string) (image.Image, error) { 19 f, err := os.Open(filename) 20 if err != nil { 21 return nil, err 22 } 23 defer f.Close() 24 m, _, err := image.Decode(f) 25 return m, err 26 } 27 28 func readGIF(filename string) (*GIF, error) { 29 f, err := os.Open(filename) 30 if err != nil { 31 return nil, err 32 } 33 defer f.Close() 34 return DecodeAll(f) 35 } 36 37 func delta(u0, u1 uint32) int64 { 38 d := int64(u0) - int64(u1) 39 if d < 0 { 40 return -d 41 } 42 return d 43 } 44 45 // averageDelta returns the average delta in RGB space. The two images must 46 // have the same bounds. 47 func averageDelta(m0, m1 image.Image) int64 { 48 b := m0.Bounds() 49 var sum, n int64 50 for y := b.Min.Y; y < b.Max.Y; y++ { 51 for x := b.Min.X; x < b.Max.X; x++ { 52 c0 := m0.At(x, y) 53 c1 := m1.At(x, y) 54 r0, g0, b0, _ := c0.RGBA() 55 r1, g1, b1, _ := c1.RGBA() 56 sum += delta(r0, r1) 57 sum += delta(g0, g1) 58 sum += delta(b0, b1) 59 n += 3 60 } 61 } 62 return sum / n 63 } 64 65 var testCase = []struct { 66 filename string 67 tolerance int64 68 }{ 69 {"../testdata/video-001.png", 1 << 12}, 70 {"../testdata/video-001.gif", 0}, 71 {"../testdata/video-001.interlaced.gif", 0}, 72 } 73 74 func TestWriter(t *testing.T) { 75 for _, tc := range testCase { 76 m0, err := readImg(tc.filename) 77 if err != nil { 78 t.Error(tc.filename, err) 79 continue 80 } 81 var buf bytes.Buffer 82 err = Encode(&buf, m0, nil) 83 if err != nil { 84 t.Error(tc.filename, err) 85 continue 86 } 87 m1, err := Decode(&buf) 88 if err != nil { 89 t.Error(tc.filename, err) 90 continue 91 } 92 if m0.Bounds() != m1.Bounds() { 93 t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds()) 94 continue 95 } 96 // Compare the average delta to the tolerance level. 97 avgDelta := averageDelta(m0, m1) 98 if avgDelta > tc.tolerance { 99 t.Errorf("%s: average delta is too high. expected: %d, got %d", tc.filename, tc.tolerance, avgDelta) 100 continue 101 } 102 } 103 } 104 105 func TestSubImage(t *testing.T) { 106 m0, err := readImg("../testdata/video-001.gif") 107 if err != nil { 108 t.Fatalf("readImg: %v", err) 109 } 110 m0 = m0.(*image.Paletted).SubImage(image.Rect(0, 0, 50, 30)) 111 var buf bytes.Buffer 112 err = Encode(&buf, m0, nil) 113 if err != nil { 114 t.Fatalf("Encode: %v", err) 115 } 116 m1, err := Decode(&buf) 117 if err != nil { 118 t.Fatalf("Decode: %v", err) 119 } 120 if m0.Bounds() != m1.Bounds() { 121 t.Fatalf("bounds differ: %v and %v", m0.Bounds(), m1.Bounds()) 122 } 123 if averageDelta(m0, m1) != 0 { 124 t.Fatalf("images differ") 125 } 126 } 127 128 var frames = []string{ 129 "../testdata/video-001.gif", 130 "../testdata/video-005.gray.gif", 131 } 132 133 func TestEncodeAll(t *testing.T) { 134 g0 := &GIF{ 135 Image: make([]*image.Paletted, len(frames)), 136 Delay: make([]int, len(frames)), 137 LoopCount: 5, 138 } 139 for i, f := range frames { 140 m, err := readGIF(f) 141 if err != nil { 142 t.Fatal(f, err) 143 } 144 g0.Image[i] = m.Image[0] 145 } 146 var buf bytes.Buffer 147 if err := EncodeAll(&buf, g0); err != nil { 148 t.Fatal("EncodeAll:", err) 149 } 150 g1, err := DecodeAll(&buf) 151 if err != nil { 152 t.Fatal("DecodeAll:", err) 153 } 154 if g0.LoopCount != g1.LoopCount { 155 t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount) 156 } 157 for i := range g0.Image { 158 m0, m1 := g0.Image[i], g1.Image[i] 159 if m0.Bounds() != m1.Bounds() { 160 t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds()) 161 } 162 d0, d1 := g0.Delay[i], g1.Delay[i] 163 if d0 != d1 { 164 t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1) 165 } 166 } 167 168 g1.Delay = make([]int, 1) 169 if err := EncodeAll(ioutil.Discard, g1); err == nil { 170 t.Error("expected error from mismatched delay and image slice lengths") 171 } 172 if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil { 173 t.Error("expected error from providing empty gif") 174 } 175 } 176 177 func BenchmarkEncode(b *testing.B) { 178 b.StopTimer() 179 180 bo := image.Rect(0, 0, 640, 480) 181 rnd := rand.New(rand.NewSource(123)) 182 183 // Restrict to a 256-color paletted image to avoid quantization path. 184 palette := make(color.Palette, 256) 185 for i := range palette { 186 palette[i] = color.RGBA{ 187 uint8(rnd.Intn(256)), 188 uint8(rnd.Intn(256)), 189 uint8(rnd.Intn(256)), 190 255, 191 } 192 } 193 img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette) 194 for y := bo.Min.Y; y < bo.Max.Y; y++ { 195 for x := bo.Min.X; x < bo.Max.X; x++ { 196 img.Set(x, y, palette[rnd.Intn(256)]) 197 } 198 } 199 200 b.SetBytes(640 * 480 * 4) 201 b.StartTimer() 202 for i := 0; i < b.N; i++ { 203 Encode(ioutil.Discard, img, nil) 204 } 205 } 206 207 func BenchmarkQuantizedEncode(b *testing.B) { 208 b.StopTimer() 209 img := image.NewRGBA(image.Rect(0, 0, 640, 480)) 210 bo := img.Bounds() 211 rnd := rand.New(rand.NewSource(123)) 212 for y := bo.Min.Y; y < bo.Max.Y; y++ { 213 for x := bo.Min.X; x < bo.Max.X; x++ { 214 img.SetRGBA(x, y, color.RGBA{ 215 uint8(rnd.Intn(256)), 216 uint8(rnd.Intn(256)), 217 uint8(rnd.Intn(256)), 218 255, 219 }) 220 } 221 } 222 b.SetBytes(640 * 480 * 4) 223 b.StartTimer() 224 for i := 0; i < b.N; i++ { 225 Encode(ioutil.Discard, img, nil) 226 } 227 }