home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1988 by Sam Leffler.
- * 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.
- */
-
- #include <io.h>
- #include <alloc.h>
- #include <mem.h>
- #include <assert.h>
- #include <string.h>
- #include "tiffio.h"
-
- #define ord(e) ((unsigned) e)
- #define howmany(x, y) (((x)+((y)-1))/(y))
- #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
- #define ReadOK(fd, buf, size) (read(fd, (char *)buf, size) == size)
- #define WriteOK(fd, buf, size) (write(fd, (char *)buf, size) == size)
- #define SeekOK(fd, off) (lseek(fd, (long)off, SEEK_SET) == (long)off)
- #define STRIPINCR 20 /* expansion factor on strip array */
-
- /*
- Initialize shift & mask tables and byte
- swapping state according to the file
- byte order.
- */
-
- static
- void TIFFInitByteOrder(TIFF *tif, unsigned magic, unsigned bigendian)
- {
-
- tif->tif_typemask[0] = 0;
- tif->tif_typemask[ord(ABYTE)] = 0xff;
- tif->tif_typemask[ord(SHORT)] = 0xffff;
- tif->tif_typemask[ord(LONG)] = 0xffffffffL;
- tif->tif_typemask[ord(RATIONAL)] = 0xffffffffL;
- tif->tif_typeshift[0] = 0;
- tif->tif_typeshift[ord(LONG)] = 0;
- tif->tif_typeshift[ord(RATIONAL)] = 0;
- if (magic == TIFF_BIGENDIAN)
- {
- tif->tif_typeshift[ord(ABYTE)] = 24;
- tif->tif_typeshift[ord(SHORT)] = 16;
- if (!bigendian)
- tif->tif_flags |= TIFF_SWAB;
- }
- else
- {
- tif->tif_typeshift[ord(ABYTE)] = 0;
- tif->tif_typeshift[ord(SHORT)] = 0;
- if (bigendian)
- tif->tif_flags |= TIFF_SWAB;
- }
- }
-
- /*
- Calculate how many bytes make up a single scan line.
- */
-
- long TIFFScanlineSize(TIFF *tif)
- {
- TIFFDirectory *td = &tif->tif_dir;
- long scanline;
-
- scanline = td->td_bitspersample * td->td_imagewidth;
- if (td->td_planarconfig == PLANARCONFIG_CONTIG)
- scanline *= td->td_samplesperpixel;
- return (howmany(scanline, 8));
- }
-
- /*
- Flush raw data to the file.
- */
- CompletionCode TIFFFlushData(TIFF *tif)
- {
- if ((tif->tif_flags & TIFF_BEENWRITING) == 0)
- return (FALSE);
- if (tif->tif_encodestrip && !(*tif->tif_encodestrip)(tif))
- return (FALSE);
- return (TIFFFlushData1(tif));
- }
-
-
- /*
- Flush a file to disk. Used before closing a written
- tiff file.
- */
- CompletionCode TIFFFlush(TIFF *tif)
- {
-
- if (tif->tif_mode != O_RDONLY)
- {
- if (tif->tif_rawcc > 0 && !TIFFFlushData(tif))
- return (FALSE);
- if ((tif->tif_flags & TIFF_DIRTYDIRECT) && !TIFFWriteDirectory(tif))
- return (FALSE);
- }
- return (TRUE);
- }
-
-
- /*
- Close a TIFF file after use.
- */
- CompletionCode TIFFClose(TIFF *tif)
- {
- if (tif->tif_mode != O_RDONLY)
- /* Flush buffered data and directory (if dirty). */
- TIFFFlush(tif);
- if (tif->tif_cleanup)
- (*tif->tif_cleanup)(tif);
- TIFFFreeDirectory(tif);
- if (tif->tif_rawdata)
- free((char *) tif->tif_rawdata);
- close(tif->tif_fd);
- free((char *) tif);
- return (TRUE);
- }
-
- /*
- Open a TIFF file for read/writing.
- */
-
- TIFF *TIFFOpen(char *name, char *mode)
- {
- static char module[] = "TIFFOpen";
- TIFF *tif;
- int fd;
- unsigned m, bigendian, one;
- char string[80];
-
- switch (mode[0])
- {
- case 'r':
- m = O_RDONLY|O_BINARY;
- break;
- case 'w':
- m = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
- break;
- default:
- TIFFError(module, "\"%s\": Bad mode", mode);
- return ((TIFF *)0);
- }
-
- /* Append a .tif file extension is necessary */
-
- if (!strchr(name,'.')) /* is there an ext ? */
- {
- strcpy(string,name); /* copy filename to buffer */
- name = string; /* FileName now pts at buffer */
- strcat(name,".tif"); /* if not add .tif ext */
- }
-
- fd = open(name, m);
- if (fd < 0)
- {
- TIFFError(module, "%s: Cannot open", name);
- return ((TIFF *)0);
- }
- tif = (TIFF *) malloc(sizeof (TIFF) + strlen(name) + 1);
- if (tif == NULL)
- {
- TIFFError(module, "%s: Out of memory (TIFF structure)", name);
- close(fd);
- return ((TIFF *)0);
- }
- memset((char *)tif,0,sizeof (*tif));
- tif->tif_name = (char *)tif + sizeof (TIFF);
- strcpy(tif->tif_name, name);
- tif->tif_fd = fd;
- tif->tif_mode = m &~ (O_CREAT|O_TRUNC|O_BINARY);
- tif->tif_curoff = 0;
- tif->tif_curstrip = -1; /* invalid strip */
- tif->tif_row = -1; /* read/write pre-increment */
-
- /*
- Determine byte order of machine running this code
- */
- one = 1;
- bigendian = (*(char *)&one == 0);
-
- /* Read in TIFF header */
- if (!ReadOK(fd, &tif->tif_header, sizeof (TIFFHeader)))
- {
- if (tif->tif_mode == O_RDONLY)
- {
- TIFFError(name, "Cannot read TIFF header");
- goto bad;
- }
- /* Setup header and write */
- tif->tif_header.tiff_magic = bigendian ?
- TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
- tif->tif_header.tiff_version = TIFF_VERSION;
- tif->tif_header.tiff_diroff = 0; /* filled in later */
- if (!WriteOK(fd, &tif->tif_header, sizeof (TIFFHeader)))
- {
- TIFFError(name, "Error writing TIFF header");
- goto bad;
- }
- /* Setup the byte order handling */
- TIFFInitByteOrder(tif, tif->tif_header.tiff_magic, bigendian);
- /* Setup default directory */
- if (!TIFFDefaultDirectory(tif))
- goto bad;
- tif->tif_diroff = 0;
- return (tif);
- }
- /* Setup the byte order handling */
- if (tif->tif_header.tiff_magic != TIFF_BIGENDIAN &&
- tif->tif_header.tiff_magic != TIFF_LITTLEENDIAN)
- {
- TIFFError(name, "Not a TIFF file, bad magic number %d (0x%x)",
- tif->tif_header.tiff_magic,
- tif->tif_header.tiff_magic);
- goto bad;
- }
- TIFFInitByteOrder(tif, tif->tif_header.tiff_magic, bigendian);
- /* Swap header if required */
- if (tif->tif_flags & TIFF_SWAB)
- {
- TIFFSwabShort(&tif->tif_header.tiff_version);
- TIFFSwabLong(&tif->tif_header.tiff_diroff);
- }
- /*
- Now check version (if needed, it's been byte-swapped). Note
- that this isn't actually a version number, it's a magic
- number that doesn't change.
- */
- if (tif->tif_header.tiff_version != TIFF_VERSION)
- {
- TIFFError(name,"Not a TIFF file, bad version number %d",
- tif->tif_header.tiff_version);
- goto bad;
- }
- /* Setup initial directory */
- switch (mode[0])
- {
- case 'r':
- tif->tif_nextdiroff = tif->tif_header.tiff_diroff;
- if (TIFFReadDirectory(tif))
- {
- tif->tif_scanlinesize = TIFFScanlineSize(tif);
- tif->tif_rawcc = -1;
- tif->tif_flags |= TIFF_BUFFERSETUP;
- return (tif);
- }
- break;
- }
- bad:
- tif->tif_mode = O_RDONLY; /* avoid flush */
- TIFFClose(tif);
- return ((TIFF *)0);
- }
-
-
-
- /*
- The following functions are used to read tiff files.
- */
-
- /*
- Set state to appear as if a strip has just been read in.
- */
- static
- CompletionCode TIFFStartStrip(TIFF *tif, long strip)
- {
- TIFFDirectory *td = &tif->tif_dir;
-
- tif->tif_curstrip = strip;
- tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip;
- tif->tif_rawcp = tif->tif_rawdata;
- tif->tif_rawcc = td->td_stripbytecount[(unsigned)strip];
- return (tif->tif_stripdecode == NULL || (*tif->tif_stripdecode)(tif));
- }
-
-
- /*
- Read a strip of data from the file.
- */
- static
- CompletionCode TIFFReadStrip(TIFF *tif, long strip)
- {
- static char module[] = "TIFFReadStrip";
- TIFFDirectory *td = &tif->tif_dir;
- long bytecount;
-
- if (!SeekOK(tif->tif_fd, td->td_stripoffset[(unsigned) strip]))
- {
- TIFFError(module, "%s: Seek error at scanline %d, strip %d",
- tif->tif_name, tif->tif_row, strip);
- return (FALSE);
- }
- /*
- 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?).
- */
- bytecount = td->td_stripbytecount[(unsigned) strip];
-
- if (bytecount > tif->tif_rawdatasize)
- {
- tif->tif_curstrip = -1; /* unknown state */
- if (tif->tif_rawdata)
- {
- free((char *) tif->tif_rawdata);
- tif->tif_rawdata = NULL;
- }
-
- tif->tif_rawdatasize = bytecount; /* bug CAL */
-
- tif->tif_rawdata = malloc((unsigned)tif->tif_rawdatasize);
- if (tif->tif_rawdata == NULL)
- {
- TIFFError(module,"%s: No space for data buffer at scanline %d",
- tif->tif_name, tif->tif_row);
- tif->tif_rawdatasize = 0;
- return (FALSE);
- }
- }
- if (!ReadOK(tif->tif_fd, tif->tif_rawdata, (unsigned) bytecount))
- {
- TIFFError(module, "%s: Read error at scanline %d",
- tif->tif_name, tif->tif_row);
- return (FALSE);
- }
- return (TIFFStartStrip(tif, strip));
- }
-
- /*
- Seek to a random row+sample in a file.
- */
-
- static
- CompletionCode TIFFSeek(TIFF *tif, long row, unsigned sample)
- {
- register TIFFDirectory *td = &tif->tif_dir;
- unsigned long strip;
-
- if (row >= td->td_imagelength)
- {
- /* out of range */
- TIFFError(tif->tif_name, "%d: Row out of range, max %d",
- row, td->td_imagelength);
- return (FALSE);
- }
- 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 (FALSE);
- }
- strip = (sample*td->td_stripsperimage+row/td->td_rowsperstrip);
- }
- else
- strip = row / td->td_rowsperstrip;
-
- if (strip != tif->tif_curstrip)
- {
- /* different strip, refill */
- if (!TIFFReadStrip(tif, strip))
- return (FALSE);
- }
- else
- {
- if (row < tif->tif_row)
- {
- /*
- Moving backwards within the same strip: backup
- to the start and then decode forward (below). If
- you're planning on lots of random access within a
- strip, it's better to just read and decode the entire
- strip, and then access the decoded data in a random fashion.
- */
- if (!TIFFStartStrip(tif, strip))
- return (FALSE);
- }
- if (row != tif->tif_row)
- {
- if (tif->tif_seek)
- {
- /* Seek forward to the desired row */
- if (!(*tif->tif_seek)(tif, row - tif->tif_row))
- return (FALSE);
- tif->tif_row = row;
- }
- else
- {
- TIFFError(tif->tif_name,
- "Compression algorithm does not support random access");
- return (FALSE);
- }
- }
- }
- return (TRUE);
- }
-
- /*
- Read a tiff scan line.
- */
- CompletionCode TIFFReadScanline(TIFF *tif, char *buf,
- long row, unsigned sample)
- {
- CompletionCode e;
-
- if (tif->tif_mode == O_WRONLY)
- {
- TIFFError(tif->tif_name, "File not open for reading");
- return (FALSE);
- }
- if ((e = TIFFSeek(tif, row, sample)) == TRUE)
- {
- /* Decompress desired row into user buffer */
- e = (*tif->tif_decoderow)(tif, buf, tif->tif_scanlinesize);
- tif->tif_row++;
- }
- return (e);
- }
-
- /*
- The following functions are used to write tiff files.
- */
-
- static char module[] = "TIFFWriteScanline";
-
- static
- CompletionCode TIFFGrowStrips(TIFF *tif, short delta)
- {
- TIFFDirectory *td = &tif->tif_dir;
-
- assert(td->td_planarconfig == PLANARCONFIG_CONTIG);
- td->td_stripoffset = (unsigned long *)realloc(td->td_stripoffset,
- (unsigned)((td->td_nstrips + delta) * sizeof (unsigned long)));
- td->td_stripbytecount = (unsigned long *)realloc(td->td_stripbytecount,
- (unsigned)((td->td_nstrips + delta) * sizeof (unsigned 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 (FALSE);
- }
- memset(td->td_stripoffset + (unsigned) td->td_nstrips,0,
- (unsigned)(delta*sizeof (unsigned long)));
- memset(td->td_stripbytecount + (unsigned) td->td_nstrips,0,
- (unsigned)(delta*sizeof (unsigned long)));
- td->td_nstrips += delta;
- return (TRUE);
- }
-
-
- CompletionCode TIFFWriteScanline(TIFF *tif, char *buf,
- long row, unsigned sample)
- {
- register TIFFDirectory *td;
- CompletionCode status;
- long strip;
-
- if (tif->tif_mode == O_RDONLY)
- {
- TIFFError(module, "%s: File not open for writing",tif->tif_name);
- return (-1);
- }
- td = &tif->tif_dir;
- /*
- 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 (!TIFFWriteSetup(tif))
- return (-1);
- tif->tif_flags |= TIFF_BEENWRITING;
- }
- /*
- 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))
- return (-1);
- tif->tif_flags |= TIFF_BUFFERSETUP;
- }
- /*
- 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 not change \"ImageLength\" when using separate planes");
- return (-1);
- }
- td->td_imagelength = row;
- }
- /*
- 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 + 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 = strip;
- 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, STRIPINCR))
- 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);
- }
-
- /*
- Verify and setup state on first write.
- */
- static
- CompletionCode TIFFWriteSetup(TIFF *tif)
- {
-
- if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS))
- {
- TIFFError(module,"%s: Must set \"ImageWidth\" before writing data",
- tif->tif_name);
- return (FALSE);
- }
- if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG))
- {
- TIFFError(module,
- "%s: Must set \"PlanarConfiguration\" before writing data",
- tif->tif_name);
- return (FALSE);
- }
- 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(td->td_imagelength, td->td_rowsperstrip);
- td->td_nstrips = td->td_stripsperimage;
- if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
- td->td_nstrips *= td->td_samplesperpixel;
- td->td_stripoffset = (unsigned long *)
- malloc((unsigned)(td->td_nstrips * sizeof (unsigned long)));
- td->td_stripbytecount = (unsigned long *)
- malloc((unsigned)(td->td_nstrips * sizeof (unsigned 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 (FALSE);
- }
- /*
- Place data at the end-of-file (by setting offsets to zero). */
- memset((char *)td->td_stripoffset,0,(unsigned)(
- td->td_nstrips * sizeof (unsigned long)));
- memset((char *)td->td_stripbytecount,0,(unsigned)(
- td->td_nstrips * sizeof (unsigned long)));
- TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS);
- TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS);
- }
- return (TRUE);
- }
-
- static
- CompletionCode TIFFBufferSetup(TIFF *tif)
- {
- long scanline;
-
- tif->tif_scanlinesize = scanline = TIFFScanlineSize(tif);
- /* Make raw data buffer at least 8K */
- if (scanline < 8*1024)
- scanline = 8*1024;
- tif->tif_rawdata = malloc((unsigned) scanline);
- if (tif->tif_rawdata == NULL)
- {
- TIFFError(module, "%s: No space for output buffer",tif->tif_name);
- return (FALSE);
- }
- tif->tif_rawdatasize = scanline;
- tif->tif_rawcc = 0;
- tif->tif_rawcp = tif->tif_rawdata;
- return (TRUE);
- }
-
- /*
- Internal version of TIFFFlushData that can be
- called by ``encodestrip routines'' w/o concern
- for infinite recursion.
- */
- CompletionCode TIFFFlushData1(TIFF *tif)
- {
- TIFFDirectory *td = &tif->tif_dir;
- long strip = tif->tif_curstrip;
-
- if (td->td_stripoffset[(unsigned) strip] == 0 || tif->tif_curoff == 0)
- {
- /* No current offset, set the current strip. */
- if (td->td_stripoffset[(unsigned) strip] != 0)
- {
- if (!SeekOK(tif->tif_fd, td->td_stripoffset[(unsigned) strip]))
- {
- TIFFError(module,"%s: Seek error at scanline %d",
- tif->tif_name, tif->tif_row);
- return (FALSE);
- }
- }
- else
- td->td_stripoffset[(unsigned) strip] = lseek(tif->tif_fd, 0L, SEEK_END);
- tif->tif_curoff = td->td_stripoffset[(unsigned) strip];
- }
- if (!WriteOK(tif->tif_fd, tif->tif_rawdata, (int) tif->tif_rawcc))
- {
- TIFFError(module, "%s: Write error at scanline %d",
- tif->tif_name, tif->tif_row);
- return (FALSE);
- }
- tif->tif_curoff += tif->tif_rawcc;
- td->td_stripbytecount[(unsigned) strip] += tif->tif_rawcc;
- tif->tif_rawcc = 0;
- tif->tif_rawcp = tif->tif_rawdata;
- return (TRUE);
- }