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  }