lib/DtHelp/il: remove register keyword
[oweals/cde.git] / cde / lib / DtHelp / il / ilupsample.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 libraries 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: ilupsample.c /main/6 1996/06/19 12:20:32 ageorge $ */
24 /**---------------------------------------------------------------------
25 ***     
26 ***    (c)Copyright 1991 Hewlett-Packard Co.
27 ***    
28 ***                             RESTRICTED RIGHTS LEGEND
29 ***    Use, duplication, or disclosure by the U.S. Government is subject to
30 ***    restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
31 ***    Technical Data and Computer Software clause in DFARS 252.227-7013.
32 ***                             Hewlett-Packard Company
33 ***                             3000 Hanover Street
34 ***                             Palo Alto, CA 94304 U.S.A.
35 ***    Rights for non-DOD U.S. Government Departments and Agencies are as set
36 ***    forth in FAR 52.227-19(c)(1,2).
37 ***
38 ***-------------------------------------------------------------------*/
39
40         /*  /ilc/ilupsample.c : Code for doing upsampling, or scaling
41             by 1, 2, 4x of planar images; each plane independent.
42         */
43 #include "ilint.h"
44 #include "ilpipelem.h"
45 #include "ilconvert.h"
46 #include "ilerrors.h"
47
48     /*  ========================== Fast Code ================================= */
49     /*  This code handles the upsampling cases where the vertical and horizontal subsample
50         factors are equal (generally the case).  It is faster than the general case code.
51     */
52
53     /*  Private for fast upsampling filters. "subsampleShift" is the shift value (0,1,2)
54         for how that sample was subsampled; "shift" is the shift for upsampling that
55         sample.  They are the same unless "double"; shift[i]=subsampleShift[i]+1 then.
56     */
57 typedef struct {
58     unsigned int        nSamples;
59     unsigned int        subsampleShift[IL_MAX_SAMPLES];
60     unsigned int        shift[IL_MAX_SAMPLES];
61     int                 scaleFactor;
62     } ilUpFastPrivRec, *ilUpFastPrivPtr;
63
64
65     /*  -------------------------- ilExecuteFastUpsample ------------------------------ */
66     /*  Called by ilExecuteFastUpsample() to quadruple (scale up by 4) one plane.
67     */
68 static void ilUpsampleQuadruple (
69     long                    nSrcLines,
70     long                    nSrcBytes,
71     long                    srcRowBytes,
72     ilPtr                   pSrcLine,
73     long           dstRowBytes,
74     ilPtr                   pDstLine
75     )
76 {
77 int                temp0, temp1, delta;
78 unsigned long      aLong;
79 long                        nSrcBytesM1, dstOffset;
80 ilPtr              pSrc, pSrcBelow, pDst;
81 int                left, leftBelow, right, rightBelow;
82
83 #define FUPINTER(_start, _delta, _temp, _result) { \
84     _result = (_start) << 8; \
85     _temp += _delta;        \
86     _result += _temp >> 2; \
87     _temp += _delta;        \
88     _result <<= 8;         \
89     _result += _temp >> 2; \
90     _temp += _delta;        \
91     _result <<= 8;         \
92     _result += _temp >> 2; \
93     }
94
95         /*  Quadrupling: similar to doubling except * 4.  Basically interpolate
96             and write 4 values from src pixels "left" and "right", by setting
97             "temp" = left * 4, then adding "right-left" each time to it and 
98             writing that result div 4.  Do down for four lines.
99         */
100     dstOffset = 4 - 3 * dstRowBytes;    /* from 3 lines down to next long */
101     while (nSrcLines-- > 0) {           /* double and interpolate next line */
102         pSrc = pSrcLine;
103         if (nSrcLines > 0)              /* else use last for below line */
104             pSrcLine += srcRowBytes;
105         pSrcBelow = pSrcLine;
106         pDst = pDstLine;
107         pDstLine += dstRowBytes << 2;   /* skip down 4 dst lines */
108
109         nSrcBytesM1 = nSrcBytes - 1;
110         if (nSrcBytesM1 > 0) {
111             left = *pSrc++;
112             leftBelow = *pSrcBelow++;
113             }
114         else {                          /* one pixel; use left as right */
115             left = *pSrc;
116             leftBelow = *pSrcBelow;
117             }
118         while (TRUE) {
119             right = *pSrc++;
120             temp0 = left << 2;
121             delta = right - left;
122             FUPINTER (left, delta, temp0, aLong)
123             *((unsigned long *)pDst) = aLong;
124             pDst += dstRowBytes;
125
126             rightBelow = *pSrcBelow++;
127             temp0 = (left << 1) + left;
128             temp0 += leftBelow;
129             temp1 = (right << 1) + right;
130             temp1 += rightBelow;
131             delta = (temp1 >> 2) - (temp0 >> 2);
132             FUPINTER (temp0>>2, delta, temp0, aLong)
133             *((unsigned long *)pDst) = aLong;
134             pDst += dstRowBytes;
135
136             temp0 = (left + leftBelow) >> 1;
137             temp1 = (right + rightBelow) >> 1;
138             delta = temp1 - temp0;
139             temp0 <<= 2;
140             FUPINTER (temp0>>2, delta, temp0, aLong)
141             *((unsigned long *)pDst) = aLong;
142             pDst += dstRowBytes;
143
144             temp0 = (leftBelow << 1) + leftBelow;
145             temp0 += left;
146             temp1 = (rightBelow << 1) + rightBelow;
147             temp1 += right;
148             delta = (temp1 >> 2) - (temp0 >> 2);
149             FUPINTER (temp0>>2, delta, temp0, aLong)
150             *((unsigned long *)pDst) = aLong;
151             pDst += dstOffset;          /* bump to next long of first line */
152
153             left = right;
154             leftBelow = rightBelow;
155             if (--nSrcBytesM1 < 0)
156                 break;                  /* last pixel; done */
157             if (nSrcBytesM1 == 0) {     /* next-to-last pixel; back up src */
158                 pSrc--;
159                 pSrcBelow--;
160                 }
161             }   /* END while bytes across */
162         }       /* END while lines */
163 }
164
165
166     /*  -------------------------- ilExecuteFastUpsample ------------------------------ */
167     /*  Execute(): upsample as necessary "pPriv->nSamples" planes of the source image,
168         where the hori/vertical upsampling factors are equal for each plane.
169     */
170 static ilError ilExecuteFastUpsample (
171     ilExecuteData          *pData,
172     long                    dstLine,
173     long                   *pNLines
174     )
175 {
176 ilUpFastPrivPtr             pPriv;
177 ilImagePlaneInfo           *pSrcPlane, *pDstPlane;
178 int                         sample, subsampleShift;
179 ilPtr                       pSrcLine, pDstLine;
180 long                        width, nLines, nSrcLines, nSrcBytes, srcRowBytes, nSrcBytesM2;
181 long               dstRowBytes;
182 ilPtr              pSrc, pSrcBelow, pDst;
183 int                left, leftBelow, right, rightBelow;
184
185         /*  Get width and height of _upsampled_ image; exit if zero. */
186     pPriv = (ilUpFastPrivPtr)pData->pPrivate;
187     nLines = *pNLines;                          /* # of lines before subsampling */
188     *pNLines = nLines << pPriv->scaleFactor;    /* # of dst lines after any scaling */
189     if (nLines <= 0)
190         return IL_OK;
191     width = pData->pSrcImage->width;
192     if (width <= 0)
193         return IL_OK;
194
195         /*  Loop on samples (components), upsample/translate each plane separately.
196             Note that "srcLine" is shifted - indexing into plane based on vert subsample.
197         */
198     pSrcPlane = pData->pSrcImage->plane;
199     pDstPlane = pData->pDstImage->plane;
200     for (sample = 0; sample < pPriv->nSamples; sample++, pSrcPlane++, pDstPlane++) {
201         subsampleShift =  pPriv->subsampleShift[sample];
202
203         srcRowBytes = pSrcPlane->nBytesPerRow;
204         pSrcLine = pSrcPlane->pPixels + (pData->srcLine >> subsampleShift) * srcRowBytes;
205         dstRowBytes = pDstPlane->nBytesPerRow;
206         pDstLine = pDstPlane->pPixels + dstLine * dstRowBytes;
207
208             /*  Handle tiny image: if subsampling left nothing, fill with zeros. */
209         nSrcLines = nLines >> subsampleShift;
210         nSrcBytes = width >> subsampleShift;
211         if ((nSrcLines <= 0) || (nSrcBytes <= 0)) {
212             long i = nLines;
213             while (i-- > 0) {
214                 bzero ((char *)pDstLine, width);
215                 pDstLine += dstRowBytes;
216                 }
217             }
218         else {
219             switch (pPriv->shift[sample]) {
220
221                 /*  No upsampling/doubling: just copy this plane */
222               case 0:
223                 pSrc = pSrcLine;
224                 pDst = pDstLine;
225                 if (srcRowBytes == dstRowBytes)
226                     bcopy ((char *)pSrc, (char *)pDst, nSrcBytes * nSrcLines);
227                 else while (nSrcLines-- > 0) {
228                     bcopy ((char *)pSrc, (char *)pDst, nSrcBytes);
229                     pSrc += srcRowBytes;                
230                     pDst += dstRowBytes;
231                     }
232                 break;
233
234                 /*  Doubling: loop while there are a pair of src lines, replicating
235                     and interpolating into dst.  For each pixel across: do left with
236                     interpolated pixel between.  For each pixel pair, interpolate
237                     between hori and vertically.  Replicate last pixel.  For last line,
238                     point "pSrcBelow" to last src line: replication of last line occurs.
239                 */
240               case 1:
241                 pSrc = pSrcLine;
242                 pDst = pDstLine;
243                 while (nSrcLines-- > 0) {           /* double and interpolate next line */
244                     pSrc = pSrcLine;
245                     if (nSrcLines > 0)              /* else use last for below line */
246                         pSrcLine += srcRowBytes;
247                     pSrcBelow = pSrcLine;
248                     pDst = pDstLine;
249                     pDstLine += dstRowBytes << 1;   /* skip down 2 dst lines */
250
251                     leftBelow = *pSrcBelow++;       /* do first (left) byte */
252                     left = *pSrc++;
253                     pDst[dstRowBytes] = (left + leftBelow) >> 1;
254                     *pDst++ = left; 
255                     nSrcBytesM2 = nSrcBytes - 2;
256                     if (nSrcBytesM2 >= 0) {         /* middle bytes with interpolation */
257                         do {
258                             rightBelow = *pSrcBelow++;
259                             right = *pSrc++;
260                             left = (left + right) >> 1;
261                             pDst[dstRowBytes] = (left + ((leftBelow + rightBelow) >> 1)) >> 1;
262                             *pDst++ = left;
263                             pDst[dstRowBytes] = (right + rightBelow) >> 1;
264                             *pDst++ = right;
265                             left = right;
266                             leftBelow = rightBelow;
267                             } while (--nSrcBytesM2 >= 0);
268                         }
269                     *pDst = left;                   /* replicate last byte */
270                     pDst[dstRowBytes] = (left + leftBelow) >> 1;
271                     }   /* END while lines */
272                 break;  /* END doubling */
273
274               case 2:
275                 ilUpsampleQuadruple (nSrcLines, nSrcBytes, srcRowBytes, pSrcLine,
276                                      dstRowBytes, pDstLine);
277                 break;
278
279                 }   /* END switch sample's shift */
280             }       /* END not tiny image */
281         }           /* END for each sample/plane */
282
283     return IL_OK;
284 }
285
286     /*  ---------------------------- _ilFastUpsample ----------------------------- */
287     /*  Attempt to upsample and scale based on "scaleFactor" (0 = no scaling; 1 = double;
288         2 = 4x), or return false if it cannot be done with the given pipe image (in which
289         case pipe->context->error == 0) or if error occurs (error != 0).  Pipe image
290         must be decompressed before calling this function.
291             Note: when scaling up by 2x or 4x, always yields even/ *4 width/height.
292         Thus, *cannot* be used to upsample odd width/height YCbCr images.
293     */
294 IL_PRIVATE ilBool _ilFastUpsample (
295     ilPipe                  pipe,
296     ilPipeInfo             *pInfo,
297     ilImageDes             *pDes,
298     ilImageFormat          *pFormat,
299     int                     scaleFactor
300     )
301 {
302     ilUpFastPrivPtr pUpPriv;
303     ilUpFastPrivRec         upPriv;
304     int                     i, j;
305     ilYCbCrSampleInfo      *pSample;
306     ilDstElementData        dstData;
307     ilSrcElementData        srcData;
308     const ilImageDes       *pNewDes;
309
310         /*  Check image type; return if not handled, else init upPrivRec */
311     pipe->context->error = IL_OK;           /* assume no error */
312     pNewDes = (ilImageDes *)NULL;           /* assume no descriptor change */
313     upPriv.scaleFactor = scaleFactor;
314
315     switch (pDes->type) {
316       case IL_GRAY:
317         if (!scaleFactor) return TRUE;      /* no scaling is noop; return */
318         if (!ilConvert (pipe, IL_DES_GRAY, IL_FORMAT_BYTE, 0, NULL))
319             return FALSE;
320         upPriv.nSamples = 1;                /* double one plane */
321         upPriv.subsampleShift[0] = 0;
322         upPriv.shift[0] = scaleFactor;
323         ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
324         break;
325
326       case IL_RGB:
327         if (!scaleFactor) return TRUE;      /* no scaling is noop; return */
328         if (!ilConvert (pipe, IL_DES_RGB, IL_FORMAT_3BYTE_PLANE, 0, NULL))
329             return FALSE;
330         upPriv.nSamples = 3;                /* double three planes */
331         upPriv.subsampleShift[0] = upPriv.subsampleShift[1] = upPriv.subsampleShift[2] = 0;
332         upPriv.shift[0] = upPriv.shift[1] = upPriv.shift[2] = scaleFactor;
333         ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
334         break;
335
336         /*  YCbCr: hori/vert subsample each plane must be equal and must be planar */
337       case IL_YCBCR:
338         if (pFormat->sampleOrder != IL_SAMPLE_PLANES)
339             return FALSE;
340         upPriv.nSamples = 3;
341         for (i = 0, pSample = pDes->typeInfo.YCbCr.sample; i < 3; i++, pSample++) {
342             if (pSample->subsampleHoriz != pSample->subsampleVert)
343                 return FALSE;
344             j = _ilSubsampleShift [pSample->subsampleHoriz];
345             upPriv.subsampleShift[i] = j;   /* shift due to subsampling */
346             j += scaleFactor;               /* plus that due to scaling */
347             if (j > 2)                      /* more than 4x scale; can't do */
348                 return FALSE;
349             upPriv.shift[i] = j;            /* shift due to subsampling and doubling */
350             }
351         pNewDes = IL_DES_YCBCR;             /* now YCbCr with no subsampling */
352         break;
353
354       default:
355         return FALSE;                       /* can't handle image type */
356         }
357
358         /*  Add the pipe element: width / height scaled if double */
359     dstData.producerObject = (ilObject)NULL;
360     dstData.pDes = pNewDes;
361     dstData.pFormat = pFormat;
362     dstData.width = pInfo->width << scaleFactor;
363     dstData.height = pInfo->height << scaleFactor;
364     dstData.stripHeight = pInfo->stripHeight << scaleFactor;
365     dstData.constantStrip = pInfo->constantStrip;
366     dstData.pPalette = (unsigned short *)NULL;
367
368         /*  Set format: 32 bit alignment required if any scales up by 4x.
369             Don't mark as such if width multiple of 4, as other filters may check it.
370         */
371     if (dstData.width & 3) {                /* not long-aligned */
372         for (i = 0; i < upPriv.nSamples; i++)
373             if (upPriv.shift[i] == 2) {
374                 pFormat->rowBitAlign = 32;
375                 break;
376                 }
377         }
378
379         /*  Use current strip height rather than allowing image to be split into
380             smaller strips; there is a copied rather than smoothed line at the end
381             of each strip, so should do min number of strips.  Could force no strips
382             for better quality, but performance penalty probably not worth it.
383         */
384     srcData.consumerImage = (ilObject)NULL;
385     srcData.stripHeight = pInfo->stripHeight;
386     srcData.constantStrip = FALSE;
387     srcData.minBufferHeight = 0;
388     pUpPriv = (ilUpFastPrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
389                  sizeof (ilUpFastPrivRec), 0, &srcData,
390                  &dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteFastUpsample, NULL, 0);
391     if (!pUpPriv)
392         return FALSE;
393     *pUpPriv = upPriv;
394
395     ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat);
396     return TRUE;
397 }
398
399
400     /*  ========================== Slow General Code ================================= */
401     /*  This code handles the upsampling cases where the vertical and horizontal subsample
402         factors are not the same.
403     */
404
405     /*  Upsample factors as shifts (1=0, 2=1, 4=2) - others not supported. */
406 typedef struct {
407     unsigned int        horiz, vert;
408     } ilUpsampleShiftRec, *ilUpsampleShiftPtr;
409
410     /*  Private for upsampling filters. */
411 typedef struct {
412     int                 nSamples;               /* # of samples (components) to process */
413     ilUpsampleShiftRec  shift[IL_MAX_SAMPLES];  /* upsample mul as shift value */
414     } ilUpsamplePrivRec, *ilUpsamplePrivPtr;
415
416
417
418     /*  -------------------------- ilUpsampleHorizontal ------------------------------ */
419     /*  Do horizontal upsampling as necessary first, spreading the dst lines
420         based on vertical upsampling.
421         # of lines to do is "height" divided by vertical upsample factor.
422     */
423 static void ilUpsampleHorizontal (
424     unsigned int    shift,          /* 0 = no upsample; 1 = * 2 (doubling); 2 = * 4  */
425     long            width,          /* width of _whole_ (un-upsampled) image */
426     long            nLines,         /* # of src lines to upsample */
427     long            srcRowBytes,    /* bytes / row of src (downsampled) image */
428     ilPtr           pSrcLine,       /* ptr to first line of src image */
429     long            dstRowInc,      /* byte offset between dst lines written */
430     ilPtr           pDstLine        /* ptr to first line of dst image */
431     )
432 {
433 long                        nMidPixels, nEndPixels;
434 long               nPixelsM1;
435 ilPtr              pSrc, pDst;
436 ilByte             pixel, prevPixel;
437
438     if (nLines <= 0)
439         return;
440
441         /*  Upsample based on "shift", i.e. scale up by 2 ++ "shift". */
442     switch (shift) {
443
444         /*  0: no horizontal upsampling - just copy */
445       case 0:
446         pSrc = pSrcLine;
447         pDst = pDstLine;
448         while (nLines-- > 0) {
449             bcopy ((char *)pSrc, (char *)pDst, width);
450             pSrc += srcRowBytes;                
451             pDst += dstRowInc;
452             }
453         break;
454
455         /*  1: doubling; The image for this plane is 1/2 "width".  Copy the first pixel, 
456             interpolate pixels between previous pixels and the last pixel, then replicate
457             last pixel to fill out width.  Example: width == 9, source width = 4 (0..3):
458                  A                    B                       C:
459                 <0> | <0+1/2> <1> <1+2/2> <2> <2+3/2> <3> | <3> <3> 
460             A: write first src pixel (pixel <0>); set into "prevPixel"
461             B: write (interpolated pixel, src pixel) pair "nMidPixels" times, leaving
462                srcPixel in prevPixel
463             C: replicate last pixel (prevPixel) "nEndPixels" times
464         */
465       case 1:
466         nMidPixels = (width >> 1) - 1;
467         nEndPixels = width - (nMidPixels << 1) - 1;
468         while (nLines-- > 0) {
469             pSrc = pSrcLine;
470             pSrcLine += srcRowBytes;
471             pDst = pDstLine;
472             pDstLine += dstRowInc;
473             prevPixel = *pSrc++;
474             *pDst++ = prevPixel;        /* prevPixel = first src pixel; copy to dst */
475             if (nMidPixels > 0) {       /* <interpolated>, <src> pairs */
476                 nPixelsM1 = nMidPixels - 1;
477                 do {
478                     pixel = *pSrc++;
479                     *pDst++ = (pixel + prevPixel) >> 1;
480                     *pDst++ = pixel;
481                     prevPixel = pixel;
482                     } while (--nPixelsM1 >= 0);
483                 }
484             if (nEndPixels > 0) {
485                 nPixelsM1 = nEndPixels - 1;
486                 do {
487                     *pDst++ = prevPixel;
488                     } while (--nPixelsM1 >= 0);
489                 }
490             }   /* END while lines */
491         break;
492
493         /*  1: quadrupling; The image for this plane is 1/4 "width".  Copy the first pixel,
494             interpolate pixels between previous pixels and the last pixel, then replicate
495             last pixel to fill out width.  To interpolate: add "delta" = pixel-prevPixel
496             to "temp" (which starts at prevPixel*4) then divide temp by 4 before storing.
497             For example, if prevPixel = 3 and pixel = 6, delta = 3, pixels are:
498                 3 (prevPixel) | (12+3)/4=3   (15+3)/4=4   (18+3)/4=5   6 (src pixel) |
499         */
500       case 2: {
501         int delta, temp;
502
503         nMidPixels = (width >> 2) - 1;
504         nEndPixels = width - (nMidPixels << 2) - 1;
505         while (nLines-- > 0) {
506             pSrc = pSrcLine;
507             pSrcLine += srcRowBytes;
508             pDst = pDstLine;
509             pDstLine += dstRowInc;
510             prevPixel = *pSrc++;
511             *pDst++ = prevPixel;        /* prevPixel = first src pixel; copy to dst */
512             if (nMidPixels > 0) {       /* <3 interpolated>, <src> 4tuples */
513                 nPixelsM1 = nMidPixels - 1;
514                 do {
515                     pixel = *pSrc++;
516                     delta = pixel - prevPixel;
517                     temp = (prevPixel << 2);
518                     temp += delta;
519                     *pDst++ = temp >> 2;
520                     temp += delta;
521                     *pDst++ = temp >> 2;
522                     temp += delta;
523                     *pDst++ = temp >> 2;
524                     *pDst++ = pixel;
525                     prevPixel = pixel;
526                     } while (--nPixelsM1 >= 0);
527                 }
528             if (nEndPixels > 0) {
529                 nPixelsM1 = nEndPixels - 1;
530                 do {
531                     *pDst++ = prevPixel;
532                     } while (--nPixelsM1 >= 0);
533                 }
534             }   /* END while lines */
535         break;
536         }   /* END case 2 */
537         }   /* END switch shift */
538 }           /* END ilUpsampleHorizontal */
539
540
541     /*  -------------------------- ilUpsampleVertical ------------------------------ */
542     /*  Do vertical upsampling as necessary, after the horizontal upsampling is done.
543         Vertical upsampling is done in the dst buffer: pLine points to first line
544         (which contains an already upsampled line); rowBytes is bytes/row of buffer.
545     */
546 static void ilUpsampleVertical (
547     unsigned int    shift,          /* 0 = no upsample; 1 = * 2 (doubling); 2 = * 4  */
548     long            width,          /* width of _whole_ (un-upsampled) image */
549     long            nLines,         /* # of dst lines (after upsampling) */
550     long   rowBytes,       /* bytes / row in src/dst image */
551     ilPtr           pLine           /* ptr to first line of src/dst image */
552     )
553 {
554 long                        nPixelsM1Init;
555 long               nPixelsM1;
556
557         /*  Return if no pixels or lines to do. */
558     if (nLines <= 0)
559         return;
560     nPixelsM1Init = width - 1;
561     if (nPixelsM1Init < 0)
562         return;
563
564         /*  Upsample based on "shift", i.e. scale up by 2 ++ "shift". */
565     switch (shift) {
566
567         /*  0: no vertical upsampling; buffer is already complete */
568       case 0:
569         return;                         /* EXIT */
570
571         /*  1: doubling.  Interpolate between pairs of lines, starting with the first
572             and third lines, filling in the second with the average of the two.
573         */
574       case 1: {
575         ilPtr  pDst, pSrc1, pSrc2;
576
577         nLines--;                       /* don't count first line already in buffer */
578         while (nLines >= 2) {           /* two src lines, line between to interpolate */
579             nLines -= 2;
580             pSrc1 = pLine;
581             pLine += rowBytes;
582             pDst = pLine;
583             pLine += rowBytes;
584             pSrc2 = pLine;
585             nPixelsM1 = nPixelsM1Init;
586             do {
587                 *pDst++ = (*pSrc1++ + *pSrc2++) >> 1;
588                 } while (--nPixelsM1 >= 0);
589             }
590
591             /*  Replicate last line to fill out to last line(s) */
592         pDst = pLine;
593         while (nLines-- > 0) {
594             pDst += rowBytes;
595             bcopy ((char *)pLine, (char *)(pDst), width);
596             }
597         break;
598         }   /* END doubling */
599
600         /*  1: quadrupling.  Interpolate between quadruples of lines, starting with lines
601             0 and 4, interpolating 1..3 - see horizontal upsampling.
602         */
603       case 2: {
604         ilPtr  pSrc, pDst;
605         int    pixel, delta;
606         long            rowBytesTimes4 = rowBytes << 2;
607
608         nLines--;                       /* don't count first line already in buffer */
609         while (nLines >= 4) {           /* four src lines, line between to interpolate */
610             nLines -= 4;
611             pSrc = pLine;
612             pLine += rowBytesTimes4;    /* point 4 lines down */
613             nPixelsM1 = nPixelsM1Init;
614             do {
615                 pDst = pSrc;
616                 pixel = *pSrc++;        /* pixel from top src line; next pixel */
617                 delta = *(pDst + rowBytesTimes4) - pixel;  /* delta = bottom - top */
618                 pixel <<= 2;            /* work in pixels * 4, /4 before storing */
619                 pixel += delta;         
620                 pDst += rowBytes;
621                 *pDst = pixel >> 2;     /* store one interpolated */
622                 pixel += delta;         
623                 pDst += rowBytes;
624                 *pDst = pixel >> 2;     /* store one interpolated */
625                 pixel += delta;         
626                 pDst += rowBytes;
627                 *pDst = pixel >> 2;     /* store one interpolated */
628                 } while (--nPixelsM1 >= 0);
629             }
630
631             /*  Replicate last line to fill out to last line(s) */
632         pDst = pLine;
633         while (nLines-- > 0) {
634             pDst += rowBytes;
635             bcopy ((char *)pLine, (char *)(pDst), width);
636             }
637         break;
638         }   /* END quadrupling */
639         }   /* END switch shift */
640 }           /* END ilUpsampleVertical */
641
642
643     /*  -------------------------- ilExecuteUpsample ------------------------------- */
644     /*  Execute(): upsample as necessary "pPriv->nSamples" planes of the source image.
645     */
646 static ilError ilExecuteUpsample (
647     ilExecuteData          *pData,
648     long                    dstLine,
649     long                   *pNLines             /* ignored on input */
650     )
651 {
652 ilUpsamplePrivPtr pPriv;
653 ilImagePlaneInfo           *pSrcPlane, *pDstPlane;
654 int                         nSamples;
655 ilPtr                       pSrcLine, pDstLine;
656 long                        height, width, nLines;
657 long                        srcRowBytes, dstRowBytes;
658 ilUpsampleShiftPtr          pShift;
659
660         /*  Get width and height of _upsampled_ image; exit if zero. */
661     pPriv = (ilUpsamplePrivPtr)pData->pPrivate;
662     height = *pNLines;
663     if (height <= 0)
664         return IL_OK;
665     width = pData->pSrcImage->width;
666     if (width <= 0)
667         return IL_OK;
668
669         /*  Loop on samples (components), upsample/translate each plane separately.
670             Note that "srcLine" is shifted - indexing into plane based on vert subsample.
671         */
672     for (pSrcPlane = pData->pSrcImage->plane, pDstPlane = pData->pDstImage->plane,
673       pShift = pPriv->shift, nSamples = pPriv->nSamples; 
674       nSamples-- > 0; 
675       pSrcPlane++, pDstPlane++, pShift++) {
676
677         srcRowBytes = pSrcPlane->nBytesPerRow;
678         pSrcLine = pSrcPlane->pPixels + (pData->srcLine >> pShift->vert) * srcRowBytes;
679         dstRowBytes = pDstPlane->nBytesPerRow;
680         pDstLine = pDstPlane->pPixels + dstLine * dstRowBytes;
681
682             /*  Upsample horizontal "height >> pShift->vert" src lines (e.g. half
683                 the lines for shift = 1).  Write the results starting at pDstLine,
684                 each line "dstRowBytes << pShift->vert" bytes after the other, e.g.
685                 if pShift->vert is 1 (double), leave one line gap between each dst line.
686                     But first, handle very small image: if subsampling left nothing,
687                 fill with zeros.  Note that small strips will not cause this to happen, 
688                 because stripHeight checked when element added.
689             */
690         nLines = height >> pShift->vert;
691         if ((nLines <= 0) || ((width >> pShift->horiz) <= 0)) {
692             long i = height;
693             while (i-- > 0) {
694                 bzero ((char *)pDstLine, width);
695                 pDstLine += dstRowBytes;
696                 }
697             }
698         else {
699             ilUpsampleHorizontal (pShift->horiz, width, nLines,
700                 srcRowBytes, pSrcLine, dstRowBytes << pShift->vert, pDstLine);
701
702                 /*  Upsample vertically, interpolating between the lines just written. */
703             ilUpsampleVertical (pShift->vert, width, height, dstRowBytes, pDstLine);
704             }
705         }
706
707     return IL_OK;
708 }
709
710     /*  ---------------------------- ilUpsampleYCbCr ----------------------------- */
711     /*  Upsample and / or convert to gray the pipe image which must be a YCbCr image.
712         If "toGray" is true, the Y plane only will be upsampled (or copied) resulting
713         in a gray image; else a planar YCbCr image will result.
714         If "upSample" is true must upsample; "upSample" and/or "toGray" must be true.
715         pFormat points to the source format; on return, *pFormat is updated
716         to the dst format, *pDes to the dst descriptor.
717     */
718 IL_PRIVATE ilBool _ilUpsampleYCbCr (
719     ilPipe                  pipe,
720     ilPipeInfo             *pInfo,
721     ilImageDes             *pDes,
722     ilImageFormat          *pFormat,
723     ilBool                  toGray,
724     ilBool                  upSample
725     )
726 {
727     ilUpsamplePrivPtr pUpPriv;
728     ilImageDes              pipeDes;
729     ilUpsampleShiftRec     *pShift;
730     ilYCbCrSampleInfo      *pSample;
731     int                     sample;
732     ilDstElementData        dstData;
733     ilSrcElementData        srcData;
734     long           i, j;
735
736         /*  Only 8 bit planar YCbCr can currently be upsampled; if not that and upsampling
737             needed, error; if no upsampling (only convert to gray) convert to planar.
738             NOTE: can't ilConvert() to planar if upsampling needed as ilConvert() will
739             call this function back to upsample, thereby recursing "forever".
740         */
741     if ((pFormat->sampleOrder != IL_SAMPLE_PLANES)
742      || (pFormat->nBitsPerSample[0] != 8)
743      || (pFormat->nBitsPerSample[1] != 8)
744      || (pFormat->nBitsPerSample[2] != 8)) {
745         if (upSample)
746             return ilDeclarePipeInvalid (pipe, IL_ERROR_NOT_IMPLEMENTED);
747         if (!ilConvert (pipe, (ilImageDes *)NULL, IL_FORMAT_3BYTE_PLANE, 0, NULL))
748             return FALSE;
749         *pFormat = *IL_FORMAT_3BYTE_PLANE;
750         }
751
752 #if 0
753
754     This code currently not included.  It tries the "fast" upsampling code, which
755     is in fact faster than the "slow" upsampling code, but for some reason the resulting
756     YCbCr data causes the conversion to RGB to be much slower, probably because more
757     out-of-range (0..255) values are generated which cause the clip code to be hit.
758     For now, the fast upsample code is used only by ilScale(..., IL_SCALE_SAMPLE) for
759     2x and 4x scales up.
760
761         /*  Try to use fast upsampling case, but not if "toGray" true, or if width/height
762             of resulting image is not multiple of subsample factor.
763         */
764     if (!toGray) {
765         ilBool  tryFast = TRUE;
766         int     factor;
767         for (sample = 0; sample < 3; sample++) {
768             factor = pDes->typeInfo.YCbCr.sample[sample].subsampleHoriz;
769             if (((factor == 2) && ((pInfo->width | pInfo->height) & 1))
770              || ((factor == 4) && ((pInfo->width | pInfo->height) & 3)))
771                 tryFast = FALSE;
772             }
773         if (tryFast) {
774             if (_ilFastUpsample (pipe, pInfo, pDes, pFormat, FALSE))
775                 return TRUE;                        /* handled; done */
776             if (pipe->context->error)               /* not handled but error; exit */
777                 return FALSE;
778             }
779         }
780 #endif
781
782         /*  Must do "slow" upsample: init dstData for filter(s) to be added. */
783     pipeDes = *pDes;
784     dstData.producerObject = (ilObject)NULL;
785     dstData.pDes = pDes;
786     dstData.pFormat = pFormat;
787     dstData.width = pInfo->width;
788     dstData.height = pInfo->height;
789     dstData.stripHeight = 0;
790     dstData.constantStrip = FALSE;
791     dstData.pPalette = (unsigned short *)NULL;
792
793         /*  Demand constant strips, a multiple of maximum subsample (4). To avoid the
794             problem of having the last strip have 3 lines or less (and therefore for * 4
795             upsampling require that the previous line be copied - but no previous line
796             available), bump stripHeight by 4 until stripHeight mod 4 > 3, or until
797             requiring whole image as one strip.
798         */
799     i = 8;
800     while (TRUE) {
801         j = dstData.height % i;
802         if ((j == 0) || (j > 3))
803             break;
804         if ((i += 4) >= dstData.height) {
805             i = dstData.height;
806             break;
807             }
808         }
809     srcData.consumerImage = (ilObject)NULL;
810     srcData.stripHeight = i;
811     srcData.constantStrip = TRUE;
812     srcData.minBufferHeight = 0;
813
814         /*  Add a filter to upsample; if "toGray" upsample/copy one plane and done. */
815     if (toGray) {
816         *pDes = *IL_DES_GRAY;
817         *pFormat = *IL_FORMAT_BYTE;
818         }
819     else {
820         pDes->typeInfo.YCbCr.sample[0].subsampleHoriz = 1;
821         pDes->typeInfo.YCbCr.sample[0].subsampleVert = 1;
822         pDes->typeInfo.YCbCr.sample[1].subsampleHoriz = 1;
823         pDes->typeInfo.YCbCr.sample[1].subsampleVert = 1;
824         pDes->typeInfo.YCbCr.sample[2].subsampleHoriz = 1;
825         pDes->typeInfo.YCbCr.sample[2].subsampleVert = 1;   /* des now upsampled */
826         }
827     pUpPriv = (ilUpsamplePrivPtr)ilAddPipeElement (pipe, IL_FILTER, 
828                  sizeof (ilUpsamplePrivRec), 0, &srcData,
829                  &dstData, IL_NPF, IL_NPF, IL_NPF, ilExecuteUpsample, NULL, 0);
830     if (!pUpPriv)
831         return FALSE;
832
833         /*  Init pUpPriv. */
834     pUpPriv->nSamples = (toGray) ? 1 : 3;
835     pSample = pipeDes.typeInfo.YCbCr.sample;
836     pShift = pUpPriv->shift;
837
838     for (sample = 0; sample < pUpPriv->nSamples; sample++, pShift++, pSample++) {
839         pShift->horiz = _ilSubsampleShift [pSample->subsampleHoriz];
840         pShift->vert = _ilSubsampleShift [pSample->subsampleVert];
841         }
842
843     return TRUE;
844 }
845
846