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