Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtHelp / il / iltiffwrite.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: iltiffwrite.c /main/4 1996/01/08 12:17:15 lehors $ */
24     /*  /ilc/iltiffwrite.c : Code for ilWriteFileImage(); add a consumer to the pipe
25         to write to a TIFF file.  See also /ilc/iltiff.c and /ilc/iltiffread.c .
26     */
27
28 #include "iltiffint.h"
29 #include "ilpipelem.h"
30 #include "iljpgencode.h"
31 #include "ilerrors.h"
32
33     /*  Tag info summary, private to ilWriteFileImage().  Encapsulates the user and std
34         tag info.  Contains ptrs to data which is created when the pipe element is added
35         and freed when pipe emptied (Destroy() function).
36             "pTagOffsets" points to an array of offsets, in the same order as the tags at
37         "pTagWriteData" + 2 (bytes).  For each tag, if the offset is zero (0), the tag
38         data is in the tag, already byte-flipped.  If non-zero, it is the offset of the
39         tag data from pTagWriteData.  The file offset where the data at pTagWriteData 
40         will be written must be added to this offset and inserted into the tag byte-flipped.
41             See ilMergeWriteTags() for details.
42     */
43 typedef struct {
44     ilPtr           pAlloc;                 /* ptr to whole mess to deallocate */
45     CARD32         *pTagOffsets;            /* see above */
46     ilPtr           pTagWriteData;          /* ptr to data to write to file */
47     long            tagWriteSize;           /* size in bytes of data to write */
48     ilFileOffset   *pStripOffsets;          /* ptr to strip offset list in memory */
49     INT32          *pStripByteCounts;       /* ptr to strip byte count list in memory */
50     ilFileOffset   *pJIF;                   /* (JIF only): offset in memory */
51     long           *pJIFLength;             /* (JIF only): length in memory */
52     unsigned short *pJPEGRestart;           /* (JPEG only): restart interval in memory */
53     long           *pJPEGQTables;           /* (JPEG only): Q tables in memory */
54     long           *pJPEGDCTables;          /* (JPEG only): DC tables in memory */
55     long           *pJPEGACTables;          /* (JPEG only): AC tables in memory */
56     unsigned short *pPalette;               /* ptr to pal; null if not palette image */
57     int             nPaletteColors;         /* # of RGB triplets at pPalette */
58     int             tagCount;               /* # of tags to write */
59     } ilTagInfoRec, *ilTagInfoPtr;
60
61
62 typedef struct {
63             /*  Data set into private when element added */
64     ilFilePtr       pFile;                  /* ptr to file being written */
65     ilTagInfoRec    tagInfo;                /* see above */
66     ilBool          bigEndian;              /* true if MM byte order file */
67     unsigned int    compression;            /* type of compression being written */
68     long            nStrips;                /* # of strips which should be written */
69     ilFileOffset    tailPtrOffset;          /* "next ptr" to point to this image */
70     ilFileImageRec  fileImage;              /* data for file image being added */
71
72             /*  Data set/inited by ilWriteFileInit() */
73     ilFileOffset   *pStripOffset;           /* next in-memory strip offset to store */
74     INT32          *pStripByteCount;        /* same for byte count (# bytes written) */
75     long            nStripsSoFar;           /* # of strips written so far */
76     long            nLinesSoFar;            /* # of lines written so far */
77     long            nBytesSoFar;            /* # of bytes written so far (JPEG only) */
78     } ilWriteFilePrivRec, *ilWriteFilePrivPtr;
79
80
81     /*  Copy 4 bytes of a long byte-flipped based on given "bigEndian" flag, to
82         the given ptr which must be of type ilPtr.  Same for a short.
83     */
84 #define IL_WRITE_FLIP_LONG(_bigEndian, _long, _ptr) {  \
85     if (_bigEndian) {                           \
86         *(_ptr)++ = (_long) >> 24;              \
87         *(_ptr)++ = ((_long) >> 16) & 0xff;     \
88         *(_ptr)++ = ((_long) >> 8) & 0xff;      \
89         *(_ptr)++ = (_long) & 0xff;             \
90         }                                       \
91     else {                                      \
92         *(_ptr)++ = (_long) & 0xff;             \
93         *(_ptr)++ = ((_long) >> 8) & 0xff;      \
94         *(_ptr)++ = ((_long) >> 16) & 0xff;     \
95         *(_ptr)++ = (_long) >> 24;              \
96         }                                       \
97     }
98
99 #define IL_WRITE_FLIP_SHORT(_bigEndian, _short, _ptr) {  \
100     if (_bigEndian) {                           \
101         *(_ptr)++ = ((_short) >> 8) & 0xff;     \
102         *(_ptr)++ = (_short) & 0xff;            \
103         }                                       \
104     else {                                      \
105         *(_ptr)++ = (_short) & 0xff;            \
106         *(_ptr)++ = ((_short) >> 8) & 0xff;     \
107         }                                       \
108     }
109
110         /*  --------------------- ilWriteFileInit -------------------------- */
111         /*  Init() function for ilWriteFileImage().
112         */
113 static ilError ilWriteFileInit (
114     register ilWriteFilePrivPtr pPriv,
115     ilImageInfo        *pSrcImage,
116     ilImageInfo        *pDstImage
117     )
118 {
119         /*  Point to beginning of strip offset/byte count memory buffers. */
120     pPriv->pStripOffset = pPriv->tagInfo.pStripOffsets;
121     pPriv->pStripByteCount = pPriv->tagInfo.pStripByteCounts;
122
123     pPriv->nStripsSoFar = 0;
124     pPriv->nLinesSoFar = 0;
125     pPriv->nBytesSoFar = 0;
126
127     return IL_OK;
128 }
129
130
131         /*  --------------------- ilWriteFileCleanup -------------------------- */
132         /*  Cleanup() function for ilWriteFileImage().
133         */
134 static ilError ilWriteFileCleanup (
135     register ilWriteFilePrivPtr pPriv,
136     ilBool                  aborting
137     )
138 {
139 register ilFilePtr          pFile;
140 ilFileOffset                position;
141 ilByte                      fourBytes[4], nextBytes[4];
142 ilPtr                       pBytes, pTag;
143 register CARD32            *pTagOffsets, l;
144 int                         i;
145
146         /*  If aborting pipe, just exit.  The net effect is that if
147             some image strips are written and the pipe aborted before completion,
148             the image strips are there but not pointed to by anything.
149         */
150     if (aborting)
151         return IL_OK;                       /* EXIT */
152
153         /*  Make sure the proper # of lines and strips have been written or error. */
154     if ((pPriv->nStripsSoFar != pPriv->nStrips) 
155      || (pPriv->nLinesSoFar != pPriv->fileImage.p.height))
156         return IL_ERROR_MALFORMED_FILE_WRITE;
157     pFile = pPriv->pFile;
158
159         /*  Read "offset to next" that will be pointed to this image; set as "offset to
160             next" for this image (IFDOffset), following tag info; see ilMergeWriteTags().
161         */
162     if (!IL_SEEK (pFile, pPriv->tailPtrOffset) || !IL_READ (pFile, 4, nextBytes))
163         return IL_ERROR_FILE_IO;
164     pBytes = pPriv->tagInfo.pTagWriteData + pPriv->tagInfo.tagCount * IL_TAG_SIZE + 2;
165     *pBytes++ = nextBytes[0]; 
166     *pBytes++ = nextBytes[1];
167     *pBytes++ = nextBytes[2];
168     *pBytes++ = nextBytes[3];
169
170         /*  Position to EOF; save file position in "position".  Update tags whose values
171             did not fit in the tag; they have a non-zero value in the array @pTagOffsets;
172             the value is the offset from the beginning of the data to be written to the
173             tag data.
174         */
175     if (fseek (pFile->stream, 0, 2))        /* seek to EOF (2); return != 0 is error */
176         return IL_ERROR_FILE_IO;
177     position = ftell (pFile->stream) - pFile->offset;
178     
179     pTagOffsets = pPriv->tagInfo.pTagOffsets;
180     pTag = pPriv->tagInfo.pTagWriteData + 2;
181     for (i = 0; i < pPriv->tagInfo.tagCount; i++, pTagOffsets++, pTag += IL_TAG_SIZE)
182         if (*pTagOffsets) {
183             l = position + *pTagOffsets;
184             pBytes = pTag + 8;              /* point to offset field of the tag */
185             IL_WRITE_FLIP_LONG (pPriv->bigEndian, l, pBytes)
186             }
187
188         /*  Write out the tag data to add the image to the file.  Patch location at
189             "tailPtrOffset" in file to point to this image to link image into list.
190         */
191     if (fwrite ((char *)pPriv->tagInfo.pTagWriteData, pPriv->tagInfo.tagWriteSize, 1, 
192                 pFile->stream) != 1)
193         return IL_ERROR_FILE_IO;
194     pBytes = fourBytes;
195 /* compatibility problem with long or unsigned long data fields */
196     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
197     if (!IL_SEEK (pFile, pPriv->tailPtrOffset) || !IL_WRITE (pFile, 4, fourBytes))
198         return IL_ERROR_FILE_IO;
199     fflush (pFile->stream);                 /* flush all buffers; image now in file */
200
201         /*  If this image is now last in file (its next offset is null), update file's 
202             offset to last image's offset to next, and copy of same in private.
203         */
204     if (!nextBytes[0] && !nextBytes[1] && !nextBytes[2] && !nextBytes[3]) {
205         pFile->IFDTailPtrOffset = position + pPriv->tagInfo.tagCount * IL_TAG_SIZE + 2;
206         pPriv->tailPtrOffset = pFile->IFDTailPtrOffset;
207         }
208
209         /*  Mark file image list as invalid, so this image will show up next time. */
210     pFile->haveImageList = FALSE;
211
212     return IL_OK;
213 }
214
215
216         /*  ------------------------ ilWriteFileDestroy ----------------------------- */
217         /*  Destroy() function for ilWriteFileImage().
218         */
219 static ilError ilWriteFileDestroy (
220     ilWriteFilePrivPtr      pPriv
221     )
222 {
223         /*  Free the tag data malloc'd by ilMergeWriteTags() when element added. */
224     IL_FREE (pPriv->tagInfo.pAlloc);
225     return IL_OK;
226 }
227
228         /*  --------------------- ilCopyPalette -------------------------- */
229         /*  Called by Execute() function before writing first strip to copy the palette
230             into the tag memory buffer, so it will be written out by ilWriteFileCleanup().
231         */
232 static void ilCopyPalette (
233     register ilWriteFilePrivPtr pPriv,
234     register unsigned short    *pSrcPalette
235     )
236 {
237 register unsigned short *pDstPalette;
238 register ilPtr          pBytes;
239 /* Since using IL_WRITE_FLIP_SHORT, palEntry should be unsigned short */
240 register unsigned short palEntry;
241 unsigned long           greenOffset, blueOffset;
242 int                     i;
243
244         /*  Copy palette into the in-memory tag area, byte-flipped.
245             It will be written out along with all the tags and tag data during Cleanup().
246             TIFF palettes are the same as IL: all the red, then green, then blue values,
247             each a short, except in TIFF each is "2 ** nBits" entries 
248             (pPriv->nPaletteColors) in length; in the IL they are always 256 entries.
249         */
250     pDstPalette = pPriv->tagInfo.pPalette;
251     if (pDstPalette) {
252         greenOffset = pPriv->tagInfo.nPaletteColors;
253         blueOffset = pPriv->tagInfo.nPaletteColors * 2;
254         for (i = 0; i < pPriv->tagInfo.nPaletteColors; i++, pSrcPalette++, pDstPalette++) {
255             palEntry = *pSrcPalette;
256             pBytes = (ilPtr)pDstPalette;
257             IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
258             palEntry = *(pSrcPalette + 256);
259             pBytes = (ilPtr)(pDstPalette + greenOffset);
260             IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
261             palEntry = *(pSrcPalette + 512);
262             pBytes = (ilPtr)(pDstPalette + blueOffset);
263             IL_WRITE_FLIP_SHORT (pPriv->bigEndian, palEntry, pBytes)
264             }
265         }
266 }
267
268
269         /*  --------------------- ilWriteFileExecute -------------------------- */
270         /*  Execute() for ilWriteFileImage().  
271         */
272 static ilError ilWriteFileExecute (
273     ilExecuteData          *pData,
274     long                    dstLine,
275     long                   *pNLines
276     )
277 {
278 register ilWriteFilePrivPtr pPriv;
279 ilFilePtr                   pFile;
280 unsigned long               nBytes, rowBytes;
281 ilPtr                       pBytes;
282 long                        position;
283
284     if (*pNLines <= 0)
285         return IL_OK;
286     pPriv = (ilWriteFilePrivPtr)pData->pPrivate;
287     pFile = pPriv->pFile;
288
289         /*  If first strip and a palette image copy the palette into tag data */
290     if ((pPriv->nStripsSoFar <= 0) && pPriv->tagInfo.pPalette)
291         ilCopyPalette (pPriv, pData->pSrcImage->pPalette);
292
293         /*  Check for too many lines/strips or inconstant strips (except last). */
294     pPriv->nLinesSoFar += *pNLines;
295     pPriv->nStripsSoFar++;
296     if ((pPriv->nStripsSoFar > pPriv->nStrips) 
297      || (pPriv->nLinesSoFar > pPriv->fileImage.p.height)
298      || ((pPriv->nLinesSoFar != pPriv->fileImage.p.height)  /* not last strip */
299              && (*pNLines != pPriv->fileImage.p.stripHeight)))
300         return IL_ERROR_MALFORMED_FILE_WRITE;
301
302         /*  Write one strip of data to the end of the file, after saving "position". */
303     if (fseek (pFile->stream, 0, 2))        /* seek to EOF (2); return != 0 is error */
304         return IL_ERROR_FILE_IO;            /* EXIT */
305     position = ftell (pFile->stream) - pFile->offset;
306     if (pPriv->compression == IL_UNCOMPRESSED) {
307         rowBytes = pData->pSrcImage->plane[0].nBytesPerRow;
308         nBytes = rowBytes * *pNLines;
309         pBytes = pData->pSrcImage->plane[0].pPixels + pData->srcLine * rowBytes;
310         }
311     else {
312         nBytes = pData->compressed.nBytesToRead;
313         pBytes = pData->pSrcImage->plane[0].pPixels + pData->compressed.srcOffset;
314         }
315     if (fwrite ((char *)pBytes, nBytes, 1, pFile->stream) != 1) 
316         return IL_ERROR_FILE_IO;            /* EXIT */
317
318         /*  Store byte-flipped the offset within the file (position) and size
319             (nBytes) of the strip just written.
320         */
321     pBytes = (ilPtr)pPriv->pStripOffset;
322     pPriv->pStripOffset++;
323 /* compatibility problem with long or unsigned long data fields */
324     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
325     pBytes = (ilPtr)pPriv->pStripByteCount;
326     pPriv->pStripByteCount++;
327 /* compatibility problem with long or unsigned long data fields */
328     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)nBytes, pBytes)
329
330     return IL_OK;
331 }
332
333
334     /*  --------------------------- ilWriteJIFData -------------------------------- */
335     /*  Called by ilWriteJPEGExecute() when first strip encountered, to encode
336         and write a JIF header into the TIFF file.
337     */
338 static ilError ilWriteJIFData (
339     register ilWriteFilePrivPtr pPriv,
340     ilFilePtr               pFile,
341     ilImageInfo            *pImage
342     )
343 {
344     iljpgJIFOffsetsRec      offsets;
345     iljpgDataRec            data;
346     long                    JIFOffset;
347     long                    position, nBytes, i, j;
348     ilJPEGEncodeStream      streamRec;
349     ilJPEGData             *pCompData;
350     ilError                 error;
351     long                    mcuWidth, mcuHeight;
352     register ilPtr          pBytes;
353
354
355         /*  Init streamRec so output is written into malloc'd memory, and encode
356             a JIF image into that memory.  For restartInterval value: if a single strip
357             use value from raw data (== 0 if no restart markers), otherwise calculate
358             it from strip size.  If multi-strip and non-zero restartMarker in raw data
359             a JIF image cannot be created - see below.
360         */
361     pCompData = (ilJPEGData *)pImage->pCompData;
362     _ilJPEGDataIn (pImage->pDes, pPriv->fileImage.p.width, pPriv->fileImage.p.height, 
363                    &data);
364     _ilJPEGTablesIn (pCompData, &data);
365
366     if (pPriv->nStrips != 1) {              /* else data.restartInterval = raw already */
367         mcuWidth = 8 * data.maxHoriFactor;
368         mcuHeight = 8 * data.maxVertFactor;
369         data.restartInterval =              /* # of mcus across * # down */
370             ((pPriv->fileImage.p.width + mcuWidth - 1) / mcuWidth) *
371             ((pPriv->fileImage.p.stripHeight + mcuHeight - 1) / mcuHeight);
372         }
373
374     streamRec.pBuffer = streamRec.pDst = streamRec.pPastEndBuffer = (ilPtr)NULL;
375     if (error = iljpgEncodeJIF (&streamRec, &data, &offsets))
376         return error;
377
378         /*  Write JIF header to end of file, at "position". */
379     if (fseek (pFile->stream, 0, 2))        /* seek to EOF (2); != 0 is error */
380         return IL_ERROR_FILE_IO;
381     position = ftell (pFile->stream) - pFile->offset;
382     nBytes = streamRec.pDst - streamRec.pBuffer;
383     if (fwrite ((char *)streamRec.pBuffer, nBytes, 1, pFile->stream) != 1) 
384         return IL_ERROR_FILE_IO;
385     IL_FREE (streamRec.pBuffer);            /* free malloc'd buffer space */
386     pPriv->nBytesSoFar = nBytes;            /* JIF data size so far */
387
388         /*  Update in-memory values for tags that point to JIF data and to
389             the Q/DC/AC tables, returned by iljpgEncodeJIF() above.
390                 Write restartInterval: if 0, no restart markers.  If non-zero,
391             then there must be only one strip in image or it cannot be encoded as
392             as a JIF image, because restart markers need to be written between the 
393             strips and would not mesh with restart markers already in the data; 
394             so in that case write JIF pointer marker = 0 => not JIF.
395             The resulting file will be able to be read by IL2.1 and later but not by
396             earlier IL versions or by other software that relies on JIF.
397         */
398     pBytes = (ilPtr)pPriv->tagInfo.pJPEGRestart;
399 /* type cast from int to short before FLIP */
400     IL_WRITE_FLIP_SHORT (pPriv->bigEndian, (short)pCompData->restartInterval, pBytes)
401
402     if (pCompData->restartInterval && (pPriv->nStrips != 1))
403          JIFOffset = 0;                     /* not valid JIF; write 0 ptr */
404     else JIFOffset = position;
405     pBytes = (ilPtr)pPriv->tagInfo.pJIF;
406 /* compatibility problem with long or unsigned long data fields */
407     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
408
409         /*  Write offsets to Q tables for each component: table offset for each 
410             table, indexed by component table index, plus file to offset to JIF hdr.
411         */
412     pBytes = (ilPtr)pPriv->tagInfo.pJPEGQTables;
413     for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
414         j = offsets.QTables[data.comp[i].QTableIndex];
415         if (!j)                             /* no Q table defined for that index */
416             return IL_ERROR_COMPRESSED_DATA;
417         j += position;                      /* now offset from start of "file" */
418 /* compatibility problem with long or unsigned long data fields */
419         IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
420         }
421
422         /*  Same thing for DC and AC tables */
423     pBytes = (ilPtr)pPriv->tagInfo.pJPEGDCTables;
424     for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
425         j = offsets.DCTables[data.comp[i].DCTableIndex];
426         if (!j)
427             return IL_ERROR_COMPRESSED_DATA;
428         j += position;
429 /* compatibility problem with long or unsigned long data fields */
430         IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
431         }
432
433     pBytes = (ilPtr)pPriv->tagInfo.pJPEGACTables;
434     for (i = 0; i < pPriv->fileImage.p.des.nSamplesPerPixel; i++) {
435         j = offsets.ACTables[data.comp[i].ACTableIndex];
436         if (!j)
437             return IL_ERROR_COMPRESSED_DATA;
438         j += position;
439 /* compatibility problem with long or unsigned long data fields */
440         IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)j, pBytes)
441         }
442
443     return IL_OK;
444 }
445
446         /*  --------------------- ilWriteJPEGExecute -------------------------- */
447         /*  Execute() for ilWriteFileImage() when writing JPEG-compressed data.
448         */
449 static ilError ilWriteJPEGExecute (
450     ilExecuteData          *pData,
451     long                    dstLine,
452     long                   *pNLines
453     )
454 {
455     register ilWriteFilePrivPtr pPriv;
456     ilFilePtr               pFile;
457     ilError                 error;
458     long                    position, nBytes, i, j;
459     register ilPtr          pBytes;
460     ilByte                  marker[2];
461
462         /*  The overall approach is to write a JIF image into the file plus strips.
463             The data written will be the JIF "header", followed by strips of JPEG
464             data, separated by restart markers.  Each strip will pointed to by
465             the normal TIFF strip offset/byte counts.  The resulting data can then
466             be read either as a JIF image (using the JPEGInterchangeFormat/Length
467             tags - how IL2.0 and earlier versions read it) or as a "raw" image using
468             the other JPEG tags (how later versions read it).
469         */
470     pPriv = (ilWriteFilePrivPtr)pData->pPrivate;
471     pFile = pPriv->pFile;
472
473         /*  Check for too many lines/strips or inconstant strips (except last). */
474     pPriv->nLinesSoFar += *pNLines;
475     pPriv->nStripsSoFar++;
476     if ((pPriv->nStripsSoFar > pPriv->nStrips) 
477      || (pPriv->nLinesSoFar > pPriv->fileImage.p.height)
478      || ((pPriv->nLinesSoFar != pPriv->fileImage.p.height)  /* not last strip */
479              && (*pNLines != pPriv->fileImage.p.stripHeight)))
480         return IL_ERROR_MALFORMED_FILE_WRITE;
481
482         /*  If first strip: encode a JIF header and write it to file.
483             Init streamRec so output is written into malloc'd memory, and encode
484             a JIF image into that memory.
485         */
486     if (pPriv->nStripsSoFar <= 1)                   /* first strip; inc'd above */
487         if (error = ilWriteJIFData (pPriv, pFile, pData->pSrcImage))
488             return error;
489
490         /*  Write one strip of data to end of the TIFF file, after saving "position". */
491     if (fseek (pFile->stream, 0, 2))        /* seek to EOF (2); return != 0 is error */
492         return IL_ERROR_FILE_IO;
493     position = ftell (pFile->stream) - pFile->offset;
494     nBytes = pData->compressed.nBytesToRead;
495     pBytes = pData->pSrcImage->plane[0].pPixels + pData->compressed.srcOffset;
496     if (fwrite ((char *)pBytes, nBytes, 1, pFile->stream) != 1) 
497         return IL_ERROR_FILE_IO;
498     pPriv->nBytesSoFar += nBytes + 2;       /* # bytes in strip + 2 for marker below */
499
500         /*  Store byte-flipped the offset within the file (position) and size
501             (nBytes) of the strip just written.
502         */
503     pBytes = (ilPtr)pPriv->pStripOffset;
504     pPriv->pStripOffset++;
505 /* compatibility problem with long or unsigned long data fields */
506     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (CARD32)position, pBytes)
507     pBytes = (ilPtr)pPriv->pStripByteCount;
508     pPriv->pStripByteCount++;
509 /* compatibility problem with long or unsigned long data fields */
510     IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)nBytes, pBytes)
511
512         /*  If last strip, add an EOI marker to end and store JIF length tag value;
513             otherwise add a restart marker, with strip count modulo 8 in it.
514         */
515     marker[0] = 0xff;
516     if (pPriv->nStripsSoFar >= pPriv->nStrips) {
517         marker[1] = ILJPGM_EOI;
518         pBytes = (ilPtr)pPriv->tagInfo.pJIFLength;
519 /* compatibility problem with long or unsigned long data fields */
520         IL_WRITE_FLIP_LONG (pPriv->bigEndian, (INT32)pPriv->nBytesSoFar, pBytes)
521         }
522     else marker[1] = ILJPGM_RST0 | ((pPriv->nStripsSoFar - 1) & 7);
523
524         /*  Write two byte restart or end of image (EOI) marker */
525     if (fseek (pFile->stream, 0, 2))
526         return IL_ERROR_FILE_IO;
527     if (fwrite ((char *)marker, 2, 1, pFile->stream) != 1) 
528         return IL_ERROR_FILE_IO;
529
530     return IL_OK;
531 }
532
533
534         /*  --------------------- ilMergeWriteTags ----------------------------------- */
535         /*  Called by ilWriteFileImage().  Merge the user and std tag info, copy the
536             tag data (flipped) and return into to *pInfo.
537         */
538 static ilError ilMergeWriteTags (
539     ilBool              bigEndian,              /* byte order to be written */
540     int                 nStdTags,               /* # of std (IL) tags */
541     ilFileTag          *pStdTags,               /* ptr to std tags */
542     int                 nUserTags,              /* # of user (IL) tags */
543     ilFileTag          *pUserTags,              /* ptr to user tags */
544     ilTagInfoPtr        pInfo)                  /* returned tag info */
545 {
546 ilFileTag             **ppTagsInit, **ppLastTag;
547 register ilFileTag    **ppTag, **ppTagTemp, *pTag;
548 ilPtr                   pTagData, pFileTags;
549 long                    tagArraySize, tagDataSize, l;
550 CARD32                 *pTagOffsets, *pLong;
551 int                     nTags, i;
552 unsigned short          tagType, *pShort;
553 register int            tagNumber;
554
555         /*  Allocate and null an array of ptrs to tags, size = # user + std tags, + 1
556             so that there is at least one guaranteed null tag ptr, for "end of array".
557         */
558     tagArraySize = nStdTags + nUserTags + 1;
559     ppTagsInit = (ilFileTag **)IL_MALLOC (tagArraySize * sizeof (ilFileTag **));
560     if (!ppTagsInit)
561         return IL_ERROR_MALLOC;
562     ppLastTag = ppTagsInit + tagArraySize - 1;
563     for (ppTag = ppTagsInit; ppTag <= ppLastTag; ppTag++)
564         *ppTag = (ilFileTag *)NULL;
565
566         /*  Fill in array with ptrs to tags, sorted in ascending numeric order (required
567             by TIFF!), with duplicates eliminated.  Do the user tags, then the std tags,
568             so that the std tag values overwrite the user if duplicates present.
569                 Sort by insertion sort: find tag with number >= this tag's number, or end
570             of list (null ptr).  If greater, shift all values up one; then insert this tag
571         */
572     for (i = 0, pTag = pUserTags, nTags = nUserTags;  i < 2;
573          i++, pTag = pStdTags, nTags = nStdTags) {
574         while (nTags-- > 0) {
575             ppTag = ppTagsInit;
576             while (*ppTag && ((*ppTag)->number < pTag->number))
577                 ppTag++;
578             if (*ppTag && ((*ppTag)->number > pTag->number)) {
579                 ppTagTemp = ppLastTag;
580                 while (ppTagTemp > ppTag) {
581                     *ppTagTemp = *(ppTagTemp - 1);
582                     ppTagTemp--;
583                     }
584                 }
585             *ppTag = pTag;
586             pTag++;
587             }
588         }
589
590         /*  Set "nTags" to the resulting number of tags, and "tagDataSize" to the size
591             of tag data which does not fit in the tag itself (rounded to mult of 2).
592         */
593     for (tagDataSize = 0, nTags = 0, ppTag = ppTagsInit; *ppTag; nTags++, ppTag++) {
594         pTag = *ppTag;
595         tagType = pTag->type;
596         if ((tagType == 0) || (tagType > IL_MAX_TAG_TYPE)) {
597             IL_FREE (ppTagsInit);
598             return IL_ERROR_FILE_TAG_TYPE;
599             }
600         if (pTag->nItems > _ilTagTypeItemsThatFit [tagType])
601             tagDataSize += (_ilTagTypeItemSizes [tagType] * pTag->nItems + 1) & ~1;
602         }
603
604         /*  Alloc room on heap for: an array of offsets to tag data for each tag, or 0
605             if tag data fits in tag; the # of tags (2 bytes); the tags themselves; the 
606             next IFD offset (4 bytes); and the tag data that does not fit in the tags.
607             pInfo->pAlloc will point to it all:
608                     -----------------------------
609                     | offset to data for each   |   <- pTagOffsets (not written)
610                     | tag or 0 if data in tag   |  (rest is written to TIFF, and is
611                     |                           |   "tagWriteSize" bytes in size
612                     ----------------------------- 
613                     | # of tags (short)         |   <- pTagWriteData
614                     ----------------------------- 
615                     | tag info for each tag     |   +2
616                     | (of type ilTIFFTagRec)    |
617                     ----------------------------- 
618                     | ptr to next IFD           |   + # tags * IL_TAG_SIZE + 2
619                     ----------------------------- 
620                     | tag data external to tags |   + # tags * IL_TAG_SIZE + 6
621                     ----------------------------- 
622         */
623     pInfo->tagCount = nTags;
624     pInfo->tagWriteSize = nTags * IL_TAG_SIZE + 2 + tagDataSize + 4;
625     pInfo->pAlloc = (ilPtr)IL_MALLOC_ZERO (nTags * sizeof(INT32) + pInfo->tagWriteSize);
626     if (!pInfo->pAlloc) {
627         IL_FREE (ppTagsInit);
628         return IL_ERROR_MALLOC;
629         }
630     pInfo->pTagOffsets = (CARD32 *)pInfo->pAlloc;
631     pInfo->pTagWriteData = pInfo->pAlloc + nTags * sizeof(INT32);
632     pTagData = (ilPtr)pInfo->pTagWriteData + nTags * IL_TAG_SIZE + 6;
633
634         /*  Write the # of tags byte-flipped, point "pFileTags" to tag data write area.
635             For byte/ascii data: "nItems" is length of string, including trailing null
636             for ascii.  Round up to even byte boundary (TIFF requirement).
637         */
638     pFileTags = (ilPtr)pInfo->pTagWriteData;
639     IL_WRITE_FLIP_SHORT (bigEndian, (unsigned short)nTags, pFileTags)
640
641     for (ppTag = ppTagsInit, pTagOffsets = pInfo->pTagOffsets; *ppTag; ppTag++) {
642         ilPtr                   pWrite;
643
644         pTag = *ppTag;
645         tagType = pTag->type;
646         IL_WRITE_FLIP_SHORT (bigEndian, pTag->number, pFileTags)
647         IL_WRITE_FLIP_SHORT (bigEndian, tagType, pFileTags)
648 /* compatibility problem with long or unsigned long data fields */
649         IL_WRITE_FLIP_LONG  (bigEndian, (CARD32)pTag->nItems, pFileTags)
650         if (pTag->nItems > _ilTagTypeItemsThatFit [tagType]) {
651             *pTagOffsets++ = pTagData - pInfo->pTagWriteData;
652             pWrite = pTagData;
653             pTagData += (_ilTagTypeItemSizes [tagType] * pTag->nItems + 1) & ~1UL;
654             }
655         else {
656             *pTagOffsets++ = 0;
657             pWrite = pFileTags;
658             }
659         pFileTags += 4;                 /* skip one TIFF long */
660
661             /*  Copy the data at "*pTag->pItems" to "pWrite", with byte-flip.
662                 pWrite points to 4 byte area in tag if items fit, else to area in pTagData.
663                 NOTE: handle strip offsets/byte counts specially: pItems is ignored.
664             */
665         tagNumber = pTag->number;
666         if (tagNumber == IL_TAG_STRIP_OFFSETS)
667             pInfo->pStripOffsets = (ilFileOffset *)pWrite;
668         else if (tagNumber == IL_TAG_STRIP_BYTE_COUNTS)
669             pInfo->pStripByteCounts = (INT32 *)pWrite;
670         else if (tagNumber == IL_TAG_COLOR_MAP)
671             pInfo->pPalette = (unsigned short *)pWrite;
672         else if (tagNumber == IL_TAG_JPEG_INTERCHANGE_FORMAT)
673             pInfo->pJIF = (ilFileOffset *)pWrite;
674         else if (tagNumber == IL_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
675             pInfo->pJIFLength = (long *)pWrite;
676         else if (tagNumber == IL_TAG_JPEG_RESTART_INTERVAL)
677             pInfo->pJPEGRestart = (unsigned short *)pWrite;
678         else if (tagNumber == IL_TAG_JPEG_Q_TABLES)
679             pInfo->pJPEGQTables = (long *)pWrite;
680         else if (tagNumber == IL_TAG_JPEG_DC_TABLES)
681             pInfo->pJPEGDCTables = (long *)pWrite;
682         else if (tagNumber == IL_TAG_JPEG_AC_TABLES)
683             pInfo->pJPEGACTables = (long *)pWrite;
684         else switch (tagType) {
685           case IL_TAG_BYTE:
686           case IL_TAG_ASCII:
687             bcopy ((char *)pTag->pItems, (char *)pWrite, pTag->nItems);
688             break;
689           case IL_TAG_SHORT:
690             for (i = 0, pShort = (unsigned short *)pTag->pItems; 
691                                    i < pTag->nItems; i++, pShort++)
692                 IL_WRITE_FLIP_SHORT (bigEndian, *pShort, pWrite)
693             break;
694           case IL_TAG_LONG:
695 /* compatibility problem with long or unsigned long data fields */
696             for (i = 0, pLong = (CARD32 *)pTag->pItems;
697                                    i < pTag->nItems; i++, pLong++)
698                 IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
699             break;
700           case IL_TAG_RATIONAL:
701 /* compatibility problem with long or unsigned long data fields */
702             for (i = 0, pLong = (CARD32 *)pTag->pItems; i < pTag->nItems; i++) {
703                 IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
704                 pLong++;
705                 IL_WRITE_FLIP_LONG (bigEndian, *pLong, pWrite)
706                 pLong++;
707                 }
708             break;
709             }
710         }   /* END for one tag */
711
712         /*  Fill in ptr to next IFD (= 0, null), free temp alloc space and exit.
713         */
714 /* compatibility problem with long or unsigned long data fields */
715     IL_WRITE_FLIP_LONG (bigEndian, 0, pFileTags)
716     IL_FREE (ppTagsInit);
717     return IL_OK;
718 }
719
720         /*  ---------------------- ilFormatPipeImage --------------------------------- */
721         /*  Reformat and compress the pipe image as necessary.  On entry, pStripHeight
722             must point to the callers requested stripHeight; on return it is the new
723             (constant) stripHeight to use to write.
724                 On entry, pInfo/pDes/pFormat must point to the current pipe values;
725             on return they are the new pipe values.  Returns false if an error occurs.
726         */
727 static ilBool ilFormatPipeImage (
728     ilPipe                  pipe,
729     unsigned int            compression,
730     ilPtr                   pCompData,
731     long                   *pStripHeight,
732     register ilPipeInfo    *pInfo,
733     register ilImageDes    *pDes,
734     register ilImageFormat *pFormat
735     )
736 {
737 #define IL_WRITE_TIFF_STRIP_SIZE (16 * 1024)    /* write 16KB strips as default */
738 register long               stripHeight;
739 long                        i, nLevels;
740 ilBool                      mustConvert;
741 unsigned long               g3Flags, g4Flags;
742 ilJPEGEncodeControl         jpegEncode;
743
744         /*  If compression is "use current compression" (which makes it easier for
745             caller to copy images), then set compression to current pipe compression,
746             and point pCompData to compression-specific current pipe data.
747         */
748     if (compression == IL_WRITE_CURRENT_COMPRESSION) {
749         compression = pDes->compression;
750         if (compression == IL_G3) {
751             g3Flags = pDes->compInfo.g3.flags;
752             pCompData = (ilPtr)&g3Flags;
753             }
754         else if (compression == IL_G4) {
755             g4Flags = pDes->compInfo.g4.flags;
756             pCompData = (ilPtr)&g4Flags;
757             }
758         }
759
760         /*  Compare the levels/sample to bits/sample, and scale up if levels != 2**bits.
761             This is because the IL separates bits and levels, but TIFF assumes # levels
762             = 2**bits.  A 16 level 8 bit gray image must be level-scaled up to 256 or
763             else the reader will assume 256 level and display it too dark.
764             However, bitonal must be bit/pixel, and for palette, levels is ignored.
765                 NOTE: the right thing to do is to convert the format (e.g. change 16
766             level 8 bit gray to 4 bit gray), but ilConvert() does not handle this yet!
767         */
768     switch (pDes->type) {
769       case IL_BITONAL:
770         if (pFormat->nBitsPerSample[0] != 1) {
771             pFormat->nBitsPerSample[0] = 1;
772             if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
773                 return FALSE;
774             }
775         break;
776       case IL_PALETTE:                      /* ignore nLevels for palette images */
777         break;
778       default:                              /* continuous tone: match levels to bits */
779         for (i = 0, mustConvert = FALSE; i < pDes->nSamplesPerPixel; i++) {
780             nLevels = 1 << pFormat->nBitsPerSample[i];
781             if (pDes->nLevelsPerSample[i] != nLevels) {
782                 pDes->nLevelsPerSample[i] = nLevels;
783                 mustConvert = TRUE;
784                 }
785             }
786         if (mustConvert)
787             if (!ilConvert (pipe, pDes, pFormat, 0, (ilPtr)NULL))
788                 return FALSE;
789         break;
790         }
791
792         /*  Convert to pixel order and reget pipe info if multi-sample and planar. 
793             However, leave planar if JPEG compression - keep YCbCr subsampled if so.
794         */
795     if (pDes->nSamplesPerPixel == 1)
796         pFormat->sampleOrder = IL_SAMPLE_PIXELS;
797     else if ((pFormat->sampleOrder != IL_SAMPLE_PIXELS) && (compression != IL_JPEG)) {
798         pFormat->sampleOrder = IL_SAMPLE_PIXELS;
799         if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
800             return FALSE;
801         ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
802         }
803
804         /*  If writing uncompressed, force rowBitAlign of 8 (required by TIFF);
805             else compress to stripHeight, using the given compression & data.
806             If stripHeight is 0 use default TIFF write size, based on uncompressed size.
807         */
808     stripHeight = *pStripHeight;
809
810     if (compression == IL_UNCOMPRESSED) {
811         ilGetPipeInfo (pipe, TRUE, pInfo, pDes, pFormat);   /* force decompression */
812         if (pFormat->rowBitAlign != 8) {
813             pFormat->rowBitAlign = 8;
814             if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, (ilPtr)NULL))
815                 return FALSE;
816             ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
817             }
818         if (stripHeight <= 0) {
819             long          bytesPerRow [IL_MAX_SAMPLES];
820             ilGetBytesPerRow (pDes, pFormat, pInfo->width, bytesPerRow);
821             stripHeight = IL_WRITE_TIFF_STRIP_SIZE / bytesPerRow[0];
822             }
823         if (stripHeight <= 0)
824             stripHeight = 1;
825         else if (stripHeight > pInfo->height)
826             stripHeight = pInfo->height;
827         *pStripHeight = stripHeight;        /* write with desired strip height */
828         }
829     else {
830             /*  If compressing JPEG, force "raw" format; get caller's Q (dflt = 0).
831                 Compress with desired strip height, unless already JPEG compressed,
832                 in which case use 0 strip height: accept current strip height.
833             */
834         if (compression == IL_JPEG) {
835             jpegEncode.mustbezero = IL_JPEGM_RAW;
836             if (pCompData)
837                  jpegEncode.Q = ((ilJPEGEncodeControl *)pCompData)->Q;
838             else jpegEncode.Q = 0;
839             pCompData = (ilPtr)&jpegEncode;
840             if (pDes->compression == IL_JPEG)
841                 stripHeight = 0;            /* accept current strip height */
842             }
843         if (!ilCompress (pipe, compression, pCompData, stripHeight, 0))
844             return FALSE;
845         ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
846         *pStripHeight = pInfo->stripHeight; /* strip height determined by compression */
847         }
848
849     return TRUE;
850 }
851
852
853         /*  ---------------------- ilWriteFileImage ---------------------------------- */
854         /*  Public function: see spec.
855             Adds a consumer to the given pipe to write the current pipe image.
856         */
857
858 IL_EXTERN char _ilVersionString[];    /* in /ilc/ilversion.c ; written for "software" tag */
859
860 ilBool ilWriteFileImage (
861     ilPipe              pipe,
862     ilFile              file,
863     unsigned int        method,
864     ilFileImage         fileImage,
865     unsigned int        compression,
866     ilPtr               pCompData,
867     long                stripHeight,
868     long                xRes,
869     long                yRes,
870     short               page,
871     short               nPages,
872     int                 nUserTags,
873     ilFileTag          *pUserTags,
874     unsigned long       mustBeZero
875     )
876 {
877 #define MAX_WRITE_TAGS  50          /* max # of std (non-user) tags added here */
878
879 #define TW_ADD_TAG(_number, _type, _nItems, _pItems) { \
880     pTag->number = (_number);                       \
881     pTag->type = (_type);                           \
882     pTag->nItems = (_nItems);                       \
883     pTag->pItems = (ilPtr)(_pItems);                \
884     pTag++;                                         \
885     nTags++;                                        \
886     }
887
888 long                    nStripsPerImage;
889 ilFileTag               tags [MAX_WRITE_TAGS];
890 register ilFileTag     *pTag;
891 int                     nTags, i;
892 ilTagInfoRec            tagInfo;
893 ilFilePtr               pFile;
894 ilPipeInfo              info;
895 ilImageDes              des;
896 ilImageFormat           format;
897 ilContext               context;
898 ilError                 error;
899 ilWriteFilePrivPtr      pPriv;
900 ilSrcElementData        srcData;
901 unsigned long           group3Options, group4Options;
902 ilFileImageRelation     writeMethod;
903 ilFileOffset            tailPtrOffset;
904
905     /*  NOTE: below data types (sizes) FIXED! - tied to TIFF type (short/long) */
906 short                   ditherLevels[IL_TAG_P0_DITHER_LEVELS_LENGTH];
907 short                   bitsPerSample [IL_MAX_SAMPLES], TIFFcompression, photo;
908 short                   nSamplesPerPixel, resolutionUnit, planarConfiguration;
909 short                   pageInfo[2], predictor, fillOrder;
910 short                   jpegProc, subsample[2], positioning;
911 long                    width, height, newSubFileType;
912 long                    xResolution[2], yResolution[2], coeff[6], refBW[12];
913
914 #define                 VERSION_LENGTH  18      /* length includes trailing null */
915 char                    versionName [VERSION_LENGTH];
916
917
918         /*  Get pipe state - must be IL_PIPE_FORMING or declare an error. */
919     pFile = (ilFilePtr)file;
920     context = pFile->o.p.context;
921     if (ilGetPipeInfo (pipe, FALSE, &info, &des, &format) != IL_PIPE_FORMING) {
922         if (!context->error)
923             ilDeclarePipeInvalid (pipe, IL_ERROR_PIPE_STATE);
924         return FALSE;
925         }
926
927     if (mustBeZero != 0)
928         return ilDeclarePipeInvalid (pipe, IL_ERROR_PAR_NOT_ZERO);
929     if (context != pipe->context)
930         return ilDeclarePipeInvalid (pipe, IL_ERROR_CONTEXT_MISMATCH);
931
932         /*  Force the file image list to be formed if not already; exit on error. */
933     ilListFileImages (file, 0);
934     if (context->error)
935         return ilDeclarePipeInvalid (pipe, context->error);
936
937         /*  Handle "method" set "writeMethod", "newSubFileType". */
938     switch (method) {
939       case IL_WRITE_MAIN:                       /* no parent, add as last image */
940         writeMethod = mainImage;
941         newSubFileType = 0;
942         fileImage = (ilFileImage)NULL;
943         break;
944
945         /*  Transparency mask: error if no parent, or parent already has or is a mask, or
946             not bitonal image.
947         */
948       case IL_WRITE_MASK:
949         if (!fileImage                          /* no parent */
950          || (fileImage->file != file)           /* not from file being written */
951          || fileImage->pMask                    /* parent already has a mask */
952          || (((ilFileImagePtr)fileImage)->imageType == maskImage)
953          || (des.nSamplesPerPixel != 1)         /* not bitonal */
954          || (format.nBitsPerSample[0] != 1))
955             return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
956         writeMethod = maskImage;
957         newSubFileType = 4;                     /* TIFF transparency mask */
958         break;
959
960         /*  Child image: must have non-mask parent fileImage with same file or error.
961             Point to last image associated with parent: find last child (its mask if
962             it has one), so that child images are in order written, and masks "bind"
963             closest to image that they are a mask of.
964         */
965       case IL_WRITE_CHILD:
966         writeMethod = childImage;
967         newSubFileType = 1;                     /* TIFF reduced resolution image */
968         if (!fileImage                          /* no parent */
969          || (fileImage->file != file)           /* not from file being written */
970          || (((ilFileImagePtr)fileImage)->imageType == maskImage))
971             return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
972         while (fileImage->pChild)
973             fileImage = fileImage->pChild;
974         if (fileImage->pMask)
975             fileImage = fileImage->pMask;       /* write after the mask image */
976         break;
977
978       default:
979         return ilDeclarePipeInvalid (pipe, IL_ERROR_FILE_WRITE_METHOD);
980         break;
981         }
982
983         /*  Reformat and compress the pipe image as necessary.  Return the stripHeight
984             to use, and the new pipe info/des/format.
985         */
986     if (!ilFormatPipeImage (pipe, compression, pCompData, &stripHeight, 
987                             &info, &des, &format))
988         return FALSE;
989
990         /*  Form the tags to be written for this file image. */
991     nTags = 0;
992     pTag = tags;
993
994         /*  Write the 1st part of the IL version string as the "software" tag, starting 4
995             chars into string (past '@(#)') - remember length includes trailing null!
996         */
997     bcopy (&_ilVersionString[4], versionName, VERSION_LENGTH - 1);
998     versionName [VERSION_LENGTH-1] = 0;
999     TW_ADD_TAG (IL_TAG_SOFTWARE, IL_TAG_ASCII, VERSION_LENGTH, versionName)
1000
1001     if (page >= 0)
1002         newSubFileType |= 2;        /* mark as multi-page image */
1003     TW_ADD_TAG (IL_TAG_NEW_SUBFILE_TYPE, IL_TAG_LONG, 1, &newSubFileType)
1004
1005     width = info.width;
1006     TW_ADD_TAG (IL_TAG_IMAGE_WIDTH, IL_TAG_LONG, 1, &width)
1007
1008     height = info.height;
1009     TW_ADD_TAG (IL_TAG_IMAGE_LENGTH, IL_TAG_LONG, 1, &height)
1010
1011     for (i = 0; i < des.nSamplesPerPixel; i++)
1012         bitsPerSample [i] = format.nBitsPerSample [i];
1013     TW_ADD_TAG (IL_TAG_BITS_PER_SAMPLE, IL_TAG_SHORT, des.nSamplesPerPixel, bitsPerSample)
1014
1015         /*  Determine TIFF compression tag based on pipe image compression and possibly
1016             des.compInfo.  Ensure pCompData null if not used.
1017         */
1018     switch (des.compression) {
1019       case IL_UNCOMPRESSED:  
1020         TIFFcompression = 1; 
1021         break;
1022
1023         /*  Group 3: use des.compInfo.g3.flags.
1024             If 0, write TIFF G3 (code 2), else code 3: must have EOL_MARKERS; init
1025             group3Options to be written below, and write FillOrder tag (required for 
1026             Class F; not recommended for other uses in TIFF).
1027             Add group 3 options tag only if "true" (not TIFF) G3 (TIFFcompression == 3)
1028         */
1029       case IL_G3: 
1030       { unsigned long userOptions;
1031         userOptions = des.compInfo.g3.flags;
1032         if (userOptions == 0)
1033             TIFFcompression = 2;
1034         else {
1035             if (userOptions & ~0x3f)        /* unhandled bits set: error */
1036                 return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
1037             group3Options = 0;
1038             fillOrder = (userOptions & IL_G3M_LSB_FIRST) ? 2 : 1;
1039             TW_ADD_TAG (IL_TAG_FILL_ORDER, IL_TAG_SHORT, 1, &fillOrder)
1040
1041             if (userOptions & IL_G3M_2D)
1042                 group3Options |= 1;
1043             if (userOptions & IL_G3M_UNCOMPRESSED)
1044                 group3Options |= 2;
1045             if (!(userOptions & IL_G3M_EOL_MARKERS))
1046                 return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
1047             if (!(userOptions & IL_G3M_EOL_UNALIGNED))
1048                 group3Options |= 4;
1049             TW_ADD_TAG (IL_TAG_GROUP_3_OPTIONS, IL_TAG_LONG, 1, &group3Options)
1050             TIFFcompression = 3;
1051             }
1052       }
1053         break;
1054
1055       case IL_G4:            
1056       {    unsigned long userOptions;
1057             userOptions = des.compInfo.g4.flags;
1058             fillOrder = (userOptions & IL_G4M_LSB_FIRST) ? 2 : 1;
1059             TW_ADD_TAG (IL_TAG_FILL_ORDER, IL_TAG_SHORT, 1, &fillOrder)
1060       }
1061         group4Options = 0;
1062         TW_ADD_TAG (IL_TAG_GROUP_4_OPTIONS, IL_TAG_LONG, 1, &group4Options)
1063         TIFFcompression = 4; 
1064         break;
1065
1066       case IL_LZW:           
1067         if (pCompData)
1068             return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
1069         TIFFcompression = 5; 
1070         break;
1071
1072         /*  JPEG: write JIF with restart markers between strips.
1073             For JIF-in-TIFF: add tags for a file ptr and length of the JIF image,
1074             and the restart interval, but dont know value until JIF image received.
1075         */
1076       case IL_JPEG:
1077         jpegProc = 1;           /* baseline sequential process */
1078         TW_ADD_TAG (IL_TAG_JPEG_PROC, IL_TAG_SHORT, 1, &jpegProc)
1079         TW_ADD_TAG (IL_TAG_JPEG_INTERCHANGE_FORMAT, IL_TAG_LONG, 1, NULL)
1080         TW_ADD_TAG (IL_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, IL_TAG_LONG, 1, NULL)
1081         TW_ADD_TAG (IL_TAG_JPEG_RESTART_INTERVAL, IL_TAG_SHORT, 1, NULL)
1082         TW_ADD_TAG (IL_TAG_JPEG_Q_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
1083         TW_ADD_TAG (IL_TAG_JPEG_DC_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
1084         TW_ADD_TAG (IL_TAG_JPEG_AC_TABLES, IL_TAG_LONG, des.nSamplesPerPixel, NULL)
1085
1086         TIFFcompression = 6;
1087         break;
1088
1089       case IL_PACKBITS:      
1090         if (pCompData)
1091             return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
1092         TIFFcompression = 32773; 
1093         break;
1094
1095       default:
1096         return ilDeclarePipeInvalid (pipe, IL_ERROR_COMPRESSION);
1097         }
1098     TW_ADD_TAG (IL_TAG_COMPRESSION, IL_TAG_SHORT, 1, &TIFFcompression)
1099
1100         /*  Image type-specific code: set PhotometricInterpretation: to 4 (transparency
1101             mask) if a maskImage, else based on image type.
1102         */
1103     if (writeMethod == maskImage)
1104         photo = 4;                      /* special code for transparency mask images */
1105     else switch (des.type) {
1106       case IL_BITONAL:
1107       case IL_GRAY:     photo = (des.blackIsZero) ? 1 : 0; break;
1108       case IL_PALETTE:  photo = 3; break;
1109       case IL_RGB:      photo = 2; break;
1110
1111         /*  YCbCr subsampling currently supported only if JPEG/JIF compressed. */
1112       case IL_YCBCR:
1113             /* Add the YCbCr-specific tags: coefficients as fractions of 10000 */
1114         coeff[0] = des.typeInfo.YCbCr.lumaRed;
1115         coeff[2] = des.typeInfo.YCbCr.lumaGreen;
1116         coeff[4] = des.typeInfo.YCbCr.lumaBlue;
1117         coeff[1] = coeff[3] = coeff[5] = 10000;
1118         TW_ADD_TAG (IL_TAG_YCBCR_COEFFICIENTS, IL_TAG_RATIONAL, 3, coeff)
1119
1120             /*  TIFF only allows subsampling as follows: Y = 1, Cr/Cb = 1,2,4 but
1121                 Cr and CB equal to each other; vert <= horiz.
1122             */
1123         { register ilYCbCrSampleInfo *pSample = des.typeInfo.YCbCr.sample;
1124
1125         if ((pSample[0].subsampleHoriz != 1)
1126          || (pSample[0].subsampleVert != 1)
1127          || (pSample[1].subsampleHoriz != pSample[2].subsampleHoriz)
1128          || (pSample[1].subsampleVert != pSample[2].subsampleVert)
1129          || (pSample[1].subsampleVert > pSample[2].subsampleHoriz))
1130             return ilDeclarePipeInvalid (pipe, IL_ERROR_YCBCR_TIFF);
1131         subsample[0] = pSample[1].subsampleHoriz;
1132         subsample[1] = pSample[1].subsampleVert;
1133         TW_ADD_TAG (IL_TAG_YCBCR_SUBSAMPLING, IL_TAG_SHORT, 2, subsample)
1134
1135             /*  Set positioning - ? is centered correct ? */
1136         positioning = 1;
1137         TW_ADD_TAG (IL_TAG_YCBCR_POSITIONING, IL_TAG_SHORT, 1, &positioning)
1138
1139         refBW[0]  = pSample[0].refBlack; refBW[1] = 1;
1140         refBW[2]  = pSample[0].refWhite; refBW[3] = 1;
1141         refBW[4]  = pSample[1].refBlack; refBW[5] = 1;
1142         refBW[6]  = pSample[1].refWhite; refBW[7] = 1;
1143         refBW[8]  = pSample[2].refBlack; refBW[9] = 1;
1144         refBW[10] = pSample[2].refWhite; refBW[11] = 1;
1145         TW_ADD_TAG (IL_TAG_REFERENCE_BLACK_WHITE, IL_TAG_RATIONAL, 6, refBW)
1146         }
1147         photo = 6;
1148         break;
1149
1150       default:
1151         return ilDeclarePipeInvalid (pipe, IL_ERROR_IMAGE_TYPE);
1152         }
1153     TW_ADD_TAG (IL_TAG_PHOTOMETRIC_INTERPRETATION, IL_TAG_SHORT, 1, &photo)
1154
1155         /*  Write # strips stripOffsets/ByteCounts, but supply no data - called
1156             function handles these specially and creates blank space, filled in later.
1157         */
1158     nStripsPerImage = (info.height + stripHeight - 1) / stripHeight;
1159     TW_ADD_TAG (IL_TAG_STRIP_OFFSETS, IL_TAG_LONG, nStripsPerImage, NULL)
1160     TW_ADD_TAG (IL_TAG_STRIP_BYTE_COUNTS, IL_TAG_LONG, nStripsPerImage, NULL)
1161
1162     nSamplesPerPixel = des.nSamplesPerPixel;
1163     TW_ADD_TAG (IL_TAG_SAMPLES_PER_PIXEL, IL_TAG_SHORT, 1, &nSamplesPerPixel)
1164
1165     TW_ADD_TAG (IL_TAG_ROWS_PER_STRIP, IL_TAG_LONG, 1, &stripHeight);
1166
1167         /*  Write x/yRes: specified in dpi * 2; default to 300 dpi if not given. */
1168     resolutionUnit = 2;
1169     TW_ADD_TAG (IL_TAG_RESOLUTION_UNIT, IL_TAG_SHORT, 1, &resolutionUnit)
1170     if (xRes <= 0) xRes = 600;
1171     xResolution [0] = xRes;
1172     xResolution [1] = 2;
1173     TW_ADD_TAG (IL_TAG_X_RESOLUTION, IL_TAG_RATIONAL, 1, xResolution)
1174     if (yRes <= 0) yRes = 600;
1175     yResolution [0] = yRes;
1176     yResolution [1] = 2;
1177     TW_ADD_TAG (IL_TAG_Y_RESOLUTION, IL_TAG_RATIONAL, 1, yResolution)
1178
1179         /*  As per TIFF spec, write planarConfiguration tag only if # samples > 1. */
1180     if (des.nSamplesPerPixel > 1) {
1181         planarConfiguration = (format.sampleOrder == IL_SAMPLE_PIXELS) ? 1 : 2;
1182         TW_ADD_TAG (IL_TAG_PLANAR_CONFIGURATION, IL_TAG_SHORT, 1, &planarConfiguration)
1183         }
1184
1185     if (page >= 0) {                        /* else no page tag written */
1186         pageInfo[0] = page;
1187         pageInfo[1] = nPages;
1188         TW_ADD_TAG (IL_TAG_PAGE_NUMBER, IL_TAG_SHORT, 2, pageInfo)
1189         }
1190
1191     if (des.compression == IL_LZW) {
1192         predictor = 1;
1193         TW_ADD_TAG (IL_TAG_PREDICTOR, IL_TAG_SHORT, 1, &predictor)
1194         }
1195
1196         /*  If a palette entry, add a colorMap tag; # colors = 2 ** # bits. 
1197             If dithered flags bit set, write private tag to mark it as such.
1198         */
1199     if (des.type == IL_PALETTE) {
1200         tagInfo.nPaletteColors = 1 << format.nBitsPerSample [0];
1201         TW_ADD_TAG (IL_TAG_COLOR_MAP, IL_TAG_SHORT, 3 * tagInfo.nPaletteColors, NULL)
1202
1203        if (des.flags & IL_DITHERED_PALETTE) {
1204             ditherLevels[0] = IL_TAG_P0_FLAG_DITHER_LEVELS;
1205             ditherLevels[1] = des.typeInfo.palette.levels[0];
1206             ditherLevels[2] = des.typeInfo.palette.levels[1];
1207             ditherLevels[3] = des.typeInfo.palette.levels[2];
1208             TW_ADD_TAG (IL_TAG_PRIVATE_0, IL_TAG_SHORT, IL_TAG_P0_DITHER_LEVELS_LENGTH,
1209                      ditherLevels)
1210             }
1211         }
1212     else tagInfo.pPalette = (unsigned short *)NULL;
1213
1214         /*  Merge the user tags with the tags formed above, convert the data to
1215             the proper byte order and return the allocated info to "tagInfo" or error.
1216         */
1217     if (error = ilMergeWriteTags (pFile->bigEndian, nTags, tags, nUserTags, pUserTags, 
1218                                   &tagInfo))
1219         return ilDeclarePipeInvalid (pipe, error);
1220
1221         /*  Add consumer pipe element, forcing constant "stripHeight" - ignored if pipe
1222             image is compressed - stripHeight was forced above by call to ilCompress().
1223             Use separate function for when writing JPEG.
1224         */
1225     srcData.consumerImage = (ilObject)NULL;
1226     srcData.stripHeight = stripHeight;
1227     srcData.constantStrip = TRUE;
1228     srcData.minBufferHeight = 0;
1229     pPriv = (ilWriteFilePrivPtr)ilAddPipeElement (pipe, IL_CONSUMER, 
1230         sizeof(ilWriteFilePrivRec), 0, &srcData, (ilDstElementData *)NULL,
1231         ilWriteFileInit, ilWriteFileCleanup, ilWriteFileDestroy, 
1232         (des.compression == IL_JPEG) ? ilWriteJPEGExecute : ilWriteFileExecute, 0);
1233     if (!pPriv)
1234         return FALSE;
1235
1236         /*  Fill in private (*pPriv), including template for file image to be added. */
1237     pPriv->pFile = pFile;
1238     pPriv->tagInfo = tagInfo;
1239     pPriv->bigEndian = pFile->bigEndian;
1240     pPriv->compression = des.compression;
1241     pPriv->nStrips = nStripsPerImage;
1242
1243         /*  Point pPriv->tailPtrOffset to location in file to point to this image: "ptr 
1244             to next" of last image in file (if main image) else ptr to next of parent.
1245         */
1246     if (fileImage)                              /* have parent: child or mask image */
1247         pPriv->tailPtrOffset = ((ilFileImagePtr)fileImage)->tagOffset +
1248                                ((ilFileImagePtr)fileImage)->tagCount * IL_TAG_SIZE;
1249     else pPriv->tailPtrOffset = pFile->IFDTailPtrOffset;
1250
1251     pPriv->fileImage.p.file = (ilFile)pFile;
1252     pPriv->fileImage.p.pNext = pPriv->fileImage.p.pPrev = 
1253         pPriv->fileImage.p.pChild = pPriv->fileImage.p.pMask = (ilFileImage)NULL;
1254     pPriv->fileImage.p.width = info.width;
1255     pPriv->fileImage.p.height = info.height;
1256     pPriv->fileImage.p.xRes = xRes;
1257     pPriv->fileImage.p.yRes = yRes;
1258     pPriv->fileImage.p.page = page;
1259     pPriv->fileImage.p.nPages = nPages;
1260     pPriv->fileImage.p.des = des;
1261     pPriv->fileImage.p.format = format;
1262     pPriv->fileImage.p.stripHeight = stripHeight;
1263     pPriv->fileImage.context = pFile->o.p.context;
1264     pPriv->fileImage.tagCount = tagInfo.tagCount;
1265
1266     pipe->context->error = IL_OK;
1267     return TRUE;
1268 }
1269
1270
1271         /*  ---------------------- ilInitFile ---------------------------------- */
1272         /*  Public function: see spec.
1273             Writes a TIFF file header at position "offset" of file "stream" using
1274             Motorola order (MM) if "MSBIsFirst", else Intel order (II).
1275         */
1276 ilBool ilInitFile (
1277     ilContext           context,
1278     FILE               *stream,
1279     long                offset,
1280     ilBool              MSBIsFirst,
1281     unsigned long       mustBeZero
1282     )
1283 {
1284 ilBool                  bigEndian;
1285 ilFilePtr               pFile;
1286 ilByte                 *pHeader;
1287 static ilByte           MSBFirstHeader[]  = { 'M', 'M', 0, 42, 0, 0, 0, 0 };
1288 static ilByte           LSBFirstHeader[] = { 'I', 'I', 42, 0, 0, 0, 0, 0 };
1289
1290     if (fseek (stream, offset, 0)) {        /* nonzero means error for fseek */
1291         context->error = IL_ERROR_FILE_IO;
1292         return FALSE;
1293         }
1294
1295     pHeader = (MSBIsFirst) ? MSBFirstHeader : LSBFirstHeader;
1296     if (fwrite ((char *)pHeader, sizeof (MSBFirstHeader), 1, stream) != 1) {
1297         context->error = IL_ERROR_FILE_IO;
1298         return FALSE;
1299         }
1300     fflush (stream);                        /* flush header out to file */
1301
1302     context->error = IL_OK;
1303     return TRUE;
1304 }
1305