...

Source file src/image/png/reader_test.go

Documentation: image/png

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

View as plain text