Home | History | Annotate | Download | only in gif
      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