123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305 |
- /*
- * CDE - Common Desktop Environment
- *
- * Copyright (c) 1993-2012, The Open Group. All rights reserved.
- *
- * These libraries and programs are free software; you can
- * redistribute them and/or modify them under the terms of the GNU
- * Lesser General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * These libraries and programs are distributed in the hope that
- * they will be useful, but WITHOUT ANY WARRANTY; without even the
- * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with these libraries and programs; if not, write
- * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
- * Floor, Boston, MA 02110-1301 USA
- */
- /* $XConsortium: iltiffwrite.c /main/4 1996/01/08 12:17:15 lehors $ */
- /* /ilc/iltiffwrite.c : Code for ilWriteFileImage(); add a consumer to the pipe
- to write to a TIFF file. See also /ilc/iltiff.c and /ilc/iltiffread.c .
- */
- #include "iltiffint.h"
- #include "ilpipelem.h"
- #include "iljpgencode.h"
- #include "ilerrors.h"
- /* Tag info summary, private to ilWriteFileImage(). Encapsulates the user and std
- tag info. Contains ptrs to data which is created when the pipe element is added
- and freed when pipe emptied (Destroy() function).
- "pTagOffsets" points to an array of offsets, in the same order as the tags at
- "pTagWriteData" + 2 (bytes). For each tag, if the offset is zero (0), the tag
- data is in the tag, already byte-flipped. If non-zero, it is the offset of the
- tag data from pTagWriteData. The file offset where the data at pTagWriteData
- will be written must be added to this offset and inserted into the tag byte-flipped.
- See ilMergeWriteTags() for details.
- */
- typedef struct {
- ilPtr pAlloc; /* ptr to whole mess to deallocate */
- CARD32 *pTagOffsets; /* see above */
- ilPtr pTagWriteData; /* ptr to data to write to file */
- long tagWriteSize; /* size in bytes of data to write */
- ilFileOffset *pStripOffsets; /* ptr to strip offset list in memory */
- INT32 *pStripByteCounts; /* ptr to strip byte count list in memory */
- ilFileOffset *pJIF; /* (JIF only): offset in memory */
- long *pJIFLength; /* (JIF only): length in memory */
- unsigned short *pJPEGRestart; /* (JPEG only): restart interval in memory */
- long *pJPEGQTables; /* (JPEG only): Q tables in memory */
- long *pJPEGDCTables; /* (JPEG only): DC tables in memory */
- long *pJPEGACTables; /* (JPEG only): AC tables in memory */
- unsigned short *pPalette; /* ptr to pal; null if not palette image */
- int nPaletteColors; /* # of RGB triplets at pPalette */
- int tagCount; /* # of tags to write */
- } ilTagInfoRec, *ilTagInfoPtr;
- typedef struct {
- /* Data set into private when element added */
- ilFilePtr pFile; /* ptr to file being written */
- ilTagInfoRec tagInfo; /* see above */
- ilBool bigEndian; /* true if MM byte order file */
- unsigned int compression; /* type of compression being written */
- long nStrips; /* # of strips which should be written */
- ilFileOffset tailPtrOffset; /* "next ptr" to point to this image */
- ilFileImageRec fileImage; /* data for file image being added */
- /* Data set/inited by ilWriteFileInit() */
- ilFileOffset *pStripOffset; /* next in-memory strip offset to store */
- INT32 *pStripByteCount; /* same for byte count (# bytes written) */
- long nStripsSoFar; /* # of strips written so far */
- long nLinesSoFar; /* # of lines written so far */
- long nBytesSoFar; /* # of bytes written so far (JPEG only) */
- } ilWriteFilePrivRec, *ilWriteFilePrivPtr;
- /* Copy 4 bytes of a long byte-flipped based on given "bigEndian" flag, to
- the given ptr which must be of type ilPtr. Same for a short.
- */
- #define IL_WRITE_FLIP_LONG(_bigEndian, _long, _ptr) { \
- if (_bigEndian) { \
- *(_ptr)++ = (_long) >> 24; \
- *(_ptr)++ = ((_long) >> 16) & 0xff; \
- *(_ptr)++ = ((_long) >> 8) & 0xff; \
- *(_ptr)++ = (_long) & 0xff; \
- } \
- else { \
- *(_ptr)++ = (_long) & 0xff; \
- *(_ptr)++ = ((_long) >> 8) & 0xff; \
- *(_ptr)++ = ((_long) >> 16) & 0xff; \
- *(_ptr)++ = (_long) >> 24; \
- } \
- }
- #define IL_WRITE_FLIP_SHORT(_bigEndian, _short, _ptr) { \
- if (_bigEndian) { \
- *(_ptr)++ = ((_short) >> 8) & 0xff; \
- *(_ptr)++ = (_short) & 0xff; \
- } \
- else { \
- *(_ptr)++ = (_short) & 0xff; \
- *(_ptr)++ = ((_short) >> 8) & 0xff; \
- } \
- }
- /* --------------------- ilWriteFileInit -------------------------- */
- /* Init() function for ilWriteFileImage().
- */
- static ilError ilWriteFileInit (
- ilWriteFilePrivPtr pPriv,
- ilImageInfo *pSrcImage,
- ilImageInfo *pDstImage
- )
- {
- /* Point to beginning of strip offset/byte count memory buffers. */
- pPriv->pStripOffset = pPriv->tagInfo.pStripOffsets;
- pPriv->pStripByteCount = pPriv->tagInfo.pStripByteCounts;
- pPriv->nStripsSoFar = 0;
- pPriv->nLinesSoFar = 0;
- pPriv->nBytesSoFar = 0;
- return IL_OK;
- }
- /* --------------------- ilWriteFileCleanup -------------------------- */
- /* Cleanup() function for ilWriteFileImage().
- */
- static ilError ilWriteFileCleanup (
- ilWriteFilePrivPtr pPriv,
- ilBool aborting
- )
- {
- ilFilePtr pFile;
- ilFileOffset position;
- ilByte fourBytes[4], nextBytes[4];
- ilPtr pBytes, pTag;
- CARD32 *pTagOffsets, l;
- int i;
- /* If aborting pipe, just exit. The net effect is that if
- some image strips are written and the pipe aborted before completion,
- the image strips are there but not pointed to by anything.
- */
- if (aborting)
- return IL_OK; /* EXIT */
- /* Make sure the proper # of lines and strips have been written or error. */
- if ((pPriv->nStripsSoFar != pPriv->nStrips)
- || (pPriv->nLinesSoFar != pPriv->fileImage.p.height))
- return IL_ERROR_MALFORMED_FILE_WRITE;
- pFile = pPriv->pFile;
- /* Read "offset to next" that will be pointed to this image; set as "offset to
- next" for this image (IFDOffset), following tag info; see ilMergeWriteTags().
- */
- if (!IL_SEEK (pFile, pPriv->tailPtrOffset) || !IL_READ (pFile, 4, nextBytes))
- return IL_ERROR_FILE_IO;
- pBytes = pPriv->tagInfo.pTagWriteData + pPriv->tagInfo.tagCount * IL_TAG_SIZE + 2;
- *pBytes++ = nextBytes[0];
- *pBytes++ = nextBytes[1];
- *pBytes++ = nextBytes[2];
- *pBytes++ = nextBytes[3];
- /* Position to EOF; save file position in "position". Update tags whose values
- did not fit in the tag; they have a non-zero value in the array @pTagOffsets;
- the value is the offset from the beginning of the data to be written to the
- tag data.
- */
- if (fseek (pFile->stream, 0, 2)) /* seek to EOF (2); return != 0 is error */
- return IL_ERROR_FILE_IO;
- position = ftell (pFile->stream) - pFile->offset;
-
- pTagOffsets = pPriv->tagInfo.pTagOffsets;
- pTag = pPriv->tagInfo.pTagWriteData + 2;
- for (i = 0; i < pPriv->tagInfo.tagCount; i++, pTagOffsets++, pTag += IL_TAG_SIZE)
- if (*pTagOffsets) {
- l = position + *pTagOffsets;
- pBytes = pTag + 8; /* point to offset field of the tag */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, l, pBytes)
- }
- /* Write out the tag data to add the image to the file. Patch location at
- "tailPtrOffset" in file to point to this image to link image into list.
- */
- if (fwrite ((char *)pPriv->tagInfo.pTagWriteData, pPriv->tagInfo.tagWriteSize, 1,
- pFile->stream) != 1)
- return IL_ERROR_FILE_IO;
- pBytes = fourBytes;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
- if (!IL_SEEK (pFile, pPriv->tailPtrOffset) || !IL_WRITE (pFile, 4, fourBytes))
- return IL_ERROR_FILE_IO;
- fflush (pFile->stream); /* flush all buffers; image now in file */
- /* If this image is now last in file (its next offset is null), update file's
- offset to last image's offset to next, and copy of same in private.
- */
- if (!nextBytes[0] && !nextBytes[1] && !nextBytes[2] && !nextBytes[3]) {
- pFile->IFDTailPtrOffset = position + pPriv->tagInfo.tagCount * IL_TAG_SIZE + 2;
- pPriv->tailPtrOffset = pFile->IFDTailPtrOffset;
- }
- /* Mark file image list as invalid, so this image will show up next time. */
- pFile->haveImageList = FALSE;
- return IL_OK;
- }
- /* ------------------------ ilWriteFileDestroy ----------------------------- */
- /* Destroy() function for ilWriteFileImage().
- */
- static ilError ilWriteFileDestroy (
- ilWriteFilePrivPtr pPriv
- )
- {
- /* Free the tag data malloc'd by ilMergeWriteTags() when element added. */
- IL_FREE (pPriv->tagInfo.pAlloc);
- return IL_OK;
- }
- /* --------------------- ilCopyPalette -------------------------- */
- /* Called by Execute() function before writing first strip to copy the palette
- into the tag memory buffer, so it will be written out by ilWriteFileCleanup().
- */
- static void ilCopyPalette (
- ilWriteFilePrivPtr pPriv,
- unsigned short *pSrcPalette
- )
- {
- unsigned short *pDstPalette;
- ilPtr pBytes;
- /* Since using IL_WRITE_FLIP_SHORT, palEntry should be unsigned short */
- unsigned short palEntry;
- unsigned long greenOffset, blueOffset;
- int i;
- /* Copy palette into the in-memory tag area, byte-flipped.
- It will be written out along with all the tags and tag data during Cleanup().
- TIFF palettes are the same as IL: all the red, then green, then blue values,
- each a short, except in TIFF each is "2 ** nBits" entries
- (pPriv->nPaletteColors) in length; in the IL they are always 256 entries.
- */
- pDstPalette = pPriv->tagInfo.pPalette;
- if (pDstPalette) {
- greenOffset = pPriv->tagInfo.nPaletteColors;
- blueOffset = pPriv->tagInfo.nPaletteColors * 2;
- for (i = 0; i < pPriv->tagInfo.nPaletteColors; i++, pSrcPalette++, pDstPalette++) {
- palEntry = *pSrcPalette;
- pBytes = (ilPtr)pDstPalette;
- IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
- palEntry = *(pSrcPalette + 256);
- pBytes = (ilPtr)(pDstPalette + greenOffset);
- IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
- palEntry = *(pSrcPalette + 512);
- pBytes = (ilPtr)(pDstPalette + blueOffset);
- IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
- }
- }
- }
- /* --------------------- ilWriteFileExecute -------------------------- */
- /* Execute() for ilWriteFileImage().
- */
- static ilError ilWriteFileExecute (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilWriteFilePrivPtr pPriv;
- ilFilePtr pFile;
- unsigned long nBytes, rowBytes;
- ilPtr pBytes;
- long position;
- if (*pNLines <= 0)
- return IL_OK;
- pPriv = (ilWriteFilePrivPtr)pData->pPrivate;
- pFile = pPriv->pFile;
- /* If first strip and a palette image copy the palette into tag data */
- if ((pPriv->nStripsSoFar <= 0) && pPriv->tagInfo.pPalette)
- ilCopyPalette (pPriv, pData->pSrcImage->pPalette);
- /* Check for too many lines/strips or inconstant strips (except last). */
- pPriv->nLinesSoFar += *pNLines;
- pPriv->nStripsSoFar++;
- if ((pPriv->nStripsSoFar > pPriv->nStrips)
- || (pPriv->nLinesSoFar > pPriv->fileImage.p.height)
- || ((pPriv->nLinesSoFar != pPriv->fileImage.p.height) /* not last strip */
- && (*pNLines != pPriv->fileImage.p.stripHeight)))
- return IL_ERROR_MALFORMED_FILE_WRITE;
- /* Write one strip of data to the end of the file, after saving "position". */
- if (fseek (pFile->stream, 0, 2)) /* seek to EOF (2); return != 0 is error */
- return IL_ERROR_FILE_IO; /* EXIT */
- position = ftell (pFile->stream) - pFile->offset;
- if (pPriv->compression == IL_UNCOMPRESSED) {
- rowBytes = pData->pSrcImage->plane[0].nBytesPerRow;
- nBytes = rowBytes * *pNLines;
- pBytes = pData->pSrcImage->plane[0].pPixels + pData->srcLine * rowBytes;
- }
- else {
- nBytes = pData->compressed.nBytesToRead;
- pBytes = pData->pSrcImage->plane[0].pPixels + pData->compressed.srcOffset;
- }
- if (fwrite ((char *)pBytes, nBytes, 1, pFile->stream) != 1)
- return IL_ERROR_FILE_IO; /* EXIT */
- /* Store byte-flipped the offset within the file (position) and size
- (nBytes) of the strip just written.
- */
- pBytes = (ilPtr)pPriv->pStripOffset;
- pPriv->pStripOffset++;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
- pBytes = (ilPtr)pPriv->pStripByteCount;
- pPriv->pStripByteCount++;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)nBytes, pBytes)
- return IL_OK;
- }
- /* --------------------------- ilWriteJIFData -------------------------------- */
- /* Called by ilWriteJPEGExecute() when first strip encountered, to encode
- and write a JIF header into the TIFF file.
- */
- static ilError ilWriteJIFData (
- ilWriteFilePrivPtr pPriv,
- ilFilePtr pFile,
- ilImageInfo *pImage
- )
- {
- iljpgJIFOffsetsRec offsets;
- iljpgDataRec data;
- long JIFOffset;
- long position, nBytes, i, j;
- ilJPEGEncodeStream streamRec;
- ilJPEGData *pCompData;
- ilError error;
- long mcuWidth, mcuHeight;
- ilPtr pBytes;
- /* Init streamRec so output is written into malloc'd memory, and encode
- a JIF image into that memory. For restartInterval value: if a single strip
- use value from raw data (== 0 if no restart markers), otherwise calculate
- it from strip size. If multi-strip and non-zero restartMarker in raw data
- a JIF image cannot be created - see below.
- */
- pCompData = (ilJPEGData *)pImage->pCompData;
- _ilJPEGDataIn (pImage->pDes, pPriv->fileImage.p.width, pPriv->fileImage.p.height,
- &data);
- _ilJPEGTablesIn (pCompData, &data);
- if (pPriv->nStrips != 1) { /* else data.restartInterval = raw already */
- mcuWidth = 8 * data.maxHoriFactor;
- mcuHeight = 8 * data.maxVertFactor;
- data.restartInterval = /* # of mcus across * # down */
- ((pPriv->fileImage.p.width + mcuWidth - 1) / mcuWidth) *
- ((pPriv->fileImage.p.stripHeight + mcuHeight - 1) / mcuHeight);
- }
- streamRec.pBuffer = streamRec.pDst = streamRec.pPastEndBuffer = (ilPtr)NULL;
- if (error = iljpgEncodeJIF (&streamRec, &data, &offsets))
- return error;
- /* Write JIF header to end of file, at "position". */
- if (fseek (pFile->stream, 0, 2)) /* seek to EOF (2); != 0 is error */
- return IL_ERROR_FILE_IO;
- position = ftell (pFile->stream) - pFile->offset;
- nBytes = streamRec.pDst - streamRec.pBuffer;
- if (fwrite ((char *)streamRec.pBuffer, nBytes, 1, pFile->stream) != 1)
- return IL_ERROR_FILE_IO;
- IL_FREE (streamRec.pBuffer); /* free malloc'd buffer space */
- pPriv->nBytesSoFar = nBytes; /* JIF data size so far */
- /* Update in-memory values for tags that point to JIF data and to
- the Q/DC/AC tables, returned by iljpgEncodeJIF() above.
- Write restartInterval: if 0, no restart markers. If non-zero,
- then there must be only one strip in image or it cannot be encoded as
- as a JIF image, because restart markers need to be written between the
- strips and would not mesh with restart markers already in the data;
- so in that case write JIF pointer marker = 0 => not JIF.
- The resulting file will be able to be read by IL2.1 and later but not by
- earlier IL versions or by other software that relies on JIF.
- */
- pBytes = (ilPtr)pPriv->tagInfo.pJPEGRestart;
- /* type cast from int to short before FLIP */
- IL_WRITE_FLIP_SHORT (pPriv->bigEndian, (short)pCompData->restartInterval, pBytes)
- if (pCompData->restartInterval && (pPriv->nStrips != 1))
- JIFOffset = 0; /* not valid JIF; write 0 ptr */
- else JIFOffset = position;
- pBytes = (ilPtr)pPriv->tagInfo.pJIF;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
- /* Write offsets to Q tables for each component: table offset for each
- table, indexed by component table index, plus file to offset to JIF hdr.
- */
- pBytes = (ilPtr)pPriv->tagInfo.pJPEGQTables;
- for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
- j = offsets.QTables[data.comp[i].QTableIndex];
- if (!j) /* no Q table defined for that index */
- return IL_ERROR_COMPRESSED_DATA;
- j += position; /* now offset from start of "file" */
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
- }
- /* Same thing for DC and AC tables */
- pBytes = (ilPtr)pPriv->tagInfo.pJPEGDCTables;
- for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
- j = offsets.DCTables[data.comp[i].DCTableIndex];
- if (!j)
- return IL_ERROR_COMPRESSED_DATA;
- j += position;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
- }
- pBytes = (ilPtr)pPriv->tagInfo.pJPEGACTables;
- for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
- j = offsets.ACTables[data.comp[i].ACTableIndex];
- if (!j)
- return IL_ERROR_COMPRESSED_DATA;
- j += position;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
- }
- return IL_OK;
- }
- /* --------------------- ilWriteJPEGExecute -------------------------- */
- /* Execute() for ilWriteFileImage() when writing JPEG-compressed data.
- */
- static ilError ilWriteJPEGExecute (
- ilExecuteData *pData,
- long dstLine,
- long *pNLines
- )
- {
- ilWriteFilePrivPtr pPriv;
- ilFilePtr pFile;
- ilError error;
- long position, nBytes, i, j;
- ilPtr pBytes;
- ilByte marker[2];
- /* The overall approach is to write a JIF image into the file plus strips.
- The data written will be the JIF "header", followed by strips of JPEG
- data, separated by restart markers. Each strip will pointed to by
- the normal TIFF strip offset/byte counts. The resulting data can then
- be read either as a JIF image (using the JPEGInterchangeFormat/Length
- tags - how IL2.0 and earlier versions read it) or as a "raw" image using
- the other JPEG tags (how later versions read it).
- */
- pPriv = (ilWriteFilePrivPtr)pData->pPrivate;
- pFile = pPriv->pFile;
- /* Check for too many lines/strips or inconstant strips (except last). */
- pPriv->nLinesSoFar += *pNLines;
- pPriv->nStripsSoFar++;
- if ((pPriv->nStripsSoFar > pPriv->nStrips)
- || (pPriv->nLinesSoFar > pPriv->fileImage.p.height)
- || ((pPriv->nLinesSoFar != pPriv->fileImage.p.height) /* not last strip */
- && (*pNLines != pPriv->fileImage.p.stripHeight)))
- return IL_ERROR_MALFORMED_FILE_WRITE;
- /* If first strip: encode a JIF header and write it to file.
- Init streamRec so output is written into malloc'd memory, and encode
- a JIF image into that memory.
- */
- if (pPriv->nStripsSoFar <= 1) /* first strip; inc'd above */
- if (error = ilWriteJIFData (pPriv, pFile, pData->pSrcImage))
- return error;
- /* Write one strip of data to end of the TIFF file, after saving "position". */
- if (fseek (pFile->stream, 0, 2)) /* seek to EOF (2); return != 0 is error */
- return IL_ERROR_FILE_IO;
- position = ftell (pFile->stream) - pFile->offset;
- nBytes = pData->compressed.nBytesToRead;
- pBytes = pData->pSrcImage->plane[0].pPixels + pData->compressed.srcOffset;
- if (fwrite ((char *)pBytes, nBytes, 1, pFile->stream) != 1)
- return IL_ERROR_FILE_IO;
- pPriv->nBytesSoFar += nBytes + 2; /* # bytes in strip + 2 for marker below */
- /* Store byte-flipped the offset within the file (position) and size
- (nBytes) of the strip just written.
- */
- pBytes = (ilPtr)pPriv->pStripOffset;
- pPriv->pStripOffset++;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
- pBytes = (ilPtr)pPriv->pStripByteCount;
- pPriv->pStripByteCount++;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)nBytes, pBytes)
- /* If last strip, add an EOI marker to end and store JIF length tag value;
- otherwise add a restart marker, with strip count modulo 8 in it.
- */
- marker[0] = 0xff;
- if (pPriv->nStripsSoFar >= pPriv->nStrips) {
- marker[1] = ILJPGM_EOI;
- pBytes = (ilPtr)pPriv->tagInfo.pJIFLength;
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)pPriv->nBytesSoFar, pBytes)
- }
- else marker[1] = ILJPGM_RST0 | ((pPriv->nStripsSoFar - 1) & 7);
- /* Write two byte restart or end of image (EOI) marker */
- if (fseek (pFile->stream, 0, 2))
- return IL_ERROR_FILE_IO;
- if (fwrite ((char *)marker, 2, 1, pFile->stream) != 1)
- return IL_ERROR_FILE_IO;
- return IL_OK;
- }
- /* --------------------- ilMergeWriteTags ----------------------------------- */
- /* Called by ilWriteFileImage(). Merge the user and std tag info, copy the
- tag data (flipped) and return into to *pInfo.
- */
- static ilError ilMergeWriteTags (
- ilBool bigEndian, /* byte order to be written */
- int nStdTags, /* # of std (IL) tags */
- ilFileTag *pStdTags, /* ptr to std tags */
- int nUserTags, /* # of user (IL) tags */
- ilFileTag *pUserTags, /* ptr to user tags */
- ilTagInfoPtr pInfo) /* returned tag info */
- {
- ilFileTag **ppTagsInit, **ppLastTag;
- ilFileTag **ppTag, **ppTagTemp, *pTag;
- ilPtr pTagData, pFileTags;
- long tagArraySize, tagDataSize, l;
- CARD32 *pTagOffsets, *pLong;
- int nTags, i;
- unsigned short tagType, *pShort;
- int tagNumber;
- /* Allocate and null an array of ptrs to tags, size = # user + std tags, + 1
- so that there is at least one guaranteed null tag ptr, for "end of array".
- */
- tagArraySize = nStdTags + nUserTags + 1;
- ppTagsInit = (ilFileTag **)IL_MALLOC (tagArraySize * sizeof (ilFileTag **));
- if (!ppTagsInit)
- return IL_ERROR_MALLOC;
- ppLastTag = ppTagsInit + tagArraySize - 1;
- for (ppTag = ppTagsInit; ppTag <= ppLastTag; ppTag++)
- *ppTag = (ilFileTag *)NULL;
- /* Fill in array with ptrs to tags, sorted in ascending numeric order (required
- by TIFF!), with duplicates eliminated. Do the user tags, then the std tags,
- so that the std tag values overwrite the user if duplicates present.
- Sort by insertion sort: find tag with number >= this tag's number, or end
- of list (null ptr). If greater, shift all values up one; then insert this tag
- */
- for (i = 0, pTag = pUserTags, nTags = nUserTags; i < 2;
- i++, pTag = pStdTags, nTags = nStdTags) {
- while (nTags-- > 0) {
- ppTag = ppTagsInit;
- while (*ppTag && ((*ppTag)->number < pTag->number))
- ppTag++;
- if (*ppTag && ((*ppTag)->number > pTag->number)) {
- ppTagTemp = ppLastTag;
- while (ppTagTemp > ppTag) {
- *ppTagTemp = *(ppTagTemp - 1);
- ppTagTemp--;
- }
- }
- *ppTag = pTag;
- pTag++;
- }
- }
- /* Set "nTags" to the resulting number of tags, and "tagDataSize" to the size
- of tag data which does not fit in the tag itself (rounded to mult of 2).
- */
- for (tagDataSize = 0, nTags = 0, ppTag = ppTagsInit; *ppTag; nTags++, ppTag++) {
- pTag = *ppTag;
- tagType = pTag->type;
- if ((tagType == 0) || (tagType > IL_MAX_TAG_TYPE)) {
- IL_FREE (ppTagsInit);
- return IL_ERROR_FILE_TAG_TYPE;
- }
- if (pTag->nItems > _ilTagTypeItemsThatFit [tagType])
- tagDataSize += (_ilTagTypeItemSizes [tagType] * pTag->nItems + 1) & ~1;
- }
- /* Alloc room on heap for: an array of offsets to tag data for each tag, or 0
- if tag data fits in tag; the # of tags (2 bytes); the tags themselves; the
- next IFD offset (4 bytes); and the tag data that does not fit in the tags.
- pInfo->pAlloc will point to it all:
- -----------------------------
- | offset to data for each | <- pTagOffsets (not written)
- | tag or 0 if data in tag | (rest is written to TIFF, and is
- | | "tagWriteSize" bytes in size
- -----------------------------
- | # of tags (short) | <- pTagWriteData
- -----------------------------
- | tag info for each tag | +2
- | (of type ilTIFFTagRec) |
- -----------------------------
- | ptr to next IFD | + # tags * IL_TAG_SIZE + 2
- -----------------------------
- | tag data external to tags | + # tags * IL_TAG_SIZE + 6
- -----------------------------
- */
- pInfo->tagCount = nTags;
- pInfo->tagWriteSize = nTags * IL_TAG_SIZE + 2 + tagDataSize + 4;
- pInfo->pAlloc = (ilPtr)IL_MALLOC_ZERO (nTags * sizeof(INT32) + pInfo->tagWriteSize);
- if (!pInfo->pAlloc) {
- IL_FREE (ppTagsInit);
- return IL_ERROR_MALLOC;
- }
- pInfo->pTagOffsets = (CARD32 *)pInfo->pAlloc;
- pInfo->pTagWriteData = pInfo->pAlloc + nTags * sizeof(INT32);
- pTagData = (ilPtr)pInfo->pTagWriteData + nTags * IL_TAG_SIZE + 6;
- /* Write the # of tags byte-flipped, point "pFileTags" to tag data write area.
- For byte/ascii data: "nItems" is length of string, including trailing null
- for ascii. Round up to even byte boundary (TIFF requirement).
- */
- pFileTags = (ilPtr)pInfo->pTagWriteData;
- IL_WRITE_FLIP_SHORT (bigEndian, (unsigned short)nTags, pFileTags)
- for (ppTag = ppTagsInit, pTagOffsets = pInfo->pTagOffsets; *ppTag; ppTag++) {
- ilPtr pWrite;
- pTag = *ppTag;
- tagType = pTag->type;
- IL_WRITE_FLIP_SHORT (bigEndian, pTag->number, pFileTags)
- IL_WRITE_FLIP_SHORT (bigEndian, tagType, pFileTags)
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (bigEndian, (CARD32)pTag->nItems, pFileTags)
- if (pTag->nItems > _ilTagTypeItemsThatFit [tagType]) {
- *pTagOffsets++ = pTagData - pInfo->pTagWriteData;
- pWrite = pTagData;
- pTagData += (_ilTagTypeItemSizes [tagType] * pTag->nItems + 1) & ~1UL;
- }
- else {
- *pTagOffsets++ = 0;
- pWrite = pFileTags;
- }
- pFileTags += 4; /* skip one TIFF long */
- /* Copy the data at "*pTag->pItems" to "pWrite", with byte-flip.
- pWrite points to 4 byte area in tag if items fit, else to area in pTagData.
- NOTE: handle strip offsets/byte counts specially: pItems is ignored.
- */
- tagNumber = pTag->number;
- if (tagNumber == IL_TAG_STRIP_OFFSETS)
- pInfo->pStripOffsets = (ilFileOffset *)pWrite;
- else if (tagNumber == IL_TAG_STRIP_BYTE_COUNTS)
- pInfo->pStripByteCounts = (INT32 *)pWrite;
- else if (tagNumber == IL_TAG_COLOR_MAP)
- pInfo->pPalette = (unsigned short *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_INTERCHANGE_FORMAT)
- pInfo->pJIF = (ilFileOffset *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
- pInfo->pJIFLength = (long *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_RESTART_INTERVAL)
- pInfo->pJPEGRestart = (unsigned short *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_Q_TABLES)
- pInfo->pJPEGQTables = (long *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_DC_TABLES)
- pInfo->pJPEGDCTables = (long *)pWrite;
- else if (tagNumber == IL_TAG_JPEG_AC_TABLES)
- pInfo->pJPEGACTables = (long *)pWrite;
- else switch (tagType) {
- case IL_TAG_BYTE:
- case IL_TAG_ASCII:
- bcopy ((char *)pTag->pItems, (char *)pWrite, pTag->nItems);
- break;
- case IL_TAG_SHORT:
- for (i = 0, pShort = (unsigned short *)pTag->pItems;
- i < pTag->nItems; i++, pShort++)
- IL_WRITE_FLIP_SHORT (bigEndian, *pShort, pWrite)
- break;
- case IL_TAG_LONG:
- /* compatibility problem with long or unsigned long data fields */
- for (i = 0, pLong = (CARD32 *)pTag->pItems;
- i < pTag->nItems; i++, pLong++)
- IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
- break;
- case IL_TAG_RATIONAL:
- /* compatibility problem with long or unsigned long data fields */
- for (i = 0, pLong = (CARD32 *)pTag->pItems; i < pTag->nItems; i++) {
- IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
- pLong++;
- IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
- pLong++;
- }
- break;
- }
- } /* END for one tag */
- /* Fill in ptr to next IFD (= 0, null), free temp alloc space and exit.
- */
- /* compatibility problem with long or unsigned long data fields */
- IL_WRITE_FLIP_LONG (bigEndian, 0, pFileTags)
- IL_FREE (ppTagsInit);
- return IL_OK;
- }
- /* ---------------------- ilFormatPipeImage --------------------------------- */
- /* Reformat and compress the pipe image as necessary. On entry, pStripHeight
- must point to the callers requested stripHeight; on return it is the new
- (constant) stripHeight to use to write.
- On entry, pInfo/pDes/pFormat must point to the current pipe values;
- on return they are the new pipe values. Returns false if an error occurs.
- */
- static ilBool ilFormatPipeImage (
- ilPipe pipe,
- unsigned int compression,
- ilPtr pCompData,
- long *pStripHeight,
- ilPipeInfo *pInfo,
- ilImageDes *pDes,
- ilImageFormat *pFormat
- )
- {
- #define IL_WRITE_TIFF_STRIP_SIZE (16 * 1024) /* write 16KB strips as default */
- long stripHeight;
- long i, nLevels;
- ilBool mustConvert;
- unsigned long g3Flags, g4Flags;
- ilJPEGEncodeControl jpegEncode;
- /* If compression is "use current compression" (which makes it easier for
- caller to copy images), then set compression to current pipe compression,
- and point pCompData to compression-specific current pipe data.
- */
- if (compression == IL_WRITE_CURRENT_COMPRESSION) {
- compression = pDes->compression;
- if (compression == IL_G3) {
- g3Flags = pDes->compInfo.g3.flags;
- pCompData = (ilPtr)&g3Flags;
- }
- else if (compression == IL_G4) {
- g4Flags = pDes->compInfo.g4.flags;
- pCompData = (ilPtr)&g4Flags;
- }
- }
- /* Compare the levels/sample to bits/sample, and scale up if levels != 2**bits.
- This is because the IL separates bits and levels, but TIFF assumes # levels
- = 2**bits. A 16 level 8 bit gray image must be level-scaled up to 256 or
- else the reader will assume 256 level and display it too dark.
- However, bitonal must be bit/pixel, and for palette, levels is ignored.
- NOTE: the right thing to do is to convert the format (e.g. change 16
- level 8 bit gray to 4 bit gray), but ilConvert() does not handle this yet!
- */
- switch (pDes->type) {
- case IL_BITONAL:
- if (pFormat->nBitsPerSample[0] != 1) {
- pFormat->nBitsPerSample[0] = 1;
- if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
- return FALSE;
- }
- break;
- case IL_PALETTE: /* ignore nLevels for palette images */
- break;
- default: /* continuous tone: match levels to bits */
- for (i = 0, mustConvert = FALSE; i < pDes->nSamplesPerPixel; i++) {
- nLevels = 1 << pFormat->nBitsPerSample[i];
- if (pDes->nLevelsPerSample[i] != nLevels) {
- pDes->nLevelsPerSample[i] = nLevels;
- mustConvert = TRUE;
- }
- }
- if (mustConvert)
- if (!ilConvert (pipe, pDes, pFormat, 0, (ilPtr)NULL))
- return FALSE;
- break;
- }
- /* Convert to pixel order and reget pipe info if multi-sample and planar.
- However, leave planar if JPEG compression - keep YCbCr subsampled if so.
- */
- if (pDes->nSamplesPerPixel == 1)
- pFormat->sampleOrder = IL_SAMPLE_PIXELS;
- else if ((pFormat->sampleOrder != IL_SAMPLE_PIXELS) && (compression != IL_JPEG)) {
- pFormat->sampleOrder = IL_SAMPLE_PIXELS;
- if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
- return FALSE;
- ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
- }
- /* If writing uncompressed, force rowBitAlign of 8 (required by TIFF);
- else compress to stripHeight, using the given compression & data.
- If stripHeight is 0 use default TIFF write size, based on uncompressed size.
- */
- stripHeight = *pStripHeight;
- if (compression == IL_UNCOMPRESSED) {
- ilGetPipeInfo (pipe, TRUE, pInfo, pDes, pFormat); /* force decompression */
- if (pFormat->rowBitAlign != 8) {
- pFormat->rowBitAlign = 8;
- if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, (ilPtr)NULL))
- return FALSE;
- ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
- }
- if (stripHeight <= 0) {
- long bytesPerRow [IL_MAX_SAMPLES];
- ilGetBytesPerRow (pDes, pFormat, pInfo->width, bytesPerRow);
- stripHeight = IL_WRITE_TIFF_STRIP_SIZE / bytesPerRow[0];
- }
- if (stripHeight <= 0)
- stripHeight = 1;
- else if (stripHeight > pInfo->height)
- stripHeight = pInfo->height;
- *pStripHeight = stripHeight; /* write with desired strip height */
- }
- else {
- /* If compressing JPEG, force "raw" format; get caller's Q (dflt = 0).
- Compress with desired strip height, unless already JPEG compressed,
- in which case use 0 strip height: accept current strip height.
- */
- if (compression == IL_JPEG) {
- jpegEncode.mustbezero = IL_JPEGM_RAW;
- if (pCompData)
- jpegEncode.Q = ((ilJPEGEncodeControl *)pCompData)->Q;
- else jpegEncode.Q = 0;
- pCompData = (ilPtr)&jpegEncode;
- if (pDes->compression == IL_JPEG)
- stripHeight = 0; /* accept current strip height */
- }
- if (!ilCompress (pipe, compression, pCompData, stripHeight, 0))
- return FALSE;
- ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
- *pStripHeight = pInfo->stripHeight; /* strip height determined by compression */
- }
- return TRUE;
- }
- /* ---------------------- ilWriteFileImage ---------------------------------- */
- /* Public function: see spec.
- Adds a consumer to the given pipe to write the current pipe image.
- */
- IL_EXTERN char _ilVersionString[]; /* in /ilc/ilversion.c ; written for "software" tag */
- ilBool ilWriteFileImage (
- ilPipe pipe,
- ilFile file,
- unsigned int method,
- ilFileImage fileImage,
- unsigned int compression,
- ilPtr pCompData,
- long stripHeight,
- long xRes,
- long yRes,
- short page,
- short nPages,
- int nUserTags,
- ilFileTag *pUserTags,
- unsigned long mustBeZero
- )
- {
- #define MAX_WRITE_TAGS 50 /* max # of std (non-user) tags added here */
- #define TW_ADD_TAG(_number, _type, _nItems, _pItems) { \
- pTag->number = (_number); \
- pTag->type = (_type); \
- pTag->nItems = (_nItems); \
- pTag->pItems = (ilPtr)(_pItems); \
- pTag++; \
- nTags++; \
- }
- long nStripsPerImage;
- ilFileTag tags [MAX_WRITE_TAGS];
- ilFileTag *pTag;
- int nTags, i;
- ilTagInfoRec tagInfo;
- ilFilePtr pFile;
- ilPipeInfo info;
- ilImageDes des;
- ilImageFormat format;
- ilContext context;
- ilError error;
- ilWriteFilePrivPtr pPriv;
- ilSrcElementData srcData;
- unsigned long group3Options, group4Options;
- ilFileImageRelation writeMethod;
- ilFileOffset tailPtrOffset;
- /* NOTE: below data types (sizes) FIXED! - tied to TIFF type (short/long) */
- short ditherLevels[IL_TAG_P0_DITHER_LEVELS_LENGTH];
- short bitsPerSample [IL_MAX_SAMPLES], TIFFcompression, photo;
- short nSamplesPerPixel, resolutionUnit, planarConfiguration;
- short pageInfo[2], predictor, fillOrder;
- short jpegProc, subsample[2], positioning;
- long width, height, newSubFileType;
- long xResolution[2], yResolution[2], coeff[6], refBW[12];
- #define VERSION_LENGTH 18 /* length includes trailing null */
- char versionName [VERSION_LENGTH];
- /* Get pipe state - must be IL_PIPE_FORMING or declare an error. */
- pFile = (ilFilePtr)file;
- context = pFile->o.p.context;
- if (ilGetPipeInfo (pipe, FALSE, &info, &des, &format) != IL_PIPE_FORMING) {
- if (!context->error)
- ilDeclarePipeInvalid (pipe, IL_ERROR_PIPE_STATE);
- return FALSE;
- }
- if (mustBeZero != 0)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_PAR_NOT_ZERO);
- if (context != pipe->context)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_CONTEXT_MISMATCH);
- /* Force the file image list to be formed if not already; exit on error. */
- ilListFileImages (file, 0);
- if (context->error)
- return ilDeclarePipeInvalid (pipe, context->error);
- /* Handle "method" set "writeMethod", "newSubFileType". */
- switch (method) {
- case IL_WRITE_MAIN: /* no parent, add as last image */
- writeMethod = mainImage;
- newSubFileType = 0;
- fileImage = (ilFileImage)NULL;
- break;
- /* Transparency mask: error if no parent, or parent already has or is a mask, or
- not bitonal image.
- */
- case IL_WRITE_MASK:
- if (!fileImage /* no parent */
- || (fileImage->file != file) /* not from file being written */
- || fileImage->pMask /* parent already has a mask */
- || (((ilFileImagePtr)fileImage)->imageType == maskImage)
- || (des.nSamplesPerPixel != 1) /* not bitonal */
- || (format.nBitsPerSample[0] != 1))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
- writeMethod = maskImage;
- newSubFileType = 4; /* TIFF transparency mask */
- break;
- /* Child image: must have non-mask parent fileImage with same file or error.
- Point to last image associated with parent: find last child (its mask if
- it has one), so that child images are in order written, and masks "bind"
- closest to image that they are a mask of.
- */
- case IL_WRITE_CHILD:
- writeMethod = childImage;
- newSubFileType = 1; /* TIFF reduced resolution image */
- if (!fileImage /* no parent */
- || (fileImage->file != file) /* not from file being written */
- || (((ilFileImagePtr)fileImage)->imageType == maskImage))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
- while (fileImage->pChild)
- fileImage = fileImage->pChild;
- if (fileImage->pMask)
- fileImage = fileImage->pMask; /* write after the mask image */
- break;
- default:
- return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
- break;
- }
- /* Reformat and compress the pipe image as necessary. Return the stripHeight
- to use, and the new pipe info/des/format.
- */
- if (!ilFormatPipeImage (pipe, compression, pCompData, &stripHeight,
- &info, &des, &format))
- return FALSE;
- /* Form the tags to be written for this file image. */
- nTags = 0;
- pTag = tags;
- /* Write the 1st part of the IL version string as the "software" tag, starting 4
- chars into string (past '@(#)') - remember length includes trailing null!
- */
- bcopy (&_ilVersionString[4], versionName, VERSION_LENGTH - 1);
- versionName [VERSION_LENGTH-1] = 0;
- TW_ADD_TAG (IL_TAG_SOFTWARE, IL_TAG_ASCII, VERSION_LENGTH, versionName)
- if (page >= 0)
- newSubFileType |= 2; /* mark as multi-page image */
- TW_ADD_TAG (IL_TAG_NEW_SUBFILE_TYPE, IL_TAG_LONG, 1, &newSubFileType)
- width = info.width;
- TW_ADD_TAG (IL_TAG_IMAGE_WIDTH, IL_TAG_LONG, 1, &width)
- height = info.height;
- TW_ADD_TAG (IL_TAG_IMAGE_LENGTH, IL_TAG_LONG, 1, &height)
- for (i = 0; i < des.nSamplesPerPixel; i++)
- bitsPerSample [i] = format.nBitsPerSample [i];
- TW_ADD_TAG (IL_TAG_BITS_PER_SAMPLE, IL_TAG_SHORT, des.nSamplesPerPixel, bitsPerSample)
- /* Determine TIFF compression tag based on pipe image compression and possibly
- des.compInfo. Ensure pCompData null if not used.
- */
- switch (des.compression) {
- case IL_UNCOMPRESSED:
- TIFFcompression = 1;
- break;
- /* Group 3: use des.compInfo.g3.flags.
- If 0, write TIFF G3 (code 2), else code 3: must have EOL_MARKERS; init
- group3Options to be written below, and write FillOrder tag (required for
- Class F; not recommended for other uses in TIFF).
- Add group 3 options tag only if "true" (not TIFF) G3 (TIFFcompression == 3)
- */
- case IL_G3:
- { unsigned long userOptions;
- userOptions = des.compInfo.g3.flags;
- if (userOptions == 0)
- TIFFcompression = 2;
- else {
- if (userOptions & ~0x3f) /* unhandled bits set: error */
- return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
- group3Options = 0;
- fillOrder = (userOptions & IL_G3M_LSB_FIRST) ? 2 : 1;
- TW_ADD_TAG (IL_TAG_FILL_ORDER, IL_TAG_SHORT, 1, &fillOrder)
- if (userOptions & IL_G3M_2D)
- group3Options |= 1;
- if (userOptions & IL_G3M_UNCOMPRESSED)
- group3Options |= 2;
- if (!(userOptions & IL_G3M_EOL_MARKERS))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
- if (!(userOptions & IL_G3M_EOL_UNALIGNED))
- group3Options |= 4;
- TW_ADD_TAG (IL_TAG_GROUP_3_OPTIONS, IL_TAG_LONG, 1, &group3Options)
- TIFFcompression = 3;
- }
- }
- break;
- case IL_G4:
- { unsigned long userOptions;
- userOptions = des.compInfo.g4.flags;
- fillOrder = (userOptions & IL_G4M_LSB_FIRST) ? 2 : 1;
- TW_ADD_TAG (IL_TAG_FILL_ORDER, IL_TAG_SHORT, 1, &fillOrder)
- }
- group4Options = 0;
- TW_ADD_TAG (IL_TAG_GROUP_4_OPTIONS, IL_TAG_LONG, 1, &group4Options)
- TIFFcompression = 4;
- break;
- case IL_LZW:
- if (pCompData)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
- TIFFcompression = 5;
- break;
- /* JPEG: write JIF with restart markers between strips.
- For JIF-in-TIFF: add tags for a file ptr and length of the JIF image,
- and the restart interval, but don't know value until JIF image received.
- */
- case IL_JPEG:
- jpegProc = 1; /* baseline sequential process */
- TW_ADD_TAG (IL_TAG_JPEG_PROC, IL_TAG_SHORT, 1, &jpegProc)
- TW_ADD_TAG (IL_TAG_JPEG_INTERCHANGE_FORMAT, IL_TAG_LONG, 1, NULL)
- TW_ADD_TAG (IL_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, IL_TAG_LONG, 1, NULL)
- TW_ADD_TAG (IL_TAG_JPEG_RESTART_INTERVAL, IL_TAG_SHORT, 1, NULL)
- TW_ADD_TAG (IL_TAG_JPEG_Q_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
- TW_ADD_TAG (IL_TAG_JPEG_DC_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
- TW_ADD_TAG (IL_TAG_JPEG_AC_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
- TIFFcompression = 6;
- break;
- case IL_PACKBITS:
- if (pCompData)
- return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
- TIFFcompression = 32773;
- break;
- default:
- return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
- }
- TW_ADD_TAG (IL_TAG_COMPRESSION, IL_TAG_SHORT, 1, &TIFFcompression)
- /* Image type-specific code: set PhotometricInterpretation: to 4 (transparency
- mask) if a maskImage, else based on image type.
- */
- if (writeMethod == maskImage)
- photo = 4; /* special code for transparency mask images */
- else switch (des.type) {
- case IL_BITONAL:
- case IL_GRAY: photo = (des.blackIsZero) ? 1 : 0; break;
- case IL_PALETTE: photo = 3; break;
- case IL_RGB: photo = 2; break;
- /* YCbCr subsampling currently supported only if JPEG/JIF compressed. */
- case IL_YCBCR:
- /* Add the YCbCr-specific tags: coefficients as fractions of 10000 */
- coeff[0] = des.typeInfo.YCbCr.lumaRed;
- coeff[2] = des.typeInfo.YCbCr.lumaGreen;
- coeff[4] = des.typeInfo.YCbCr.lumaBlue;
- coeff[1] = coeff[3] = coeff[5] = 10000;
- TW_ADD_TAG (IL_TAG_YCBCR_COEFFICIENTS, IL_TAG_RATIONAL, 3, coeff)
- /* TIFF only allows subsampling as follows: Y = 1, Cr/Cb = 1,2,4 but
- Cr and CB equal to each other; vert <= horiz.
- */
- { ilYCbCrSampleInfo *pSample = des.typeInfo.YCbCr.sample;
- if ((pSample[0].subsampleHoriz != 1)
- || (pSample[0].subsampleVert != 1)
- || (pSample[1].subsampleHoriz != pSample[2].subsampleHoriz)
- || (pSample[1].subsampleVert != pSample[2].subsampleVert)
- || (pSample[1].subsampleVert > pSample[2].subsampleHoriz))
- return ilDeclarePipeInvalid (pipe, IL_ERROR_YCBCR_TIFF);
- subsample[0] = pSample[1].subsampleHoriz;
- subsample[1] = pSample[1].subsampleVert;
- TW_ADD_TAG (IL_TAG_YCBCR_SUBSAMPLING, IL_TAG_SHORT, 2, subsample)
- /* Set positioning - ? is centered correct ? */
- positioning = 1;
- TW_ADD_TAG (IL_TAG_YCBCR_POSITIONING, IL_TAG_SHORT, 1, &positioning)
- refBW[0] = pSample[0].refBlack; refBW[1] = 1;
- refBW[2] = pSample[0].refWhite; refBW[3] = 1;
- refBW[4] = pSample[1].refBlack; refBW[5] = 1;
- refBW[6] = pSample[1].refWhite; refBW[7] = 1;
- refBW[8] = pSample[2].refBlack; refBW[9] = 1;
- refBW[10] = pSample[2].refWhite; refBW[11] = 1;
- TW_ADD_TAG (IL_TAG_REFERENCE_BLACK_WHITE, IL_TAG_RATIONAL, 6, refBW)
- }
- photo = 6;
- break;
- default:
- return ilDeclarePipeInvalid (pipe, IL_ERROR_IMAGE_TYPE);
- }
- TW_ADD_TAG (IL_TAG_PHOTOMETRIC_INTERPRETATION, IL_TAG_SHORT, 1, &photo)
- /* Write # strips stripOffsets/ByteCounts, but supply no data - called
- function handles these specially and creates blank space, filled in later.
- */
- nStripsPerImage = (info.height + stripHeight - 1) / stripHeight;
- TW_ADD_TAG (IL_TAG_STRIP_OFFSETS, IL_TAG_LONG, nStripsPerImage, NULL)
- TW_ADD_TAG (IL_TAG_STRIP_BYTE_COUNTS, IL_TAG_LONG, nStripsPerImage, NULL)
- nSamplesPerPixel = des.nSamplesPerPixel;
- TW_ADD_TAG (IL_TAG_SAMPLES_PER_PIXEL, IL_TAG_SHORT, 1, &nSamplesPerPixel)
- TW_ADD_TAG (IL_TAG_ROWS_PER_STRIP, IL_TAG_LONG, 1, &stripHeight);
- /* Write x/yRes: specified in dpi * 2; default to 300 dpi if not given. */
- resolutionUnit = 2;
- TW_ADD_TAG (IL_TAG_RESOLUTION_UNIT, IL_TAG_SHORT, 1, &resolutionUnit)
- if (xRes <= 0) xRes = 600;
- xResolution [0] = xRes;
- xResolution [1] = 2;
- TW_ADD_TAG (IL_TAG_X_RESOLUTION, IL_TAG_RATIONAL, 1, xResolution)
- if (yRes <= 0) yRes = 600;
- yResolution [0] = yRes;
- yResolution [1] = 2;
- TW_ADD_TAG (IL_TAG_Y_RESOLUTION, IL_TAG_RATIONAL, 1, yResolution)
- /* As per TIFF spec, write planarConfiguration tag only if # samples > 1. */
- if (des.nSamplesPerPixel > 1) {
- planarConfiguration = (format.sampleOrder == IL_SAMPLE_PIXELS) ? 1 : 2;
- TW_ADD_TAG (IL_TAG_PLANAR_CONFIGURATION, IL_TAG_SHORT, 1, &planarConfiguration)
- }
- if (page >= 0) { /* else no page tag written */
- pageInfo[0] = page;
- pageInfo[1] = nPages;
- TW_ADD_TAG (IL_TAG_PAGE_NUMBER, IL_TAG_SHORT, 2, pageInfo)
- }
- if (des.compression == IL_LZW) {
- predictor = 1;
- TW_ADD_TAG (IL_TAG_PREDICTOR, IL_TAG_SHORT, 1, &predictor)
- }
- /* If a palette entry, add a colorMap tag; # colors = 2 ** # bits.
- If dithered flags bit set, write private tag to mark it as such.
- */
- if (des.type == IL_PALETTE) {
- tagInfo.nPaletteColors = 1 << format.nBitsPerSample [0];
- TW_ADD_TAG (IL_TAG_COLOR_MAP, IL_TAG_SHORT, 3 * tagInfo.nPaletteColors, NULL)
- if (des.flags & IL_DITHERED_PALETTE) {
- ditherLevels[0] = IL_TAG_P0_FLAG_DITHER_LEVELS;
- ditherLevels[1] = des.typeInfo.palette.levels[0];
- ditherLevels[2] = des.typeInfo.palette.levels[1];
- ditherLevels[3] = des.typeInfo.palette.levels[2];
- TW_ADD_TAG (IL_TAG_PRIVATE_0, IL_TAG_SHORT, IL_TAG_P0_DITHER_LEVELS_LENGTH,
- ditherLevels)
- }
- }
- else tagInfo.pPalette = (unsigned short *)NULL;
- /* Merge the user tags with the tags formed above, convert the data to
- the proper byte order and return the allocated info to "tagInfo" or error.
- */
- if (error = ilMergeWriteTags (pFile->bigEndian, nTags, tags, nUserTags, pUserTags,
- &tagInfo))
- return ilDeclarePipeInvalid (pipe, error);
- /* Add consumer pipe element, forcing constant "stripHeight" - ignored if pipe
- image is compressed - stripHeight was forced above by call to ilCompress().
- Use separate function for when writing JPEG.
- */
- srcData.consumerImage = (ilObject)NULL;
- srcData.stripHeight = stripHeight;
- srcData.constantStrip = TRUE;
- srcData.minBufferHeight = 0;
- pPriv = (ilWriteFilePrivPtr)ilAddPipeElement (pipe, IL_CONSUMER,
- sizeof(ilWriteFilePrivRec), 0, &srcData, (ilDstElementData *)NULL,
- ilWriteFileInit, ilWriteFileCleanup, ilWriteFileDestroy,
- (des.compression == IL_JPEG) ? ilWriteJPEGExecute : ilWriteFileExecute, 0);
- if (!pPriv)
- return FALSE;
- /* Fill in private (*pPriv), including template for file image to be added. */
- pPriv->pFile = pFile;
- pPriv->tagInfo = tagInfo;
- pPriv->bigEndian = pFile->bigEndian;
- pPriv->compression = des.compression;
- pPriv->nStrips = nStripsPerImage;
- /* Point pPriv->tailPtrOffset to location in file to point to this image: "ptr
- to next" of last image in file (if main image) else ptr to next of parent.
- */
- if (fileImage) /* have parent: child or mask image */
- pPriv->tailPtrOffset = ((ilFileImagePtr)fileImage)->tagOffset +
- ((ilFileImagePtr)fileImage)->tagCount * IL_TAG_SIZE;
- else pPriv->tailPtrOffset = pFile->IFDTailPtrOffset;
- pPriv->fileImage.p.file = (ilFile)pFile;
- pPriv->fileImage.p.pNext = pPriv->fileImage.p.pPrev =
- pPriv->fileImage.p.pChild = pPriv->fileImage.p.pMask = (ilFileImage)NULL;
- pPriv->fileImage.p.width = info.width;
- pPriv->fileImage.p.height = info.height;
- pPriv->fileImage.p.xRes = xRes;
- pPriv->fileImage.p.yRes = yRes;
- pPriv->fileImage.p.page = page;
- pPriv->fileImage.p.nPages = nPages;
- pPriv->fileImage.p.des = des;
- pPriv->fileImage.p.format = format;
- pPriv->fileImage.p.stripHeight = stripHeight;
- pPriv->fileImage.context = pFile->o.p.context;
- pPriv->fileImage.tagCount = tagInfo.tagCount;
- pipe->context->error = IL_OK;
- return TRUE;
- }
- /* ---------------------- ilInitFile ---------------------------------- */
- /* Public function: see spec.
- Writes a TIFF file header at position "offset" of file "stream" using
- Motorola order (MM) if "MSBIsFirst", else Intel order (II).
- */
- ilBool ilInitFile (
- ilContext context,
- FILE *stream,
- long offset,
- ilBool MSBIsFirst,
- unsigned long mustBeZero
- )
- {
- ilBool bigEndian;
- ilFilePtr pFile;
- ilByte *pHeader;
- static ilByte MSBFirstHeader[] = { 'M', 'M', 0, 42, 0, 0, 0, 0 };
- static ilByte LSBFirstHeader[] = { 'I', 'I', 42, 0, 0, 0, 0, 0 };
- if (fseek (stream, offset, 0)) { /* nonzero means error for fseek */
- context->error = IL_ERROR_FILE_IO;
- return FALSE;
- }
- pHeader = (MSBIsFirst) ? MSBFirstHeader : LSBFirstHeader;
- if (fwrite ((char *)pHeader, sizeof (MSBFirstHeader), 1, stream) != 1) {
- context->error = IL_ERROR_FILE_IO;
- return FALSE;
- }
- fflush (stream); /* flush header out to file */
- context->error = IL_OK;
- return TRUE;
- }
|