golang.org/x/image@v0.15.0/tiff/reader_test.go (about) 1 // Copyright 2011 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 tiff 6 7 import ( 8 "bytes" 9 "compress/zlib" 10 "encoding/binary" 11 "encoding/hex" 12 "errors" 13 "fmt" 14 "image" 15 "io" 16 "io/ioutil" 17 "os" 18 "sort" 19 "strings" 20 "testing" 21 22 _ "image/png" 23 ) 24 25 const testdataDir = "../testdata/" 26 27 // Read makes *buffer implements io.Reader, so that we can pass one to Decode. 28 func (*buffer) Read([]byte) (int, error) { 29 panic("unimplemented") 30 } 31 32 func load(name string) (image.Image, error) { 33 f, err := os.Open(testdataDir + name) 34 if err != nil { 35 return nil, err 36 } 37 defer f.Close() 38 img, _, err := image.Decode(f) 39 if err != nil { 40 return nil, err 41 } 42 return img, nil 43 } 44 45 // TestNoRPS tests decoding an image that has no RowsPerStrip tag. The tag is 46 // mandatory according to the spec but some software omits it in the case of a 47 // single strip. 48 func TestNoRPS(t *testing.T) { 49 _, err := load("no_rps.tiff") 50 if err != nil { 51 t.Fatal(err) 52 } 53 } 54 55 // TestNoCompression tests decoding an image that has no Compression tag. This 56 // tag is mandatory, but most tools interpret a missing value as no 57 // compression. 58 func TestNoCompression(t *testing.T) { 59 _, err := load("no_compress.tiff") 60 if err != nil { 61 t.Fatal(err) 62 } 63 } 64 65 // TestUnpackBits tests the decoding of PackBits-encoded data. 66 func TestUnpackBits(t *testing.T) { 67 var unpackBitsTests = []struct { 68 compressed string 69 uncompressed string 70 }{{ 71 // Example data from Wikipedia. 72 "\xfe\xaa\x02\x80\x00\x2a\xfd\xaa\x03\x80\x00\x2a\x22\xf7\xaa", 73 "\xaa\xaa\xaa\x80\x00\x2a\xaa\xaa\xaa\xaa\x80\x00\x2a\x22\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 74 }} 75 for _, u := range unpackBitsTests { 76 buf, err := unpackBits(strings.NewReader(u.compressed)) 77 if err != nil { 78 t.Fatal(err) 79 } 80 if string(buf) != u.uncompressed { 81 t.Fatalf("unpackBits: want %x, got %x", u.uncompressed, buf) 82 } 83 } 84 } 85 86 func TestShortBlockData(t *testing.T) { 87 b, err := ioutil.ReadFile("../testdata/bw-uncompressed.tiff") 88 if err != nil { 89 t.Fatal(err) 90 } 91 // The bw-uncompressed.tiff image is a 153x55 bi-level image. This is 1 bit 92 // per pixel, or 20 bytes per row, times 55 rows, or 1100 bytes of pixel 93 // data. 1100 in hex is 0x44c, or "\x4c\x04" in little-endian. We replace 94 // that byte count (StripByteCounts-tagged data) by something less than 95 // that, so that there is not enough pixel data. 96 old := []byte{0x4c, 0x04} 97 new := []byte{0x01, 0x01} 98 i := bytes.Index(b, old) 99 if i < 0 { 100 t.Fatal(`could not find "\x4c\x04" byte count`) 101 } 102 if bytes.Contains(b[i+len(old):], old) { 103 t.Fatal(`too many occurrences of "\x4c\x04"`) 104 } 105 b[i+0] = new[0] 106 b[i+1] = new[1] 107 if _, err = Decode(bytes.NewReader(b)); err == nil { 108 t.Fatal("got nil error, want non-nil") 109 } 110 } 111 112 func TestDecodeInvalidDataType(t *testing.T) { 113 b, err := ioutil.ReadFile("../testdata/bw-uncompressed.tiff") 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 // off is the offset of the ImageWidth tag. It is the offset of the overall 119 // IFD block (0x00000454), plus 2 for the uint16 number of IFD entries, plus 12 120 // to skip the first entry. 121 const off = 0x00000454 + 2 + 12*1 122 123 if v := binary.LittleEndian.Uint16(b[off : off+2]); v != tImageWidth { 124 t.Fatal(`could not find ImageWidth tag`) 125 } 126 binary.LittleEndian.PutUint16(b[off+2:], uint16(len(lengths))) // invalid datatype 127 128 if _, err = Decode(bytes.NewReader(b)); err == nil { 129 t.Fatal("got nil error, want non-nil") 130 } 131 } 132 133 func compare(t *testing.T, img0, img1 image.Image) { 134 t.Helper() 135 b0 := img0.Bounds() 136 b1 := img1.Bounds() 137 if b0.Dx() != b1.Dx() || b0.Dy() != b1.Dy() { 138 t.Fatalf("wrong image size: want %s, got %s", b0, b1) 139 } 140 x1 := b1.Min.X - b0.Min.X 141 y1 := b1.Min.Y - b0.Min.Y 142 for y := b0.Min.Y; y < b0.Max.Y; y++ { 143 for x := b0.Min.X; x < b0.Max.X; x++ { 144 c0 := img0.At(x, y) 145 c1 := img1.At(x+x1, y+y1) 146 r0, g0, b0, a0 := c0.RGBA() 147 r1, g1, b1, a1 := c1.RGBA() 148 if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { 149 t.Fatalf("pixel at (%d, %d) has wrong color: want %v, got %v", x, y, c0, c1) 150 } 151 } 152 } 153 } 154 155 // TestDecode tests that decoding a PNG image and a TIFF image result in the 156 // same pixel data. 157 func TestDecode(t *testing.T) { 158 img0, err := load("video-001.png") 159 if err != nil { 160 t.Fatal(err) 161 } 162 img1, err := load("video-001.tiff") 163 if err != nil { 164 t.Fatal(err) 165 } 166 img2, err := load("video-001-strip-64.tiff") 167 if err != nil { 168 t.Fatal(err) 169 } 170 img3, err := load("video-001-tile-64x64.tiff") 171 if err != nil { 172 t.Fatal(err) 173 } 174 img4, err := load("video-001-16bit.tiff") 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 compare(t, img0, img1) 180 compare(t, img0, img2) 181 compare(t, img0, img3) 182 compare(t, img0, img4) 183 } 184 185 // TestDecodeLZW tests that decoding a PNG image and a LZW-compressed TIFF 186 // image result in the same pixel data. 187 func TestDecodeLZW(t *testing.T) { 188 img0, err := load("blue-purple-pink.png") 189 if err != nil { 190 t.Fatal(err) 191 } 192 img1, err := load("blue-purple-pink.lzwcompressed.tiff") 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 compare(t, img0, img1) 198 } 199 200 // TestEOF tests that decoding a TIFF image returns io.ErrUnexpectedEOF 201 // when there are no headers or data is empty 202 func TestEOF(t *testing.T) { 203 _, err := Decode(bytes.NewReader(nil)) 204 if err != io.ErrUnexpectedEOF { 205 t.Errorf("Error should be io.ErrUnexpectedEOF on nil but got %v", err) 206 } 207 } 208 209 // TestDecodeCCITT tests that decoding a PNG image and a CCITT compressed TIFF 210 // image result in the same pixel data. 211 func TestDecodeCCITT(t *testing.T) { 212 // TODO Add more tests. 213 for _, fn := range []string{ 214 "bw-gopher", 215 } { 216 img0, err := load(fn + ".png") 217 if err != nil { 218 t.Fatal(err) 219 } 220 221 img1, err := load(fn + "_ccittGroup3.tiff") 222 if err != nil { 223 t.Fatal(err) 224 } 225 compare(t, img0, img1) 226 227 img2, err := load(fn + "_ccittGroup4.tiff") 228 if err != nil { 229 t.Fatal(err) 230 } 231 compare(t, img0, img2) 232 } 233 } 234 235 // TestDecodeTagOrder tests that a malformed image with unsorted IFD entries is 236 // correctly rejected. 237 func TestDecodeTagOrder(t *testing.T) { 238 data, err := ioutil.ReadFile("../testdata/video-001.tiff") 239 if err != nil { 240 t.Fatal(err) 241 } 242 243 // Swap the first two IFD entries. 244 ifdOffset := int64(binary.LittleEndian.Uint32(data[4:8])) 245 for i := ifdOffset + 2; i < ifdOffset+14; i++ { 246 data[i], data[i+12] = data[i+12], data[i] 247 } 248 if _, _, err := image.Decode(bytes.NewReader(data)); err == nil { 249 t.Fatal("got nil error, want non-nil") 250 } 251 } 252 253 // TestDecompress tests that decoding some TIFF images that use different 254 // compression formats result in the same pixel data. 255 func TestDecompress(t *testing.T) { 256 var decompressTests = []string{ 257 "bw-uncompressed.tiff", 258 "bw-deflate.tiff", 259 "bw-packbits.tiff", 260 } 261 var img0 image.Image 262 for _, name := range decompressTests { 263 img1, err := load(name) 264 if err != nil { 265 t.Fatalf("decoding %s: %v", name, err) 266 } 267 if img0 == nil { 268 img0 = img1 269 continue 270 } 271 compare(t, img0, img1) 272 } 273 } 274 275 func replace(src []byte, find, repl string) ([]byte, error) { 276 removeSpaces := func(r rune) rune { 277 if r != ' ' { 278 return r 279 } 280 return -1 281 } 282 283 f, err := hex.DecodeString(strings.Map(removeSpaces, find)) 284 if err != nil { 285 return nil, err 286 } 287 r, err := hex.DecodeString(strings.Map(removeSpaces, repl)) 288 if err != nil { 289 return nil, err 290 } 291 dst := bytes.Replace(src, f, r, 1) 292 if bytes.Equal(dst, src) { 293 return nil, errors.New("replacement failed") 294 } 295 return dst, nil 296 } 297 298 // TestZeroBitsPerSample tests that an IFD with a bitsPerSample of 0 does not 299 // cause a crash. 300 // Issue 10711. 301 func TestZeroBitsPerSample(t *testing.T) { 302 b0, err := ioutil.ReadFile(testdataDir + "bw-deflate.tiff") 303 if err != nil { 304 t.Fatal(err) 305 } 306 307 // Mutate the loaded image to have the problem. 308 // 02 01: tag number (tBitsPerSample) 309 // 03 00: data type (short, or uint16) 310 // 01 00 00 00: count 311 // ?? 00 00 00: value (1 -> 0) 312 b1, err := replace(b0, 313 "02 01 03 00 01 00 00 00 01 00 00 00", 314 "02 01 03 00 01 00 00 00 00 00 00 00", 315 ) 316 if err != nil { 317 t.Fatal(err) 318 } 319 320 _, err = Decode(bytes.NewReader(b1)) 321 if err == nil { 322 t.Fatal("Decode with 0 bits per sample: got nil error, want non-nil") 323 } 324 } 325 326 // TestTileTooBig tests that we do not panic when a tile is too big compared to 327 // the data available. 328 // Issue 10712 329 func TestTileTooBig(t *testing.T) { 330 b0, err := ioutil.ReadFile(testdataDir + "video-001-tile-64x64.tiff") 331 if err != nil { 332 t.Fatal(err) 333 } 334 335 // Mutate the loaded image to have the problem. 336 // 337 // 42 01: tag number (tTileWidth) 338 // 03 00: data type (short, or uint16) 339 // 01 00 00 00: count 340 // xx 00 00 00: value (0x40 -> 0x44: a wider tile consumes more data 341 // than is available) 342 b1, err := replace(b0, 343 "42 01 03 00 01 00 00 00 40 00 00 00", 344 "42 01 03 00 01 00 00 00 44 00 00 00", 345 ) 346 if err != nil { 347 t.Fatal(err) 348 } 349 350 // Turn off the predictor, which makes it possible to hit the 351 // place with the defect. Without this patch to the image, we run 352 // out of data too early, and do not hit the part of the code where 353 // the original panic was. 354 // 355 // 3d 01: tag number (tPredictor) 356 // 03 00: data type (short, or uint16) 357 // 01 00 00 00: count 358 // xx 00 00 00: value (2 -> 1: 2 = horizontal, 1 = none) 359 b2, err := replace(b1, 360 "3d 01 03 00 01 00 00 00 02 00 00 00", 361 "3d 01 03 00 01 00 00 00 01 00 00 00", 362 ) 363 if err != nil { 364 t.Fatal(err) 365 } 366 367 _, err = Decode(bytes.NewReader(b2)) 368 if err == nil { 369 t.Fatal("did not expect nil error") 370 } 371 } 372 373 // TestZeroSizedImages tests that decoding does not panic when image dimensions 374 // are zero, and returns a zero-sized image instead. 375 // Issue 10393. 376 func TestZeroSizedImages(t *testing.T) { 377 testsizes := []struct { 378 w, h int 379 }{ 380 {0, 0}, 381 {1, 0}, 382 {0, 1}, 383 {1, 1}, 384 } 385 for _, r := range testsizes { 386 img := image.NewRGBA(image.Rect(0, 0, r.w, r.h)) 387 var buf bytes.Buffer 388 if err := Encode(&buf, img, nil); err != nil { 389 t.Errorf("encode w=%d h=%d: %v", r.w, r.h, err) 390 continue 391 } 392 if _, err := Decode(&buf); err != nil { 393 t.Errorf("decode w=%d h=%d: %v", r.w, r.h, err) 394 } 395 } 396 } 397 398 // TestLargeIFDEntry tests that a large IFD entry does not cause Decode to 399 // panic. 400 // Issue 10596. 401 func TestLargeIFDEntry(t *testing.T) { 402 testdata := "II*\x00\x08\x00\x00\x00\f\x000000000000" + 403 "00000000000000000000" + 404 "00000000000000000000" + 405 "00000000000000000000" + 406 "00000000000000\x17\x01\x04\x00\x01\x00" + 407 "\x00\xc0000000000000000000" + 408 "00000000000000000000" + 409 "00000000000000000000" + 410 "000000" 411 _, err := Decode(strings.NewReader(testdata)) 412 if err == nil { 413 t.Fatal("Decode with large IFD entry: got nil error, want non-nil") 414 } 415 } 416 417 // benchmarkDecode benchmarks the decoding of an image. 418 func benchmarkDecode(b *testing.B, filename string) { 419 b.Helper() 420 contents, err := ioutil.ReadFile(testdataDir + filename) 421 if err != nil { 422 b.Fatal(err) 423 } 424 benchmarkDecodeData(b, contents) 425 } 426 427 func benchmarkDecodeData(b *testing.B, data []byte) { 428 b.Helper() 429 r := &buffer{buf: data} 430 b.ResetTimer() 431 for i := 0; i < b.N; i++ { 432 _, err := Decode(r) 433 if err != nil { 434 b.Fatal("Decode:", err) 435 } 436 } 437 } 438 439 func BenchmarkDecodeCompressed(b *testing.B) { benchmarkDecode(b, "video-001.tiff") } 440 func BenchmarkDecodeUncompressed(b *testing.B) { benchmarkDecode(b, "video-001-uncompressed.tiff") } 441 442 func BenchmarkZeroHeightTile(b *testing.B) { 443 enc := binary.BigEndian 444 data := newTIFF(enc) 445 data = appendIFD(data, enc, map[uint16]interface{}{ 446 tImageWidth: uint32(4294967295), 447 tImageLength: uint32(0), 448 tTileWidth: uint32(1), 449 tTileLength: uint32(0), 450 }) 451 benchmarkDecodeData(b, data) 452 } 453 454 func BenchmarkRepeatedOversizedTileData(b *testing.B) { 455 const ( 456 imageWidth = 256 457 imageHeight = 256 458 tileWidth = 8 459 tileLength = 8 460 numTiles = (imageWidth * imageHeight) / (tileWidth * tileLength) 461 ) 462 463 // Create a chunk of tile data that decompresses to a large size. 464 zdata := func() []byte { 465 var zbuf bytes.Buffer 466 zw := zlib.NewWriter(&zbuf) 467 zeros := make([]byte, 1024) 468 for i := 0; i < 1<<16; i++ { 469 zw.Write(zeros) 470 } 471 zw.Close() 472 return zbuf.Bytes() 473 }() 474 475 enc := binary.BigEndian 476 data := newTIFF(enc) 477 478 zoff := len(data) 479 data = append(data, zdata...) 480 481 // Each tile refers to the same compressed data chunk. 482 var tileoffs []uint32 483 var tilesizes []uint32 484 for i := 0; i < numTiles; i++ { 485 tileoffs = append(tileoffs, uint32(zoff)) 486 tilesizes = append(tilesizes, uint32(len(zdata))) 487 } 488 489 data = appendIFD(data, enc, map[uint16]interface{}{ 490 tImageWidth: uint32(imageWidth), 491 tImageLength: uint32(imageHeight), 492 tTileWidth: uint32(tileWidth), 493 tTileLength: uint32(tileLength), 494 tTileOffsets: tileoffs, 495 tTileByteCounts: tilesizes, 496 tCompression: uint16(cDeflate), 497 tBitsPerSample: []uint16{16, 16, 16}, 498 tPhotometricInterpretation: uint16(pRGB), 499 }) 500 benchmarkDecodeData(b, data) 501 } 502 503 type byteOrder interface { 504 binary.ByteOrder 505 binary.AppendByteOrder 506 } 507 508 // newTIFF returns the TIFF header. 509 func newTIFF(enc byteOrder) []byte { 510 b := []byte{0, 0, 0, 42, 0, 0, 0, 0} 511 switch enc.Uint16([]byte{1, 0}) { 512 case 0x1: 513 b[0], b[1] = 'I', 'I' 514 case 0x100: 515 b[0], b[1] = 'M', 'M' 516 default: 517 panic("odd byte order") 518 } 519 return b 520 } 521 522 // appendIFD appends an IFD to the TIFF in b, 523 // updating the IFD location in the header. 524 func appendIFD(b []byte, enc byteOrder, entries map[uint16]interface{}) []byte { 525 var tags []uint16 526 for tag := range entries { 527 tags = append(tags, tag) 528 } 529 sort.Slice(tags, func(i, j int) bool { 530 return tags[i] < tags[j] 531 }) 532 533 var ifd []byte 534 for _, tag := range tags { 535 ifd = enc.AppendUint16(ifd, tag) 536 switch v := entries[tag].(type) { 537 case uint16: 538 ifd = enc.AppendUint16(ifd, dtShort) 539 ifd = enc.AppendUint32(ifd, 1) 540 ifd = enc.AppendUint16(ifd, v) 541 ifd = enc.AppendUint16(ifd, v) 542 case uint32: 543 ifd = enc.AppendUint16(ifd, dtLong) 544 ifd = enc.AppendUint32(ifd, 1) 545 ifd = enc.AppendUint32(ifd, v) 546 case []uint16: 547 ifd = enc.AppendUint16(ifd, dtShort) 548 ifd = enc.AppendUint32(ifd, uint32(len(v))) 549 switch len(v) { 550 case 0: 551 ifd = enc.AppendUint32(ifd, 0) 552 case 1: 553 ifd = enc.AppendUint16(ifd, v[0]) 554 ifd = enc.AppendUint16(ifd, v[1]) 555 default: 556 ifd = enc.AppendUint32(ifd, uint32(len(b))) 557 for _, e := range v { 558 b = enc.AppendUint16(b, e) 559 } 560 } 561 case []uint32: 562 ifd = enc.AppendUint16(ifd, dtLong) 563 ifd = enc.AppendUint32(ifd, uint32(len(v))) 564 switch len(v) { 565 case 0: 566 ifd = enc.AppendUint32(ifd, 0) 567 case 1: 568 ifd = enc.AppendUint32(ifd, v[0]) 569 default: 570 ifd = enc.AppendUint32(ifd, uint32(len(b))) 571 for _, e := range v { 572 b = enc.AppendUint32(b, e) 573 } 574 } 575 default: 576 panic(fmt.Errorf("unhandled type %T", v)) 577 } 578 } 579 580 enc.PutUint32(b[4:8], uint32(len(b))) 581 b = enc.AppendUint16(b, uint16(len(entries))) 582 b = append(b, ifd...) 583 b = enc.AppendUint32(b, 0) 584 return b 585 }