Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtHelp / il / ilwriteimage.c
1 /* $XConsortium: ilwriteimage.c /main/4 1996/01/08 12:17:24 lehors $ */
2 /**---------------------------------------------------------------------
3 ***     
4 ***    (c)Copyright 1991 Hewlett-Packard Co.
5 ***    
6 ***                             RESTRICTED RIGHTS LEGEND
7 ***    Use, duplication, or disclosure by the U.S. Government is subject to
8 ***    restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
9 ***    Technical Data and Computer Software clause in DFARS 252.227-7013.
10 ***                             Hewlett-Packard Company
11 ***                             3000 Hanover Street
12 ***                             Palo Alto, CA 94304 U.S.A.
13 ***    Rights for non-DOD U.S. Government Departments and Agencies are as set
14 ***    forth in FAR 52.227-19(c)(1,2).
15 ***
16 ***-------------------------------------------------------------------*/
17
18 #include "ilint.h"
19 #include "ilpipelem.h"
20 #include "ilimage.h"
21 #include "ilerrors.h"
22 #include "ilutiljpeg.h"
23
24
25         /*  Private data for Read/WriteImage pipe functions. */
26 typedef struct {
27     ilImagePtr          pImage;         /* ptr to image read/written */
28     unsigned short     *pPalette;       /* ptr to palette or null if not palette image */
29     ilPtr               pCompData;      /* ptr to comp data or null if none */
30     long                nLinesWritten;  /* Init(): # of lines written so far */
31
32         /*  Remaining fields used only for compressed images  */
33     long                nStripsWritten; /* Init(): # of strips written to image */
34     } ilImagePrivRec, *ilImagePrivPtr;
35
36
37     /*  ------------------------- ilWriteImageInit ----------------------------------- */
38     /*  Init() function for ilWriteImage().
39     */
40 static ilError ilWriteImageInit (
41     ilImagePrivPtr      pPriv,
42     ilImageInfo        *pSrcImage,
43     ilImageInfo        *pDstImage
44     )
45 {
46     pPriv->nStripsWritten = 0;
47     pPriv->nLinesWritten = 0;
48     return IL_OK;
49 }
50
51
52     /*  ------------------------ ilWriteImageDestroy ------------------------------ */
53     /*  Destroy() function for ilWriteImage().  Calls ilDestroyObject() with
54         element's pObject, which should point to the image.  The image's refCount
55         is inc'd when pipe element added.  ilDestroyObject() will dec it and free
56         the image if the refCount is 0.  Using refCount this way prevents the user
57         from destroying an image still attached to a pipe.
58     */
59 static ilError ilWriteImageDestroy (
60     ilImagePrivPtr      pPriv
61     )
62 {
63     ilDestroyObject ((ilObject)pPriv->pImage);
64     return IL_OK;
65 }
66
67
68 /*  ============================ WRITE COMPRESSED CODE ============================= */
69
70     /*  ------------------------ ilWriteCompressedCleanup ------------------------- */
71     /*  Cleanup() function for ilWriteImage(), writing compressed images.
72     */
73 static ilError ilWriteCompressedCleanup (
74     ilImagePrivPtr      pPriv,
75     ilBool              aborting
76     )
77 {
78 register ilImagePlaneInfo *pImagePlane;
79 register ilImagePtr     pImage;
80 ilError                 error;
81
82     pImage = pPriv->pImage;
83     pImagePlane = &pImage->i.plane[0];
84
85         /*  If # of strips written by pipe is not equal to # of strips in image, error */
86     if (!aborting && (pPriv->nStripsWritten != pImage->nStrips)) {
87         aborting = TRUE;                        /* take error path below */
88         error = IL_ERROR_MALFORMED_IMAGE_WRITE;
89         }
90     else error = IL_OK;
91
92         /*  If aborting, free up pixels if they dont belong to client */
93     if (aborting) {
94         if (!pImage->i.clientPixels && pImagePlane->pPixels) {
95             IL_FREE (pImagePlane->pPixels);
96             pImagePlane->pPixels = (ilPtr)NULL;
97             pImagePlane->bufferSize = 0;
98             }
99         }
100     else {
101             /*  Success: realloc (shrink) the image down to the written size (offset to
102                 strip past last strip), to free up extra unused-but-allocated space.  
103             */
104         pImagePlane->bufferSize = pImage->pStripOffsets[pImage->nStrips];
105         if (pImagePlane->pPixels) 
106             pImagePlane->pPixels = (ilPtr)IL_REALLOC (pImagePlane->pPixels, 
107                                                       pImagePlane->bufferSize);
108         }
109
110     return error;
111 }
112
113
114     /*  ------------------------ ilWriteCompressedExecute ------------------------- */
115     /*  Execute() function for ilWriteImage(), writing compressed images.
116     */
117 static ilError ilWriteCompressedExecute (
118     register ilExecuteData  *pData,
119     long                dstLine,
120     long               *pNLines
121     )
122 {
123 register ilImagePrivPtr pPriv;
124 register ilImagePtr     pImage;
125
126     pPriv = (ilImagePrivPtr)pData->pPrivate;
127     pImage = pPriv->pImage;
128
129         /*  If first strip: copy non-null pipe palette (pPriv->pPalette) and comp data
130             (NOTE: assumed to be JPEG!), after freeing old tables, to the image.
131         */
132     if (pData->srcLine <= 0) {
133         if (pPriv->pPalette)
134             bcopy ((char *)pPriv->pPalette, (char *)pPriv->pImage->i.pPalette, 
135                    sizeof (unsigned short) * (3 * 256));
136         if (pPriv->pCompData) {
137             _ilJPEGFreeTables ((ilJPEGData *)pPriv->pImage->i.pCompData);
138             if (!_ilJPEGCopyData ((ilJPEGData *)pPriv->pCompData, 
139                                   (ilJPEGData *)pPriv->pImage->i.pCompData))
140             return IL_ERROR_MALLOC;
141             }
142         }
143
144         /*  Bump srcLine (index to next line); if > # lines in image, error */
145     pData->srcLine += *pNLines;
146     if (pData->srcLine > pImage->i.height)
147         return IL_ERROR_MALFORMED_IMAGE_WRITE;
148
149         /*  If # lines written != strip size and not last strip, inconstant strips */
150     if ((*pNLines != pImage->stripHeight) && (pData->srcLine != pImage->i.height))
151         return IL_ERROR_MALFORMED_IMAGE_WRITE;
152
153         /*  Store offset to this and next strip; bump offset for next write */
154     pImage->pStripOffsets[pPriv->nStripsWritten++] = pData->compressed.srcOffset;
155     pData->compressed.srcOffset += pData->compressed.nBytesToRead;
156     pImage->pStripOffsets[pPriv->nStripsWritten] = pData->compressed.srcOffset;
157
158     return IL_OK;
159 }
160
161
162     /*  -------------------- ilWriteCompressedImage -------------------- */
163     /*  Called by ilWriteImage() to add a consumer to "pipe" which writes compressed
164         data to the given "image".  "pipe" and "image" are guaranteed to be the correct 
165         object types and to belong to the same context.  The pipe is guaranteed to
166         be in the correct state (IL_PIPE_FORMING) and "image" is guaranteed to be the
167         correct size (size of the pipe image).
168     */
169 static ilBool ilWriteCompressedImage (
170     ilPipe              pipe,
171     ilObject            image
172     )
173 {
174 register ilImagePtr     pImage;
175 ilPipeInfo              info;                              
176 ilImageDes              des;
177 ilImageFormat           format;
178 ilBool                  mustConvert;
179 register ilImagePrivPtr pPriv;
180 ilSrcElementData        srcData;
181 long                    stripHeight;
182 ilPtr                   pCompData;
183
184         /* Get pipe info, dont force decompression (yet). */
185     ilGetPipeInfo (pipe, FALSE, &info, &des, &format);
186
187         /*  Convert (and decompress) as necessary to dest image des */
188     pImage = (ilImagePtr)image;
189     mustConvert = FALSE;
190     if ((des.type != pImage->des.type)
191      || (des.blackIsZero != pImage->des.blackIsZero)
192      || (des.nSamplesPerPixel != pImage->des.nSamplesPerPixel))
193         mustConvert = TRUE;
194     else {
195         int     plane;
196         for (plane = 0; plane < des.nSamplesPerPixel; plane++)
197             if ((des.nLevelsPerSample[plane] != pImage->des.nLevelsPerSample[plane])
198              || (format.nBitsPerSample[plane] != pImage->format.nBitsPerSample[plane])) {
199                 mustConvert = TRUE;
200                 break;
201                 }
202         }
203     if (mustConvert) {
204         ilImageDes      tempDes;
205         tempDes = *pImage->i.pDes;
206         tempDes.compression = IL_UNCOMPRESSED;      /* ilConvert() error otherwise */
207         if (!ilConvert (pipe, &tempDes, (ilImageFormat *)NULL, 0, (ilPtr)NULL))
208             return FALSE;
209         des.compression = IL_UNCOMPRESSED;          /* force recompress below */
210         }
211
212         /*  Compress to compression type of image, including type-specific (G3/JPEG)
213             compression data.  Compress to image's strip height if is established 
214             (pStripOffsets non-null), else set "stripHeight" to 0 and use default.
215         */
216     if (pImage->des.compression == IL_G3)
217          pCompData = (ilPtr)&pImage->des.compInfo.g3.flags;
218     else if (pImage->des.compression == IL_G4)
219          pCompData = (ilPtr)&pImage->des.compInfo.g4.flags;
220     else if (pImage->des.compression == IL_JPEG)
221          pCompData = (ilPtr)&pImage->des.compInfo.JPEG;
222     else pCompData = (ilPtr)NULL;
223
224     stripHeight = (pImage->pStripOffsets) ? pImage->stripHeight : 0;
225     if (!ilCompress (pipe, pImage->des.compression, pCompData, stripHeight, 0))
226         return FALSE;
227     ilGetPipeInfo (pipe, FALSE, &info, (ilImageDes *)NULL, (ilImageFormat *)NULL);                    
228
229         /*  If image does not yet have a strip height, set it to pipe's strip height */
230     if (!stripHeight)
231         if (!_ilAllocStripOffsets (pImage, info.stripHeight))
232             return ilDeclarePipeInvalid (pipe, pipe->context->error);
233
234         /*  Add a consumer to write to the image - merely bumps offsets. */
235     srcData.consumerImage = (ilObject)pImage;
236     srcData.stripHeight = 0;
237     srcData.constantStrip = FALSE;
238     srcData.minBufferHeight = 0;
239     pPriv = (ilImagePrivPtr)ilAddPipeElement(pipe, IL_CONSUMER, sizeof(ilImagePrivRec), 0,
240          &srcData, (ilDstElementData *)NULL, ilWriteImageInit, ilWriteCompressedCleanup, 
241          ilWriteImageDestroy, ilWriteCompressedExecute, 0);
242     if (!pPriv)
243         return FALSE;
244
245     pPriv->pImage = pImage;
246     pPriv->pPalette = (pImage->des.type == IL_PALETTE) ? info.pPalette : 
247                                                          (unsigned short *)NULL;
248
249         /*  NOTE: assumes JPEG pCompData!  Point to pipe comp data if raw JPEG */
250     if ((pImage->des.compression == IL_JPEG) 
251      && (pImage->des.compInfo.JPEG.reserved & IL_JPEGM_RAW))
252          pPriv->pCompData = info.pCompData;
253     else pPriv->pCompData = (ilPtr)NULL;
254
255     pImage->o.refCount++;               /* see ilWriteImageDestroy() */
256
257     pipe->context->error = IL_OK;
258     return TRUE;
259 }
260
261
262
263 /*  ============================ WRITE UNCOMPRESSED CODE ============================= */
264
265
266         /*  --------------------- ilWriteImageExecute -------------------------------- */
267         /*  Execute() for WriteImage(): merely bumps srcLine by # lines written.
268         */
269 static ilError ilWriteImageExecute (
270     register ilExecuteData  *pData,
271     long                    dstLine,
272     long                   *pNLines
273     )
274 {
275     ilImagePrivPtr      pPriv;
276
277     pPriv = (ilImagePrivPtr)pData->pPrivate;
278
279         /*  If first strip, copy pipe palette (pPriv->pPalette) to the image palette. */
280     if ((pData->srcLine <= 0) && pPriv->pPalette) {
281         bcopy ((char *)pPriv->pPalette, (char *)pPriv->pImage->i.pPalette, 
282                sizeof (unsigned short) * (3 * 256));
283         }
284
285         /*  Bump srcLine (index to next line); if > # lines in image, error */
286     pData->srcLine += *pNLines;
287     if (pData->srcLine > pPriv->pImage->i.height)
288         return IL_ERROR_MALFORMED_IMAGE_WRITE;
289
290     return IL_OK;
291 }
292
293
294         /*  ------------------------ ilWriteImage ---------------------------------- */
295         /*  Public function: see spec.
296             Adds the given image as a producer of the pipe.
297         */
298 ilBool ilWriteImage (
299     ilPipe              pipe,
300     ilObject            image
301     )
302 {
303 register ilImagePtr     pImage;
304 register ilImagePrivPtr pPriv;
305 register ilContext      context;
306 ilPipeInfo              info;
307 ilSrcElementData        srcData;
308
309         /*  Validate that pipe and image are such, and that they have the same context. */
310     pImage = (ilImagePtr)image;
311     context = pipe->context;
312     if (pipe->objectType != IL_PIPE) {
313         context->error = IL_ERROR_OBJECT_TYPE;
314         return;
315         }
316
317     if ((pImage->o.p.objectType != IL_INTERNAL_IMAGE) 
318      && (pImage->o.p.objectType != IL_CLIENT_IMAGE))
319         return ilDeclarePipeInvalid (pipe, IL_ERROR_OBJECT_TYPE);
320
321     if (pImage->o.p.context != context)
322         return ilDeclarePipeInvalid (pipe, IL_ERROR_CONTEXT_MISMATCH);
323
324         /*  Get pipe info; if pipe not in IL_PIPE_FORMING state: error.
325             If producerObject is the given image, error: cant read/write same image.
326         */                          
327     if (ilGetPipeInfo (pipe, FALSE, &info, 
328                       (ilImageDes *)NULL, (ilImageFormat *)NULL) != IL_PIPE_FORMING) {
329         if (!context->error)
330             ilDeclarePipeInvalid (pipe, IL_ERROR_PIPE_STATE);
331         return FALSE;
332         }
333     if (info.producerObject == (ilObject)pImage)
334         return ilDeclarePipeInvalid (pipe, IL_ERROR_CIRCULAR_PIPE);
335
336         /*  Validate pipe image.  width, height and descriptor must be the same. */
337     if ((info.width != pImage->i.width) || (info.height != pImage->i.height))
338         return ilDeclarePipeInvalid (pipe, IL_ERROR_IMAGE_SIZE);
339
340         /*  If a writing to a compressed image, call separate function and exit. */
341     if (pImage->des.compression != IL_UNCOMPRESSED)
342         return ilWriteCompressedImage (pipe, image);        /* EXIT */
343
344         /*  Convert des and/or format if not same as the images format; exit if error. */
345     if (!ilConvert (pipe, pImage->i.pDes, pImage->i.pFormat, 0, NULL))
346         return FALSE;
347     ilGetPipeInfo (pipe, TRUE, &info, (ilImageDes *)NULL, (ilImageFormat *)NULL);
348
349         /*  Add a consumer element which writes to the given image.
350             If writing a palette image, supply an Init() function to copy the palette.
351         */
352     srcData.consumerImage = (ilObject)pImage;
353     srcData.stripHeight = 0;
354     srcData.constantStrip = FALSE;
355     srcData.minBufferHeight = 0;
356     pPriv = (ilImagePrivPtr)ilAddPipeElement (pipe, IL_CONSUMER, sizeof (ilImagePrivRec), 
357         0, &srcData, (ilDstElementData *)NULL, ilWriteImageInit, IL_NPF, 
358         ilWriteImageDestroy, ilWriteImageExecute, 0);
359     if (!pPriv)
360         return FALSE;
361
362         /*  Element succesfully added; setup private data.
363             Increment the refCount in the image; see notes for ilImageDestroy().
364         */
365     pPriv->pImage = pImage;
366     pPriv->pPalette = (pImage->des.type == IL_PALETTE) ? info.pPalette : 
367                                                          (unsigned short *)NULL;
368     pImage->o.refCount++;               /* see ilWriteImageDestroy() */
369
370     context->error = IL_OK;
371     return TRUE;
372 }
373