/* * Copyright (c) 1988-1997 Sam Leffler * Copyright (c) 1991-1997 Silicon Graphics, Inc. * * 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. * * Scanline-oriented Write Support */ #include "tiffiop.h" #include #define STRIPINCR 20 /* expansion factor on strip array */ #define WRITECHECKSTRIPS(tif, module) \ (((tif)->tif_flags & TIFF_BEENWRITING) || TIFFWriteCheck((tif), 0, module)) #define WRITECHECKTILES(tif, module) \ (((tif)->tif_flags & TIFF_BEENWRITING) || TIFFWriteCheck((tif), 1, module)) #define BUFFERCHECK(tif) \ ((((tif)->tif_flags & TIFF_BUFFERSETUP) && tif->tif_rawdata) || \ TIFFWriteBufferSetup((tif), NULL, (tmsize_t)-1)) static int TIFFGrowStrips(TIFF *tif, uint32_t delta, const char *module); static int TIFFAppendToStrip(TIFF *tif, uint32_t strip, uint8_t *data, tmsize_t cc); int TIFFWriteScanline(TIFF *tif, void *buf, uint32_t row, uint16_t sample) { static const char module[] = "TIFFWriteScanline"; register TIFFDirectory *td; int status, imagegrew = 0; uint32_t strip; if (!WRITECHECKSTRIPS(tif, module)) return (-1); /* * Handle delayed allocation of data buffer. This * permits it to be sized more intelligently (using * directory information). */ if (!BUFFERCHECK(tif)) return (-1); tif->tif_flags |= TIFF_BUF4WRITE; /* not strictly sure this is right*/ td = &tif->tif_dir; /* * Extend image length if needed * (but only for PlanarConfig=1). */ if (row >= td->td_imagelength) { /* extend image */ if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { TIFFErrorExtR( tif, module, "Can not change \"ImageLength\" when using separate planes"); return (-1); } td->td_imagelength = row + 1; imagegrew = 1; } /* * Calculate strip and check for crossings. */ if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { if (sample >= td->td_samplesperpixel) { TIFFErrorExtR(tif, module, "%lu: Sample out of range, max %lu", (unsigned long)sample, (unsigned long)td->td_samplesperpixel); return (-1); } strip = sample * td->td_stripsperimage + row / td->td_rowsperstrip; } else strip = row / td->td_rowsperstrip; /* * Check strip array to make sure there's space. We don't support * dynamically growing files that have data organized in separate * bitplanes because it's too painful. In that case we require that * the imagelength be set properly before the first write (so that the * strips array will be fully allocated above). */ if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module)) return (-1); if (strip != tif->tif_curstrip) { /* * Changing strips -- flush any data present. */ if (!TIFFFlushData(tif)) return (-1); tif->tif_curstrip = strip; /* * Watch out for a growing image. The value of strips/image * will initially be 1 (since it can't be deduced until the * imagelength is known). */ if (strip >= td->td_stripsperimage && imagegrew) td->td_stripsperimage = TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip); if (td->td_stripsperimage == 0) { TIFFErrorExtR(tif, module, "Zero strips per image"); return (-1); } tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { if (!(*tif->tif_setupencode)(tif)) return (-1); tif->tif_flags |= TIFF_CODERSETUP; } tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; /* this informs TIFFAppendToStrip() we have changed strip */ tif->tif_curoff = 0; if (!(*tif->tif_preencode)(tif, sample)) return (-1); tif->tif_flags |= TIFF_POSTENCODE; } /* * Ensure the write is either sequential or at the * beginning of a strip (or that we can randomly * access the data -- i.e. no encoding). */ if (row != tif->tif_row) { if (row < tif->tif_row) { /* * Moving backwards within the same strip: * backup to the start and then decode * forward (below). */ tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; tif->tif_rawcp = tif->tif_rawdata; } /* * Seek forward to the desired row. */ if (!(*tif->tif_seek)(tif, row - tif->tif_row)) return (-1); tif->tif_row = row; } /* swab if needed - note that source buffer will be altered */ tif->tif_postdecode(tif, (uint8_t *)buf, tif->tif_scanlinesize); status = (*tif->tif_encoderow)(tif, (uint8_t *)buf, tif->tif_scanlinesize, sample); /* we are now poised at the beginning of the next row */ tif->tif_row = row + 1; return (status); } /* Make sure that at the first attempt of rewriting a tile/strip, we will have */ /* more bytes available in the output buffer than the previous byte count, */ /* so that TIFFAppendToStrip() will detect the overflow when it is called the * first */ /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */ static int _TIFFReserveLargeEnoughWriteBuffer(TIFF *tif, uint32_t strip_or_tile) { TIFFDirectory *td = &tif->tif_dir; 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_t safe_buffer_size = (uint64_t)(td->td_stripbytecount_p[strip_or_tile] + 1 + 4); if (tif->tif_rawdatasize <= (tmsize_t)safe_buffer_size) { if (!(TIFFWriteBufferSetup( tif, NULL, (tmsize_t)TIFFroundup_64(safe_buffer_size, 1024)))) return 0; } } return 1; } /* * Encode the supplied data and write it to the * specified strip. * * NB: Image length must be setup before writing. */ tmsize_t TIFFWriteEncodedStrip(TIFF *tif, uint32_t strip, void *data, tmsize_t cc) { static const char module[] = "TIFFWriteEncodedStrip"; TIFFDirectory *td = &tif->tif_dir; uint16_t sample; if (!WRITECHECKSTRIPS(tif, module)) return ((tmsize_t)-1); /* * Check strip array to make sure there's space. * We don't support dynamically growing files that * have data organized in separate bitplanes because * it's too painful. In that case we require that * the imagelength be set properly before the first * write (so that the strips array will be fully * allocated above). */ if (strip >= td->td_nstrips) { if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { TIFFErrorExtR( tif, module, "Can not grow image by strips when using separate planes"); return ((tmsize_t)-1); } if (!TIFFGrowStrips(tif, 1, module)) return ((tmsize_t)-1); td->td_stripsperimage = TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip); } /* * Handle delayed allocation of data buffer. This * permits it to be sized according to the directory * info. */ if (!BUFFERCHECK(tif)) return ((tmsize_t)-1); tif->tif_flags |= TIFF_BUF4WRITE; tif->tif_curstrip = strip; /* this informs TIFFAppendToStrip() we have changed or reset strip */ tif->tif_curoff = 0; if (!_TIFFReserveLargeEnoughWriteBuffer(tif, strip)) { return ((tmsize_t)(-1)); } tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; if (td->td_stripsperimage == 0) { TIFFErrorExtR(tif, module, "Zero strips per image"); return ((tmsize_t)-1); } tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { if (!(*tif->tif_setupencode)(tif)) return ((tmsize_t)-1); tif->tif_flags |= TIFF_CODERSETUP; } tif->tif_flags &= ~TIFF_POSTENCODE; /* shortcut to avoid an extra memcpy() */ if (td->td_compression == COMPRESSION_NONE) { /* swab if needed - note that source buffer will be altered */ tif->tif_postdecode(tif, (uint8_t *)data, cc); if (!isFillOrder(tif, td->td_fillorder) && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((uint8_t *)data, cc); if (cc > 0 && !TIFFAppendToStrip(tif, strip, (uint8_t *)data, cc)) return ((tmsize_t)-1); return (cc); } sample = (uint16_t)(strip / td->td_stripsperimage); if (!(*tif->tif_preencode)(tif, sample)) return ((tmsize_t)-1); /* swab if needed - note that source buffer will be altered */ tif->tif_postdecode(tif, (uint8_t *)data, cc); if (!(*tif->tif_encodestrip)(tif, (uint8_t *)data, cc, sample)) return ((tmsize_t)-1); if (!(*tif->tif_postencode)(tif)) return ((tmsize_t)-1); if (!isFillOrder(tif, td->td_fillorder) && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc); if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc)) return ((tmsize_t)-1); tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; return (cc); } /* * Write the supplied data to the specified strip. * * NB: Image length must be setup before writing. */ tmsize_t TIFFWriteRawStrip(TIFF *tif, uint32_t strip, void *data, tmsize_t cc) { static const char module[] = "TIFFWriteRawStrip"; TIFFDirectory *td = &tif->tif_dir; if (!WRITECHECKSTRIPS(tif, module)) return ((tmsize_t)-1); /* * Check strip array to make sure there's space. * We don't support dynamically growing files that * have data organized in separate bitplanes because * it's too painful. In that case we require that * the imagelength be set properly before the first * write (so that the strips array will be fully * allocated above). */ if (strip >= td->td_nstrips) { if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { TIFFErrorExtR( tif, module, "Can not grow image by strips when using separate planes"); return ((tmsize_t)-1); } /* * Watch out for a growing image. The value of * strips/image will initially be 1 (since it * can't be deduced until the imagelength is known). */ if (strip >= td->td_stripsperimage) td->td_stripsperimage = TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip); if (!TIFFGrowStrips(tif, 1, module)) return ((tmsize_t)-1); } if (tif->tif_curstrip != strip) { tif->tif_curstrip = strip; /* this informs TIFFAppendToStrip() we have changed or reset strip */ tif->tif_curoff = 0; } if (td->td_stripsperimage == 0) { TIFFErrorExtR(tif, module, "Zero strips per image"); return ((tmsize_t)-1); } tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; return (TIFFAppendToStrip(tif, strip, (uint8_t *)data, cc) ? cc : (tmsize_t)-1); } /* * Write and compress a tile of data. The * tile is selected by the (x,y,z,s) coordinates. */ tmsize_t TIFFWriteTile(TIFF *tif, void *buf, uint32_t x, uint32_t y, uint32_t z, uint16_t s) { if (!TIFFCheckTile(tif, x, y, z, s)) return ((tmsize_t)(-1)); /* * NB: A tile size of -1 is used instead of tif_tilesize knowing * that TIFFWriteEncodedTile will clamp this to the tile size. * This is done because the tile size may not be defined until * after the output buffer is setup in TIFFWriteBufferSetup. */ return (TIFFWriteEncodedTile(tif, TIFFComputeTile(tif, x, y, z, s), buf, (tmsize_t)(-1))); } /* * Encode the supplied data and write it to the * specified tile. There must be space for the * data. The function clamps individual writes * to a tile to the tile size, but does not (and * can not) check that multiple writes to the same * tile do not write more than tile size data. * * NB: Image length must be setup before writing; this * interface does not support automatically growing * the image on each write (as TIFFWriteScanline does). */ tmsize_t TIFFWriteEncodedTile(TIFF *tif, uint32_t tile, void *data, tmsize_t cc) { static const char module[] = "TIFFWriteEncodedTile"; TIFFDirectory *td; uint16_t sample; uint32_t howmany32; if (!WRITECHECKTILES(tif, module)) return ((tmsize_t)(-1)); td = &tif->tif_dir; if (tile >= td->td_nstrips) { TIFFErrorExtR(tif, module, "Tile %lu out of range, max %lu", (unsigned long)tile, (unsigned long)td->td_nstrips); return ((tmsize_t)(-1)); } /* * Handle delayed allocation of data buffer. This * permits it to be sized more intelligently (using * directory information). */ if (!BUFFERCHECK(tif)) return ((tmsize_t)(-1)); tif->tif_flags |= TIFF_BUF4WRITE; tif->tif_curtile = tile; /* this informs TIFFAppendToStrip() we have changed or reset tile */ tif->tif_curoff = 0; if (!_TIFFReserveLargeEnoughWriteBuffer(tif, tile)) { return ((tmsize_t)(-1)); } tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; /* * Compute tiles per row & per column to compute * current row and column */ howmany32 = TIFFhowmany_32(td->td_imagelength, td->td_tilelength); if (howmany32 == 0) { TIFFErrorExtR(tif, module, "Zero tiles"); return ((tmsize_t)(-1)); } tif->tif_row = (tile % howmany32) * td->td_tilelength; howmany32 = TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth); if (howmany32 == 0) { TIFFErrorExtR(tif, module, "Zero tiles"); return ((tmsize_t)(-1)); } tif->tif_col = (tile % howmany32) * td->td_tilewidth; if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { if (!(*tif->tif_setupencode)(tif)) return ((tmsize_t)(-1)); tif->tif_flags |= TIFF_CODERSETUP; } tif->tif_flags &= ~TIFF_POSTENCODE; /* * Clamp write amount to the tile size. This is mostly * done so that callers can pass in some large number * (e.g. -1) and have the tile size used instead. */ if (cc < 1 || cc > tif->tif_tilesize) cc = tif->tif_tilesize; /* shortcut to avoid an extra memcpy() */ if (td->td_compression == COMPRESSION_NONE) { /* swab if needed - note that source buffer will be altered */ tif->tif_postdecode(tif, (uint8_t *)data, cc); if (!isFillOrder(tif, td->td_fillorder) && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((uint8_t *)data, cc); if (cc > 0 && !TIFFAppendToStrip(tif, tile, (uint8_t *)data, cc)) return ((tmsize_t)-1); return (cc); } sample = (uint16_t)(tile / td->td_stripsperimage); if (!(*tif->tif_preencode)(tif, sample)) return ((tmsize_t)(-1)); /* swab if needed - note that source buffer will be altered */ tif->tif_postdecode(tif, (uint8_t *)data, cc); if (!(*tif->tif_encodetile)(tif, (uint8_t *)data, cc, sample)) return ((tmsize_t)-1); if (!(*tif->tif_postencode)(tif)) return ((tmsize_t)(-1)); if (!isFillOrder(tif, td->td_fillorder) && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((uint8_t *)tif->tif_rawdata, tif->tif_rawcc); if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile, tif->tif_rawdata, tif->tif_rawcc)) return ((tmsize_t)(-1)); tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; return (cc); } /* * Write the supplied data to the specified strip. * There must be space for the data; we don't check * if strips overlap! * * NB: Image length must be setup before writing; this * interface does not support automatically growing * the image on each write (as TIFFWriteScanline does). */ tmsize_t TIFFWriteRawTile(TIFF *tif, uint32_t tile, void *data, tmsize_t cc) { static const char module[] = "TIFFWriteRawTile"; if (!WRITECHECKTILES(tif, module)) return ((tmsize_t)(-1)); if (tile >= tif->tif_dir.td_nstrips) { TIFFErrorExtR(tif, module, "Tile %lu out of range, max %lu", (unsigned long)tile, (unsigned long)tif->tif_dir.td_nstrips); return ((tmsize_t)(-1)); } return (TIFFAppendToStrip(tif, tile, (uint8_t *)data, cc) ? cc : (tmsize_t)(-1)); } #define isUnspecified(tif, f) \ (TIFFFieldSet(tif, f) && (tif)->tif_dir.td_imagelength == 0) int TIFFSetupStrips(TIFF *tif) { TIFFDirectory *td = &tif->tif_dir; if (isTiled(tif)) td->td_stripsperimage = isUnspecified(tif, FIELD_TILEDIMENSIONS) ? td->td_samplesperpixel : TIFFNumberOfTiles(tif); else td->td_stripsperimage = isUnspecified(tif, FIELD_ROWSPERSTRIP) ? td->td_samplesperpixel : TIFFNumberOfStrips(tif); td->td_nstrips = td->td_stripsperimage; /* TIFFWriteDirectoryTagData has a limitation to 0x80000000U bytes */ if (td->td_nstrips >= 0x80000000U / ((tif->tif_flags & TIFF_BIGTIFF) ? 0x8U : 0x4U)) { TIFFErrorExtR(tif, "TIFFSetupStrips", "Too large Strip/Tile Offsets/ByteCounts arrays"); return 0; } if (td->td_planarconfig == PLANARCONFIG_SEPARATE) td->td_stripsperimage /= td->td_samplesperpixel; td->td_stripoffset_p = (uint64_t *)_TIFFCheckMalloc( tif, td->td_nstrips, sizeof(uint64_t), "for \"StripOffsets\" array"); td->td_stripbytecount_p = (uint64_t *)_TIFFCheckMalloc( tif, td->td_nstrips, sizeof(uint64_t), "for \"StripByteCounts\" array"); 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_p, 0, td->td_nstrips * sizeof(uint64_t)); _TIFFmemset(td->td_stripbytecount_p, 0, td->td_nstrips * sizeof(uint64_t)); TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); return (1); } #undef isUnspecified /* * Verify file is writable and that the directory * information is setup properly. In doing the latter * we also "freeze" the state of the directory so * that important information is not changed. */ int TIFFWriteCheck(TIFF *tif, int tiles, const char *module) { if (tif->tif_mode == O_RDONLY) { TIFFErrorExtR(tif, module, "File not open for writing"); return (0); } if (tiles ^ isTiled(tif)) { TIFFErrorExtR(tif, module, tiles ? "Can not write tiles to a striped image" : "Can not write scanlines to a tiled image"); return (0); } _TIFFFillStriles(tif); /* * On the first write verify all the required information * has been setup and initialize any data structures that * had to wait until directory information was set. * Note that a lot of our work is assumed to remain valid * because we disallow any of the important parameters * from changing after we start writing (i.e. once * TIFF_BEENWRITING is set, TIFFSetField will only allow * the image's length to be changed). */ if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { TIFFErrorExtR(tif, module, "Must set \"ImageWidth\" before writing data"); return (0); } if (tif->tif_dir.td_stripoffset_p == NULL && !TIFFSetupStrips(tif)) { tif->tif_dir.td_nstrips = 0; TIFFErrorExtR(tif, module, "No space for %s arrays", isTiled(tif) ? "tile" : "strip"); return (0); } if (isTiled(tif)) { tif->tif_tilesize = TIFFTileSize(tif); if (tif->tif_tilesize == 0) return (0); } else tif->tif_tilesize = (tmsize_t)(-1); tif->tif_scanlinesize = TIFFScanlineSize(tif); if (tif->tif_scanlinesize == 0) return (0); tif->tif_flags |= TIFF_BEENWRITING; if (tif->tif_dir.td_stripoffset_entry.tdir_tag != 0 && tif->tif_dir.td_stripoffset_entry.tdir_count == 0 && tif->tif_dir.td_stripoffset_entry.tdir_type == 0 && tif->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8 == 0 && tif->tif_dir.td_stripbytecount_entry.tdir_tag != 0 && tif->tif_dir.td_stripbytecount_entry.tdir_count == 0 && tif->tif_dir.td_stripbytecount_entry.tdir_type == 0 && tif->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8 == 0 && !(tif->tif_flags & TIFF_DIRTYDIRECT)) { TIFFForceStrileArrayWriting(tif); } return (1); } /* * Setup the raw data buffer used for encoding. */ int TIFFWriteBufferSetup(TIFF *tif, void *bp, tmsize_t size) { static const char module[] = "TIFFWriteBufferSetup"; if (tif->tif_rawdata) { if (tif->tif_flags & TIFF_MYBUFFER) { _TIFFfreeExt(tif, tif->tif_rawdata); tif->tif_flags &= ~TIFF_MYBUFFER; } tif->tif_rawdata = NULL; } if (size == (tmsize_t)(-1)) { size = (isTiled(tif) ? tif->tif_tilesize : TIFFStripSize(tif)); /* Adds 10% margin for cases where compression would expand a bit */ if (size < TIFF_TMSIZE_T_MAX - size / 10) size += size / 10; /* * Make raw data buffer at least 8K */ if (size < 8 * 1024) size = 8 * 1024; bp = NULL; /* NB: force malloc */ } if (bp == NULL) { bp = _TIFFmallocExt(tif, size); if (bp == NULL) { TIFFErrorExtR(tif, module, "No space for output buffer"); return (0); } tif->tif_flags |= TIFF_MYBUFFER; } else tif->tif_flags &= ~TIFF_MYBUFFER; tif->tif_rawdata = (uint8_t *)bp; tif->tif_rawdatasize = size; tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; tif->tif_flags |= TIFF_BUFFERSETUP; return (1); } /* * Grow the strip data structures by delta strips. */ static int TIFFGrowStrips(TIFF *tif, uint32_t delta, const char *module) { TIFFDirectory *td = &tif->tif_dir; uint64_t *new_stripoffset; uint64_t *new_stripbytecount; assert(td->td_planarconfig == PLANARCONFIG_CONTIG); new_stripoffset = (uint64_t *)_TIFFreallocExt( tif, td->td_stripoffset_p, (td->td_nstrips + delta) * sizeof(uint64_t)); new_stripbytecount = (uint64_t *)_TIFFreallocExt( tif, td->td_stripbytecount_p, (td->td_nstrips + delta) * sizeof(uint64_t)); if (new_stripoffset == NULL || new_stripbytecount == NULL) { if (new_stripoffset) _TIFFfreeExt(tif, new_stripoffset); if (new_stripbytecount) _TIFFfreeExt(tif, new_stripbytecount); td->td_nstrips = 0; TIFFErrorExtR(tif, module, "No space to expand strip arrays"); return (0); } td->td_stripoffset_p = new_stripoffset; td->td_stripbytecount_p = new_stripbytecount; _TIFFmemset(td->td_stripoffset_p + td->td_nstrips, 0, delta * sizeof(uint64_t)); _TIFFmemset(td->td_stripbytecount_p + td->td_nstrips, 0, delta * sizeof(uint64_t)); td->td_nstrips += delta; tif->tif_flags |= TIFF_DIRTYDIRECT; return (1); } /* * Append the data to the specified strip. */ static int TIFFAppendToStrip(TIFF *tif, uint32_t strip, uint8_t *data, tmsize_t cc) { static const char module[] = "TIFFAppendToStrip"; TIFFDirectory *td = &tif->tif_dir; uint64_t m; int64_t old_byte_count = -1; if (tif->tif_curoff == 0) tif->tif_lastvalidoff = 0; if (td->td_stripoffset_p[strip] == 0 || tif->tif_curoff == 0) { assert(td->td_nstrips > 0); if (td->td_stripbytecount_p[strip] != 0 && td->td_stripoffset_p[strip] != 0 && td->td_stripbytecount_p[strip] >= (uint64_t)cc) { /* * There is already tile data on disk, and the new tile * data we have will fit in the same space. The only * aspect of this that is risky is that there could be * more data to append to this strip before we are done * depending on how we are getting called. */ if (!SeekOK(tif, td->td_stripoffset_p[strip])) { TIFFErrorExtR(tif, module, "Seek error at scanline %lu", (unsigned long)tif->tif_row); return (0); } tif->tif_lastvalidoff = td->td_stripoffset_p[strip] + td->td_stripbytecount_p[strip]; } else { /* * Seek to end of file, and set that as our location to * write this strip. */ td->td_stripoffset_p[strip] = TIFFSeekFile(tif, 0, SEEK_END); tif->tif_flags |= TIFF_DIRTYSTRIP; } 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_p[strip]; td->td_stripbytecount_p[strip] = 0; } m = tif->tif_curoff + cc; if (!(tif->tif_flags & TIFF_BIGTIFF)) m = (uint32_t)m; if ((m < tif->tif_curoff) || (m < (uint64_t)cc)) { TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded"); return (0); } if (tif->tif_lastvalidoff != 0 && m > tif->tif_lastvalidoff && td->td_stripbytecount_p[strip] > 0) { /* Ouch: we have detected that we are rewriting in place a strip/tile */ /* with several calls to TIFFAppendToStrip(). The first call was with */ /* a size smaller than the previous size of the strip/tile, so we */ /* opted to rewrite in place, but a following call causes us to go */ /* outsize of the strip/tile area, so we have to finally go for a */ /* append-at-end-of-file strategy, and start by moving what we already */ /* wrote. */ tmsize_t tempSize; void *temp; uint64_t offsetRead; uint64_t offsetWrite; uint64_t toCopy = td->td_stripbytecount_p[strip]; if (toCopy < 1024 * 1024) tempSize = (tmsize_t)toCopy; else tempSize = 1024 * 1024; offsetRead = td->td_stripoffset_p[strip]; offsetWrite = TIFFSeekFile(tif, 0, SEEK_END); m = offsetWrite + toCopy + cc; if (!(tif->tif_flags & TIFF_BIGTIFF) && m != (uint32_t)m) { TIFFErrorExtR(tif, module, "Maximum TIFF file size exceeded"); return (0); } temp = _TIFFmallocExt(tif, tempSize); if (temp == NULL) { TIFFErrorExtR(tif, module, "No space for output buffer"); return (0); } tif->tif_flags |= TIFF_DIRTYSTRIP; td->td_stripoffset_p[strip] = offsetWrite; td->td_stripbytecount_p[strip] = 0; /* Move data written by previous calls to us at end of file */ while (toCopy > 0) { if (!SeekOK(tif, offsetRead)) { TIFFErrorExtR(tif, module, "Seek error"); _TIFFfreeExt(tif, temp); return (0); } if (!ReadOK(tif, temp, tempSize)) { TIFFErrorExtR(tif, module, "Cannot read"); _TIFFfreeExt(tif, temp); return (0); } if (!SeekOK(tif, offsetWrite)) { TIFFErrorExtR(tif, module, "Seek error"); _TIFFfreeExt(tif, temp); return (0); } if (!WriteOK(tif, temp, tempSize)) { TIFFErrorExtR(tif, module, "Cannot write"); _TIFFfreeExt(tif, temp); return (0); } offsetRead += tempSize; offsetWrite += tempSize; td->td_stripbytecount_p[strip] += tempSize; toCopy -= tempSize; } _TIFFfreeExt(tif, temp); /* Append the data of this call */ offsetWrite += cc; m = offsetWrite; } if (!WriteOK(tif, data, cc)) { TIFFErrorExtR(tif, module, "Write error at scanline %lu", (unsigned long)tif->tif_row); return (0); } tif->tif_curoff = m; td->td_stripbytecount_p[strip] += cc; if ((int64_t)td->td_stripbytecount_p[strip] != old_byte_count) tif->tif_flags |= TIFF_DIRTYSTRIP; return (1); } /* * Internal version of TIFFFlushData that can be * called by ``encodestrip routines'' w/o concern * for infinite recursion. */ int TIFFFlushData1(TIFF *tif) { if (tif->tif_rawcc > 0 && tif->tif_flags & TIFF_BUF4WRITE) { if (!isFillOrder(tif, tif->tif_dir.td_fillorder) && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((uint8_t *)tif->tif_rawdata, tif->tif_rawcc); if (!TIFFAppendToStrip( tif, isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip, tif->tif_rawdata, tif->tif_rawcc)) { /* We update those variables even in case of error since there's */ /* code that doesn't really check the return code of this */ /* function */ tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; return (0); } tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; } return (1); } /* * Set the current write offset. This should only be * used to set the offset to a known previous location * (very carefully), or to 0 so that the next write gets * appended to the end of the file. */ void TIFFSetWriteOffset(TIFF *tif, toff_t off) { tif->tif_curoff = off; tif->tif_lastvalidoff = 0; }