From 371ad2658c189329d9b34707d36894dfda3905a0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 10 May 2019 14:46:45 +0200 Subject: [PATCH] Make defer strile offset/bytecount loading available at runtime ... and add per-strile offset/bytecount loading capabilities. Part of this commit makes the behaviour that was previously met when libtiff was compiled with -DDEFER_STRILE_LOAD available for default builds when specifying the new 'D' (Deferred) TIFFOpen() flag. In that mode, the [Tile/Strip][ByteCounts/Offsets] arrays are only loaded when first accessed. This can speed-up the opening of files stored on the network when just metadata retrieval is needed. This mode has been used for years by the GDAL library when compiled with its embeded libtiff copy. To avoid potential out-of-tree code (typically codecs) that would use the td_stripbytecount and td_stripoffset array inconditionnaly assuming they have been loaded, those have been suffixed with _p (for protected). The use of the new functions mentionned below is then recommended. Another addition of this commit is the capability of loading only the values of the offset/bytecount of the strile of interest instead of the whole array. This is enabled with the new 'O' (Ondemand) flag of TIFFOpen() (which implies 'D'). That behaviour has also been used by GDAL, which hacked into the td_stripoffset/td_stripbytecount arrays directly. The new code added in the _TIFFFetchStrileValue() and _TIFFPartialReadStripArray() internal functions is mostly a port of what was in GDAL GTiff driver previously. Related to that, the public TIFFGetStrileOffset[WithErr]() and TIFFGetStrileByteCount[WithErr]() functions have been added to API. They are of particular interest when using sparse files (with offset == bytecount == 0) and you want to detect if a strile is present or not without decompressing the data, or updating an existing sparse file. They will also be used to enable a future enhancement where client code can entirely skip bytecount loading in some situtations A new test/defer_strile_loading.c test has been added to test the above capabilities. --- .gitignore | 1 + CMakeLists.txt | 2 +- configure.ac | 7 +- libtiff/tif_dir.c | 13 +- libtiff/tif_dir.h | 9 +- libtiff/tif_dirread.c | 603 ++++++++++++++++++++++++++++-------- libtiff/tif_dirwrite.c | 14 +- libtiff/tif_jpeg.c | 11 +- libtiff/tif_ojpeg.c | 17 +- libtiff/tif_open.c | 17 + libtiff/tif_print.c | 10 +- libtiff/tif_read.c | 87 ++---- libtiff/tif_strip.c | 3 +- libtiff/tif_write.c | 54 ++-- libtiff/tiffio.h | 5 + libtiff/tiffiop.h | 2 + man/TIFFOpen.3tiff | 15 + test/CMakeLists.txt | 3 + test/Makefile.am | 4 +- test/defer_strile_loading.c | 256 +++++++++++++++ 20 files changed, 863 insertions(+), 270 deletions(-) create mode 100644 test/defer_strile_loading.c diff --git a/.gitignore b/.gitignore index a48b1f03..53fd3432 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ test/raw_decode test/rewrite test/short_tag test/strip_rw +test/defer_strile_loading test/*.log test/*.trs test/o-* diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e88d4df..22cf520c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -623,7 +623,7 @@ if(strip-chopping) endif() # Defer loading of strip/tile offsets -option(defer-strile-load "enable deferred strip/tile offset/size loading (experimental)" OFF) +option(defer-strile-load "enable deferred strip/tile offset/size loading (also available at runtime with the 'D' flag of TIFFOpen())" OFF) set(DEFER_STRILE_LOAD ${defer-strile-load}) # CHUNKY_STRIP_READ_SUPPORT diff --git a/configure.ac b/configure.ac index c7b02e25..54915e05 100644 --- a/configure.ac +++ b/configure.ac @@ -1070,17 +1070,16 @@ fi dnl --------------------------------------------------------------------------- dnl Should we try to defer loading of strip/tile offsets and sizes to -dnl optimize directory scanning? These is an experimental feature for -dnl libtiff 4.0. +dnl optimize directory scanning? dnl --------------------------------------------------------------------------- AC_ARG_ENABLE(defer-strile-load, AS_HELP_STRING([--enable-defer-strile-load], - [enable deferred strip/tile offset/size loading (experimental)]), + [enable deferred strip/tile offset/size loading (also available at runtime with the 'D' flag of TIFFOpen()]), [HAVE_DEFER_STRILE_LOAD=$enableval], [HAVE_DEFER_STRILE_LOAD=no]) if test "$HAVE_DEFER_STRILE_LOAD" = "yes" ; then - AC_DEFINE(DEFER_STRILE_LOAD,1,[enable deferred strip/tile offset/size loading (experimental)]) + AC_DEFINE(DEFER_STRILE_LOAD,1,[enable deferred strip/tile offset/size loading]) fi diff --git a/libtiff/tif_dir.c b/libtiff/tif_dir.c index b4ecd44f..4a3fcd55 100644 --- a/libtiff/tif_dir.c +++ b/libtiff/tif_dir.c @@ -1018,12 +1018,12 @@ _TIFFVGetField(TIFF* tif, uint32 tag, va_list ap) case TIFFTAG_STRIPOFFSETS: case TIFFTAG_TILEOFFSETS: _TIFFFillStriles( tif ); - *va_arg(ap, uint64**) = td->td_stripoffset; + *va_arg(ap, uint64**) = td->td_stripoffset_p; break; case TIFFTAG_STRIPBYTECOUNTS: case TIFFTAG_TILEBYTECOUNTS: _TIFFFillStriles( tif ); - *va_arg(ap, uint64**) = td->td_stripbytecount; + *va_arg(ap, uint64**) = td->td_stripbytecount_p; break; case TIFFTAG_MATTEING: *va_arg(ap, uint16*) = @@ -1282,8 +1282,9 @@ TIFFFreeDirectory(TIFF* tif) CleanupField(td_transferfunction[0]); CleanupField(td_transferfunction[1]); CleanupField(td_transferfunction[2]); - CleanupField(td_stripoffset); - CleanupField(td_stripbytecount); + CleanupField(td_stripoffset_p); + CleanupField(td_stripbytecount_p); + td->td_stripoffsetbyteallocsize = 0; TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING); TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING); @@ -1296,10 +1297,8 @@ TIFFFreeDirectory(TIFF* tif) td->td_customValueCount = 0; CleanupField(td_customValues); -#if defined(DEFER_STRILE_LOAD) _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry)); _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry)); -#endif } #undef CleanupField @@ -1387,7 +1386,9 @@ TIFFDefaultDirectory(TIFF* tif) td->td_tilewidth = 0; td->td_tilelength = 0; td->td_tiledepth = 1; +#ifdef STRIPBYTECOUNTSORTED_UNUSED td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */ +#endif td->td_resolutionunit = RESUNIT_INCH; td->td_sampleformat = SAMPLEFORMAT_UINT; td->td_imagedepth = 1; diff --git a/libtiff/tif_dir.h b/libtiff/tif_dir.h index b2f5e694..76f3eaf7 100644 --- a/libtiff/tif_dir.h +++ b/libtiff/tif_dir.h @@ -97,13 +97,14 @@ typedef struct { * number of striles */ uint32 td_stripsperimage; uint32 td_nstrips; /* size of offset & bytecount arrays */ - uint64* td_stripoffset; - uint64* td_stripbytecount; + uint64* td_stripoffset_p; /* should be accessed with TIFFGetStrileOffset */ + uint64* td_stripbytecount_p; /* should be accessed with TIFFGetStrileByteCount */ + uint32 td_stripoffsetbyteallocsize; /* number of elements currently allocated for td_stripoffset/td_stripbytecount. Only used if TIFF_LAZYSTRILELOAD is set */ +#ifdef STRIPBYTECOUNTSORTED_UNUSED int td_stripbytecountsorted; /* is the bytecount array sorted ascending? */ -#if defined(DEFER_STRILE_LOAD) +#endif TIFFDirEntry td_stripoffset_entry; /* for deferred loading */ TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */ -#endif uint16 td_nsubifd; uint64* td_subifd; /* YCbCr parameters */ diff --git a/libtiff/tif_dirread.c b/libtiff/tif_dirread.c index 65675d0f..023c4c9b 100644 --- a/libtiff/tif_dirread.c +++ b/libtiff/tif_dirread.c @@ -3536,6 +3536,41 @@ static int _TIFFGetMaxColorChannels( uint16 photometric ) } } +static int ByteCountLooksBad(TIFF* tif) +{ + /* + * Assume we have wrong StripByteCount value (in case + * of single strip) in following cases: + * - it is equal to zero along with StripOffset; + * - it is larger than file itself (in case of uncompressed + * image); + * - it is smaller than the size of the bytes per row + * multiplied on the number of rows. The last case should + * not be checked in the case of writing new image, + * because we may do not know the exact strip size + * until the whole image will be written and directory + * dumped out. + */ + uint64 bytecount = TIFFGetStrileByteCount(tif, 0); + uint64 offset = TIFFGetStrileOffset(tif, 0); + uint64 filesize; + + if( offset == 0 ) + return 0; + if (bytecount == 0) + return 1; + if ( tif->tif_dir.td_compression != COMPRESSION_NONE ) + return 0; + filesize = TIFFGetFileSize(tif); + if( offset <= filesize && bytecount > filesize - offset ) + return 1; + if( tif->tif_mode == O_RDONLY && + bytecount < TIFFScanlineSize64(tif) * tif->tif_dir.td_imagelength) + return 1; + return 0; +} + + /* * Read the next TIFF directory from a file and convert it to the internal * format. We read directories sequentially. @@ -3870,39 +3905,39 @@ TIFFReadDirectory(TIFF* tif) break; case TIFFTAG_STRIPOFFSETS: case TIFFTAG_TILEOFFSETS: -#if defined(DEFER_STRILE_LOAD) _TIFFmemcpy( &(tif->tif_dir.td_stripoffset_entry), dp, sizeof(TIFFDirEntry) ); -#else - if( tif->tif_dir.td_stripoffset != NULL ) + if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) { - TIFFErrorExt(tif->tif_clientdata, module, - "tif->tif_dir.td_stripoffset is " - "already allocated. Likely duplicated " - "StripOffsets/TileOffsets tag"); - goto bad; + if( tif->tif_dir.td_stripoffset_p != NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "tif->tif_dir.td_stripoffset is " + "already allocated. Likely duplicated " + "StripOffsets/TileOffsets tag"); + goto bad; + } + if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripoffset_p)) + goto bad; } - if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripoffset)) - goto bad; -#endif break; case TIFFTAG_STRIPBYTECOUNTS: case TIFFTAG_TILEBYTECOUNTS: -#if defined(DEFER_STRILE_LOAD) _TIFFmemcpy( &(tif->tif_dir.td_stripbytecount_entry), dp, sizeof(TIFFDirEntry) ); -#else - if( tif->tif_dir.td_stripbytecount != NULL ) + if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) { - TIFFErrorExt(tif->tif_clientdata, module, - "tif->tif_dir.td_stripbytecount is " - "already allocated. Likely duplicated " - "StripByteCounts/TileByteCounts tag"); - goto bad; + if( tif->tif_dir.td_stripbytecount_p != NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "tif->tif_dir.td_stripbytecount is " + "already allocated. Likely duplicated " + "StripByteCounts/TileByteCounts tag"); + goto bad; + } + if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripbytecount_p)) + goto bad; } - if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripbytecount)) - goto bad; -#endif break; case TIFFTAG_COLORMAP: case TIFFTAG_TRANSFERFUNCTION: @@ -4130,33 +4165,10 @@ TIFFReadDirectory(TIFF* tif) "\"StripByteCounts\" field, calculating from imagelength"); if (EstimateStripByteCounts(tif, dir, dircount) < 0) goto bad; - /* - * Assume we have wrong StripByteCount value (in case - * of single strip) in following cases: - * - it is equal to zero along with StripOffset; - * - it is larger than file itself (in case of uncompressed - * image); - * - it is smaller than the size of the bytes per row - * multiplied on the number of rows. The last case should - * not be checked in the case of writing new image, - * because we may do not know the exact strip size - * until the whole image will be written and directory - * dumped out. - */ - #define BYTECOUNTLOOKSBAD \ - ( (tif->tif_dir.td_stripbytecount[0] == 0 && tif->tif_dir.td_stripoffset[0] != 0) || \ - (tif->tif_dir.td_compression == COMPRESSION_NONE && \ - (tif->tif_dir.td_stripoffset[0] <= TIFFGetFileSize(tif) && \ - tif->tif_dir.td_stripbytecount[0] > TIFFGetFileSize(tif) - tif->tif_dir.td_stripoffset[0])) || \ - (tif->tif_mode == O_RDONLY && \ - tif->tif_dir.td_compression == COMPRESSION_NONE && \ - tif->tif_dir.td_stripbytecount[0] < TIFFScanlineSize64(tif) * tif->tif_dir.td_imagelength) ) } else if (tif->tif_dir.td_nstrips == 1 && !(tif->tif_flags&TIFF_ISTILED) - && _TIFFFillStriles(tif) - && tif->tif_dir.td_stripoffset[0] != 0 - && BYTECOUNTLOOKSBAD) { + && ByteCountLooksBad(tif)) { /* * XXX: Plexus (and others) sometimes give a value of * zero for a tag when they don't know what the @@ -4168,13 +4180,13 @@ TIFFReadDirectory(TIFF* tif) if(EstimateStripByteCounts(tif, dir, dircount) < 0) goto bad; -#if !defined(DEFER_STRILE_LOAD) - } else if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG + } else if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD) + && tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG && tif->tif_dir.td_nstrips > 2 && tif->tif_dir.td_compression == COMPRESSION_NONE - && tif->tif_dir.td_stripbytecount[0] != tif->tif_dir.td_stripbytecount[1] - && tif->tif_dir.td_stripbytecount[0] != 0 - && tif->tif_dir.td_stripbytecount[1] != 0 ) { + && TIFFGetStrileByteCount(tif, 0) != TIFFGetStrileByteCount(tif, 1) + && TIFFGetStrileByteCount(tif, 0) != 0 + && TIFFGetStrileByteCount(tif, 1) != 0 ) { /* * XXX: Some vendors fill StripByteCount array with * absolutely wrong values (it can be equal to @@ -4189,7 +4201,6 @@ TIFFReadDirectory(TIFF* tif) "Wrong \"StripByteCounts\" field, ignoring and calculating from imagelength"); if (EstimateStripByteCounts(tif, dir, dircount) < 0) goto bad; -#endif /* !defined(DEFER_STRILE_LOAD) */ } } if (dir) @@ -4204,26 +4215,27 @@ TIFFReadDirectory(TIFF* tif) else tif->tif_dir.td_maxsamplevalue = (uint16)((1L<tif_dir.td_bitspersample)-1); } + +#ifdef STRIPBYTECOUNTSORTED_UNUSED /* * XXX: We can optimize checking for the strip bounds using the sorted * bytecounts array. See also comments for TIFFAppendToStrip() * function in tif_write.c. */ -#if !defined(DEFER_STRILE_LOAD) - if (tif->tif_dir.td_nstrips > 1) { + if (!(tif->tif_flags&TIFF_DEFERSTRILELOAD) && tif->tif_dir.td_nstrips > 1) { uint32 strip; tif->tif_dir.td_stripbytecountsorted = 1; for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) { - if (tif->tif_dir.td_stripoffset[strip - 1] > - tif->tif_dir.td_stripoffset[strip]) { + if (TIFFGetStrileOffset(tif, strip - 1) > + TIFFGetStrileOffset(tif, strip)) { tif->tif_dir.td_stripbytecountsorted = 0; break; } } } -#endif /* !defined(DEFER_STRILE_LOAD) */ - +#endif + /* * An opportunity for compression mode dependent tag fixup */ @@ -4242,11 +4254,9 @@ TIFFReadDirectory(TIFF* tif) (tif->tif_dir.td_nstrips==1)&& (tif->tif_dir.td_compression==COMPRESSION_NONE)&& ((tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED))==TIFF_STRIPCHOP)) - { - if ( !_TIFFFillStriles(tif) || !tif->tif_dir.td_stripbytecount ) - return 0; - ChopUpSingleUncompressedStrip(tif); - } + { + ChopUpSingleUncompressedStrip(tif); + } /* There are also uncompressed stripped files with strips larger than */ /* 2 GB, which make them unfriendly with a lot of code. If possible, */ @@ -4256,8 +4266,6 @@ TIFFReadDirectory(TIFF* tif) (tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED)) == TIFF_STRIPCHOP && TIFFStripSize64(tif) > 0x7FFFFFFFUL ) { - if ( !_TIFFFillStriles(tif) || !tif->tif_dir.td_stripbytecount ) - return 0; TryChopUpUncompressedBigTiff(tif); } @@ -4502,12 +4510,12 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) if( !_TIFFFillStrilesInternal( tif, 0 ) ) return -1; - if (td->td_stripbytecount) - _TIFFfree(td->td_stripbytecount); - td->td_stripbytecount = (uint64*) + if (td->td_stripbytecount_p) + _TIFFfree(td->td_stripbytecount_p); + td->td_stripbytecount_p = (uint64*) _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64), "for \"StripByteCounts\" array"); - if( td->td_stripbytecount == NULL ) + if( td->td_stripbytecount_p == NULL ) return -1; if (td->td_compression != COMPRESSION_NONE) { @@ -4552,7 +4560,7 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) if (td->td_planarconfig == PLANARCONFIG_SEPARATE) space /= td->td_samplesperpixel; for (strip = 0; strip < td->td_nstrips; strip++) - td->td_stripbytecount[strip] = space; + td->td_stripbytecount_p[strip] = space; /* * This gross hack handles the case were the offset to * the last strip is past the place where we think the strip @@ -4561,18 +4569,18 @@ EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) * of data in the strip and trim this number back accordingly. */ strip--; - if (td->td_stripoffset[strip]+td->td_stripbytecount[strip] > filesize) - td->td_stripbytecount[strip] = filesize - td->td_stripoffset[strip]; + if (td->td_stripoffset_p[strip]+td->td_stripbytecount_p[strip] > filesize) + td->td_stripbytecount_p[strip] = filesize - td->td_stripoffset_p[strip]; } else if (isTiled(tif)) { uint64 bytespertile = TIFFTileSize64(tif); for (strip = 0; strip < td->td_nstrips; strip++) - td->td_stripbytecount[strip] = bytespertile; + td->td_stripbytecount_p[strip] = bytespertile; } else { uint64 rowbytes = TIFFScanlineSize64(tif); uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage; for (strip = 0; strip < td->td_nstrips; strip++) - td->td_stripbytecount[strip] = rowbytes * rowsperstrip; + td->td_stripbytecount_p[strip] = rowbytes * rowsperstrip; } TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP)) @@ -5744,9 +5752,9 @@ static void allocChoppedUpStripArrays(TIFF* tif, uint32 nstrips, * Fill the strip information arrays with new bytecounts and offsets * that reflect the broken-up format. */ - offset = td->td_stripoffset[0]; - bytecount = td->td_stripoffset[td->td_nstrips-1] + - td->td_stripbytecount[td->td_nstrips-1] - offset; + offset = TIFFGetStrileOffset(tif, 0); + bytecount = TIFFGetStrileOffset(tif, td->td_nstrips-1) + + TIFFGetStrileByteCount(tif, td->td_nstrips-1) - offset; for (i = 0; i < nstrips; i++) { if (stripbytes > bytecount) @@ -5763,11 +5771,14 @@ static void allocChoppedUpStripArrays(TIFF* tif, uint32 nstrips, td->td_stripsperimage = td->td_nstrips = nstrips; TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip); - _TIFFfree(td->td_stripbytecount); - _TIFFfree(td->td_stripoffset); - td->td_stripbytecount = newcounts; - td->td_stripoffset = newoffsets; + _TIFFfree(td->td_stripbytecount_p); + _TIFFfree(td->td_stripoffset_p); + td->td_stripbytecount_p = newcounts; + td->td_stripoffset_p = newoffsets; +#ifdef STRIPBYTECOUNTSORTED_UNUSED td->td_stripbytecountsorted = 1; +#endif + tif->tif_flags &= ~TIFF_DEFERSTRILELOAD; } @@ -5789,13 +5800,13 @@ ChopUpSingleUncompressedStrip(TIFF* tif) uint32 nstrips; uint32 rowsperstrip; - bytecount = td->td_stripbytecount[0]; + bytecount = TIFFGetStrileByteCount(tif, 0); /* On a newly created file, just re-opened to be filled, we */ /* don't want strip chop to trigger as it is going to cause issues */ /* later ( StripOffsets and StripByteCounts improperly filled) . */ if( bytecount == 0 && tif->tif_mode != O_RDONLY ) return; - offset = td->td_stripoffset[0]; + offset = TIFFGetStrileByteCount(tif, 0); assert(td->td_planarconfig == PLANARCONFIG_CONTIG); if ((td->td_photometric == PHOTOMETRIC_YCBCR)&& (!isUpSampled(tif))) @@ -5870,7 +5881,7 @@ static void TryChopUpUncompressedBigTiff( TIFF* tif ) /* On a newly created file, just re-opened to be filled, we */ /* don't want strip chop to trigger as it is going to cause issues */ /* later ( StripOffsets and StripByteCounts improperly filled) . */ - if( td->td_stripbytecount[0] == 0 && tif->tif_mode != O_RDONLY ) + if( TIFFGetStrileByteCount(tif, 0) == 0 && tif->tif_mode != O_RDONLY ) return; if ((td->td_photometric == PHOTOMETRIC_YCBCR)&& @@ -5890,7 +5901,7 @@ static void TryChopUpUncompressedBigTiff( TIFF* tif ) { if( i == td->td_nstrips - 1 ) { - if( td->td_stripbytecount[i] < TIFFVStripSize64( + if( TIFFGetStrileByteCount(tif, i) < TIFFVStripSize64( tif, td->td_imagelength - i * td->td_rowsperstrip ) ) { return; @@ -5898,12 +5909,12 @@ static void TryChopUpUncompressedBigTiff( TIFF* tif ) } else { - if( td->td_stripbytecount[i] != stripsize ) + if( TIFFGetStrileByteCount(tif, i) != stripsize ) { return; } - if( i > 0 && td->td_stripoffset[i] != - td->td_stripoffset[i-1] + td->td_stripbytecount[i - 1] ) + if( i > 0 && TIFFGetStrileOffset(tif, i) != + TIFFGetStrileOffset(tif, i-1) + TIFFGetStrileByteCount(tif, i-1) ) { return; } @@ -5926,9 +5937,9 @@ static void TryChopUpUncompressedBigTiff( TIFF* tif ) /* file is as big as needed */ if( tif->tif_mode == O_RDONLY && nstrips > 1000000 && - (td->td_stripoffset[td->td_nstrips-1] > TIFFGetFileSize(tif) || - td->td_stripoffset[td->td_nstrips-1] + - td->td_stripbytecount[td->td_nstrips-1] > TIFFGetFileSize(tif)) ) + (TIFFGetStrileOffset(tif, td->td_nstrips-1) > TIFFGetFileSize(tif) || + TIFFGetStrileOffset(tif, td->td_nstrips-1) + + TIFFGetStrileByteCount(tif, td->td_nstrips-1) > TIFFGetFileSize(tif)) ) { return; } @@ -5936,6 +5947,317 @@ static void TryChopUpUncompressedBigTiff( TIFF* tif ) allocChoppedUpStripArrays(tif, nstrips, stripbytes, rowsperstrip); } +/* Read the value of [Strip|Tile]Offset or [Strip|Tile]ByteCount around + * strip/tile of number strile. Also fetch the neighbouring values using a + * 4096 byte page size. + */ +static +int _TIFFPartialReadStripArray( TIFF* tif, TIFFDirEntry* dirent, + int strile, uint64* panVals ) +{ + static const char module[] = "_TIFFPartialReadStripArray"; +#define IO_CACHE_PAGE_SIZE 4096 + + size_t sizeofval; + const int bSwab = (tif->tif_flags & TIFF_SWAB) != 0; + int sizeofvalint; + uint64 nBaseOffset; + uint64 nOffset; + uint64 nOffsetStartPage; + uint64 nOffsetEndPage; + tmsize_t nToRead; + tmsize_t nRead; + uint64 nLastStripOffset; + int iStartBefore; + int i; + const uint32 arraySize = tif->tif_dir.td_stripoffsetbyteallocsize; + unsigned char buffer[2 * IO_CACHE_PAGE_SIZE]; + + assert( dirent->tdir_count > 4 ); + + if( dirent->tdir_type == TIFF_SHORT ) + { + sizeofval = sizeof(uint16); + } + else if( dirent->tdir_type == TIFF_LONG ) + { + sizeofval = sizeof(uint32); + } + else if( dirent->tdir_type == TIFF_LONG8 ) + { + sizeofval = sizeof(uint64); + } + else + { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid type for [Strip|Tile][Offset/ByteCounnt] tag"); + panVals[strile] = 0; + return 0; + } + sizeofvalint = (int)(sizeofval); + + if( tif->tif_flags&TIFF_BIGTIFF ) + { + uint64 offset = dirent->tdir_offset.toff_long8; + if( bSwab ) + TIFFSwabLong8(&offset); + nBaseOffset = offset; + } + else + { + uint32 offset = dirent->tdir_offset.toff_long; + if( bSwab ) + TIFFSwabLong(&offset); + nBaseOffset = offset; + } + nOffset = nBaseOffset + sizeofval * strile; + nOffsetStartPage = + (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE; + nOffsetEndPage = nOffsetStartPage + IO_CACHE_PAGE_SIZE; + + if( nOffset + sizeofval > nOffsetEndPage ) + nOffsetEndPage += IO_CACHE_PAGE_SIZE; +#undef IO_CACHE_PAGE_SIZE + + nLastStripOffset = nBaseOffset + arraySize * sizeofval; + if( nLastStripOffset < nOffsetEndPage ) + nOffsetEndPage = nLastStripOffset; + if( nOffsetStartPage >= nOffsetEndPage ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot read offset/size for strile %d", strile); + panVals[strile] = 0; + return 0; + } + if (!SeekOK(tif,nOffsetStartPage)) + { + panVals[strile] = 0; + return 0; + } + + nToRead = (tmsize_t)(nOffsetEndPage - nOffsetStartPage); + nRead = TIFFReadFile(tif, buffer, nToRead); + if( nRead < nToRead ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot read offset/size for strile around ~%d", strile); + return 0; + } + iStartBefore = -(int)((nOffset - nOffsetStartPage) / sizeofval); + if( strile + iStartBefore < 0 ) + iStartBefore = -strile; + for( i = iStartBefore; + (uint32)(strile + i) < arraySize && + (uint64)(nOffset) + (i + 1) * sizeofvalint <= nOffsetEndPage; + ++i ) + { + if( sizeofval == sizeof(uint16) ) + { + uint16 val; + memcpy(&val, + buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint, + sizeof(val)); + if( bSwab ) + TIFFSwabShort(&val); + panVals[strile + i] = val; + } + else if( sizeofval == sizeof(uint32) ) + { + uint32 val; + memcpy(&val, + buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint, + sizeof(val)); + if( bSwab ) + TIFFSwabLong(&val); + panVals[strile + i] = val; + } + else + { + uint64 val; + memcpy(&val, + buffer + (nOffset - nOffsetStartPage) + i * sizeofvalint, + sizeof(val)); + if( bSwab ) + TIFFSwabLong8(&val); + panVals[strile + i] = val; + } + } + return 1; +} + +static int _TIFFFetchStrileValue(TIFF* tif, + uint32 strile, + TIFFDirEntry* dirent, + uint64** parray) +{ + static const char module[] = "_TIFFFetchStrileValue"; + TIFFDirectory *td = &tif->tif_dir; + if( strile >= dirent->tdir_count ) + { + return 0; + } + if( strile >= td->td_stripoffsetbyteallocsize ) + { + uint32 nStripArrayAllocBefore = td->td_stripoffsetbyteallocsize; + uint32 nStripArrayAllocNew; + uint64 nArraySize64; + size_t nArraySize; + uint64* offsetArray; + uint64* bytecountArray; + + if( strile > 1000000 ) + { + uint64 filesize = TIFFGetFileSize(tif); + /* Avoid excessive memory allocation attempt */ + /* For such a big blockid we need at least a TIFF_LONG per strile */ + /* for the offset array. */ + if( strile > filesize / sizeof(uint32) ) + { + TIFFErrorExt(tif->tif_clientdata, module, "File too short"); + return 0; + } + } + + if( td->td_stripoffsetbyteallocsize == 0 && + td->td_nstrips < 1024 * 1024 ) + { + nStripArrayAllocNew = td->td_nstrips; + } + else + { +#define TIFF_MAX(a,b) (((a)>(b)) ? (a) : (b)) +#define TIFF_MIN(a,b) (((a)<(b)) ? (a) : (b)) + nStripArrayAllocNew = TIFF_MAX(strile + 1, 1024U * 512U ); + if( nStripArrayAllocNew < 0xFFFFFFFFU / 2 ) + nStripArrayAllocNew *= 2; + nStripArrayAllocNew = TIFF_MIN(nStripArrayAllocNew, td->td_nstrips); + } + assert( strile < nStripArrayAllocNew ); + nArraySize64 = (uint64)sizeof(uint64) * nStripArrayAllocNew; + nArraySize = (size_t)(nArraySize64); +#if SIZEOF_SIZE_T == 4 + if( nArraySize != nArraySize64 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate strip offset and bytecount arrays"); + return 0; + } +#endif + offsetArray = (uint64*)( + _TIFFrealloc( td->td_stripoffset_p, nArraySize ) ); + bytecountArray = (uint64*)( + _TIFFrealloc( td->td_stripbytecount_p, nArraySize ) ); + if( offsetArray ) + td->td_stripoffset_p = offsetArray; + if( bytecountArray ) + td->td_stripbytecount_p = bytecountArray; + if( offsetArray && bytecountArray ) + { + td->td_stripoffsetbyteallocsize = nStripArrayAllocNew; + /* Initialize new entries to ~0 / -1 */ + memset(td->td_stripoffset_p + nStripArrayAllocBefore, + 0xFF, + (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) ); + memset(td->td_stripbytecount_p + nStripArrayAllocBefore, + 0xFF, + (td->td_stripoffsetbyteallocsize - nStripArrayAllocBefore) * sizeof(uint64) ); + } + else + { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot allocate strip offset and bytecount arrays"); + _TIFFfree(td->td_stripoffset_p); + td->td_stripoffset_p = NULL; + _TIFFfree(td->td_stripbytecount_p); + td->td_stripbytecount_p = NULL; + td->td_stripoffsetbyteallocsize = 0; + } + } + if( *parray == NULL || strile >= td->td_stripoffsetbyteallocsize ) + return 0; + + if( ~((*parray)[strile]) == 0 ) + { + if( !_TIFFPartialReadStripArray( tif, dirent, strile, *parray ) ) + { + (*parray)[strile] = 0; + return 0; + } + } + + return 1; +} + +static uint64 _TIFFGetStrileOffsetOrByteCountValue(TIFF *tif, uint32 strile, + TIFFDirEntry* dirent, + uint64** parray, + int *pbErr) +{ + TIFFDirectory *td = &tif->tif_dir; + if( pbErr ) + *pbErr = 0; + if( tif->tif_flags&TIFF_DEFERSTRILELOAD ) + { + if( !(tif->tif_flags&TIFF_LAZYSTRILELOAD) || + /* If the values may fit in the toff_long/toff_long8 member */ + /* then use _TIFFFillStriles to simplify _TIFFFetchStrileValue */ + dirent->tdir_count <= 4 ) + { + if( !_TIFFFillStriles(tif) ) + { + if( pbErr ) + *pbErr = 1; + return 0; + } + } + else + { + if( !_TIFFFetchStrileValue(tif, strile, dirent, parray) ) + { + if( pbErr ) + *pbErr = 1; + return 0; + } + } + } + if( *parray == NULL || strile >= td->td_nstrips ) + { + if( pbErr ) + *pbErr = 1; + return 0; + } + return (*parray)[strile]; +} + +/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */ +uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile) +{ + return TIFFGetStrileOffsetWithErr(tif, strile, NULL); +} + +/* Return the value of the TileOffsets/StripOffsets array for the specified tile/strile */ +uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr) +{ + TIFFDirectory *td = &tif->tif_dir; + return _TIFFGetStrileOffsetOrByteCountValue(tif, strile, + &(td->td_stripoffset_entry), + &(td->td_stripoffset_p), pbErr); +} + +/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */ +uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile) +{ + return TIFFGetStrileByteCountWithErr(tif, strile, NULL); +} + +/* Return the value of the TileByteCounts/StripByteCounts array for the specified tile/strile */ +uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr) +{ + TIFFDirectory *td = &tif->tif_dir; + return _TIFFGetStrileOffsetOrByteCountValue(tif, strile, + &(td->td_stripbytecount_entry), + &(td->td_stripbytecount_p), pbErr); +} int _TIFFFillStriles( TIFF *tif ) @@ -5945,51 +6267,64 @@ int _TIFFFillStriles( TIFF *tif ) static int _TIFFFillStrilesInternal( TIFF *tif, int loadStripByteCount ) { -#if defined(DEFER_STRILE_LOAD) - register TIFFDirectory *td = &tif->tif_dir; - int return_value = 1; + register TIFFDirectory *td = &tif->tif_dir; + int return_value = 1; - if( td->td_stripoffset != NULL ) - return 1; - - if( td->td_stripoffset_entry.tdir_count == 0 ) - return 0; - - if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry), - td->td_nstrips,&td->td_stripoffset)) - { - return_value = 0; - } - - if (loadStripByteCount && - !TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry), - td->td_nstrips,&td->td_stripbytecount)) - { - return_value = 0; - } - - _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry)); - _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry)); - - if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) { - uint32 strip; - - tif->tif_dir.td_stripbytecountsorted = 1; - for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) { - if (tif->tif_dir.td_stripoffset[strip - 1] > - tif->tif_dir.td_stripoffset[strip]) { - tif->tif_dir.td_stripbytecountsorted = 0; - break; - } - } - } - - return return_value; -#else /* !defined(DEFER_STRILE_LOAD) */ - (void) tif; - (void) loadStripByteCount; + /* Do not do anything if TIFF_DEFERSTRILELOAD is not set */ + if( !(tif->tif_flags&TIFF_DEFERSTRILELOAD) ) return 1; -#endif + + if( tif->tif_flags&TIFF_LAZYSTRILELOAD ) + { + /* In case of lazy loading, reload completely the arrays */ + _TIFFfree(td->td_stripoffset_p); + _TIFFfree(td->td_stripbytecount_p); + td->td_stripoffset_p = NULL; + td->td_stripbytecount_p = NULL; + td->td_stripoffsetbyteallocsize = 0; + tif->tif_flags &= ~TIFF_LAZYSTRILELOAD; + } + + /* If stripoffset array is already loaded, exit with success */ + if( td->td_stripoffset_p != NULL ) + return 1; + + /* If tdir_count was cancelled, then we already got there, but in error */ + if( td->td_stripoffset_entry.tdir_count == 0 ) + return 0; + + if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry), + td->td_nstrips,&td->td_stripoffset_p)) + { + return_value = 0; + } + + if (loadStripByteCount && + !TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry), + td->td_nstrips,&td->td_stripbytecount_p)) + { + return_value = 0; + } + + _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry)); + _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry)); + +#ifdef STRIPBYTECOUNTSORTED_UNUSED + if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) { + uint32 strip; + + tif->tif_dir.td_stripbytecountsorted = 1; + for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) { + if (tif->tif_dir.td_stripoffset_p[strip - 1] > + tif->tif_dir.td_stripoffset_p[strip]) { + tif->tif_dir.td_stripbytecountsorted = 0; + break; + } + } + } +#endif + + return return_value; } diff --git a/libtiff/tif_dirwrite.c b/libtiff/tif_dirwrite.c index 83c01b24..cc0e6217 100644 --- a/libtiff/tif_dirwrite.c +++ b/libtiff/tif_dirwrite.c @@ -192,7 +192,7 @@ TIFFCheckpointDirectory(TIFF* tif) { int rc; /* Setup the strips arrays, if they haven't already been. */ - if (tif->tif_dir.td_stripoffset == NULL) + if (tif->tif_dir.td_stripoffset_p == NULL) (void) TIFFSetupStrips(tif); rc = TIFFWriteDirectorySec(tif,TRUE,FALSE,NULL); (void) TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END)); @@ -527,12 +527,12 @@ TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff) { if (!isTiled(tif)) { - if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount)) + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p)) goto bad; } else { - if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount)) + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount_p)) goto bad; } } @@ -540,7 +540,7 @@ TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff) { if (!isTiled(tif)) { - /* td_stripoffset might be NULL in an odd OJPEG case. See + /* td_stripoffset_p might be NULL in an odd OJPEG case. See * tif_dirread.c around line 3634. * XXX: OJPEG hack. * If a) compression is OJPEG, b) it's not a tiled TIFF, @@ -551,13 +551,13 @@ TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff) * We can get here when using tiffset on such a file. * See http://bugzilla.maptools.org/show_bug.cgi?id=2500 */ - if (tif->tif_dir.td_stripoffset != NULL && - !TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset)) + if (tif->tif_dir.td_stripoffset_p != NULL && + !TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p)) goto bad; } else { - if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset)) + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset_p)) goto bad; } } diff --git a/libtiff/tif_jpeg.c b/libtiff/tif_jpeg.c index f2ddc331..8606f623 100644 --- a/libtiff/tif_jpeg.c +++ b/libtiff/tif_jpeg.c @@ -780,12 +780,9 @@ JPEGFixupTagsSubsampling(TIFF* tif) */ static const char module[] = "JPEGFixupTagsSubsampling"; struct JPEGFixupTagsSubsamplingData m; + uint64 fileoffset = TIFFGetStrileOffset(tif, 0); - _TIFFFillStriles( tif ); - - if( tif->tif_dir.td_stripbytecount == NULL - || tif->tif_dir.td_stripoffset == NULL - || tif->tif_dir.td_stripbytecount[0] == 0 ) + if( fileoffset == 0 ) { /* Do not even try to check if the first strip/tile does not yet exist, as occurs when GDAL has created a new NULL file @@ -804,9 +801,9 @@ JPEGFixupTagsSubsampling(TIFF* tif) } m.buffercurrentbyte=NULL; m.bufferbytesleft=0; - m.fileoffset=tif->tif_dir.td_stripoffset[0]; + m.fileoffset=fileoffset; m.filepositioned=0; - m.filebytesleft=tif->tif_dir.td_stripbytecount[0]; + m.filebytesleft=TIFFGetStrileByteCount(tif, 0); if (!JPEGFixupTagsSubsamplingSec(&m)) TIFFWarningExt(tif->tif_clientdata,module, "Unable to auto-correct subsampling values, likely corrupt JPEG compressed data in first strip/tile; auto-correcting skipped"); diff --git a/libtiff/tif_ojpeg.c b/libtiff/tif_ojpeg.c index 27385d8c..c01d71a2 100644 --- a/libtiff/tif_ojpeg.c +++ b/libtiff/tif_ojpeg.c @@ -990,7 +990,6 @@ OJPEGSubsamplingCorrect(TIFF* tif) OJPEGState* sp=(OJPEGState*)tif->tif_data; uint8 mh; uint8 mv; - _TIFFFillStriles( tif ); assert(sp->subsamplingcorrect_done==0); if ((tif->tif_dir.td_samplesperpixel!=3) || ((tif->tif_dir.td_photometric!=PHOTOMETRIC_YCBCR) && @@ -1989,29 +1988,21 @@ OJPEGReadBufferFill(OJPEGState* sp) sp->in_buffer_source=osibsStrile; break; case osibsStrile: - if (!_TIFFFillStriles( sp->tif ) - || sp->tif->tif_dir.td_stripoffset == NULL - || sp->tif->tif_dir.td_stripbytecount == NULL) - return 0; - if (sp->in_buffer_next_strile==sp->in_buffer_strile_count) sp->in_buffer_source=osibsEof; else { - sp->in_buffer_file_pos=sp->tif->tif_dir.td_stripoffset[sp->in_buffer_next_strile]; + sp->in_buffer_file_pos=TIFFGetStrileOffset(sp->tif, sp->in_buffer_next_strile); if (sp->in_buffer_file_pos!=0) { + uint64 bytecount = TIFFGetStrileByteCount(sp->tif, sp->in_buffer_next_strile); if (sp->in_buffer_file_pos>=sp->file_size) sp->in_buffer_file_pos=0; - else if (sp->tif->tif_dir.td_stripbytecount==NULL) + else if (bytecount==0) sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos; else { - if (sp->tif->tif_dir.td_stripbytecount == 0) { - TIFFErrorExt(sp->tif->tif_clientdata,sp->tif->tif_name,"Strip byte counts are missing"); - return(0); - } - sp->in_buffer_file_togo=sp->tif->tif_dir.td_stripbytecount[sp->in_buffer_next_strile]; + sp->in_buffer_file_togo=bytecount; if (sp->in_buffer_file_togo==0) sp->in_buffer_file_pos=0; else if (sp->in_buffer_file_pos+sp->in_buffer_file_togo>sp->file_size) diff --git a/libtiff/tif_open.c b/libtiff/tif_open.c index c574c452..f7b7e0f8 100644 --- a/libtiff/tif_open.c +++ b/libtiff/tif_open.c @@ -181,6 +181,8 @@ TIFFClientOpen( * 'h' read TIFF header only, do not load the first IFD * '4' ClassicTIFF for creating a file (default) * '8' BigTIFF for creating a file + * 'D' enable use of deferred strip/tile offset/bytecount array loading. + * 'O' on-demand loading of values instead of whole array loading (implies D) * * The use of the 'l' and 'b' flags is strongly discouraged. * These flags are provided solely because numerous vendors, @@ -262,7 +264,22 @@ TIFFClientOpen( if (m&O_CREAT) tif->tif_flags |= TIFF_BIGTIFF; break; + case 'D': + tif->tif_flags |= TIFF_DEFERSTRILELOAD; + break; + case 'O': + if( m == O_RDONLY ) + tif->tif_flags |= (TIFF_LAZYSTRILELOAD | TIFF_DEFERSTRILELOAD); + break; } + +#ifdef DEFER_STRILE_LOAD + /* Compatibility with old DEFER_STRILE_LOAD compilation flag */ + /* Probably unneeded, since to the best of my knowledge (E. Rouault) */ + /* GDAL was the only user of this, and will now use the new 'D' flag */ + tif->tif_flags |= TIFF_DEFERSTRILELOAD; +#endif + /* * Read in TIFF header. */ diff --git a/libtiff/tif_print.c b/libtiff/tif_print.c index 1d86adbf..a0737941 100644 --- a/libtiff/tif_print.c +++ b/libtiff/tif_print.c @@ -652,8 +652,6 @@ TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags) if (tif->tif_tagmethods.printdir) (*tif->tif_tagmethods.printdir)(tif, fd, flags); - _TIFFFillStriles( tif ); - if ((flags & TIFFPRINT_STRIPS) && TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) { uint32 s; @@ -665,13 +663,13 @@ TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags) #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) fprintf(fd, " %3lu: [%8I64u, %8I64u]\n", (unsigned long) s, - td->td_stripoffset ? (unsigned __int64) td->td_stripoffset[s] : 0, - td->td_stripbytecount ? (unsigned __int64) td->td_stripbytecount[s] : 0); + (unsigned __int64) TIFFGetStrileOffset(tif, s), + (unsigned __int64) TIFFGetStrileByteCount(tif, s)); #else fprintf(fd, " %3lu: [%8llu, %8llu]\n", (unsigned long) s, - td->td_stripoffset ? (unsigned long long) td->td_stripoffset[s] : 0, - td->td_stripbytecount ? (unsigned long long) td->td_stripbytecount[s] : 0); + (unsigned long long) TIFFGetStrileOffset(tif, s), + (unsigned long long) TIFFGetStrileByteCount(tif, s)); #endif } } diff --git a/libtiff/tif_read.c b/libtiff/tif_read.c index 79c470cb..c65abcea 100644 --- a/libtiff/tif_read.c +++ b/libtiff/tif_read.c @@ -175,17 +175,14 @@ TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart ) tmsize_t to_read; tmsize_t read_ahead_mod; /* tmsize_t bytecountm; */ - - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - + /* * Expand raw data buffer, if needed, to hold data * strip coming from file (perhaps should set upper * bound on the size of a buffer we'll use?). */ - /* bytecountm=(tmsize_t) td->td_stripbytecount[strip]; */ + /* bytecountm=(tmsize_t) TIFFGetStrileByteCount(tif, strip); */ /* Not completely sure where the * 2 comes from, but probably for */ /* an exponentional growth strategy of tif_rawdatasize */ @@ -229,7 +226,7 @@ TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart ) /* ** Seek to the point in the file where more data should be read. */ - read_offset = td->td_stripoffset[strip] + read_offset = TIFFGetStrileOffset(tif, strip) + tif->tif_rawdataoff + tif->tif_rawdataloaded; if (!SeekOK(tif, read_offset)) { @@ -246,10 +243,10 @@ TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart ) to_read = read_ahead_mod - unused_data; else to_read = tif->tif_rawdatasize - unused_data; - if( (uint64) to_read > td->td_stripbytecount[strip] + if( (uint64) to_read > TIFFGetStrileByteCount(tif, strip) - tif->tif_rawdataoff - tif->tif_rawdataloaded ) { - to_read = (tmsize_t) td->td_stripbytecount[strip] + to_read = (tmsize_t) TIFFGetStrileByteCount(tif, strip) - tif->tif_rawdataoff - tif->tif_rawdataloaded; } @@ -288,7 +285,7 @@ TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart ) /* For JPEG, if there are multiple scans (can generally be known */ /* with the read_ahead used), we need to read the whole strip */ if( tif->tif_dir.td_compression==COMPRESSION_JPEG && - (uint64)tif->tif_rawcc < td->td_stripbytecount[strip] ) + (uint64)tif->tif_rawcc < TIFFGetStrileByteCount(tif, strip) ) { if( TIFFJPEGIsFullStripRequired(tif) ) { @@ -347,9 +344,7 @@ TIFFSeek(TIFF* tif, uint32 row, uint16 sample ) * read it a few lines at a time? */ #if defined(CHUNKY_STRIP_READ_SUPPORT) - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - whole_strip = tif->tif_dir.td_stripbytecount[strip] < 10 + whole_strip = TIFFGetStrileByteCount(tif, strip) < 10 || isMapped(tif); if( td->td_compression == COMPRESSION_LERC || td->td_compression == COMPRESSION_JBIG ) @@ -402,7 +397,7 @@ TIFFSeek(TIFF* tif, uint32 row, uint16 sample ) else if( !whole_strip ) { if( ((tif->tif_rawdata + tif->tif_rawdataloaded) - tif->tif_rawcp) < read_ahead - && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < td->td_stripbytecount[strip] ) + && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < TIFFGetStrileByteCount(tif, strip) ) { if( !TIFFFillStripPartial(tif,strip,read_ahead,0) ) return 0; @@ -599,16 +594,11 @@ static tmsize_t TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size, const char* module) { - TIFFDirectory *td = &tif->tif_dir; - - if (!_TIFFFillStriles( tif )) - return ((tmsize_t)(-1)); - assert((tif->tif_flags&TIFF_NOREADRAW)==0); if (!isMapped(tif)) { tmsize_t cc; - if (!SeekOK(tif, td->td_stripoffset[strip])) { + if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip))) { TIFFErrorExt(tif->tif_clientdata, module, "Seek error at scanline %lu, strip %lu", (unsigned long) tif->tif_row, (unsigned long) strip); @@ -634,8 +624,8 @@ TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size, } else { tmsize_t ma = 0; tmsize_t n; - if ((td->td_stripoffset[strip] > (uint64)TIFF_TMSIZE_T_MAX)|| - ((ma=(tmsize_t)td->td_stripoffset[strip])>tif->tif_size)) + if ((TIFFGetStrileOffset(tif, strip) > (uint64)TIFF_TMSIZE_T_MAX)|| + ((ma=(tmsize_t)TIFFGetStrileOffset(tif, strip))>tif->tif_size)) { n=0; } @@ -679,12 +669,10 @@ static tmsize_t TIFFReadRawStripOrTile2(TIFF* tif, uint32 strip_or_tile, int is_strip, tmsize_t size, const char* module) { - TIFFDirectory *td = &tif->tif_dir; - assert( !isMapped(tif) ); assert((tif->tif_flags&TIFF_NOREADRAW)==0); - if (!SeekOK(tif, td->td_stripoffset[strip_or_tile])) { + if (!SeekOK(tif, TIFFGetStrileOffset(tif, strip_or_tile))) { if( is_strip ) { TIFFErrorExt(tif->tif_clientdata, module, @@ -738,7 +726,7 @@ TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size) "Compression scheme does not support access to raw uncompressed data"); return ((tmsize_t)(-1)); } - bytecount = td->td_stripbytecount[strip]; + bytecount = TIFFGetStrileByteCount(tif, strip); if ((int64)bytecount <= 0) { #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) TIFFErrorExt(tif->tif_clientdata, module, @@ -773,12 +761,9 @@ TIFFFillStrip(TIFF* tif, uint32 strip) static const char module[] = "TIFFFillStrip"; TIFFDirectory *td = &tif->tif_dir; - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - if ((tif->tif_flags&TIFF_NOREADRAW)==0) { - uint64 bytecount = td->td_stripbytecount[strip]; + uint64 bytecount = TIFFGetStrileByteCount(tif, strip); if ((int64)bytecount <= 0) { #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) TIFFErrorExt(tif->tif_clientdata, module, @@ -831,13 +816,13 @@ TIFFFillStrip(TIFF* tif, uint32 strip) * We must check for overflow, potentially causing * an OOB read. Instead of simple * - * td->td_stripoffset[strip]+bytecount > tif->tif_size + * TIFFGetStrileOffset(tif, strip)+bytecount > tif->tif_size * * comparison (which can overflow) we do the following * two comparisons: */ if (bytecount > (uint64)tif->tif_size || - td->td_stripoffset[strip] > (uint64)tif->tif_size - bytecount) { + TIFFGetStrileOffset(tif, strip) > (uint64)tif->tif_size - bytecount) { /* * This error message might seem strange, but * it's what would happen if a read were done @@ -849,7 +834,7 @@ TIFFFillStrip(TIFF* tif, uint32 strip) "Read error on strip %lu; " "got %I64u bytes, expected %I64u", (unsigned long) strip, - (unsigned __int64) tif->tif_size - td->td_stripoffset[strip], + (unsigned __int64) tif->tif_size - TIFFGetStrileOffset(tif, strip), (unsigned __int64) bytecount); #else TIFFErrorExt(tif->tif_clientdata, module, @@ -857,7 +842,7 @@ TIFFFillStrip(TIFF* tif, uint32 strip) "Read error on strip %lu; " "got %llu bytes, expected %llu", (unsigned long) strip, - (unsigned long long) tif->tif_size - td->td_stripoffset[strip], + (unsigned long long) tif->tif_size - TIFFGetStrileOffset(tif, strip), (unsigned long long) bytecount); #endif tif->tif_curstrip = NOSTRIP; @@ -886,7 +871,7 @@ TIFFFillStrip(TIFF* tif, uint32 strip) } tif->tif_flags &= ~TIFF_MYBUFFER; tif->tif_rawdatasize = (tmsize_t)bytecount; - tif->tif_rawdata = tif->tif_base + (tmsize_t)td->td_stripoffset[strip]; + tif->tif_rawdata = tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, strip); tif->tif_rawdataoff = 0; tif->tif_rawdataloaded = (tmsize_t) bytecount; @@ -1101,16 +1086,11 @@ _TIFFReadEncodedTileAndAllocBuffer(TIFF* tif, uint32 tile, static tmsize_t TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module) { - TIFFDirectory *td = &tif->tif_dir; - - if (!_TIFFFillStriles( tif )) - return ((tmsize_t)(-1)); - assert((tif->tif_flags&TIFF_NOREADRAW)==0); if (!isMapped(tif)) { tmsize_t cc; - if (!SeekOK(tif, td->td_stripoffset[tile])) { + if (!SeekOK(tif, TIFFGetStrileOffset(tif, tile))) { TIFFErrorExt(tif->tif_clientdata, module, "Seek error at row %lu, col %lu, tile %lu", (unsigned long) tif->tif_row, @@ -1140,9 +1120,9 @@ TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* m } else { tmsize_t ma,mb; tmsize_t n; - ma=(tmsize_t)td->td_stripoffset[tile]; + ma=(tmsize_t)TIFFGetStrileOffset(tif, tile); mb=ma+size; - if ((td->td_stripoffset[tile] > (uint64)TIFF_TMSIZE_T_MAX)||(ma>tif->tif_size)) + if ((TIFFGetStrileOffset(tif, tile) > (uint64)TIFF_TMSIZE_T_MAX)||(ma>tif->tif_size)) n=0; else if ((mbtif->tif_size)) n=tif->tif_size-ma; @@ -1198,7 +1178,7 @@ TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size) "Compression scheme does not support access to raw uncompressed data"); return ((tmsize_t)(-1)); } - bytecount64 = td->td_stripbytecount[tile]; + bytecount64 = TIFFGetStrileByteCount(tif, tile); if (size != (tmsize_t)(-1) && (uint64)size < bytecount64) bytecount64 = (uint64)size; bytecountm = (tmsize_t)bytecount64; @@ -1220,12 +1200,9 @@ TIFFFillTile(TIFF* tif, uint32 tile) static const char module[] = "TIFFFillTile"; TIFFDirectory *td = &tif->tif_dir; - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - if ((tif->tif_flags&TIFF_NOREADRAW)==0) { - uint64 bytecount = td->td_stripbytecount[tile]; + uint64 bytecount = TIFFGetStrileByteCount(tif, tile); if ((int64)bytecount <= 0) { #if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) TIFFErrorExt(tif->tif_clientdata, module, @@ -1278,13 +1255,13 @@ TIFFFillTile(TIFF* tif, uint32 tile) * We must check for overflow, potentially causing * an OOB read. Instead of simple * - * td->td_stripoffset[tile]+bytecount > tif->tif_size + * TIFFGetStrileOffset(tif, tile)+bytecount > tif->tif_size * * comparison (which can overflow) we do the following * two comparisons: */ if (bytecount > (uint64)tif->tif_size || - td->td_stripoffset[tile] > (uint64)tif->tif_size - bytecount) { + TIFFGetStrileOffset(tif, tile) > (uint64)tif->tif_size - bytecount) { tif->tif_curtile = NOTILE; return (0); } @@ -1313,7 +1290,7 @@ TIFFFillTile(TIFF* tif, uint32 tile) tif->tif_rawdatasize = (tmsize_t)bytecount; tif->tif_rawdata = - tif->tif_base + (tmsize_t)td->td_stripoffset[tile]; + tif->tif_base + (tmsize_t)TIFFGetStrileOffset(tif, tile); tif->tif_rawdataoff = 0; tif->tif_rawdataloaded = (tmsize_t) bytecount; tif->tif_flags |= TIFF_BUFFERMMAP; @@ -1440,9 +1417,6 @@ TIFFStartStrip(TIFF* tif, uint32 strip) { TIFFDirectory *td = &tif->tif_dir; - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { if (!(*tif->tif_setupdecode)(tif)) return (0); @@ -1463,7 +1437,7 @@ TIFFStartStrip(TIFF* tif, uint32 strip) if( tif->tif_rawdataloaded > 0 ) tif->tif_rawcc = tif->tif_rawdataloaded; else - tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[strip]; + tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, strip); } return ((*tif->tif_predecode)(tif, (uint16)(strip / td->td_stripsperimage))); @@ -1480,9 +1454,6 @@ TIFFStartTile(TIFF* tif, uint32 tile) TIFFDirectory *td = &tif->tif_dir; uint32 howmany32; - if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) - return 0; - if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { if (!(*tif->tif_setupdecode)(tif)) return (0); @@ -1513,7 +1484,7 @@ TIFFStartTile(TIFF* tif, uint32 tile) if( tif->tif_rawdataloaded > 0 ) tif->tif_rawcc = tif->tif_rawdataloaded; else - tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[tile]; + tif->tif_rawcc = (tmsize_t)TIFFGetStrileByteCount(tif, tile); } return ((*tif->tif_predecode)(tif, (uint16)(tile/td->td_stripsperimage))); diff --git a/libtiff/tif_strip.c b/libtiff/tif_strip.c index 5b76fba5..2f3cd883 100644 --- a/libtiff/tif_strip.c +++ b/libtiff/tif_strip.c @@ -147,8 +147,7 @@ uint64 TIFFRawStripSize64(TIFF* tif, uint32 strip) { static const char module[] = "TIFFRawStripSize64"; - TIFFDirectory* td = &tif->tif_dir; - uint64 bytecount = td->td_stripbytecount[strip]; + uint64 bytecount = TIFFGetStrileByteCount(tif, strip); if (bytecount == 0) { diff --git a/libtiff/tif_write.c b/libtiff/tif_write.c index 4e5d8175..b79fd945 100644 --- a/libtiff/tif_write.c +++ b/libtiff/tif_write.c @@ -128,10 +128,10 @@ TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample) tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; - if( td->td_stripbytecount[strip] > 0 ) + if( td->td_stripbytecount_p[strip] > 0 ) { /* if we are writing over existing tiles, zero length */ - td->td_stripbytecount[strip] = 0; + td->td_stripbytecount_p[strip] = 0; /* this forces TIFFAppendToStrip() to do a seek */ tif->tif_curoff = 0; @@ -183,11 +183,11 @@ TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample) static int _TIFFReserveLargeEnoughWriteBuffer(TIFF* tif, uint32 strip_or_tile) { TIFFDirectory *td = &tif->tif_dir; - if( td->td_stripbytecount[strip_or_tile] > 0 ) + if( td->td_stripbytecount_p[strip_or_tile] > 0 ) { /* The +1 is to ensure at least one extra bytes */ /* The +4 is because the LZW encoder flushes 4 bytes before the limit */ - uint64 safe_buffer_size = (uint64)(td->td_stripbytecount[strip_or_tile] + 1 + 4); + uint64 safe_buffer_size = (uint64)(td->td_stripbytecount_p[strip_or_tile] + 1 + 4); if( tif->tif_rawdatasize <= (tmsize_t)safe_buffer_size ) { if( !(TIFFWriteBufferSetup(tif, NULL, @@ -535,20 +535,20 @@ TIFFSetupStrips(TIFF* tif) td->td_nstrips = td->td_stripsperimage; if (td->td_planarconfig == PLANARCONFIG_SEPARATE) td->td_stripsperimage /= td->td_samplesperpixel; - td->td_stripoffset = (uint64 *) + td->td_stripoffset_p = (uint64 *) _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64), "for \"StripOffsets\" array"); - td->td_stripbytecount = (uint64 *) + td->td_stripbytecount_p = (uint64 *) _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64), "for \"StripByteCounts\" array"); - if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) + if (td->td_stripoffset_p == NULL || td->td_stripbytecount_p == NULL) return (0); /* * Place data at the end-of-file * (by setting offsets to zero). */ - _TIFFmemset(td->td_stripoffset, 0, td->td_nstrips*sizeof (uint64)); - _TIFFmemset(td->td_stripbytecount, 0, td->td_nstrips*sizeof (uint64)); + _TIFFmemset(td->td_stripoffset_p, 0, td->td_nstrips*sizeof (uint64)); + _TIFFmemset(td->td_stripbytecount_p, 0, td->td_nstrips*sizeof (uint64)); TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); return (1); @@ -608,7 +608,7 @@ TIFFWriteCheck(TIFF* tif, int tiles, const char* module) return (0); } } - if (tif->tif_dir.td_stripoffset == NULL && !TIFFSetupStrips(tif)) { + if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif)) { tif->tif_dir.td_nstrips = 0; TIFFErrorExt(tif->tif_clientdata, module, "No space for %s arrays", isTiled(tif) ? "tile" : "strip"); @@ -682,9 +682,9 @@ TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module) uint64* new_stripbytecount; assert(td->td_planarconfig == PLANARCONFIG_CONTIG); - new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset, + new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset_p, (td->td_nstrips + delta) * sizeof (uint64)); - new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount, + new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount_p, (td->td_nstrips + delta) * sizeof (uint64)); if (new_stripoffset == NULL || new_stripbytecount == NULL) { if (new_stripoffset) @@ -695,11 +695,11 @@ TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module) TIFFErrorExt(tif->tif_clientdata, module, "No space to expand strip arrays"); return (0); } - td->td_stripoffset = new_stripoffset; - td->td_stripbytecount = new_stripbytecount; - _TIFFmemset(td->td_stripoffset + td->td_nstrips, + td->td_stripoffset_p = new_stripoffset; + td->td_stripbytecount_p = new_stripbytecount; + _TIFFmemset(td->td_stripoffset_p + td->td_nstrips, 0, delta*sizeof (uint64)); - _TIFFmemset(td->td_stripbytecount + td->td_nstrips, + _TIFFmemset(td->td_stripbytecount_p + td->td_nstrips, 0, delta*sizeof (uint64)); td->td_nstrips += delta; tif->tif_flags |= TIFF_DIRTYDIRECT; @@ -718,12 +718,12 @@ TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc) uint64 m; int64 old_byte_count = -1; - if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) { + if (td->td_stripoffset_p[strip] == 0 || tif->tif_curoff == 0) { assert(td->td_nstrips > 0); - if( td->td_stripbytecount[strip] != 0 - && td->td_stripoffset[strip] != 0 - && td->td_stripbytecount[strip] >= (uint64) cc ) + if( td->td_stripbytecount_p[strip] != 0 + && td->td_stripoffset_p[strip] != 0 + && td->td_stripbytecount_p[strip] >= (uint64) cc ) { /* * There is already tile data on disk, and the new tile @@ -732,7 +732,7 @@ TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc) * more data to append to this strip before we are done * depending on how we are getting called. */ - if (!SeekOK(tif, td->td_stripoffset[strip])) { + if (!SeekOK(tif, td->td_stripoffset_p[strip])) { TIFFErrorExt(tif->tif_clientdata, module, "Seek error at scanline %lu", (unsigned long)tif->tif_row); @@ -745,17 +745,17 @@ TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc) * Seek to end of file, and set that as our location to * write this strip. */ - td->td_stripoffset[strip] = TIFFSeekFile(tif, 0, SEEK_END); + td->td_stripoffset_p[strip] = TIFFSeekFile(tif, 0, SEEK_END); tif->tif_flags |= TIFF_DIRTYSTRIP; } - tif->tif_curoff = td->td_stripoffset[strip]; + tif->tif_curoff = td->td_stripoffset_p[strip]; /* * We are starting a fresh strip/tile, so set the size to zero. */ - old_byte_count = td->td_stripbytecount[strip]; - td->td_stripbytecount[strip] = 0; + old_byte_count = td->td_stripbytecount_p[strip]; + td->td_stripbytecount_p[strip] = 0; } m = tif->tif_curoff+cc; @@ -772,9 +772,9 @@ TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc) return (0); } tif->tif_curoff = m; - td->td_stripbytecount[strip] += cc; + td->td_stripbytecount_p[strip] += cc; - if( (int64) td->td_stripbytecount[strip] != old_byte_count ) + if( (int64) td->td_stripbytecount_p[strip] != old_byte_count ) tif->tif_flags |= TIFF_DIRTYSTRIP; return (1); diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h index 31c2e676..7193fbcf 100644 --- a/libtiff/tiffio.h +++ b/libtiff/tiffio.h @@ -488,6 +488,11 @@ extern void TIFFSwabArrayOfDouble(double* dp, tmsize_t n); extern void TIFFReverseBits(uint8* cp, tmsize_t n); extern const unsigned char* TIFFGetBitRevTable(int); +extern uint64 TIFFGetStrileOffset(TIFF *tif, uint32 strile); +extern uint64 TIFFGetStrileByteCount(TIFF *tif, uint32 strile); +extern uint64 TIFFGetStrileOffsetWithErr(TIFF *tif, uint32 strile, int *pbErr); +extern uint64 TIFFGetStrileByteCountWithErr(TIFF *tif, uint32 strile, int *pbErr); + #ifdef LOGLUV_PUBLIC #define U_NEU 0.210526316 #define V_NEU 0.473684211 diff --git a/libtiff/tiffiop.h b/libtiff/tiffiop.h index 47a553aa..ef63de53 100644 --- a/libtiff/tiffiop.h +++ b/libtiff/tiffiop.h @@ -127,6 +127,8 @@ struct tiff { #define TIFF_DIRTYSTRIP 0x200000U /* stripoffsets/stripbytecount dirty*/ #define TIFF_PERSAMPLE 0x400000U /* get/set per sample tags as arrays */ #define TIFF_BUFFERMMAP 0x800000U /* read buffer (tif_rawdata) points into mmap() memory */ + #define TIFF_DEFERSTRILELOAD 0x1000000U /* defer strip/tile offset/bytecount array loading. */ + #define TIFF_LAZYSTRILELOAD 0x2000000U /* lazy/ondemand loading of strip/tile offset/bytecount values. Only used if TIFF_DEFERSTRILELOAD is set and in read-only mode */ uint64 tif_diroff; /* file offset of current directory */ uint64 tif_nextdiroff; /* file offset of following directory */ uint64* tif_dirlist; /* list of offsets to already seen directories to prevent IFD looping */ diff --git a/man/TIFFOpen.3tiff b/man/TIFFOpen.3tiff index 5a6e96b8..c6ed28e6 100644 --- a/man/TIFFOpen.3tiff +++ b/man/TIFFOpen.3tiff @@ -207,6 +207,21 @@ Disable the use of strip chopping when reading images. Read TIFF header only, do not load the first image directory. That could be useful in case of the broken first directory. We can open the file and proceed to the other directories. +.TP +.B 4 +ClassicTIFF for creating a file (default) +.TP +.B 8 +BigTIFF for creating a file. +.TP +.B D +Enable use of deferred strip/tile offset/bytecount array loading. They will +be loaded the first time they are accessed to. This loading will be done in +its entirety unless the O flag is also specified. +.TP +.B O +On-demand loading of values of the strip/tile offset/bytecount arrays, limited +to the requested strip/tile, instead of whole array loading (implies D) .SH "BYTE ORDER" The .SM TIFF diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bfcc3b0a..808f6228 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -188,6 +188,9 @@ endif() add_executable(custom_dir custom_dir.c) target_link_libraries(custom_dir tiff port) +add_executable(defer_strile_loading defer_strile_loading.c) +target_link_libraries(defer_strile_loading tiff port) + set(TEST_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output") file(MAKE_DIRECTORY "${TEST_OUTPUT}") diff --git a/test/Makefile.am b/test/Makefile.am index cc3fb683..891a77b3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -63,7 +63,7 @@ endif # Executable programs which need to be built in order to support tests check_PROGRAMS = \ - ascii_tag long_tag short_tag strip_rw rewrite custom_dir \ + ascii_tag long_tag short_tag strip_rw rewrite custom_dir defer_strile_loading \ $(JPEG_DEPENDENT_CHECK_PROG) # Test scripts to execute @@ -202,6 +202,8 @@ raw_decode_SOURCES = raw_decode.c raw_decode_LDADD = $(LIBTIFF) custom_dir_SOURCES = custom_dir.c custom_dir_LDADD = $(LIBTIFF) +defer_strile_loading_SOURCES = defer_strile_loading.c +defer_strile_loading_LDADD = $(LIBTIFF) AM_CPPFLAGS = -I$(top_srcdir)/libtiff diff --git a/test/defer_strile_loading.c b/test/defer_strile_loading.c new file mode 100644 index 00000000..54595b25 --- /dev/null +++ b/test/defer_strile_loading.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2019, Even Rouault + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Module to test 'D' and 'O' open flags + */ + +#include "tif_config.h" + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "tiffio.h" +#include "tifftest.h" + +int test(int classictif, int height, int tiled) +{ + const char* filename = "defer_strile_loading.tif"; + TIFF* tif; + int i; + int ret; + + tif = TIFFOpen(filename, classictif ? "wDO" : "w8DO"); /* O should be ignored in write mode */ + if(!tif) + { + fprintf(stderr, "cannot create %s\n", filename); + return 1; + } + ret = TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + assert(ret); + ret = TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, 1); + assert(ret); + ret = TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + assert(ret); + ret = TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + assert(ret); + ret = TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + assert(ret); + ret = TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + assert(ret); + if( tiled ) + { + int j; + ret = TIFFSetField(tif, TIFFTAG_TILEWIDTH, 16); + assert( ret ); + ret = TIFFSetField(tif, TIFFTAG_TILELENGTH, 16); + assert( ret ); + for( j = 0; j < (height+15) / 16; j++ ) + { + unsigned char tilebuffer[256]; + memset(tilebuffer, (unsigned char)j, 256); + ret = TIFFWriteEncodedTile( tif, j, tilebuffer, 256 ); + assert(ret == 256); + } + } + else + { + ret = TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1); + assert(ret); + for( i = 0; i < height; i++ ) + { + unsigned char c = (unsigned char)i; + ret = TIFFWriteEncodedStrip( tif, i, &c, 1 ); + assert(ret == 1); + + if( i == 1 && height > 100000 ) + i = height - 2; + } + } + TIFFClose(tif); + + for( i = 0; i < 2; i++ ) + { + tif = TIFFOpen(filename, i == 0 ? "rD" : "rO"); + if(!tif) + { + fprintf(stderr, "cannot open %s\n", filename); + return 1; + } + if( tiled ) + { + int j; + for( j = 0; j < (height+15) / 16; j++ ) + { + int retry; + for( retry = 0; retry < 2; retry++ ) + { + unsigned char tilebuffer[256]; + unsigned char expected_c = (unsigned char)j; + memset(tilebuffer,0, 256); + ret = TIFFReadEncodedTile( tif, j, tilebuffer, 256 ); + assert(ret == 256); + if( tilebuffer[0] != expected_c || + tilebuffer[255] != expected_c ) + { + fprintf(stderr, "unexpected value at tile %d: %d %d\n", + j, tilebuffer[0], tilebuffer[255]); + TIFFClose(tif); + return 1; + } + } + + { + int err = 0; + ret = TIFFGetStrileOffsetWithErr(tif, j, &err); + assert(ret != 0); + assert(err == 0); + + ret = TIFFGetStrileByteCountWithErr(tif, j, &err); + assert(ret == 256); + assert(err == 0); + } + } + } + else + { + int j; + for( j = 0; j < height; j++ ) + { + int retry; + for( retry = 0; retry < 2; retry++ ) + { + unsigned char c = 0; + unsigned char expected_c = (unsigned char)j; + ret = TIFFReadEncodedStrip( tif, j, &c, 1 ); + assert(ret == 1); + if( c != expected_c ) + { + fprintf(stderr, "unexpected value at line %d: %d\n", + j, c); + TIFFClose(tif); + return 1; + } + } + + { + int err = 0; + ret = TIFFGetStrileOffsetWithErr(tif, j, &err); + assert(ret != 0); + assert(err == 0); + + ret = TIFFGetStrileByteCountWithErr(tif, j, &err); + assert(ret == 1); + assert(err == 0); + } + + if( j == 1 && height > 100000 ) + j = height - 2; + } + + if( height > 100000 ) + { + /* Missing strip */ + int err = 0; + ret = TIFFGetStrileOffsetWithErr(tif, 2, &err); + assert(ret == 0); + assert(err == 0); + + ret = TIFFGetStrileByteCountWithErr(tif, 2, &err); + assert(ret == 0); + assert(err == 0); + + } + } + + { + int err = 0; + ret = TIFFGetStrileOffsetWithErr(tif, 0xFFFFFFFFU, &err); + assert(ret == 0); + assert(err == 1); + + ret = TIFFGetStrileByteCountWithErr(tif, 0xFFFFFFFFU, &err); + assert(ret == 0); + assert(err == 1); + } + + { + toff_t* offsets = NULL; + toff_t* bytecounts = NULL; + ret = TIFFGetField( tif, + tiled ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS, &offsets ); + assert(ret); + assert(offsets); + ret = TIFFGetField( tif, + tiled ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS, &bytecounts ); + assert(ret); + assert(bytecounts); + if( tiled ) + { + assert(bytecounts[0] == 256); + } + else + { + assert(bytecounts[0] == 1); + if( height > 1 && height <= 100000) + { + assert(offsets[1] == offsets[0] + 1); + assert(offsets[height - 1] == offsets[0] + height - 1); + } + assert(bytecounts[height - 1] == 1); + } + } + + TIFFClose(tif); + } + + unlink(filename); + return 0; +} + +int +main() +{ + int is_classic; + for( is_classic = 1; is_classic >= 0; is_classic-- ) + { + int tiled; + for( tiled = 0; tiled <= 1; tiled ++ ) + { + if( test(is_classic, 1, tiled) ) + return 1; + if( test(is_classic, 8192, tiled) ) + return 1; + } + if( test(is_classic, 2000000, 0) ) + return 1; + } + return 0; +}