Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtHelp / il / ilycbcr.c
1 /* $XConsortium: ilycbcr.c /main/5 1996/06/19 12:20:22 ageorge $ */
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         /*  /ilc/ilycbcr.c : Code for handling IL_YCBCR images, including
19             conversions to/from RGB, and subsampling - upsampling is in /ilc/ilupsample.c
20         */
21 #include "ilint.h"
22 #include "ilpipelem.h"
23 #include "ilconvert.h"
24 #include "ilerrors.h"
25
26     /*  ========================== Sub/Upsample Code ================================== */
27
28 #define NSUBSAMPLES         3               /* hardwired to 3 samples: Y, Cb, Cr */
29
30     /*  Sub/Upsubsample factors as shifts (1=0, 2=1, 4=2) - others not supported. */
31 typedef struct {
32     unsigned int        horiz, vert;
33     } ilSubsampleShiftRec, *ilSubsampleShiftPtr;
34
35     /*  Private for sub/upsampling filters. */
36 typedef struct {
37     int                 nSamples;           /* # of samples (components) to process */
38     ilSubsampleShiftRec shift[NSUBSAMPLES]; /* upsample mul as shift value */
39     } ilSubsamplePrivRec, *ilSubsamplePrivPtr;
40
41
42     /*  ========================== Subsample Code =================================== */
43
44     /*  -------------------------- ilExecuteSubsample ------------------------------- */
45     /*  Execute(): subsample as necessary "pPriv->nSamples" planes of the source image.
46         The source image must be planar, byte/pixel.
47     */
48 static ilError ilExecuteSubsample (
49     ilExecuteData          *pData,
50     long                    dstLine,
51     long                   *pNLines             /* ignored on input */
52     )
53 {
54 register ilSubsamplePrivPtr pPriv;
55 ilImagePlaneInfo           *pSrcPlane, *pDstPlane;
56 int                         nSamples;
57 ilPtr                       pSrcLine, pDstLine;
58 long                        height, width;
59 long                        srcRowInc, dstRowBytes;
60 long                        nDstLinesM1, nDstPixelsM1Init;
61 ilSubsampleShiftPtr          pShift;
62 register long               nDstPixelsM1, srcRowBytes, i;
63 register ilPtr              pSrc, pDst;
64 register unsigned int       pixel;
65
66         /*  Get width and height of _upsampled_ image; exit if zero. */
67     pPriv = (ilSubsamplePrivPtr)pData->pPrivate;
68     height = *pNLines;
69     if (height <= 0)
70         return IL_OK;
71     width = pData->pSrcImage->width;
72     if (width <= 0)
73         return IL_OK;
74
75         /*  Loop on samples (components), upsample/translate each plane separately.
76             Note that "srcLine" is shifted - indexing into plane based on vert subsample.
77         */
78     for (pSrcPlane = pData->pSrcImage->plane, pDstPlane = pData->pDstImage->plane,
79       pShift = pPriv->shift, nSamples = pPriv->nSamples; 
80       nSamples-- > 0; 
81       pSrcPlane++, pDstPlane++, pShift++) {
82
83             /*  Get # dst lines/pixels across - 1; skip plane if none */
84         nDstLinesM1 = height >> pShift->vert;
85         if (nDstLinesM1 <= 0)
86             continue;
87         nDstLinesM1--;
88         nDstPixelsM1Init = width >> pShift->horiz;
89         if (nDstPixelsM1Init <= 0)
90             continue;
91         nDstPixelsM1Init--;
92
93             /*  Point to src/dst; note dstLine scaled down by vert subsample shift.
94                 Set srcRowInc to srcRowBytes scaled up by vertical shift.
95             */
96         srcRowBytes = pSrcPlane->nBytesPerRow;
97         pSrcLine = pSrcPlane->pPixels + pData->srcLine * srcRowBytes;
98         srcRowInc = srcRowBytes << pShift->vert;
99         dstRowBytes = pDstPlane->nBytesPerRow;
100         pDstLine = pDstPlane->pPixels + (dstLine >> pShift->vert) * dstRowBytes;
101
102         switch (pShift->horiz) {            /* NOTE: vert shift must be the same! */
103
104             /*  0 = no subsampling; just copy plane info */
105           case 0:
106             pSrc = pSrcLine;
107             pDst = pDstLine;
108             do {
109                 bcopy ((char *)pSrc, (char *)pDst, width);
110                 pSrc += srcRowBytes;
111                 pDst += dstRowBytes;
112                 } while (--nDstLinesM1 >= 0);
113             break;
114
115             /*  1 = 1/4 of pixels (half in each dimension). Take the average of 4
116                 pixels: (x,y), (x+1,y), (x,y+1), (x+1,y+1), for each even x,y.
117             */
118           case 1:
119             do {
120                 pSrc = pSrcLine;
121                 pSrcLine += srcRowInc;
122                 pDst = pDstLine;
123                 pDstLine += dstRowBytes;
124                 nDstPixelsM1 = nDstPixelsM1Init;
125                 do {
126                     pixel = *pSrc;
127                     pixel += *(pSrc + 1);
128                     pixel += *(pSrc + srcRowBytes);
129                     pixel += *(pSrc + srcRowBytes + 1);
130                     pSrc += 2;                  /* two pixels to the right */
131                     *pDst++ = pixel >> 2;       /* store 1/4 sum of 4 pixels */
132                     } while (--nDstPixelsM1 >= 0);
133                 } while (--nDstLinesM1 >= 0);
134             break;
135
136             /*  2 = 1/16 of pixels (1/4 in each dimension). Take the average of 16 pixels,
137                 a block below and to the right of (x,y), x,y = multiple of 4.
138             */
139           case 2:
140             do {
141                 pSrc = pSrcLine;
142                 pSrcLine += srcRowInc;
143                 pDst = pDstLine;
144                 pDstLine += dstRowBytes;
145                 nDstPixelsM1 = nDstPixelsM1Init;
146                 do {
147                     pixel = 0;
148                     i = 3;
149                     do {
150                         pixel += *pSrc;
151                         pixel += *(pSrc + 1);
152                         pixel += *(pSrc + 2);
153                         pixel += *(pSrc + 3);
154                         pSrc += srcRowBytes;
155                         } while (--i >= 0);
156                     *pDst++ = pixel >> 4;       /* store 1/16 sum of 16 pixels */
157                     pSrc += -srcRowInc + 4;     /* back to top line, 4 to right */
158                     } while (--nDstPixelsM1 >= 0);
159                 } while (--nDstLinesM1 >= 0);
160             break;
161
162             }   /* END switch subsample shift */
163         }       /* END each plane */
164
165     return IL_OK;
166 }
167
168
169     /*  ---------------------------- ilSubsampleYCbCr ----------------------------- */
170     /*  Subsample the pipe image which must be a fully upsampled YCbCr image.
171         pFormat points to the source format; on return, *pFormat is updated
172         to the dst format.  *pDes to the _destination_ des, i.e. has subsample values.
173     */
174 IL_PRIVATE ilBool _ilSubsampleYCbCr (
175     ilPipe                  pipe,
176     ilPipeInfo             *pInfo,
177     ilImageDes             *pDes,
178     ilImageFormat          *pFormat
179     )
180 {
181     register ilSubsamplePrivPtr pSubPriv;
182     ilSubsamplePrivRec       priv;
183     ilSubsampleShiftRec     *pShift;
184     ilYCbCrSampleInfo      *pSample;
185     int                     sample;
186     ilDstElementData        dstData;
187     ilSrcElementData        srcData;
188
189         /*  Only 8 bit planar YCbCr current handled; cvt if not that format. */
190     if ((pFormat->sampleOrder != IL_SAMPLE_PLANES)
191      || (pFormat->nBitsPerSample[0] != 8)
192      || (pFormat->nBitsPerSample[1] != 8)
193      || (pFormat->nBitsPerSample[2] != 8)) {
194         *pFormat = *IL_FORMAT_3BYTE_PLANE;
195         if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, NULL))
196             return FALSE;
197         }
198
199         /*  Determine subsample values: only 1 (no subsampling), 2 (1/4 = 1/2 in each 
200             dimension) or 4 (1/16) allowed; same both vertical/horizontal.
201         */
202     priv.nSamples = NSUBSAMPLES;
203     pSample = pDes->typeInfo.YCbCr.sample;
204     pShift = priv.shift;
205     for (sample = 0; sample < NSUBSAMPLES; sample++, pShift++, pSample++) {
206         if (pSample->subsampleHoriz != pSample->subsampleVert)
207             return ilDeclarePipeInvalid (pipe, IL_ERROR_SUBSAMPLE);
208         switch ((unsigned int)pSample->subsampleHoriz) {
209           case 1: pShift->horiz = 0; break;
210           case 2: pShift->horiz = 1; break;
211           case 4: pShift->horiz = 2; break;
212           default: return ilDeclarePipeInvalid (pipe, IL_ERROR_SUBSAMPLE);
213             }
214         pShift->vert = pShift->horiz;       /* same subsampling both dimensions */
215         }
216
217         /*  Init dstData, add subsample filter. */
218     dstData.producerObject = (ilObject)NULL;
219     dstData.pDes = pDes;
220     dstData.pFormat = pFormat;
221     dstData.width = pInfo->width;
222     dstData.height = pInfo->height;
223     dstData.stripHeight = 0;
224     dstData.constantStrip = FALSE;
225     dstData.pPalette = (unsigned short *)NULL;
226  
227         /*  Demand constant strips, a multiple of maximum subsample (4). */
228     srcData.consumerImage = (ilObject)NULL;
229     srcData.stripHeight = 8;                /* Arbitrary, but must be multiple of 4 */
230     srcData.constantStrip = TRUE;
231     srcData.minBufferHeight = 0;
232
233     pSubPriv = (ilSubsamplePrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
234                  sizeof (ilSubsamplePrivRec), 0, &srcData,
235                  &dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteSubsample, NULL, 0);
236     if (!pSubPriv)
237         return FALSE;
238     *pSubPriv = priv;                   /* copy priv to priv ptr */
239     return TRUE;
240 }
241
242
243
244     /*  ======================= YCbCr to RGB Code =================================== */
245
246     /*  Lookup tables for YCbCr to RGB conversion.  Each table is indexed by Y, Cb or Cr,
247         and contains two values; one the upper short (1st) the other in the lower (2nd);
248         the upper short values are used to compute green.
249             Y   G cvt: Y factor = (1 - Lb - Lr)/Lg      scale for refBlack/White
250             Cb  G cvt: Cb factor = -Lb*(2 - 2*Lb)/Lg    Cb scale * (2 - 2 * Lr)
251             Cr  G cvt: Cr factor = -Lr*(2 - 2*Lr)/Lg    Cr scale * (2 - 2 * Lb)
252     */
253 typedef struct {
254     long        YTrans[256];        /* values indexed by Y, as above */
255     long        CbTrans[256];       /* values indexed by Cb, as above */
256     long        CrTrans[256];       /* values indexed by Cr, as above */
257     } ilRGBTransRec, *ilRGBTransPtr;
258
259     /*  Private for RGB to YCbCr filter. */
260 typedef struct {
261     ilRGBTransPtr   pTrans;         /* ptr to all the translate tables */
262     long            y;              /* for dither only: current Y */
263     } ilYCbCrToRGBPrivRec, *ilYCbCrToRGBPrivPtr;
264
265     /*  Pointer to "std" translation table: works for "std" YCbCr values -
266         those in IL_DES_YCBCR.
267     */
268 static ilRGBTransPtr _ilStdYCbCrTransPtr = (ilRGBTransPtr)NULL;
269
270
271     /*  --------------------------- ilDestroyTransTable --------------------- */
272     /*  Destroy function: used only if pPriv->pTrans points to locally malloc'd
273         translate table, i.e. not using std values and table.
274     */
275 static ilError ilDestroyTransTable (
276     ilYCbCrToRGBPrivPtr pPriv
277     )
278 {
279     IL_FREE (pPriv->pTrans);
280     return IL_OK;
281 }
282
283     /*  --------------------- ilExecutePlanarYCbCrToRGB ----------------------------- */
284     /*  Execute(): convert _planar_ src YCbCr to _pixel_ dst RGB.
285         NOTE: the YCbCr image must have been upsampled, i.e. thru ilExecuteUpsample().
286     */
287 static ilError ilExecutePlanarYCbCrToRGB (
288     ilExecuteData          *pData,
289     long                    dstLine,
290     long                   *pNLines
291     )
292 {
293 ilPtr                       pYLine, pCbLine, pCrLine, pDstLine;
294 register ilRGBTransPtr      pTrans;
295 register ilPtr              pY, pCb, pCr, pDst;
296 register long               YTrans, CbTrans, CrTrans, pixel, nPixelsM1;
297 register int                Y;
298 long                        nPixelsM1Init, nLinesM1;
299 long                        YRowBytes, CbRowBytes, CrRowBytes, dstRowBytes;
300 ilImagePlaneInfo           *pPlane;
301
302
303         /*  Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
304     nPixelsM1Init = pData->pSrcImage->width;
305     if (nPixelsM1Init <= 0)
306         return;
307     nPixelsM1Init--;
308     nLinesM1 = *pNLines;
309     if (nLinesM1 <= 0)
310         return;
311     nLinesM1--;
312
313         /*  Point pY/Cb/CrLine to 1st line in src planes; pDstLine to 1st line in dst.
314             Get bytes/row for each of them.
315         */
316     pPlane = pData->pSrcImage->plane;
317     YRowBytes = pPlane->nBytesPerRow;
318     pYLine = pPlane->pPixels + pData->srcLine * YRowBytes;
319     pPlane++;
320     CbRowBytes = pPlane->nBytesPerRow;
321     pCbLine = pPlane->pPixels + pData->srcLine * CbRowBytes;
322     pPlane++;
323     CrRowBytes = pPlane->nBytesPerRow;
324     pCrLine = pPlane->pPixels + pData->srcLine * CrRowBytes;
325
326     pPlane = pData->pDstImage->plane;
327     dstRowBytes = pPlane->nBytesPerRow;
328     pDstLine = pPlane->pPixels + dstLine * dstRowBytes;
329
330         /*  Convert planar YCbCr to pixel RGB. */
331     pTrans = ((ilYCbCrToRGBPrivPtr)pData->pPrivate)->pTrans;
332     do {
333         pY = pYLine;
334         pYLine += YRowBytes;
335         pCb = pCbLine;
336         pCbLine += CbRowBytes;
337         pCr = pCrLine;
338         pCrLine += CrRowBytes;
339         pDst = pDstLine;
340         pDstLine += dstRowBytes;
341
342         nPixelsM1 = nPixelsM1Init;
343         do {
344             YTrans = pTrans->YTrans[*pY++];
345             CrTrans = pTrans->CrTrans[*pCr++];
346             Y = ((short)YTrans);
347             CbTrans = pTrans->CbTrans[*pCb++];
348             pixel = Y + ((short)CrTrans);
349             if (pixel >> 8) goto plClipR;
350 plRClipped: *pDst++ = pixel;                /* store red */
351             pixel = YTrans >> 16;
352             pixel += CbTrans >> 16;
353             pixel += CrTrans >> 16;
354             if (pixel >> 8) goto plClipG;
355 plGClipped: *pDst++ = pixel;                /* store green */
356             pixel = Y + ((short)CbTrans);
357             if (pixel >> 8) goto plClipB;
358 plBClipped: *pDst++ = pixel;                /* store blue */
359             } while (--nPixelsM1 >= 0);
360         } while (--nLinesM1 >= 0);
361
362     return IL_OK;
363
364     /*  goto point for when R, G or B out of range: clamp pixel and go back and continue.
365         This is done to avoid taking a branch in the most common case: not out of range.
366     */
367 plClipR: if (pixel > 255) pixel = 255; else pixel = 0;
368         goto plRClipped;
369 plClipG: if (pixel > 255) pixel = 255; else pixel = 0;
370         goto plGClipped;
371 plClipB: if (pixel > 255) pixel = 255; else pixel = 0;
372         goto plBClipped;
373 }
374
375
376     /*  --------------------- ilExecutePixelYCbCrToRGB ----------------------------- */
377     /*  Execute(): convert _pixel_ src YCbCr to _pixel_ dst RGB.
378         NOTE: the YCbCr image must have been upsampled, i.e. thru ilExecuteUpsample().
379     */
380 static ilError ilExecutePixelYCbCrToRGB (
381     ilExecuteData          *pData,
382     long                    dstLine,
383     long                   *pNLines
384     )
385 {
386 ilPtr                       pSrcLine, pDstLine;
387 register ilRGBTransPtr      pTrans;
388 register ilPtr              pSrc, pDst;
389 register long               YTrans, CbTrans, CrTrans, pixel, nPixelsM1;
390 register int                Y;
391 long                        nPixelsM1Init, nLinesM1;
392 long                        srcRowBytes, dstRowBytes;
393 ilImagePlaneInfo           *pPlane;
394
395
396         /*  Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
397     nPixelsM1Init = pData->pSrcImage->width;
398     if (nPixelsM1Init <= 0)
399         return;
400     nPixelsM1Init--;
401     nLinesM1 = *pNLines;
402     if (nLinesM1 <= 0)
403         return;
404     nLinesM1--;
405
406         /*  Point pY/Cb/CrLine to 1st line in src planes; pDstLine to 1st line in dst.
407             Get bytes/row for each of them.
408         */
409     pPlane = pData->pSrcImage->plane;
410     srcRowBytes = pPlane->nBytesPerRow;
411     pSrcLine = pPlane->pPixels + pData->srcLine * srcRowBytes;
412
413     pPlane = pData->pDstImage->plane;
414     dstRowBytes = pPlane->nBytesPerRow;
415     pDstLine = pPlane->pPixels + dstLine * dstRowBytes;
416
417         /*  Convert pixel YCbCr to pixel RGB. */
418     pTrans = ((ilYCbCrToRGBPrivPtr)pData->pPrivate)->pTrans;
419     do {
420         pSrc = pSrcLine;
421         pSrcLine += srcRowBytes;
422         pDst = pDstLine;
423         pDstLine += dstRowBytes;
424
425         nPixelsM1 = nPixelsM1Init;
426         do {
427             YTrans = pTrans->YTrans[*pSrc++];
428             CbTrans = pTrans->CbTrans[*pSrc++];
429             Y = ((short)YTrans);
430             CrTrans = pTrans->CrTrans[*pSrc++];
431             pixel = Y + ((short)CrTrans);
432             if (pixel >> 8) goto piClipR;
433 piRClipped: *pDst++ = pixel;                /* store red */
434             pixel = YTrans >> 16;
435             pixel += CbTrans >> 16;
436             pixel += CrTrans >> 16;
437             if (pixel >> 8) goto piClipG;
438 piGClipped: *pDst++ = pixel;                /* store green */
439             pixel = Y + ((short)CbTrans);
440             if (pixel >> 8) goto piClipB;
441 piBClipped: *pDst++ = pixel;                /* store blue */
442             } while (--nPixelsM1 >= 0);
443         } while (--nLinesM1 >= 0);
444
445     return IL_OK;
446
447     /*  goto point for when R, G or B out of range: clamp pixel and go back and continue.
448         This is done to avoid taking a branch in the most common case: not out of range.
449     */
450 piClipR: if (pixel > 255) pixel = 255; else pixel = 0;
451         goto piRClipped;
452 piClipG: if (pixel > 255) pixel = 255; else pixel = 0;
453         goto piGClipped;
454 piClipB: if (pixel > 255) pixel = 255; else pixel = 0;
455         goto piBClipped;
456 }
457
458
459     /*  ------------------------- ilGetYCbCrConvertTable -------------------------- */
460     /*  Returns a pointer to the YCbCr conversion table (ilRGBTransRec); either
461         "_ilStdYCbCrTransPtr" or a ptr to a table that caller must destroy, or
462         null if a malloc error occurs.
463     */
464 static ilRGBTransPtr ilGetYCbCrConvertTable (
465     ilImageDes             *pDes
466     )
467 {
468     register ilRGBTransPtr  pTrans;
469     const ilYCbCrInfo      *pYCbCr, *pStdYCbCr;
470     double                  Lr, Lg, Lb, scaledY;
471     register double         factor, gFactor;
472     register int            i, refBlack, upper, lower;
473     ilBool                  isStd;
474     struct {
475         int                 refBlack;
476         double              scale;
477         } sample [3];
478
479         /*  If luma and refBlack/White info same as std IL_DES_YCBCR, then reuse
480             the table at _ilStdYCbCrTransPtr; create it here if does not exist yet.
481         */
482     pYCbCr = &pDes->typeInfo.YCbCr;
483     pStdYCbCr = &IL_DES_YCBCR->typeInfo.YCbCr;
484     if ((pYCbCr->sample[0].refBlack == pStdYCbCr->sample[0].refBlack)
485      && (pYCbCr->sample[0].refWhite == pStdYCbCr->sample[0].refWhite)
486      && (pYCbCr->sample[1].refBlack == pStdYCbCr->sample[1].refBlack)
487      && (pYCbCr->sample[1].refWhite == pStdYCbCr->sample[1].refWhite)
488      && (pYCbCr->sample[2].refBlack == pStdYCbCr->sample[2].refBlack)
489      && (pYCbCr->sample[2].refWhite == pStdYCbCr->sample[2].refWhite)
490      && (pYCbCr->lumaRed == pStdYCbCr->lumaRed)
491      && (pYCbCr->lumaGreen == pStdYCbCr->lumaGreen)
492      && (pYCbCr->lumaBlue == pStdYCbCr->lumaBlue)) {
493         isStd = TRUE;
494         pTrans = _ilStdYCbCrTransPtr;       /* alloc below if not there yet */
495         }
496     else {
497         isStd = FALSE;
498         pTrans = (ilRGBTransPtr)NULL;       /* allocate it below */
499         }
500
501         /*  Build translate tables.  First, from pg 3 of Appendix O of TIFF spec:
502                 R = Cr * (2 - 2 * Lr) + Y
503                 B = Cb * (2 - 2 * Lb) + Y
504                 G = (Y - Lb * B - Lr * R) / Lg
505             Get green in terms of Cb and Cr:
506                 G = (Y - Lb*B - Lr*R) / Lg
507                 G = (Y - Lb*(Cb * (2 - 2*Lb) + Y) - Lr*(Cr * (2 - 2*Lr) + Y)) / Lg
508                 G = (Y - Lb*Cb*(2 - 2*Lb) - Lb*Y - Lr*Cr*(2 - 2*Lr) - Lr*Y) / Lg
509                 G = (Y - Lb*Y - Lr*Y - Cb*Lb*(2 - 2*Lb) - Cr*Lr*(2 - 2*Lr)) / Lg
510                 G = (Y * (1 - Lb - Lr) - Cb*Lb*(2 - 2*Lb) - Cr*Lr*(2 - 2*Lr)) / Lg
511                 G = Y * (1 - Lb - Lr)/Lg + Cb * -Lb*(2 - 2*Lb)/Lg + Cr * -Lr*(2 - 2*Lr)/Lg
512         */
513     if (!pTrans) {                      /* non-std or std table not build yet */
514         pTrans = (ilRGBTransPtr)IL_MALLOC (sizeof (ilRGBTransRec));
515         if (!pTrans)
516             return pTrans;
517
518             /*  The Y, Cb and Cr components must be scaled up based on refBlack/White.
519                 Factor that scaling in everywhere Y/Cb/Cr are looked up.
520                 First get the scale and refBlack for each sample.
521             */
522         for (i = 0; i < 3; i++) {
523             sample[i].refBlack = pYCbCr->sample[i].refBlack;
524             sample[i].scale = ((i == 0) ? 255 : 127) / 
525                               (pYCbCr->sample[i].refWhite - pYCbCr->sample[i].refBlack);
526             }
527
528             /*  Calc the Cb/Cr lookup tables, factoring in their scale factors. */
529         Lr = ((double)pYCbCr->lumaRed / (double)10000);
530         Lg = ((double)pYCbCr->lumaGreen / (double)10000);
531         Lb = ((double)pYCbCr->lumaBlue / (double)10000);
532
533             /*  Y translation: lower is scaled Y, upper is value added to get green */
534         gFactor = (1 - Lb - Lr) / Lg;
535         for (i = 0; i < 256; i++) {
536             scaledY = sample[0].scale * i - sample[0].refBlack;
537             upper = scaledY * gFactor + 0.5;
538             lower = scaledY;
539             pTrans->YTrans[i] = (upper << 16) | (lower & 0xffff);
540             }
541
542             /*  Cb: lower is added to Y to get blue, upper added to get green */
543         factor = (2 - 2 * Lb) * sample[1].scale;
544         gFactor = -Lb * (2 - 2 * Lb) / Lg;
545         refBlack = sample[1].refBlack;
546         for (i = 0; i < 256; i++) {
547             upper = (i - refBlack) * gFactor + 0.5;
548             lower = (i - refBlack) * factor + 0.5;
549             pTrans->CbTrans[i] = (upper << 16) | (lower & 0xffff);
550             }
551
552             /*  Cr: lower is added to Y to get red, upper added to get green */
553         factor = (2 - 2 * Lr) * sample[2].scale;
554         gFactor = -Lr * (2 - 2 * Lr) / Lg;
555         refBlack = sample[2].refBlack;
556         for (i = 0; i < 256; i++) {
557             upper = (i - refBlack) * gFactor + 0.5;
558             lower = (i - refBlack) * factor + 0.5;
559             pTrans->CrTrans[i] = (upper << 16) | (lower & 0xffff);
560             }
561
562             /*  If creating "std" conversion table for first time, set ptr to it */
563         if (isStd)
564             _ilStdYCbCrTransPtr = pTrans;
565         }   /* END build table */
566
567     return pTrans;
568 }
569
570
571     /*  ---------------------------- ilConvertYCbCrToRGB ----------------------------- */
572     /*  Convert from source type (pDes->type) == IL_YCBCR to RBB.  
573         pFormat points to the source format; on return, *pFormat is updated
574         to the dst format, *pDes to the dst descriptor.
575     */
576
577 IL_PRIVATE ilBool _ilConvertYCbCrToRGB (
578     ilPipe                  pipe,
579     ilPipeInfo             *pInfo,
580     ilImageDes             *pDes,
581     ilImageFormat          *pFormat
582     )
583 {
584     ilDstElementData        dstData;
585     ilError                 (*executeFunction)(), (*destroyFunction)();
586     ilYCbCrToRGBPrivPtr     pPriv;
587     register ilRGBTransPtr  pTrans;
588
589     if ((pFormat->nBitsPerSample[0] != 8)
590      || (pFormat->nBitsPerSample[1] != 8)
591      || (pFormat->nBitsPerSample[2] != 8))
592         return ilDeclarePipeInvalid (pipe, IL_ERROR_NOT_IMPLEMENTED);
593
594         /*  Handle planar/pixel src format with different executeFunction, but in 
595             either case output RGB in 3 byte pixel format.
596         */
597     if (pFormat->sampleOrder == IL_SAMPLE_PIXELS)
598         executeFunction = ilExecutePixelYCbCrToRGB;
599     else {
600         executeFunction = ilExecutePlanarYCbCrToRGB;
601         *pFormat = *IL_FORMAT_3BYTE_PIXEL;
602         }
603
604         /*  Get translation table or error; destroy it if not the "std" one */
605     pTrans = ilGetYCbCrConvertTable (pDes);
606     if (!pTrans)
607         return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
608     destroyFunction = (pTrans == _ilStdYCbCrTransPtr) ? IL_NPF : ilDestroyTransTable;
609
610         /*  Add element to the pipe, init private */
611     dstData.producerObject = (ilObject)NULL;
612     *pDes = *IL_DES_RGB;
613     dstData.pDes = pDes;
614     dstData.pFormat = pFormat;
615     dstData.width = pInfo->width;
616     dstData.height = pInfo->height;
617     dstData.stripHeight = 0;
618     dstData.constantStrip = FALSE;
619     dstData.pPalette = (unsigned short *)NULL;
620     pPriv = (ilYCbCrToRGBPrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
621             sizeof(ilYCbCrToRGBPrivRec), 0, (ilSrcElementData *)NULL, &dstData, 
622             IL_NPF, IL_NPF, destroyFunction, executeFunction, NULL, 0);
623     if (!pPriv) {
624         if (destroyFunction)        /* local copy of pTrans; free it */
625             IL_FREE (pTrans);
626         return FALSE;
627         }
628     pPriv->pTrans = pTrans;
629
630     return TRUE;
631 }
632
633
634     /*  ======================= Dither YCbCr Code =================================== */
635
636     /*  Defines for # bits of RGB to dither to. NOTE! however that these are not
637         arbitrary; # bits of red must = # bits of green, because the pre-dither-mul'd
638         value of "Y" is the same for both.
639     */
640 #define YCBCR2_NR_BITS      2                   /* # of bits of R/G/B to dither to */
641 #define YCBCR2_NG_BITS      3
642 #define YCBCR2_NB_BITS      2
643 #define YCBCR2_MAX_R        ((1 << YCBCR2_NR_BITS) - 1)     /* max value for R/G/B */
644 #define YCBCR2_MAX_G        ((1 << YCBCR2_NG_BITS) - 1)
645 #define YCBCR2_MAX_B        ((1 << YCBCR2_NB_BITS) - 1)
646
647     /*  Private for RGB to dithered pixel filter. */
648 typedef struct {
649     ilRGBTransRec   trans;          /* YCbCr->dither-mul'd values tables */
650     ilPtr           pTranslate;     /* ptr to ilMap() lookup table pixels */
651     unsigned short *pPalette;       /* ptr to palette to destroy or null */
652     long            y;              /* Init(): current Y within dst image */
653     ilByte          translate [256];        /* identity image if no mapImage given */
654     } ilYCbCr2DitherPrivRec, *ilYCbCr2DitherPrivPtr;
655
656         /*  Init() function: init the counter of "y" within private */
657 static ilError ilYCbCr2DitherInit (
658     ilYCbCr2DitherPrivPtr pPriv,
659     ilImageInfo        *pSrcImage,
660     ilImageInfo        *pDstImage
661     )
662 {
663     pPriv->y = 0;
664     return IL_OK;
665 }
666
667     /*  Destroy function: destroy pTrans if not std and pPalette if not null */
668 static ilError ilYCbCr2DitherDestroy (
669     ilYCbCr2DitherPrivPtr pPriv
670     )
671 {
672     if (pPriv->pPalette)
673         IL_FREE (pPriv->pPalette);
674     return IL_OK;
675 }
676
677
678         /*  ---------------------- ilYCbCr2DitherExecute ------------------------ */
679         /*  Execute() function: dither from subsampled-by-2 YCbCr to 8 bit pixels.
680             Dithering is done to RGB levels of 484 (232 in bits).
681         */
682 static ilError ilYCbCr2DitherExecute (
683     ilExecuteData          *pData,
684     long                    dstLine,
685     long                   *pNLines
686     )
687 {
688 ilYCbCr2DitherPrivPtr       pPriv;
689 ilImagePlaneInfo           *pPlane;
690 long                        nLinesDiv2, halfWidthM1, y;
691 long                        CbRowBytes, CrRowBytes;
692 ilPtr                       pYLine, pCbLine, pCrLine, pDstLine;
693 int                        *pKernelRowEnd;
694 long                        nPixelsDiv2M1;
695 ilPtr                       pCb, pCr;
696 register long               YRowBytes, dstRowBytes;
697 register long               Y, Cb, Cr, YTrans, CbTrans, CrTrans, comp, pixel;
698 register ilPtr              pY, pDst;
699 register ilRGBTransPtr      pTrans;
700 register ilPtr              pTranslate;
701 register int               *pKernel, kernel;
702 #define YCBCR2_KERNEL_SIZE  8                   /* size of dither kernel used */
703
704         /*  This filter handles a pipe image of YCbCr subsampled by 2 in Cb/Cr only.
705             The # of lines of Cb/Cr is therefore 1/2 the # of lines of Y.
706             Note: can only handle images with even width/height; checked elsewhere.
707         */
708     pPlane = pData->pSrcImage->plane;
709     YRowBytes = pPlane->nBytesPerRow;
710     pYLine = pPlane->pPixels;
711     pPlane++;
712     CbRowBytes = pPlane->nBytesPerRow;
713     pCbLine = pPlane->pPixels;
714     pPlane++;
715     CrRowBytes = pPlane->nBytesPerRow;
716     pCrLine = pPlane->pPixels;
717     if (pData->srcLine) {
718         pYLine  += pData->srcLine * YRowBytes;
719         pCbLine += (pData->srcLine >> 1) * CbRowBytes;
720         pCrLine += (pData->srcLine >> 1) * CrRowBytes;
721         }
722     pPlane = pData->pDstImage->plane;
723     dstRowBytes = pPlane->nBytesPerRow;
724     pDstLine = pPlane->pPixels + dstLine * dstRowBytes;
725
726     pPriv = (ilYCbCr2DitherPrivPtr)pData->pPrivate;
727     halfWidthM1 = pData->pSrcImage->width >> 1; /* 1/2 # of pixels - 1 */
728     nLinesDiv2 = *pNLines >> 1;
729     if ((halfWidthM1 <= 0) || (nLinesDiv2 <= 0))
730         return IL_OK;
731     halfWidthM1 -= 1;
732     y = pPriv->y;
733     pPriv->y += *pNLines;
734
735     pTrans = &pPriv->trans;
736     pTranslate = pPriv->pTranslate;
737
738         /*  Do 4 pixels at a time, using 4 Ys (below L, L, below R, R) for each Cb,Cr pair.
739             For each Y, do one pixel: get "comp" (RGB) as done in other YCbCr->RGB
740             filters, except that all values are pre-multiplied by what the dither tables
741             would yield if the lookup to _ilMul4/8[] was done.  "pixel" is then used as
742             an index into pTranslate table to get pixel to write to dst.
743                 See comments in ilGetYCbCrDitherTable() for details.
744         */
745     while (nLinesDiv2-- > 0) {
746         pY = pYLine;
747         pYLine += YRowBytes << 1;       /* skip 2 lines; read 2 lines each loop */
748         pCb = pCbLine;
749         pCbLine += CbRowBytes;
750         pCr = pCrLine;
751         pCrLine += CrRowBytes;
752         pDst = pDstLine;
753         pDstLine += dstRowBytes << 1;   /* skip 2 lines; write 2 lines each loop */
754         nPixelsDiv2M1 = halfWidthM1;
755
756             /*  Point to even row within kernel based on "y"; point to end of row */
757         pKernel = (int *)&_ilDitherKernel[(y & 6) * YCBCR2_KERNEL_SIZE];
758         y += 2;
759         pKernelRowEnd = pKernel + 8;
760
761             /*  Dither 4 pixels, using one set of Cb,Cr and 4 Y's. */
762         do {
763             CrTrans = pTrans->CrTrans[*pCr++];
764             CbTrans = pTrans->CbTrans[*pCb++];
765             Cr = ((short)CrTrans);
766             CrTrans >>= 16;
767             Cb = ((short)CbTrans);
768             CbTrans >>= 16;
769             CbTrans += CrTrans;                 /* add Cb, Cr contributions to green */
770
771                 /*  Do pixel below */
772             YTrans = pTrans->YTrans[pY[YRowBytes]];
773             kernel = pKernel[YCBCR2_KERNEL_SIZE];
774             Y = ((short)YTrans);
775             Y += kernel;                        /* pre-add kernel to Y */
776             pixel = (Y + Cr) >> 8;
777             if (pixel >> YCBCR2_NR_BITS) goto d0ClipR;
778 d0RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
779             if (comp >> YCBCR2_NG_BITS) goto d0ClipG;
780 d0GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
781             comp = (Y + Cb) >> 8;
782             if (comp >> YCBCR2_NB_BITS) goto d0ClipB;
783 d0BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
784             pDst[dstRowBytes] = pTranslate[pixel];
785
786                 /*  Do pixel, move pKernel, pY, pDst one to the right */
787             YTrans = pTrans->YTrans[*pY++];
788             kernel = *pKernel++;
789             Y = ((short)YTrans);
790             Y += kernel;
791             pixel = (Y + Cr) >> 8;
792             if (pixel >> YCBCR2_NR_BITS) goto d1ClipR;
793 d1RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
794             if (comp >> YCBCR2_NG_BITS) goto d1ClipG;
795 d1GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
796             comp = (Y + Cb) >> 8;
797             if (comp >> YCBCR2_NB_BITS) goto d1ClipB;
798 d1BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
799             *pDst++ = pTranslate[pixel];
800
801                 /*  Same as above two steps for one pixel to the right */
802             YTrans = pTrans->YTrans[pY[YRowBytes]];
803             kernel = pKernel[YCBCR2_KERNEL_SIZE];
804             Y = ((short)YTrans);
805             Y += kernel;                        /* pre-add kernel to Y */
806             pixel = (Y + Cr) >> 8;
807             if (pixel >> YCBCR2_NR_BITS) goto d2ClipR;
808 d2RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
809             if (comp >> YCBCR2_NG_BITS) goto d2ClipG;
810 d2GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
811             comp = (Y + Cb) >> 8;
812             if (comp >> YCBCR2_NB_BITS) goto d2ClipB;
813 d2BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
814             pDst[dstRowBytes] = pTranslate[pixel];
815
816                 /*  Do pixel, move pKernel, pY, pDst one to the right */
817             YTrans = pTrans->YTrans[*pY++];
818             kernel = *pKernel++;
819             Y = ((short)YTrans);
820             Y += kernel;
821             pixel = (Y + Cr) >> 8;
822             if (pixel >> YCBCR2_NR_BITS) goto d3ClipR;
823 d3RClipped: comp = ((YTrans >> 16) + CbTrans + kernel) >> 8;
824             if (comp >> YCBCR2_NG_BITS) goto d3ClipG;
825 d3GClipped: pixel = (pixel << YCBCR2_NG_BITS) + comp;
826             comp = (Y + Cb) >> 8;
827             if (comp >> YCBCR2_NB_BITS) goto d3ClipB;
828 d3BClipped: pixel = (pixel << YCBCR2_NB_BITS) + comp;
829             *pDst++ = pTranslate[pixel];
830
831                 /*  If pKernel off end of row, reset back (-8) to beginning of row */
832             if (pKernel >= pKernelRowEnd)
833                 pKernel -= YCBCR2_KERNEL_SIZE;
834             } while (--nPixelsDiv2M1 >= 0);
835         }
836
837     return IL_OK;
838
839     /*  goto point for when R, G or B out of range: clamp pixel and go back and continue.
840         This is done to avoid taking a branch in the most common case: not out of range.
841     */
842 d0ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
843         goto d0RClipped;
844 d0ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
845         goto d0GClipped;
846 d0ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
847         goto d0BClipped;
848 d1ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
849         goto d1RClipped;
850 d1ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
851         goto d1GClipped;
852 d1ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
853         goto d1BClipped;
854 d2ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
855         goto d2RClipped;
856 d2ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
857         goto d2GClipped;
858 d2ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
859         goto d2BClipped;
860 d3ClipR: if (pixel > YCBCR2_MAX_R) pixel = YCBCR2_MAX_R; else pixel = 0;
861         goto d3RClipped;
862 d3ClipG: if (comp > YCBCR2_MAX_G) comp = YCBCR2_MAX_G; else comp = 0;
863         goto d3GClipped;
864 d3ClipB: if (comp > YCBCR2_MAX_B) comp = YCBCR2_MAX_B; else comp = 0;
865         goto d3BClipped;
866 }
867
868     /*  ------------------------- ilSetupYCbCrDitherTable -------------------------- */
869     /*  Setup the given YCbCr conversion table with values for converting YCbCr->RGB
870         with the dither values pre-multiplied into them.
871     */
872 static void ilSetupYCbCrDitherTable (
873     register ilRGBTransPtr  pTrans,
874     ilImageDes             *pDes
875     )
876 {
877     ilYCbCrInfo            *pYCbCr;
878     double                  Lr, Lg, Lb, scaledY;
879     register double         factor, gFactor, rbDither, gDither;
880     register int            i, refBlack, upper, lower;
881     struct {
882         int                 refBlack;
883         double              scale;
884         } sample [3];
885
886         /*  This function sets up the given translation table, similar to 
887             ilGetYCbCrConvertTable(); see that function for more details.  The difference
888             is that the multiplications done for dithering (i.e. by table lookup into
889             _ilMul4/8[]) are done into the values stored in the table.
890                 For example, red is obtained from Y and Cr by:
891                     red = (short)YTrans[Y] + (short)CrTrans[Cr]         (1)
892                 then red is dithered down to 4 levels by:
893                     red = (pMul4[red] + kernel) >> 8;                   (2)
894                 where "pMul4[i]" = i * (nLevels(4) - 1) * 256 / 255"; see /ilc/ildither.c 
895                 But if the values in the low-order short of YTrans and CrTrans are
896                 premultiplied by "(nLevels - 1) * 256 / 255" then the lookup into pMul4[]
897                 can be skipped, and step (2) above can be skipped.
898         */
899     pYCbCr = &pDes->typeInfo.YCbCr;
900
901     for (i = 0; i < 3; i++) {
902         sample[i].refBlack = pYCbCr->sample[i].refBlack;
903         sample[i].scale = ((i == 0) ? 255 : 127) / 
904                           (pYCbCr->sample[i].refWhite - pYCbCr->sample[i].refBlack);
905         }
906
907         /*  Calc the Cb/Cr lookup tables, factoring in their scale factors. */
908     Lr = ((double)pYCbCr->lumaRed / (double)10000);
909     Lg = ((double)pYCbCr->lumaGreen / (double)10000);
910     Lb = ((double)pYCbCr->lumaBlue / (double)10000);
911
912         /*  Calc the RGB dither multiply factors; R and B must be the same! */
913     rbDither = ((1 << YCBCR2_NR_BITS) - 1) * 256 / 255;
914     gDither = ((1 << YCBCR2_NG_BITS) - 1) * 256 / 255;
915
916         /*  Y translation: lower is scaled Y, upper is value added to get green */
917     gFactor = (1 - Lb - Lr) / Lg;
918     for (i = 0; i < 256; i++) {
919         scaledY = sample[0].scale * i - sample[0].refBlack;
920         upper = scaledY * gFactor * gDither + 0.5;
921         lower = scaledY * rbDither;
922         pTrans->YTrans[i] = (upper << 16) | (lower & 0xffff);
923         }
924
925         /*  Cb: lower is added to Y to get blue, upper added to get green */
926     factor = (2 - 2 * Lb) * sample[1].scale;
927     gFactor = -Lb * (2 - 2 * Lb) / Lg;
928     refBlack = sample[1].refBlack;
929     for (i = 0; i < 256; i++) {
930         upper = (i - refBlack) * gFactor * gDither + 0.5;
931         lower = (i - refBlack) * factor * rbDither + 0.5;
932         pTrans->CbTrans[i] = (upper << 16) | (lower & 0xffff);
933         }
934
935         /*  Cr: lower is added to Y to get red, upper added to get green */
936     factor = (2 - 2 * Lr) * sample[2].scale;
937     gFactor = -Lr * (2 - 2 * Lr) / Lg;
938     refBlack = sample[2].refBlack;
939     for (i = 0; i < 256; i++) {
940         upper = (i - refBlack) * gFactor * gDither + 0.5;
941         lower = (i - refBlack) * factor * rbDither + 0.5;
942         pTrans->CrTrans[i] = (upper << 16) | (lower & 0xffff);
943         }
944
945 }
946
947
948     /*  ---------------------------- _ilDitherYCbCr ----------------------------- */
949     /*  Does conversions of some forms of YCbCr to dithered IL_PALETTE.
950         Returns "true" if conversion handled, else "false" if not handled or if error.
951         The pipe image must be an uncompressed YCbCr image.
952     */
953 IL_PRIVATE ilBool _ilDitherYCbCr (
954     ilPipe                  pipe,
955     ilPipeInfo             *pInfo,
956     ilImageDes             *pDes,
957     ilImageFormat          *pFormat,
958     int                     option,
959     ilConvertToPaletteInfo *pData
960     )
961 {
962     ilSrcElementData        srcData;
963     ilDstElementData        dstData;
964     ilPtr                   pTranslate;
965     ilYCbCr2DitherPrivPtr   pPriv;
966     unsigned short         *pPalette;
967     ilImageDes              des;
968
969         /*  Check for case handled here: 8x8 area dither, levels 484 specified; pipe
970             image is 8 bit/comp YCbCr, subsampled by 2, even width and height.
971         */
972     if (((option != IL_CONVERT_TO_PALETTE) && (option != 0))
973      || !pData
974      || (pData->method != IL_AREA_DITHER)
975      || (pData->levels[0] != 4)
976      || (pData->levels[1] != 8) 
977      || (pData->levels[0] != 4)
978      || (pData->kernelSize != 8)
979      || (pFormat->sampleOrder == IL_SAMPLE_PIXELS) 
980      || (pFormat->nBitsPerSample[0] != 8)
981      || (pFormat->nBitsPerSample[1] != 8)
982      || (pFormat->nBitsPerSample[2] != 8)
983      || (pDes->typeInfo.YCbCr.sample[0].subsampleHoriz != 1)
984      || (pDes->typeInfo.YCbCr.sample[0].subsampleVert != 1)
985      || (pDes->typeInfo.YCbCr.sample[1].subsampleHoriz != 2)
986      || (pDes->typeInfo.YCbCr.sample[1].subsampleVert != 2)
987      || (pDes->typeInfo.YCbCr.sample[2].subsampleHoriz != 2)
988      || (pDes->typeInfo.YCbCr.sample[2].subsampleVert != 2)
989      || ((pInfo->width | pInfo->height) & 1)) { /* width or height odd */
990         pipe->context->error = 0;               /* can't handle; not an error */
991         return FALSE;
992         }
993
994         /*  If mapImage given, validate it and point to its pixels.  If not given,
995             pTranslate becomes null and will point to "identity" mapImage below.
996         */
997     pTranslate = (ilPtr)NULL;
998     if (pData->mapImage) {
999         ilImageInfo *pInfo;
1000         if (!ilQueryClientImage (pData->mapImage, &pInfo, 0)
1001          || (pInfo->width != 256) || (pInfo->height != 1) 
1002          || (pInfo->pDes->compression != IL_UNCOMPRESSED)
1003          || (pInfo->pDes->nSamplesPerPixel != 1)
1004          || (pInfo->pFormat->nBitsPerSample[0] != 8))
1005             return ilDeclarePipeInvalid (pipe, IL_ERROR_MAP_IMAGE);
1006         pTranslate = pInfo->plane[0].pPixels;
1007         }
1008
1009         /*  alloc pPalette unless not palette dst; init with ramp if not choosing */
1010     pPalette = (unsigned short *)NULL;
1011     if (pData->dstType == IL_PALETTE) {
1012         register int            red, green, blue;
1013         register int            redLevel, greenLevel, blueLevel;
1014         register unsigned short *pPal;
1015
1016         pPalette = (unsigned short *)IL_MALLOC_ZERO (3 * 256 * sizeof(unsigned short));
1017         if (!pPalette)
1018             return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
1019         pPal = pPalette;
1020         for (red = 0; red < pData->levels[0]; red++) {
1021             redLevel = 65535 * red / (pData->levels[0] - 1);
1022             for (green = 0; green < pData->levels[1]; green++) {
1023                 greenLevel = 65535 * green / (pData->levels[1] - 1);
1024                 for (blue = 0; blue < pData->levels[2]; blue++) {
1025                     blueLevel = 65535 * blue / (pData->levels[2] - 1);
1026                     pPal [0]   = redLevel;
1027                     pPal [256] = greenLevel;
1028                     pPal [512] = blueLevel;
1029                     pPal++;
1030                     }
1031                 }
1032             }
1033         }
1034
1035         /*  Add element to the pipe, init private.  Accept strips as they are;
1036             presumably they will be even # lines because image is subsampled.
1037         */
1038     srcData.consumerImage = (ilObject)NULL;
1039     srcData.stripHeight = pInfo->stripHeight;
1040     srcData.constantStrip = FALSE;
1041     srcData.minBufferHeight = 0;
1042
1043     dstData.producerObject = (ilObject)NULL;
1044     des = *IL_DES_GRAY;
1045     des.type = pData->dstType;
1046     if (des.type == IL_PALETTE) {
1047         des.flags = IL_DITHERED_PALETTE;
1048         des.typeInfo.palette.levels[0] = pData->levels[0];
1049         des.typeInfo.palette.levels[1] = pData->levels[1];
1050         des.typeInfo.palette.levels[2] = pData->levels[2];
1051         }
1052     dstData.pDes = &des;
1053     dstData.pFormat = IL_FORMAT_BYTE;
1054     dstData.width = pInfo->width;
1055     dstData.height = pInfo->height;
1056     dstData.stripHeight = 0;
1057     dstData.constantStrip = FALSE;
1058     dstData.pPalette = pPalette;
1059
1060     pPriv = (ilYCbCr2DitherPrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
1061             sizeof(ilYCbCr2DitherPrivRec), 0, &srcData, &dstData, 
1062             ilYCbCr2DitherInit, IL_NPF, ilYCbCr2DitherDestroy, ilYCbCr2DitherExecute, NULL, 0);
1063     if (!pPriv) {
1064         if (pPriv->pPalette)
1065             IL_FREE (pPriv->pPalette);
1066         return FALSE;
1067         }
1068
1069         /*  Init pPriv; point pTranslate to identity image if no mapImage given */
1070     if (pTranslate)
1071         pPriv->pTranslate = pTranslate;
1072     else {
1073         int     i;
1074         pPriv->pTranslate = pPriv->translate;
1075         for (i = 0; i < 256; i++)
1076             pPriv->translate[i] = i;
1077         }
1078     pPriv->pPalette = pPalette;
1079
1080         /*  Init tables for YCbCr->RGB conversion plus dithering */
1081     ilSetupYCbCrDitherTable (&pPriv->trans, pDes);
1082
1083         /*  Update pipe info and return */
1084     ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
1085     return TRUE;
1086 }
1087
1088
1089
1090     /*  ======================= RGB To YCbCr Code =================================== */
1091
1092     /*  # of bits of fixed point precision for interim values during conversion */
1093 #define PR2Y    16
1094
1095     /*  Private for RGB to YCbCr filter. For each component to be calculated (Y/Cb/Cr),
1096         a table indexed by the src component (R/G/B) which gives the contribution
1097         for that src component, in fixed point: <32-PR2Y bits>.<PR2Y bits>
1098     */
1099 typedef struct {
1100     long    YR[256], YG[256], YB[256];          /* component lookup tables */
1101     long    CbR[256], CbG[256], CbB[256];
1102     long    CrR[256], CrG[256], CrB[256];
1103     long    YRefBlack, CbRefBlack, CrRefBlack;  /* component reference black */
1104     } ilRGBToYCbCrPrivRec, *ilRGBToYCbCrPrivPtr;
1105
1106     /*  Macro to do floor/ceiling of given value: makes it 0..255. 
1107         Shift right by 8; if not zero, is < 0 or > 255; handle those cases.
1108     */
1109 #define BYTE_RANGE(_pixel) {          \
1110     if (_pixel & ~0xff)  \
1111         if ((_pixel) < 0) _pixel = 0; \
1112         else _pixel = 255;            \
1113     }
1114
1115
1116     /*  ----------------------- ilExecuteRGBToYCbCr -------------------------------- */
1117     /*  Execute(): convert _pixel_ src RGB to _planar_ dst YCbCr.
1118     */
1119 static ilError ilExecuteRGBToYCbCr (
1120     ilExecuteData          *pData,
1121     long                    dstLine,
1122     long                   *pNLines
1123     )
1124 {
1125 register ilRGBToYCbCrPrivPtr pPriv;
1126 ilPtr                       pSrcLine, pYLine, pCbLine, pCrLine;
1127 register ilPtr              pSrc, pY, pCb, pCr;
1128 register long               nPixelsM1;
1129 register long               YRefBlack, CbRefBlack, CrRefBlack;
1130 long                        nPixelsM1Init, nLinesM1;
1131 long                        YRowBytes, CbRowBytes, CrRowBytes, srcRowBytes;
1132 ilImagePlaneInfo           *pPlane;
1133 register long               R, G, B, pixel;
1134
1135
1136         /*  Set nPixels/LinesM1 to # pixels / lines - 1; exit if either 0. */
1137     pPriv = (ilRGBToYCbCrPrivPtr)pData->pPrivate;
1138     nPixelsM1Init = pData->pSrcImage->width;
1139     if (nPixelsM1Init <= 0)
1140         return;
1141     nPixelsM1Init--;
1142     nLinesM1 = *pNLines;
1143     if (nLinesM1 <= 0)
1144         return;
1145     nLinesM1--;
1146
1147         /*  Point pSrcLine to first line of src RGB (pixel-order) data; point 
1148             pY/Cb/CrLine to 1st line in dst YCbCr (planar-order) data planes.
1149             Get bytes/row for each of them.
1150         */
1151     pPlane = pData->pSrcImage->plane;
1152     srcRowBytes = pPlane->nBytesPerRow;
1153     pSrcLine = pPlane->pPixels + pData->srcLine * srcRowBytes;
1154
1155     pPlane = pData->pDstImage->plane;
1156     YRowBytes = pPlane->nBytesPerRow;
1157     pYLine = pPlane->pPixels + dstLine * YRowBytes;
1158     pPlane++;
1159     CbRowBytes = pPlane->nBytesPerRow;
1160     pCbLine = pPlane->pPixels + dstLine * CbRowBytes;
1161     pPlane++;
1162     CrRowBytes = pPlane->nBytesPerRow;
1163     pCrLine = pPlane->pPixels + dstLine * CrRowBytes;
1164
1165         /*  Load component refBlack values, plus 1/2 shift (precision) amt for rounding 
1166             into values, shifted up so it can be added before right shift.
1167         */
1168     YRefBlack = (pPriv->YRefBlack << PR2Y) + (1 << (PR2Y - 1));
1169     CbRefBlack = (pPriv->CbRefBlack << PR2Y) + (1 << (PR2Y - 1));
1170     CrRefBlack = (pPriv->CrRefBlack << PR2Y) + (1 << (PR2Y - 1));
1171
1172         /*  Convert pixel RGB to planar YCbCr. */
1173     do {
1174         pSrc = pSrcLine;
1175         pSrcLine += srcRowBytes;
1176         pY = pYLine;
1177         pYLine += YRowBytes;
1178         pCb = pCbLine;
1179         pCbLine += CbRowBytes;
1180         pCr = pCrLine;
1181         pCrLine += CrRowBytes;
1182
1183         nPixelsM1 = nPixelsM1Init;
1184         do {
1185             R = *pSrc++;
1186             G = *pSrc++;
1187             B = *pSrc++;
1188
1189             pixel = (pPriv->YR[R] + pPriv->YG[G] + pPriv->YB[B] + YRefBlack) >> PR2Y;
1190             if (pixel >> 8) goto YClip;
1191 YClipped:   *pY++ = pixel;
1192
1193             pixel = (pPriv->CbR[R] + pPriv->CbG[G] + pPriv->CbB[B] + CbRefBlack) >> PR2Y;
1194             if (pixel >> 8) goto CbClip;
1195 CbClipped:  *pCb++ = pixel;
1196
1197             pixel = (pPriv->CrR[R] + pPriv->CrG[G] + pPriv->CrB[B] + CrRefBlack) >> PR2Y;
1198             if (pixel >> 8) goto CrClip;
1199 CrClipped:  *pCr++ = pixel;
1200
1201             } while (--nPixelsM1 >= 0);
1202         } while (--nLinesM1 >= 0);
1203
1204     return IL_OK;
1205
1206     /*  goto point for when Y/Cb/Cr out of range: clamp pixel and go back and continue.
1207         This is done to avoid taking a branch in the most common case: not out of range.
1208     */
1209 YClip:  if (pixel > 255) pixel = 255; else pixel = 0;
1210         goto YClipped;
1211 CbClip:  if (pixel > 255) pixel = 255; else pixel = 0;
1212         goto CbClipped;
1213 CrClip:  if (pixel > 255) pixel = 255; else pixel = 0;
1214         goto CrClipped;
1215 }
1216
1217     /*  ---------------------------- ilConvertRGBToYCbCr ----------------------------- */
1218     /*  Convert from source type (pDes->type) == RGB to YCbCr.
1219         pFormat points to the source format; on return, *pFormat is updated
1220         to the dst format. pNewDes points to the dst dest; *pDes to the src (pipe) des;
1221         on return it is updated to the new descriptor.
1222     */
1223 IL_PRIVATE ilBool _ilConvertRGBToYCbCr (
1224     ilPipe                  pipe,
1225     ilPipeInfo             *pInfo,
1226     register ilImageDes    *pDes,
1227     register const ilImageDes *pNewDes,
1228     ilImageFormat          *pFormat
1229     )
1230 {
1231     register ilRGBToYCbCrPrivPtr pPriv;
1232     register ilYCbCrInfo   *pYCbCr;
1233     ilDstElementData        dstData;
1234     double                  Lr, Lg, Lb, range;
1235     long                    YR, YG, YB;
1236     long                    CbR, CbG, CbB;
1237     long                    CrR, CrG, CrB;
1238     int                     i;
1239
1240         /*  Get format = planar order, 8 bits / pixel, or error */
1241     if ((pFormat->nBitsPerSample[0] != 8)
1242      || (pFormat->nBitsPerSample[1] != 8)
1243      || (pFormat->nBitsPerSample[2] != 8)
1244      || (pFormat->sampleOrder != IL_SAMPLE_PIXELS)) {
1245         if (!ilConvert (pipe, (ilImageDes *)NULL, IL_FORMAT_3BYTE_PIXEL, 0, NULL))
1246             return FALSE;
1247         }
1248
1249         /*  Add filter to write 3 planar YCbCr format image.  Update pDes: std YCbCr but
1250             with caller's sample refBlack/White and lumaRed/Green/Blue.
1251         */
1252     *pDes = *IL_DES_YCBCR;
1253     pDes->typeInfo.YCbCr.sample[0].refBlack = pNewDes->typeInfo.YCbCr.sample[0].refBlack;
1254     pDes->typeInfo.YCbCr.sample[0].refWhite = pNewDes->typeInfo.YCbCr.sample[0].refWhite;
1255     pDes->typeInfo.YCbCr.sample[1].refBlack = pNewDes->typeInfo.YCbCr.sample[1].refBlack;
1256     pDes->typeInfo.YCbCr.sample[1].refWhite = pNewDes->typeInfo.YCbCr.sample[1].refWhite;
1257     pDes->typeInfo.YCbCr.sample[2].refBlack = pNewDes->typeInfo.YCbCr.sample[2].refBlack;
1258     pDes->typeInfo.YCbCr.sample[2].refWhite = pNewDes->typeInfo.YCbCr.sample[2].refWhite;
1259     pDes->typeInfo.YCbCr.lumaRed   = pNewDes->typeInfo.YCbCr.lumaRed;
1260     pDes->typeInfo.YCbCr.lumaGreen = pNewDes->typeInfo.YCbCr.lumaGreen;
1261     pDes->typeInfo.YCbCr.lumaBlue  = pNewDes->typeInfo.YCbCr.lumaBlue;
1262
1263     *pFormat = *IL_FORMAT_3BYTE_PLANE;
1264     dstData.producerObject = (ilObject)NULL;
1265     dstData.pDes = pDes;
1266     dstData.pFormat = pFormat;
1267     dstData.width = pInfo->width;
1268     dstData.height = pInfo->height;
1269     dstData.stripHeight = 0;
1270     dstData.constantStrip = FALSE;
1271     dstData.pPalette = (unsigned short *)NULL;
1272     pPriv = (ilRGBToYCbCrPrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
1273             sizeof(ilRGBToYCbCrPrivRec), 0, (ilSrcElementData *)NULL, &dstData, 
1274             IL_NPF, IL_NPF, IL_NPF, ilExecuteRGBToYCbCr, NULL, 0);
1275     if (!pPriv)
1276         return FALSE;
1277
1278         /*  See Appendix O pg 3 of TIFF spec.  Use fixed point (16.16) for 
1279             floating point values.
1280                 Y = Lr*R + Lg*G + Lb*B
1281                 Cb = (B - Y) / (2-2*Lb)
1282                    = (B - Lr*R - Lg*G - Lb*B) / (2-2*Lb)
1283                    = R * -Lr/(2-2*Lb) + G * -Lg/(2-2*Lb) + B * (1-Lb)/(2-2*Lb)
1284                 Cr = (R - Y) / (2-2*Lr)
1285                    = (R - Lr*R - Lg*G - Lb*B) / (2-2*Lr)
1286                    = R * (1-Lr)/(2-2*Lr) + G * -Lg/(2-2*Lr) + B * -Lb/(2-2*Lr)
1287             In each case, the scale up to account for the scaling range is done
1288             by multiplying the scaling range into each RGB factor.  The refBlack is
1289             then added (not shown above).  Each of the values multiplied times R/G/B
1290             is called "Y/Cb/Cr|R/G/B".
1291         */
1292     pYCbCr = &pDes->typeInfo.YCbCr;
1293     Lr = (double)pYCbCr->lumaRed / 10000;
1294     Lg = (double)pYCbCr->lumaGreen / 10000;
1295     Lb = (double)pYCbCr->lumaBlue / 10000;
1296
1297         /*  Y R/G/B multiples, plus refBlack. */
1298     range = (1 << PR2Y) * (double)(pYCbCr->sample[0].refWhite - pYCbCr->sample[0].refBlack) / 255;
1299     YR = range * Lr + 0.5;
1300     YG = range * Lg + 0.5;
1301     YB = range * Lb + 0.5;
1302     pPriv->YRefBlack = pYCbCr->sample[0].refBlack;
1303
1304         /*  Cb and Cr multiples, refBlack.  Note coding range for Cb/Cr == 127. */
1305     range = (1 << PR2Y) * (double)(pYCbCr->sample[1].refWhite - pYCbCr->sample[1].refBlack) / 127;
1306     CbR = range * -Lr/(2-2*Lb) + 0.5;
1307     CbG = range * -Lg/(2-2*Lb) + 0.5;
1308     CbB = range * (1-Lb)/(2-2*Lb) + 0.5;
1309     pPriv->CbRefBlack = pYCbCr->sample[1].refBlack;
1310
1311     range = (1 << PR2Y) * (double)(pYCbCr->sample[2].refWhite - pYCbCr->sample[2].refBlack) / 127;
1312     CrR = range * (1-Lr)/(2-2*Lr) + 0.5;
1313     CrG = range * -Lg/(2-2*Lr) + 0.5;
1314     CrB = range * -Lb/(2-2*Lr) + 0.5;
1315     pPriv->CrRefBlack = pYCbCr->sample[2].refBlack;
1316
1317         /*  Build lookup table which is the result of multiplying the above by 0..255,
1318             i.e. the source RGB values.  Using this lookup table is equivalent to multiply
1319             but a whole lot faster.
1320         */
1321     for (i = 0; i < 256; i++) {
1322         pPriv->YR[i] = YR * i;
1323         pPriv->YG[i] = YG * i;
1324         pPriv->YB[i] = YB * i;
1325         pPriv->CbR[i] = CbR * i;
1326         pPriv->CbG[i] = CbG * i;
1327         pPriv->CbB[i] = CbB * i;
1328         pPriv->CrR[i] = CrR * i;
1329         pPriv->CrG[i] = CrG * i;
1330         pPriv->CrB[i] = CrB * i;
1331         }
1332
1333     return TRUE;
1334 }
1335
1336