github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/image/png/reader_test.go (about)

     1  // Copyright 2009 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 png
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"image"
    12  	"image/color"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  var filenames = []string{
    22  	"basn0g01",
    23  	"basn0g01-30",
    24  	"basn0g02",
    25  	"basn0g02-29",
    26  	"basn0g04",
    27  	"basn0g04-31",
    28  	"basn0g08",
    29  	"basn0g16",
    30  	"basn2c08",
    31  	"basn2c16",
    32  	"basn3p01",
    33  	"basn3p02",
    34  	"basn3p04",
    35  	"basn3p04-31i",
    36  	"basn3p08",
    37  	"basn3p08-trns",
    38  	"basn4a08",
    39  	"basn4a16",
    40  	"basn6a08",
    41  	"basn6a16",
    42  	"ftbbn0g01",
    43  	"ftbbn0g02",
    44  	"ftbbn0g04",
    45  	"ftbbn2c16",
    46  	"ftbbn3p08",
    47  	"ftbgn2c16",
    48  	"ftbgn3p08",
    49  	"ftbrn2c08",
    50  	"ftbwn0g16",
    51  	"ftbwn3p08",
    52  	"ftbyn3p08",
    53  	"ftp0n0g08",
    54  	"ftp0n2c08",
    55  	"ftp0n3p08",
    56  	"ftp1n3p08",
    57  }
    58  
    59  var filenamesPaletted = []string{
    60  	"basn3p01",
    61  	"basn3p02",
    62  	"basn3p04",
    63  	"basn3p08",
    64  	"basn3p08-trns",
    65  }
    66  
    67  var filenamesShort = []string{
    68  	"basn0g01",
    69  	"basn0g04-31",
    70  	"basn6a16",
    71  }
    72  
    73  func readPNG(filename string) (image.Image, error) {
    74  	f, err := os.Open(filename)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	defer f.Close()
    79  	return Decode(f)
    80  }
    81  
    82  // fakebKGDs maps from filenames to fake bKGD chunks for our approximation to
    83  // the sng command-line tool. Package png doesn't keep that metadata when
    84  // png.Decode returns an image.Image.
    85  var fakebKGDs = map[string]string{
    86  	"ftbbn0g01": "bKGD {gray: 0;}\n",
    87  	"ftbbn0g02": "bKGD {gray: 0;}\n",
    88  	"ftbbn0g04": "bKGD {gray: 0;}\n",
    89  	"ftbbn2c16": "bKGD {red: 0;  green: 0;  blue: 65535;}\n",
    90  	"ftbbn3p08": "bKGD {index: 245}\n",
    91  	"ftbgn2c16": "bKGD {red: 0;  green: 65535;  blue: 0;}\n",
    92  	"ftbgn3p08": "bKGD {index: 245}\n",
    93  	"ftbrn2c08": "bKGD {red: 255;  green: 0;  blue: 0;}\n",
    94  	"ftbwn0g16": "bKGD {gray: 65535;}\n",
    95  	"ftbwn3p08": "bKGD {index: 0}\n",
    96  	"ftbyn3p08": "bKGD {index: 245}\n",
    97  }
    98  
    99  // fakegAMAs maps from filenames to fake gAMA chunks for our approximation to
   100  // the sng command-line tool. Package png doesn't keep that metadata when
   101  // png.Decode returns an image.Image.
   102  var fakegAMAs = map[string]string{
   103  	"ftbbn0g01": "",
   104  	"ftbbn0g02": "gAMA {0.45455}\n",
   105  }
   106  
   107  // fakeIHDRUsings maps from filenames to fake IHDR "using" lines for our
   108  // approximation to the sng command-line tool. The PNG model is that
   109  // transparency (in the tRNS chunk) is separate to the color/grayscale/palette
   110  // color model (in the IHDR chunk). The Go model is that the concrete
   111  // image.Image type returned by png.Decode, such as image.RGBA (with all pixels
   112  // having 100% alpha) or image.NRGBA, encapsulates whether or not the image has
   113  // transparency. This map is a hack to work around the fact that the Go model
   114  // can't otherwise discriminate PNG's "IHDR says color (with no alpha) but tRNS
   115  // says alpha" and "IHDR says color with alpha".
   116  var fakeIHDRUsings = map[string]string{
   117  	"ftbbn0g01": "    using grayscale;\n",
   118  	"ftbbn0g02": "    using grayscale;\n",
   119  	"ftbbn0g04": "    using grayscale;\n",
   120  	"ftbbn2c16": "    using color;\n",
   121  	"ftbgn2c16": "    using color;\n",
   122  	"ftbrn2c08": "    using color;\n",
   123  	"ftbwn0g16": "    using grayscale;\n",
   124  }
   125  
   126  // An approximation of the sng command-line tool.
   127  func sng(w io.WriteCloser, filename string, png image.Image) {
   128  	defer w.Close()
   129  	bounds := png.Bounds()
   130  	cm := png.ColorModel()
   131  	var bitdepth int
   132  	switch cm {
   133  	case color.RGBAModel, color.NRGBAModel, color.AlphaModel, color.GrayModel:
   134  		bitdepth = 8
   135  	default:
   136  		bitdepth = 16
   137  	}
   138  	cpm, _ := cm.(color.Palette)
   139  	var paletted *image.Paletted
   140  	if cpm != nil {
   141  		switch {
   142  		case len(cpm) <= 2:
   143  			bitdepth = 1
   144  		case len(cpm) <= 4:
   145  			bitdepth = 2
   146  		case len(cpm) <= 16:
   147  			bitdepth = 4
   148  		default:
   149  			bitdepth = 8
   150  		}
   151  		paletted = png.(*image.Paletted)
   152  	}
   153  
   154  	// Write the filename and IHDR.
   155  	io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
   156  	fmt.Fprintf(w, "    width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
   157  	if s, ok := fakeIHDRUsings[filename]; ok {
   158  		io.WriteString(w, s)
   159  	} else {
   160  		switch {
   161  		case cm == color.RGBAModel, cm == color.RGBA64Model:
   162  			io.WriteString(w, "    using color;\n")
   163  		case cm == color.NRGBAModel, cm == color.NRGBA64Model:
   164  			io.WriteString(w, "    using color alpha;\n")
   165  		case cm == color.GrayModel, cm == color.Gray16Model:
   166  			io.WriteString(w, "    using grayscale;\n")
   167  		case cpm != nil:
   168  			io.WriteString(w, "    using color palette;\n")
   169  		default:
   170  			io.WriteString(w, "unknown PNG decoder color model\n")
   171  		}
   172  	}
   173  	io.WriteString(w, "}\n")
   174  
   175  	// We fake a gAMA chunk. The test files have a gAMA chunk but the go PNG
   176  	// parser ignores it (the PNG spec section 11.3 says "Ancillary chunks may
   177  	// be ignored by a decoder").
   178  	if s, ok := fakegAMAs[filename]; ok {
   179  		io.WriteString(w, s)
   180  	} else {
   181  		io.WriteString(w, "gAMA {1.0000}\n")
   182  	}
   183  
   184  	// Write the PLTE and tRNS (if applicable).
   185  	useTransparent := false
   186  	if cpm != nil {
   187  		lastAlpha := -1
   188  		io.WriteString(w, "PLTE {\n")
   189  		for i, c := range cpm {
   190  			var r, g, b, a uint8
   191  			switch c := c.(type) {
   192  			case color.RGBA:
   193  				r, g, b, a = c.R, c.G, c.B, 0xff
   194  			case color.NRGBA:
   195  				r, g, b, a = c.R, c.G, c.B, c.A
   196  			default:
   197  				panic("unknown palette color type")
   198  			}
   199  			if a != 0xff {
   200  				lastAlpha = i
   201  			}
   202  			fmt.Fprintf(w, "    (%3d,%3d,%3d)     # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
   203  		}
   204  		io.WriteString(w, "}\n")
   205  		if s, ok := fakebKGDs[filename]; ok {
   206  			io.WriteString(w, s)
   207  		}
   208  		if lastAlpha != -1 {
   209  			io.WriteString(w, "tRNS {\n")
   210  			for i := 0; i <= lastAlpha; i++ {
   211  				_, _, _, a := cpm[i].RGBA()
   212  				a >>= 8
   213  				fmt.Fprintf(w, " %d", a)
   214  			}
   215  			io.WriteString(w, "}\n")
   216  		}
   217  	} else if strings.HasPrefix(filename, "ft") {
   218  		if s, ok := fakebKGDs[filename]; ok {
   219  			io.WriteString(w, s)
   220  		}
   221  		// We fake a tRNS chunk. The test files' grayscale and truecolor
   222  		// transparent images all have their top left corner transparent.
   223  		switch c := png.At(0, 0).(type) {
   224  		case color.NRGBA:
   225  			if c.A == 0 {
   226  				useTransparent = true
   227  				io.WriteString(w, "tRNS {\n")
   228  				switch filename {
   229  				case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
   230  					// The standard image package doesn't have a "gray with
   231  					// alpha" type. Instead, we use an image.NRGBA.
   232  					fmt.Fprintf(w, "    gray: %d;\n", c.R)
   233  				default:
   234  					fmt.Fprintf(w, "    red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
   235  				}
   236  				io.WriteString(w, "}\n")
   237  			}
   238  		case color.NRGBA64:
   239  			if c.A == 0 {
   240  				useTransparent = true
   241  				io.WriteString(w, "tRNS {\n")
   242  				switch filename {
   243  				case "ftbwn0g16":
   244  					// The standard image package doesn't have a "gray16 with
   245  					// alpha" type. Instead, we use an image.NRGBA64.
   246  					fmt.Fprintf(w, "    gray: %d;\n", c.R)
   247  				default:
   248  					fmt.Fprintf(w, "    red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
   249  				}
   250  				io.WriteString(w, "}\n")
   251  			}
   252  		}
   253  	}
   254  
   255  	// Write the IMAGE.
   256  	io.WriteString(w, "IMAGE {\n    pixels hex\n")
   257  	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
   258  		switch {
   259  		case cm == color.GrayModel:
   260  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   261  				gray := png.At(x, y).(color.Gray)
   262  				fmt.Fprintf(w, "%02x", gray.Y)
   263  			}
   264  		case cm == color.Gray16Model:
   265  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   266  				gray16 := png.At(x, y).(color.Gray16)
   267  				fmt.Fprintf(w, "%04x ", gray16.Y)
   268  			}
   269  		case cm == color.RGBAModel:
   270  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   271  				rgba := png.At(x, y).(color.RGBA)
   272  				fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B)
   273  			}
   274  		case cm == color.RGBA64Model:
   275  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   276  				rgba64 := png.At(x, y).(color.RGBA64)
   277  				fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B)
   278  			}
   279  		case cm == color.NRGBAModel:
   280  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   281  				nrgba := png.At(x, y).(color.NRGBA)
   282  				switch filename {
   283  				case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
   284  					fmt.Fprintf(w, "%02x", nrgba.R)
   285  				default:
   286  					if useTransparent {
   287  						fmt.Fprintf(w, "%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B)
   288  					} else {
   289  						fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
   290  					}
   291  				}
   292  			}
   293  		case cm == color.NRGBA64Model:
   294  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   295  				nrgba64 := png.At(x, y).(color.NRGBA64)
   296  				switch filename {
   297  				case "ftbwn0g16":
   298  					fmt.Fprintf(w, "%04x ", nrgba64.R)
   299  				default:
   300  					if useTransparent {
   301  						fmt.Fprintf(w, "%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B)
   302  					} else {
   303  						fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
   304  					}
   305  				}
   306  			}
   307  		case cpm != nil:
   308  			var b, c int
   309  			for x := bounds.Min.X; x < bounds.Max.X; x++ {
   310  				b = b<<uint(bitdepth) | int(paletted.ColorIndexAt(x, y))
   311  				c++
   312  				if c == 8/bitdepth {
   313  					fmt.Fprintf(w, "%02x", b)
   314  					b = 0
   315  					c = 0
   316  				}
   317  			}
   318  			if c != 0 {
   319  				for c != 8/bitdepth {
   320  					b = b << uint(bitdepth)
   321  					c++
   322  				}
   323  				fmt.Fprintf(w, "%02x", b)
   324  			}
   325  		}
   326  		io.WriteString(w, "\n")
   327  	}
   328  	io.WriteString(w, "}\n")
   329  }
   330  
   331  func TestReader(t *testing.T) {
   332  	names := filenames
   333  	if testing.Short() {
   334  		names = filenamesShort
   335  	}
   336  	for _, fn := range names {
   337  		// Read the .png file.
   338  		img, err := readPNG("testdata/pngsuite/" + fn + ".png")
   339  		if err != nil {
   340  			t.Error(fn, err)
   341  			continue
   342  		}
   343  
   344  		if fn == "basn4a16" {
   345  			// basn4a16.sng is gray + alpha but sng() will produce true color + alpha
   346  			// so we just check a single random pixel.
   347  			c := img.At(2, 1).(color.NRGBA64)
   348  			if c.R != 0x11a7 || c.G != 0x11a7 || c.B != 0x11a7 || c.A != 0x1085 {
   349  				t.Error(fn, fmt.Errorf("wrong pixel value at (2, 1): %x", c))
   350  			}
   351  			continue
   352  		}
   353  
   354  		piper, pipew := io.Pipe()
   355  		pb := bufio.NewScanner(piper)
   356  		go sng(pipew, fn, img)
   357  		defer piper.Close()
   358  
   359  		// Read the .sng file.
   360  		sf, err := os.Open("testdata/pngsuite/" + fn + ".sng")
   361  		if err != nil {
   362  			t.Error(fn, err)
   363  			continue
   364  		}
   365  		defer sf.Close()
   366  		sb := bufio.NewScanner(sf)
   367  
   368  		// Compare the two, in SNG format, line by line.
   369  		for {
   370  			pdone := !pb.Scan()
   371  			sdone := !sb.Scan()
   372  			if pdone && sdone {
   373  				break
   374  			}
   375  			if pdone || sdone {
   376  				t.Errorf("%s: Different sizes", fn)
   377  				break
   378  			}
   379  			ps := pb.Text()
   380  			ss := sb.Text()
   381  
   382  			// Newer versions of the sng command line tool append an optional
   383  			// color name to the RGB tuple. For example:
   384  			//	# rgb = (0xff,0xff,0xff) grey100
   385  			//	# rgb = (0x00,0x00,0xff) blue1
   386  			// instead of the older version's plainer:
   387  			//	# rgb = (0xff,0xff,0xff)
   388  			//	# rgb = (0x00,0x00,0xff)
   389  			// We strip any such name.
   390  			if strings.Contains(ss, "# rgb = (") && !strings.HasSuffix(ss, ")") {
   391  				if i := strings.LastIndex(ss, ") "); i >= 0 {
   392  					ss = ss[:i+1]
   393  				}
   394  			}
   395  
   396  			if ps != ss {
   397  				t.Errorf("%s: Mismatch\n%s\nversus\n%s\n", fn, ps, ss)
   398  				break
   399  			}
   400  		}
   401  		if pb.Err() != nil {
   402  			t.Error(fn, pb.Err())
   403  		}
   404  		if sb.Err() != nil {
   405  			t.Error(fn, sb.Err())
   406  		}
   407  	}
   408  }
   409  
   410  var readerErrors = []struct {
   411  	file string
   412  	err  string
   413  }{
   414  	{"invalid-zlib.png", "zlib: invalid checksum"},
   415  	{"invalid-crc32.png", "invalid checksum"},
   416  	{"invalid-noend.png", "unexpected EOF"},
   417  	{"invalid-trunc.png", "unexpected EOF"},
   418  }
   419  
   420  func TestReaderError(t *testing.T) {
   421  	for _, tt := range readerErrors {
   422  		img, err := readPNG("testdata/" + tt.file)
   423  		if err == nil {
   424  			t.Errorf("decoding %s: missing error", tt.file)
   425  			continue
   426  		}
   427  		if !strings.Contains(err.Error(), tt.err) {
   428  			t.Errorf("decoding %s: %s, want %s", tt.file, err, tt.err)
   429  		}
   430  		if img != nil {
   431  			t.Errorf("decoding %s: have image + error", tt.file)
   432  		}
   433  	}
   434  }
   435  
   436  func TestPalettedDecodeConfig(t *testing.T) {
   437  	for _, fn := range filenamesPaletted {
   438  		f, err := os.Open("testdata/pngsuite/" + fn + ".png")
   439  		if err != nil {
   440  			t.Errorf("%s: open failed: %v", fn, err)
   441  			continue
   442  		}
   443  		defer f.Close()
   444  		cfg, err := DecodeConfig(f)
   445  		if err != nil {
   446  			t.Errorf("%s: %v", fn, err)
   447  			continue
   448  		}
   449  		pal, ok := cfg.ColorModel.(color.Palette)
   450  		if !ok {
   451  			t.Errorf("%s: expected paletted color model", fn)
   452  			continue
   453  		}
   454  		if pal == nil {
   455  			t.Errorf("%s: palette not initialized", fn)
   456  			continue
   457  		}
   458  	}
   459  }
   460  
   461  func TestInterlaced(t *testing.T) {
   462  	a, err := readPNG("testdata/gray-gradient.png")
   463  	if err != nil {
   464  		t.Fatal(err)
   465  	}
   466  	b, err := readPNG("testdata/gray-gradient.interlaced.png")
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	if !reflect.DeepEqual(a, b) {
   471  		t.Fatalf("decodings differ:\nnon-interlaced:\n%#v\ninterlaced:\n%#v", a, b)
   472  	}
   473  }
   474  
   475  func TestIncompleteIDATOnRowBoundary(t *testing.T) {
   476  	// The following is an invalid 1x2 grayscale PNG image. The header is OK,
   477  	// but the zlib-compressed IDAT payload contains two bytes "\x02\x00",
   478  	// which is only one row of data (the leading "\x02" is a row filter).
   479  	const (
   480  		ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb"
   481  		idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
   482  		iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
   483  	)
   484  	_, err := Decode(strings.NewReader(pngHeader + ihdr + idat + iend))
   485  	if err == nil {
   486  		t.Fatal("got nil error, want non-nil")
   487  	}
   488  }
   489  
   490  func TestTrailingIDATChunks(t *testing.T) {
   491  	// The following is a valid 1x1 PNG image containing color.Gray{255} and
   492  	// a trailing zero-length IDAT chunk (see PNG specification section 12.9):
   493  	const (
   494  		ihdr      = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x00\x00\x00\x00\x3a\x7e\x9b\x55"
   495  		idatWhite = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\xfa\x0f\x08\x00\x00\xff\xff\x01\x05\x01\x02\x5a\xdd\x39\xcd"
   496  		idatZero  = "\x00\x00\x00\x00IDAT\x35\xaf\x06\x1e"
   497  		iend      = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
   498  	)
   499  	_, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatZero + iend))
   500  	if err != nil {
   501  		t.Fatalf("decoding valid image: %v", err)
   502  	}
   503  
   504  	// Non-zero-length trailing IDAT chunks should be ignored (recoverable error).
   505  	// The following chunk contains a single pixel with color.Gray{0}.
   506  	const idatBlack = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
   507  
   508  	img, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatBlack + iend))
   509  	if err != nil {
   510  		t.Fatalf("trailing IDAT not ignored: %v", err)
   511  	}
   512  	if img.At(0, 0) == (color.Gray{0}) {
   513  		t.Fatal("decoded image from trailing IDAT chunk")
   514  	}
   515  }
   516  
   517  func TestMultipletRNSChunks(t *testing.T) {
   518  	/*
   519  		The following is a valid 1x1 paletted PNG image with a 1-element palette
   520  		containing color.NRGBA{0xff, 0x00, 0x00, 0x7f}:
   521  			0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
   522  			0000010: 0000 0001 0000 0001 0803 0000 0028 cb34  .............(.4
   523  			0000020: bb00 0000 0350 4c54 45ff 0000 19e2 0937  .....PLTE......7
   524  			0000030: 0000 0001 7452 4e53 7f80 5cb4 cb00 0000  ....tRNS..\.....
   525  			0000040: 0e49 4441 5478 9c62 6200 0400 00ff ff00  .IDATx.bb.......
   526  			0000050: 0600 03fa d059 ae00 0000 0049 454e 44ae  .....Y.....IEND.
   527  			0000060: 4260 82                                  B`.
   528  		Dropping the tRNS chunk makes that color's alpha 0xff instead of 0x7f.
   529  	*/
   530  	const (
   531  		ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x03\x00\x00\x00\x28\xcb\x34\xbb"
   532  		plte = "\x00\x00\x00\x03PLTE\xff\x00\x00\x19\xe2\x09\x37"
   533  		trns = "\x00\x00\x00\x01tRNS\x7f\x80\x5c\xb4\xcb"
   534  		idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
   535  		iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
   536  	)
   537  	for i := 0; i < 4; i++ {
   538  		var b []byte
   539  		b = append(b, pngHeader...)
   540  		b = append(b, ihdr...)
   541  		b = append(b, plte...)
   542  		for j := 0; j < i; j++ {
   543  			b = append(b, trns...)
   544  		}
   545  		b = append(b, idat...)
   546  		b = append(b, iend...)
   547  
   548  		var want color.Color
   549  		m, err := Decode(bytes.NewReader(b))
   550  		switch i {
   551  		case 0:
   552  			if err != nil {
   553  				t.Errorf("%d tRNS chunks: %v", i, err)
   554  				continue
   555  			}
   556  			want = color.RGBA{0xff, 0x00, 0x00, 0xff}
   557  		case 1:
   558  			if err != nil {
   559  				t.Errorf("%d tRNS chunks: %v", i, err)
   560  				continue
   561  			}
   562  			want = color.NRGBA{0xff, 0x00, 0x00, 0x7f}
   563  		default:
   564  			if err == nil {
   565  				t.Errorf("%d tRNS chunks: got nil error, want non-nil", i)
   566  			}
   567  			continue
   568  		}
   569  		if got := m.At(0, 0); got != want {
   570  			t.Errorf("%d tRNS chunks: got %T %v, want %T %v", i, got, got, want, want)
   571  		}
   572  	}
   573  }
   574  
   575  func TestUnknownChunkLengthUnderflow(t *testing.T) {
   576  	data := []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xff, 0xff,
   577  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0xf4, 0x7c, 0x55, 0x04, 0x1a,
   578  		0xd3, 0x11, 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e, 0x00, 0x00,
   579  		0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf4, 0x7c, 0x55, 0x04, 0x1a,
   580  		0xd3}
   581  	_, err := Decode(bytes.NewReader(data))
   582  	if err == nil {
   583  		t.Errorf("Didn't fail reading an unknown chunk with length 0xffffffff")
   584  	}
   585  }
   586  
   587  func TestPaletted8OutOfRangePixel(t *testing.T) {
   588  	// IDAT contains a reference to a palette index that does not exist in the file.
   589  	img, err := readPNG("testdata/invalid-palette.png")
   590  	if err != nil {
   591  		t.Errorf("decoding invalid-palette.png: unexpected error %v", err)
   592  		return
   593  	}
   594  
   595  	// Expect that the palette is extended with opaque black.
   596  	want := color.RGBA{0x00, 0x00, 0x00, 0xff}
   597  	if got := img.At(15, 15); got != want {
   598  		t.Errorf("got %F %v, expected %T %v", got, got, want, want)
   599  	}
   600  }
   601  
   602  func TestGray8Transparent(t *testing.T) {
   603  	// These bytes come from https://golang.org/issues/19553
   604  	m, err := Decode(bytes.NewReader([]byte{
   605  		0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
   606  		0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x85, 0x2c, 0x88,
   607  		0x80, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x00, 0xff, 0x5b, 0x91, 0x22, 0xb5, 0x00,
   608  		0x00, 0x00, 0x02, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x00, 0x00, 0x00,
   609  		0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x0a, 0xf0, 0x01, 0x42, 0xac,
   610  		0x34, 0x98, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd5, 0x04, 0x02, 0x12, 0x11,
   611  		0x11, 0xf7, 0x65, 0x3d, 0x8b, 0x00, 0x00, 0x00, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63,
   612  		0xf8, 0xff, 0xff, 0xff, 0xb9, 0xbd, 0x70, 0xf0, 0x8c, 0x01, 0xc8, 0xaf, 0x6e, 0x99, 0x02, 0x05,
   613  		0xd9, 0x7b, 0xc1, 0xfc, 0x6b, 0xff, 0xa1, 0xa0, 0x87, 0x30, 0xff, 0xd9, 0xde, 0xbd, 0xd5, 0x4b,
   614  		0xf7, 0xee, 0xfd, 0x0e, 0xe3, 0xef, 0xcd, 0x06, 0x19, 0x14, 0xf5, 0x1e, 0xce, 0xef, 0x01, 0x31,
   615  		0x92, 0xd7, 0x82, 0x41, 0x31, 0x9c, 0x3f, 0x07, 0x02, 0xee, 0xa1, 0xaa, 0xff, 0xff, 0x9f, 0xe1,
   616  		0xd9, 0x56, 0x30, 0xf8, 0x0e, 0xe5, 0x03, 0x00, 0xa9, 0x42, 0x84, 0x3d, 0xdf, 0x8f, 0xa6, 0x8f,
   617  		0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
   618  	}))
   619  	if err != nil {
   620  		t.Fatalf("Decode: %v", err)
   621  	}
   622  
   623  	const hex = "0123456789abcdef"
   624  	var got []byte
   625  	bounds := m.Bounds()
   626  	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
   627  		for x := bounds.Min.X; x < bounds.Max.X; x++ {
   628  			if r, _, _, a := m.At(x, y).RGBA(); a != 0 {
   629  				got = append(got,
   630  					hex[0x0f&(r>>12)],
   631  					hex[0x0f&(r>>8)],
   632  					' ',
   633  				)
   634  			} else {
   635  				got = append(got,
   636  					'.',
   637  					'.',
   638  					' ',
   639  				)
   640  			}
   641  		}
   642  		got = append(got, '\n')
   643  	}
   644  
   645  	const want = "" +
   646  		".. .. .. ce bd bd bd bd bd bd bd bd bd bd e6 \n" +
   647  		".. .. .. 7b 84 94 94 94 94 94 94 94 94 6b bd \n" +
   648  		".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" +
   649  		".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" +
   650  		".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" +
   651  		"e6 bd bd 7b a5 bd bd f7 .. .. .. .. .. 8c bd \n" +
   652  		"bd 6b 94 94 94 94 5a ef .. .. .. .. .. 8c bd \n" +
   653  		"bd 8c .. .. .. .. 63 ad ad ad ad ad ad 73 bd \n" +
   654  		"bd 8c .. .. .. .. 63 9c 9c 9c 9c 9c 9c 9c de \n" +
   655  		"bd 6b 94 94 94 94 5a ef .. .. .. .. .. .. .. \n" +
   656  		"e6 b5 b5 b5 b5 b5 b5 f7 .. .. .. .. .. .. .. \n"
   657  
   658  	if string(got) != want {
   659  		t.Errorf("got:\n%swant:\n%s", got, want)
   660  	}
   661  }
   662  
   663  func TestDimensionOverflow(t *testing.T) {
   664  	// These bytes come from https://golang.org/issues/22304
   665  	//
   666  	// It encodes a 2147483646 × 2147483646 (i.e. 0x7ffffffe × 0x7ffffffe)
   667  	// NRGBA image. The (width × height) per se doesn't overflow an int64, but
   668  	// (width × height × bytesPerPixel) will.
   669  	_, err := Decode(bytes.NewReader([]byte{
   670  		0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
   671  		0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3,
   672  		0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c,
   673  		0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef,
   674  		0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
   675  	}))
   676  	if _, ok := err.(UnsupportedError); !ok {
   677  		t.Fatalf("Decode: got %v (of type %T), want non-nil error (of type png.UnsupportedError)", err, err)
   678  	}
   679  }
   680  
   681  func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
   682  	data, err := ioutil.ReadFile(filename)
   683  	if err != nil {
   684  		b.Fatal(err)
   685  	}
   686  	cfg, err := DecodeConfig(bytes.NewReader(data))
   687  	if err != nil {
   688  		b.Fatal(err)
   689  	}
   690  	b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel))
   691  	b.ReportAllocs()
   692  	b.ResetTimer()
   693  	for i := 0; i < b.N; i++ {
   694  		Decode(bytes.NewReader(data))
   695  	}
   696  }
   697  
   698  func BenchmarkDecodeGray(b *testing.B) {
   699  	benchmarkDecode(b, "testdata/benchGray.png", 1)
   700  }
   701  
   702  func BenchmarkDecodeNRGBAGradient(b *testing.B) {
   703  	benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4)
   704  }
   705  
   706  func BenchmarkDecodeNRGBAOpaque(b *testing.B) {
   707  	benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4)
   708  }
   709  
   710  func BenchmarkDecodePaletted(b *testing.B) {
   711  	benchmarkDecode(b, "testdata/benchPaletted.png", 1)
   712  }
   713  
   714  func BenchmarkDecodeRGB(b *testing.B) {
   715  	benchmarkDecode(b, "testdata/benchRGB.png", 4)
   716  }
   717  
   718  func BenchmarkDecodeInterlacing(b *testing.B) {
   719  	benchmarkDecode(b, "testdata/benchRGB-interlace.png", 4)
   720  }