#pragma warn -use static char *sccsid = "@(#)TIFF/tif_write.c 1.25, Copyright (c) Sam Leffler, Dieter Linde, "__DATE__; #pragma warn .use /* * Copyright (c) 1988, 1990 by Sam Leffler, Oct 8 1990 * All rights reserved. * * This file is provided for unrestricted use provided that this legend is included on all tape media and as a part of the * software program in whole or part. Users may copy, modify or distribute this file at will. * * TIFF Library. * * Scanline-oriented Write Support */ #include #include #include "tiffio.h" #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define howmany(x, y) (((x) + ((y) - 1)) / (y)) #define STRIPINCR 20 /* expansion factor on strip array */ /**************************************************************************** * 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. */ static int TIFFWriteCheck( register TIFF *tif, char module[] ) { if (tif->tif_mode == O_RDONLY) { TIFFError(module, "%s: File not open for writing", tif->tif_name); return(0); } /* * 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 ((tif->tif_flags & TIFF_BEENWRITING) == 0) { if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { TIFFError(module, "%s: Must set \"ImageWidth\" before writing data", tif->tif_name); return(0); } if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { TIFFError(module, "%s: Must set \"PlanarConfiguration\" before writing data", tif->tif_name); return(0); } if (tif->tif_dir.td_stripoffset == NULL) { register TIFFDirectory *td = &tif->tif_dir; td->td_stripsperimage = (td->td_rowsperstrip == 0xffffffffL || td->td_imagelength == 0) ? 1 : howmany((long)td->td_imagelength, td->td_rowsperstrip); td->td_nstrips = td->td_stripsperimage; if (td->td_planarconfig == PLANARCONFIG_SEPARATE) td->td_nstrips *= (long)td->td_samplesperpixel; td->td_stripoffset = (u_long *)malloc(td->td_nstrips * sizeof(u_long)); td->td_stripbytecount = (u_long *)malloc(td->td_nstrips * sizeof(u_long)); if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) { td->td_nstrips = 0; TIFFError(module, "%s: No space for strip arrays", tif->tif_name); return(0); } /* * Place data at the end-of-file (by setting offsets to zero). */ bzero(td->td_stripoffset, td->td_nstrips * sizeof(u_long)); bzero(td->td_stripbytecount, td->td_nstrips * sizeof(u_long)); TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); } tif->tif_flags |= TIFF_BEENWRITING; } return(1); } /**************************************************************************** * Setup the raw data buffer used for encoding. */ static int TIFFBufferSetup( register TIFF *tif, char module[] ) { int scanline; tif->tif_scanlinesize = scanline = TIFFScanlineSize(tif); /* * Make raw data buffer at least 8K. */ if (scanline < 8 * 1024) scanline = 8 * 1024; if ((tif->tif_rawdata = (u_char *)malloc(scanline)) == NULL) { TIFFError(module, "%s: No space for output buffer", tif->tif_name); return(0); } tif->tif_rawdatasize = (long)scanline; tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; return(1); } /**************************************************************************** * Append the data to the specified strip. * * NB: We don't check that there's space in the file (i.e. that strips do not overlap). */ static int TIFFAppendToStrip( TIFF *tif, u_int strip, void *data, u_int cc ) { static char module[] = "TIFFAppendToStrip"; TIFFDirectory *td = &tif->tif_dir; if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) { /* * No current offset, set the current strip. */ if (td->td_stripoffset[strip] != 0) { if (!SeekOK(tif->tif_fd, td->td_stripoffset[strip])) { TIFFError(module, "%s: Seek error at scanline %d", tif->tif_name, tif->tif_row); return(0); } } else td->td_stripoffset[strip] = lseek(tif->tif_fd, 0, L_XTND); tif->tif_curoff = td->td_stripoffset[strip]; } if (!WriteOK(tif->tif_fd, data, cc)) { TIFFError(module, "%s: Write error at scanline %d", tif->tif_name, tif->tif_row); return(0); } tif->tif_curoff += (long)cc; td->td_stripbytecount[strip] += (long)cc; return(1); } /**************************************************************************** * Internal version of TIFFFlushData that can be called by ``encodestrip routines'' w/o concern * for infinite recursion. */ int TIFFFlushData1( register TIFF *tif ) { if (tif->tif_dir.td_fillorder != tif->tif_fillorder && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((u_char *)tif->tif_rawdata, tif->tif_rawcc); if (!TIFFAppendToStrip(tif, tif->tif_curstrip, tif->tif_rawdata, (u_int)tif->tif_rawcc)) return(0); tif->tif_rawcc = 0; tif->tif_rawcp = tif->tif_rawdata; return(1); } /**************************************************************************** * Flush buffered data to the file. */ int TIFFFlushData( TIFF *tif ) { if ((tif->tif_flags & TIFF_BEENWRITING) == 0) return(0); if (tif->tif_encodestrip && !(*tif->tif_encodestrip)(tif)) return(0); return(TIFFFlushData1(tif)); } /**************************************************************************** * Grow the strip data structures by delta strips. */ static int TIFFGrowStrips( TIFF *tif, long delta, char module[] ) { TIFFDirectory *td = &tif->tif_dir; assert(td->td_planarconfig == PLANARCONFIG_CONTIG); td->td_stripoffset = (u_long *)realloc(td->td_stripoffset, (td->td_nstrips + delta) * sizeof(u_long)); td->td_stripbytecount = (u_long *)realloc(td->td_stripbytecount, (td->td_nstrips + delta) * sizeof(u_long)); if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) { td->td_nstrips = 0; TIFFError(module, "%s: No space to expand strip arrays", tif->tif_name); return(0); } bzero(td->td_stripoffset + td->td_nstrips, delta * sizeof(u_long)); bzero(td->td_stripbytecount + td->td_nstrips, delta * sizeof(u_long)); td->td_nstrips += delta; return(1); } /**************************************************************************** * */ int TIFFWriteScanline( register TIFF *tif, void *buf, u_int row, u_int sample ) { static char module[] = "TIFFWriteScanline"; register TIFFDirectory *td; long strip; int status; int imagegrew = 0; if (!TIFFWriteCheck(tif, module)) return(-1); /* * Handle delayed allocation of data buffer. This permits it to be sized more intelligently (using * directory information). */ if ((tif->tif_flags & TIFF_BUFFERSETUP) == 0) { if (!TIFFBufferSetup(tif, module)) return(-1); tif->tif_flags |= TIFF_BUFFERSETUP; } 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) { TIFFError(tif->tif_name, "Can't 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) { TIFFError(tif->tif_name, "%d: Sample out of range, max %d", sample, td->td_samplesperpixel); return(-1); } strip = (long)sample * td->td_stripsperimage + (long)row / td->td_rowsperstrip; } else strip = (long)row / td->td_rowsperstrip; if (strip != tif->tif_curstrip) { /* * Changing strips -- flush any data present. */ if (tif->tif_rawcc > 0 && !TIFFFlushData(tif)) return(-1); tif->tif_curstrip = (int)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 = howmany((long)td->td_imagelength, td->td_rowsperstrip); tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; if (tif->tif_stripencode && !(*tif->tif_stripencode)(tif)) return(-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 && !TIFFGrowStrips(tif, 1, module)) return(-1); /* * 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 (tif->tif_seek) { 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; } else { TIFFError(tif->tif_name, "Compression algorithm does not support random access"); return(-1); } } status = (*tif->tif_encoderow)(tif, buf, tif->tif_scanlinesize); tif->tif_row++; return(status); } /**************************************************************************** * Encode the supplied data and write it 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). */ int TIFFWriteEncodedStrip( register TIFF *tif, u_int strip, void *data, u_int cc ) { static char module[] = "TIFFWriteEncodedStrip"; if (!TIFFWriteCheck(tif, module)) return(-1); if (strip >= tif->tif_dir.td_nstrips) { TIFFError(module, "%s: Strip %d out of range, max %d", tif->tif_name, strip, tif->tif_dir.td_nstrips); return(-1); } /* * Handle delayed allocation of data buffer. This permits it to be sized more intelligently (using * directory information). */ if ((tif->tif_flags & TIFF_BUFFERSETUP) == 0) { if (!TIFFBufferSetup(tif, module)) return(-1); tif->tif_flags |= TIFF_BUFFERSETUP; } tif->tif_curstrip = strip; if (tif->tif_encodestrip && !(*tif->tif_encodestrip)(tif)) return(0); /* * Note that this assumes that encoding routines can handle multiple scanlines. All the "standard" * ones in the library do! */ if (!(*tif->tif_encoderow)(tif, data, cc)) return(0); if (tif->tif_dir.td_fillorder != tif->tif_fillorder && (tif->tif_flags & TIFF_NOBITREV) == 0) TIFFReverseBits((u_char *)tif->tif_rawdata, tif->tif_rawcc); if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, (u_int)tif->tif_rawcc)) return(-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). */ int TIFFWriteRawStrip( TIFF *tif, u_int strip, void *data, u_int cc ) { static char module[] = "TIFFWriteRawStrip"; if (!TIFFWriteCheck(tif, module)) return(-1); if (strip >= tif->tif_dir.td_nstrips) { TIFFError(module, "%s: Strip %d out of range, max %d", tif->tif_name, strip, tif->tif_dir.td_nstrips); return(-1); } return(TIFFAppendToStrip(tif, strip, data, cc) ? -1 : cc); }