@@ -6,13 +6,16 @@ package tiff
6
6
7
7
import (
8
8
"bytes"
9
+ "compress/zlib"
9
10
"encoding/binary"
10
11
"encoding/hex"
11
12
"errors"
13
+ "fmt"
12
14
"image"
13
15
"io"
14
16
"io/ioutil"
15
17
"os"
18
+ "sort"
16
19
"strings"
17
20
"testing"
18
21
@@ -414,13 +417,17 @@ func TestLargeIFDEntry(t *testing.T) {
414
417
// benchmarkDecode benchmarks the decoding of an image.
415
418
func benchmarkDecode (b * testing.B , filename string ) {
416
419
b .Helper ()
417
- b .StopTimer ()
418
420
contents , err := ioutil .ReadFile (testdataDir + filename )
419
421
if err != nil {
420
422
b .Fatal (err )
421
423
}
422
- r := & buffer {buf : contents }
423
- b .StartTimer ()
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 ()
424
431
for i := 0 ; i < b .N ; i ++ {
425
432
_ , err := Decode (r )
426
433
if err != nil {
@@ -431,3 +438,148 @@ func benchmarkDecode(b *testing.B, filename string) {
431
438
432
439
func BenchmarkDecodeCompressed (b * testing.B ) { benchmarkDecode (b , "video-001.tiff" ) }
433
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
+ }
0 commit comments