github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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  }