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 if err != nil { 368 t.Error(fn, err) 369 continue 370 } 371 372 // Compare the two, in SNG format, line by line. 373 for { 374 pdone := !pb.Scan() 375 sdone := !sb.Scan() 376 if pdone && sdone { 377 break 378 } 379 if pdone || sdone { 380 t.Errorf("%s: Different sizes", fn) 381 break 382 } 383 ps := pb.Text() 384 ss := sb.Text() 385 386 // Newer versions of the sng command line tool append an optional 387 // color name to the RGB tuple. For example: 388 // # rgb = (0xff,0xff,0xff) grey100 389 // # rgb = (0x00,0x00,0xff) blue1 390 // instead of the older version's plainer: 391 // # rgb = (0xff,0xff,0xff) 392 // # rgb = (0x00,0x00,0xff) 393 // We strip any such name. 394 if strings.Contains(ss, "# rgb = (") && !strings.HasSuffix(ss, ")") { 395 if i := strings.LastIndex(ss, ") "); i >= 0 { 396 ss = ss[:i+1] 397 } 398 } 399 400 if ps != ss { 401 t.Errorf("%s: Mismatch\n%s\nversus\n%s\n", fn, ps, ss) 402 break 403 } 404 } 405 if pb.Err() != nil { 406 t.Error(fn, pb.Err()) 407 } 408 if sb.Err() != nil { 409 t.Error(fn, sb.Err()) 410 } 411 } 412 } 413 414 var readerErrors = []struct { 415 file string 416 err string 417 }{ 418 {"invalid-zlib.png", "zlib: invalid checksum"}, 419 {"invalid-crc32.png", "invalid checksum"}, 420 {"invalid-noend.png", "unexpected EOF"}, 421 {"invalid-trunc.png", "unexpected EOF"}, 422 } 423 424 func TestReaderError(t *testing.T) { 425 for _, tt := range readerErrors { 426 img, err := readPNG("testdata/" + tt.file) 427 if err == nil { 428 t.Errorf("decoding %s: missing error", tt.file) 429 continue 430 } 431 if !strings.Contains(err.Error(), tt.err) { 432 t.Errorf("decoding %s: %s, want %s", tt.file, err, tt.err) 433 } 434 if img != nil { 435 t.Errorf("decoding %s: have image + error", tt.file) 436 } 437 } 438 } 439 440 func TestPalettedDecodeConfig(t *testing.T) { 441 for _, fn := range filenamesPaletted { 442 f, err := os.Open("testdata/pngsuite/" + fn + ".png") 443 if err != nil { 444 t.Errorf("%s: open failed: %v", fn, err) 445 continue 446 } 447 defer f.Close() 448 cfg, err := DecodeConfig(f) 449 if err != nil { 450 t.Errorf("%s: %v", fn, err) 451 continue 452 } 453 pal, ok := cfg.ColorModel.(color.Palette) 454 if !ok { 455 t.Errorf("%s: expected paletted color model", fn) 456 continue 457 } 458 if pal == nil { 459 t.Errorf("%s: palette not initialized", fn) 460 continue 461 } 462 } 463 } 464 465 func TestInterlaced(t *testing.T) { 466 a, err := readPNG("testdata/gray-gradient.png") 467 if err != nil { 468 t.Fatal(err) 469 } 470 b, err := readPNG("testdata/gray-gradient.interlaced.png") 471 if err != nil { 472 t.Fatal(err) 473 } 474 if !reflect.DeepEqual(a, b) { 475 t.Fatalf("decodings differ:\nnon-interlaced:\n%#v\ninterlaced:\n%#v", a, b) 476 } 477 } 478 479 func TestIncompleteIDATOnRowBoundary(t *testing.T) { 480 // The following is an invalid 1x2 grayscale PNG image. The header is OK, 481 // but the zlib-compressed IDAT payload contains two bytes "\x02\x00", 482 // which is only one row of data (the leading "\x02" is a row filter). 483 const ( 484 ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb" 485 idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" 486 iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" 487 ) 488 _, err := Decode(strings.NewReader(pngHeader + ihdr + idat + iend)) 489 if err == nil { 490 t.Fatal("got nil error, want non-nil") 491 } 492 } 493 494 func TestTrailingIDATChunks(t *testing.T) { 495 // The following is a valid 1x1 PNG image containing color.Gray{255} and 496 // a trailing zero-length IDAT chunk (see PNG specification section 12.9): 497 const ( 498 ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x00\x00\x00\x00\x3a\x7e\x9b\x55" 499 idatWhite = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\xfa\x0f\x08\x00\x00\xff\xff\x01\x05\x01\x02\x5a\xdd\x39\xcd" 500 idatZero = "\x00\x00\x00\x00IDAT\x35\xaf\x06\x1e" 501 iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" 502 ) 503 _, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatZero + iend)) 504 if err != nil { 505 t.Fatalf("decoding valid image: %v", err) 506 } 507 508 // Non-zero-length trailing IDAT chunks should be ignored (recoverable error). 509 // The following chunk contains a single pixel with color.Gray{0}. 510 const idatBlack = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" 511 512 img, err := Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatBlack + iend)) 513 if err != nil { 514 t.Fatalf("trailing IDAT not ignored: %v", err) 515 } 516 if img.At(0, 0) == (color.Gray{0}) { 517 t.Fatal("decoded image from trailing IDAT chunk") 518 } 519 } 520 521 func TestMultipletRNSChunks(t *testing.T) { 522 /* 523 The following is a valid 1x1 paletted PNG image with a 1-element palette 524 containing color.NRGBA{0xff, 0x00, 0x00, 0x7f}: 525 0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR 526 0000010: 0000 0001 0000 0001 0803 0000 0028 cb34 .............(.4 527 0000020: bb00 0000 0350 4c54 45ff 0000 19e2 0937 .....PLTE......7 528 0000030: 0000 0001 7452 4e53 7f80 5cb4 cb00 0000 ....tRNS..\..... 529 0000040: 0e49 4441 5478 9c62 6200 0400 00ff ff00 .IDATx.bb....... 530 0000050: 0600 03fa d059 ae00 0000 0049 454e 44ae .....Y.....IEND. 531 0000060: 4260 82 B`. 532 Dropping the tRNS chunk makes that color's alpha 0xff instead of 0x7f. 533 */ 534 const ( 535 ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x03\x00\x00\x00\x28\xcb\x34\xbb" 536 plte = "\x00\x00\x00\x03PLTE\xff\x00\x00\x19\xe2\x09\x37" 537 trns = "\x00\x00\x00\x01tRNS\x7f\x80\x5c\xb4\xcb" 538 idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" 539 iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" 540 ) 541 for i := 0; i < 4; i++ { 542 var b []byte 543 b = append(b, pngHeader...) 544 b = append(b, ihdr...) 545 b = append(b, plte...) 546 for j := 0; j < i; j++ { 547 b = append(b, trns...) 548 } 549 b = append(b, idat...) 550 b = append(b, iend...) 551 552 var want color.Color 553 m, err := Decode(bytes.NewReader(b)) 554 switch i { 555 case 0: 556 if err != nil { 557 t.Errorf("%d tRNS chunks: %v", i, err) 558 continue 559 } 560 want = color.RGBA{0xff, 0x00, 0x00, 0xff} 561 case 1: 562 if err != nil { 563 t.Errorf("%d tRNS chunks: %v", i, err) 564 continue 565 } 566 want = color.NRGBA{0xff, 0x00, 0x00, 0x7f} 567 default: 568 if err == nil { 569 t.Errorf("%d tRNS chunks: got nil error, want non-nil", i) 570 } 571 continue 572 } 573 if got := m.At(0, 0); got != want { 574 t.Errorf("%d tRNS chunks: got %T %v, want %T %v", i, got, got, want, want) 575 } 576 } 577 } 578 579 func TestUnknownChunkLengthUnderflow(t *testing.T) { 580 data := []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xff, 0xff, 581 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0xf4, 0x7c, 0x55, 0x04, 0x1a, 582 0xd3, 0x11, 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e, 0x00, 0x00, 583 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf4, 0x7c, 0x55, 0x04, 0x1a, 584 0xd3} 585 _, err := Decode(bytes.NewReader(data)) 586 if err == nil { 587 t.Errorf("Didn't fail reading an unknown chunk with length 0xffffffff") 588 } 589 } 590 591 func TestGray8Transparent(t *testing.T) { 592 // These bytes come from https://golang.org/issues/19553 593 m, err := Decode(bytes.NewReader([]byte{ 594 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 595 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x85, 0x2c, 0x88, 596 0x80, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52, 0x4e, 0x53, 0x00, 0xff, 0x5b, 0x91, 0x22, 0xb5, 0x00, 597 0x00, 0x00, 0x02, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x00, 0x00, 0x00, 598 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x0a, 0xf0, 0x01, 0x42, 0xac, 599 0x34, 0x98, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd5, 0x04, 0x02, 0x12, 0x11, 600 0x11, 0xf7, 0x65, 0x3d, 0x8b, 0x00, 0x00, 0x00, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 601 0xf8, 0xff, 0xff, 0xff, 0xb9, 0xbd, 0x70, 0xf0, 0x8c, 0x01, 0xc8, 0xaf, 0x6e, 0x99, 0x02, 0x05, 602 0xd9, 0x7b, 0xc1, 0xfc, 0x6b, 0xff, 0xa1, 0xa0, 0x87, 0x30, 0xff, 0xd9, 0xde, 0xbd, 0xd5, 0x4b, 603 0xf7, 0xee, 0xfd, 0x0e, 0xe3, 0xef, 0xcd, 0x06, 0x19, 0x14, 0xf5, 0x1e, 0xce, 0xef, 0x01, 0x31, 604 0x92, 0xd7, 0x82, 0x41, 0x31, 0x9c, 0x3f, 0x07, 0x02, 0xee, 0xa1, 0xaa, 0xff, 0xff, 0x9f, 0xe1, 605 0xd9, 0x56, 0x30, 0xf8, 0x0e, 0xe5, 0x03, 0x00, 0xa9, 0x42, 0x84, 0x3d, 0xdf, 0x8f, 0xa6, 0x8f, 606 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 607 })) 608 if err != nil { 609 t.Fatalf("Decode: %v", err) 610 } 611 612 const hex = "0123456789abcdef" 613 var got []byte 614 bounds := m.Bounds() 615 for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 616 for x := bounds.Min.X; x < bounds.Max.X; x++ { 617 if r, _, _, a := m.At(x, y).RGBA(); a != 0 { 618 got = append(got, 619 hex[0x0f&(r>>12)], 620 hex[0x0f&(r>>8)], 621 ' ', 622 ) 623 } else { 624 got = append(got, 625 '.', 626 '.', 627 ' ', 628 ) 629 } 630 } 631 got = append(got, '\n') 632 } 633 634 const want = "" + 635 ".. .. .. ce bd bd bd bd bd bd bd bd bd bd e6 \n" + 636 ".. .. .. 7b 84 94 94 94 94 94 94 94 94 6b bd \n" + 637 ".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" + 638 ".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" + 639 ".. .. .. 7b d6 .. .. .. .. .. .. .. .. 8c bd \n" + 640 "e6 bd bd 7b a5 bd bd f7 .. .. .. .. .. 8c bd \n" + 641 "bd 6b 94 94 94 94 5a ef .. .. .. .. .. 8c bd \n" + 642 "bd 8c .. .. .. .. 63 ad ad ad ad ad ad 73 bd \n" + 643 "bd 8c .. .. .. .. 63 9c 9c 9c 9c 9c 9c 9c de \n" + 644 "bd 6b 94 94 94 94 5a ef .. .. .. .. .. .. .. \n" + 645 "e6 b5 b5 b5 b5 b5 b5 f7 .. .. .. .. .. .. .. \n" 646 647 if string(got) != want { 648 t.Errorf("got:\n%swant:\n%s", got, want) 649 } 650 } 651 652 func TestDimensionOverflow(t *testing.T) { 653 // These bytes come from https://golang.org/issues/22304 654 // 655 // It encodes a 2147483646 2147483646 (i.e. 0x7ffffffe 0x7ffffffe) 656 // NRGBA image. The (width height) per se doesn't overflow an int64, but 657 // (width height bytesPerPixel) will. 658 _, err := Decode(bytes.NewReader([]byte{ 659 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 660 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3, 661 0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c, 662 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef, 663 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 664 })) 665 if _, ok := err.(UnsupportedError); !ok { 666 t.Fatalf("Decode: got %v (of type %T), want non-nil error (of type png.UnsupportedError)", err, err) 667 } 668 } 669 670 func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) { 671 data, err := ioutil.ReadFile(filename) 672 if err != nil { 673 b.Fatal(err) 674 } 675 cfg, err := DecodeConfig(bytes.NewReader(data)) 676 if err != nil { 677 b.Fatal(err) 678 } 679 b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel)) 680 b.ReportAllocs() 681 b.ResetTimer() 682 for i := 0; i < b.N; i++ { 683 Decode(bytes.NewReader(data)) 684 } 685 } 686 687 func BenchmarkDecodeGray(b *testing.B) { 688 benchmarkDecode(b, "testdata/benchGray.png", 1) 689 } 690 691 func BenchmarkDecodeNRGBAGradient(b *testing.B) { 692 benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4) 693 } 694 695 func BenchmarkDecodeNRGBAOpaque(b *testing.B) { 696 benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4) 697 } 698 699 func BenchmarkDecodePaletted(b *testing.B) { 700 benchmarkDecode(b, "testdata/benchPaletted.png", 1) 701 } 702 703 func BenchmarkDecodeRGB(b *testing.B) { 704 benchmarkDecode(b, "testdata/benchRGB.png", 4) 705 } 706 707 func BenchmarkDecodeInterlacing(b *testing.B) { 708 benchmarkDecode(b, "testdata/benchRGB-interlace.png", 4) 709 } 710