github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/image/gif/reader_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 "compress/lzw" 10 "image" 11 "image/color" 12 "reflect" 13 "strings" 14 "testing" 15 ) 16 17 // header, palette and trailer are parts of a valid 2x1 GIF image. 18 const ( 19 headerStr = "GIF89a" + 20 "\x02\x00\x01\x00" + // width=2, height=1 21 "\x80\x00\x00" // headerFields=(a color table of 2 pixels), backgroundIndex, aspect 22 paletteStr = "\x10\x20\x30\x40\x50\x60" // the color table, also known as a palette 23 trailerStr = "\x3b" 24 ) 25 26 // lzwEncode returns an LZW encoding (with 2-bit literals) of in. 27 func lzwEncode(in []byte) []byte { 28 b := &bytes.Buffer{} 29 w := lzw.NewWriter(b, lzw.LSB, 2) 30 if _, err := w.Write(in); err != nil { 31 panic(err) 32 } 33 if err := w.Close(); err != nil { 34 panic(err) 35 } 36 return b.Bytes() 37 } 38 39 func TestDecode(t *testing.T) { 40 testCases := []struct { 41 nPix int // The number of pixels in the image data. 42 extra bool // Whether to write an extra block after the LZW-encoded data. 43 wantErr error 44 }{ 45 {0, false, errNotEnough}, 46 {1, false, errNotEnough}, 47 {2, false, nil}, 48 {2, true, errTooMuch}, 49 {3, false, errTooMuch}, 50 } 51 for _, tc := range testCases { 52 b := &bytes.Buffer{} 53 b.WriteString(headerStr) 54 b.WriteString(paletteStr) 55 // Write an image with bounds 2x1 but tc.nPix pixels. If tc.nPix != 2 56 // then this should result in an invalid GIF image. First, write a 57 // magic 0x2c (image descriptor) byte, bounds=(0,0)-(2,1), a flags 58 // byte, and 2-bit LZW literals. 59 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 60 if tc.nPix > 0 { 61 enc := lzwEncode(make([]byte, tc.nPix)) 62 if len(enc) > 0xff { 63 t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) 64 continue 65 } 66 b.WriteByte(byte(len(enc))) 67 b.Write(enc) 68 } 69 if tc.extra { 70 b.WriteString("\x01\x02") // A 1-byte payload with an 0x02 byte. 71 } 72 b.WriteByte(0x00) // An empty block signifies the end of the image data. 73 b.WriteString(trailerStr) 74 75 got, err := Decode(b) 76 if err != tc.wantErr { 77 t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, err, tc.wantErr) 78 } 79 80 if tc.wantErr != nil { 81 continue 82 } 83 want := &image.Paletted{ 84 Pix: []uint8{0, 0}, 85 Stride: 2, 86 Rect: image.Rect(0, 0, 2, 1), 87 Palette: color.Palette{ 88 color.RGBA{0x10, 0x20, 0x30, 0xff}, 89 color.RGBA{0x40, 0x50, 0x60, 0xff}, 90 }, 91 } 92 if !reflect.DeepEqual(got, want) { 93 t.Errorf("nPix=%d, extra=%t\ngot %v\nwant %v", tc.nPix, tc.extra, got, want) 94 } 95 } 96 } 97 98 func TestTransparentIndex(t *testing.T) { 99 b := &bytes.Buffer{} 100 b.WriteString(headerStr) 101 b.WriteString(paletteStr) 102 for transparentIndex := 0; transparentIndex < 3; transparentIndex++ { 103 if transparentIndex < 2 { 104 // Write the graphic control for the transparent index. 105 b.WriteString("\x21\xf9\x04\x01\x00\x00") 106 b.WriteByte(byte(transparentIndex)) 107 b.WriteByte(0) 108 } 109 // Write an image with bounds 2x1, as per TestDecode. 110 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 111 enc := lzwEncode([]byte{0x00, 0x00}) 112 if len(enc) > 0xff { 113 t.Fatalf("compressed length %d is too large", len(enc)) 114 } 115 b.WriteByte(byte(len(enc))) 116 b.Write(enc) 117 b.WriteByte(0x00) 118 } 119 b.WriteString(trailerStr) 120 121 g, err := DecodeAll(b) 122 if err != nil { 123 t.Fatalf("DecodeAll: %v", err) 124 } 125 c0 := color.RGBA{paletteStr[0], paletteStr[1], paletteStr[2], 0xff} 126 c1 := color.RGBA{paletteStr[3], paletteStr[4], paletteStr[5], 0xff} 127 cz := color.RGBA{} 128 wants := []color.Palette{ 129 {cz, c1}, 130 {c0, cz}, 131 {c0, c1}, 132 } 133 if len(g.Image) != len(wants) { 134 t.Fatalf("got %d images, want %d", len(g.Image), len(wants)) 135 } 136 for i, want := range wants { 137 got := g.Image[i].Palette 138 if !reflect.DeepEqual(got, want) { 139 t.Errorf("palette #%d:\ngot %v\nwant %v", i, got, want) 140 } 141 } 142 } 143 144 // testGIF is a simple GIF that we can modify to test different scenarios. 145 var testGIF = []byte{ 146 'G', 'I', 'F', '8', '9', 'a', 147 1, 0, 1, 0, // w=1, h=1 (6) 148 128, 0, 0, // headerFields, bg, aspect (10) 149 0, 0, 0, 1, 1, 1, // color table and graphics control (13) 150 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, // (19) 151 // frame 1 (0,0 - 1,1) 152 0x2c, 153 0x00, 0x00, 0x00, 0x00, 154 0x01, 0x00, 0x01, 0x00, // (32) 155 0x00, 156 0x02, 0x02, 0x4c, 0x01, 0x00, // lzw pixels 157 // trailer 158 0x3b, 159 } 160 161 func try(t *testing.T, b []byte, want string) { 162 _, err := DecodeAll(bytes.NewReader(b)) 163 var got string 164 if err != nil { 165 got = err.Error() 166 } 167 if got != want { 168 t.Fatalf("got %v, want %v", got, want) 169 } 170 } 171 172 func TestBounds(t *testing.T) { 173 // Make a local copy of testGIF. 174 gif := make([]byte, len(testGIF)) 175 copy(gif, testGIF) 176 // Make the bounds too big, just by one. 177 gif[32] = 2 178 want := "gif: frame bounds larger than image bounds" 179 try(t, gif, want) 180 181 // Make the bounds too small; does not trigger bounds 182 // check, but now there's too much data. 183 gif[32] = 0 184 want = "gif: too much image data" 185 try(t, gif, want) 186 gif[32] = 1 187 188 // Make the bounds really big, expect an error. 189 want = "gif: frame bounds larger than image bounds" 190 for i := 0; i < 4; i++ { 191 gif[32+i] = 0xff 192 } 193 try(t, gif, want) 194 } 195 196 func TestNoPalette(t *testing.T) { 197 b := &bytes.Buffer{} 198 199 // Manufacture a GIF with no palette, so any pixel at all 200 // will be invalid. 201 b.WriteString(headerStr[:len(headerStr)-3]) 202 b.WriteString("\x00\x00\x00") // No global palette. 203 204 // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. 205 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 206 207 // Encode the pixels: neither is in range, because there is no palette. 208 enc := lzwEncode([]byte{0x00, 0x03}) 209 b.WriteByte(byte(len(enc))) 210 b.Write(enc) 211 b.WriteByte(0x00) // An empty block signifies the end of the image data. 212 213 b.WriteString(trailerStr) 214 215 try(t, b.Bytes(), "gif: no color table") 216 } 217 218 func TestPixelOutsidePaletteRange(t *testing.T) { 219 for _, pval := range []byte{0, 1, 2, 3} { 220 b := &bytes.Buffer{} 221 222 // Manufacture a GIF with a 2 color palette. 223 b.WriteString(headerStr) 224 b.WriteString(paletteStr) 225 226 // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. 227 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 228 229 // Encode the pixels; some pvals trigger the expected error. 230 enc := lzwEncode([]byte{pval, pval}) 231 b.WriteByte(byte(len(enc))) 232 b.Write(enc) 233 b.WriteByte(0x00) // An empty block signifies the end of the image data. 234 235 b.WriteString(trailerStr) 236 237 // No error expected, unless the pixels are beyond the 2 color palette. 238 want := "" 239 if pval >= 2 { 240 want = "gif: invalid pixel value" 241 } 242 try(t, b.Bytes(), want) 243 } 244 } 245 246 func TestTransparentPixelOutsidePaletteRange(t *testing.T) { 247 b := &bytes.Buffer{} 248 249 // Manufacture a GIF with a 2 color palette. 250 b.WriteString(headerStr) 251 b.WriteString(paletteStr) 252 253 // Graphic Control Extension: transparency, transparent color index = 3. 254 // 255 // This index, 3, is out of range of the global palette and there is no 256 // local palette in the subsequent image descriptor. This is an error 257 // according to the spec, but Firefox and Google Chrome seem OK with this. 258 // 259 // See golang.org/issue/15059. 260 b.WriteString("\x21\xf9\x04\x01\x00\x00\x03\x00") 261 262 // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. 263 b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 264 265 // Encode the pixels. 266 enc := lzwEncode([]byte{0x03, 0x03}) 267 b.WriteByte(byte(len(enc))) 268 b.Write(enc) 269 b.WriteByte(0x00) // An empty block signifies the end of the image data. 270 271 b.WriteString(trailerStr) 272 273 try(t, b.Bytes(), "") 274 } 275 276 func TestLoopCount(t *testing.T) { 277 data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" + 278 "\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;") 279 img, err := DecodeAll(bytes.NewReader(data)) 280 if err != nil { 281 t.Fatal("DecodeAll:", err) 282 } 283 w := new(bytes.Buffer) 284 err = EncodeAll(w, img) 285 if err != nil { 286 t.Fatal("EncodeAll:", err) 287 } 288 img1, err := DecodeAll(w) 289 if err != nil { 290 t.Fatal("DecodeAll:", err) 291 } 292 if img.LoopCount != img1.LoopCount { 293 t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, img1.LoopCount) 294 } 295 } 296 297 func TestUnexpectedEOF(t *testing.T) { 298 for i := len(testGIF) - 1; i >= 0; i-- { 299 _, err := Decode(bytes.NewReader(testGIF[:i])) 300 if err == errNotEnough { 301 continue 302 } 303 text := "" 304 if err != nil { 305 text = err.Error() 306 } 307 if !strings.HasPrefix(text, "gif:") || !strings.HasSuffix(text, ": unexpected EOF") { 308 t.Errorf("Decode(testGIF[:%d]) = %v, want gif: ...: unexpected EOF", i, err) 309 } 310 } 311 }