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