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