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