Tizen 2.1 base
[platform/upstream/hplip.git] / ip / xrotate.c
1 /* libhpojip -- HP OfficeJet image-processing library. */
2
3 /* Copyright (C) 1995-2002 Hewlett-Packard Company
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
12  * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
13  * NON-INFRINGEMENT.  See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18  * MA 02111-1307, USA.
19  *
20  * In addition, as a special exception, Hewlett-Packard Company
21  * gives permission to link the code of this program with any
22  * version of the OpenSSL library which is distributed under a
23  * license identical to that listed in the included LICENSE.OpenSSL
24  * file, and distribute linked combinations including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL.  If you modify
27  * this file, you may extend this exception to your version of the
28  * file, but you are not obligated to do so.  If you do not wish to
29  * do so, delete this exception statement from your version.
30  */
31
32 /* Original author: Mark Overton and others.
33  *
34  * Ported to Linux by David Paschal.
35  */
36
37 /******************************************************************************\
38  *
39  * xrotate.c - Rotates the image
40  *
41  * Mark Overton, Feb 2000
42  *
43  ******************************************************************************
44  *
45  * Imagine a rotated rectangle contained somewhere within the input image, or
46  * even partially outside the input image.  This xform outputs that rectangle
47  * unrotated.  This xform crops out all image-area outside the rotated rectangle,
48  * and white-fills any part of the rectangle outside the image-area.  It is
49  * intended for procuring a selected portion of a scan.
50  *
51  * A rotated rectangle is specified using three points in the input image.
52  * Depending on the orientation of these points, horizontal and/or vertical
53  * flipping can be performed, as well as rotation by any angle.
54  *
55  * Scaling is also performed.  The interpolation for rotation also does the
56  * scaling in the same step, resulting in higher quality output than the usual
57  * use of separate rotation and scaling, as well as being faster.
58  *
59  * Rotation can be fast or slow, depending on an aXformInfo flag.  If slow,
60  * interpolation (anti-aliasing) is done with superb results.  If fast, the
61  * results have jaggies.
62  *
63  * Name of Global Jump-Table:
64  *
65  *    rotateTbl
66  *
67  * Items in aXformInfo array passed into setXformSpec:
68  *
69  *      aXformInfo[IP_ROTATE_UPPER_LEFT]  = [xUL,yUL] = pos of upper-left corner of rotated rect
70  *      aXformInfo[IP_ROTATE_UPPER_RIGHT] = [xUR,yUR] = pos of upper-right corner of rotated rect
71  *      aXformInfo[IP_ROTATE_LOWER_LEFT]  = [xLL,yLL] = pos of lower-left corner of rotated rect
72  *      aXformInfo[IP_ROTATE_OUTPUT_SIZE] = [outWidth,outHeight] = size of output image (0 = no scaling)
73  *      aXformInfo[IP_ROTATE_FAST]        = rotate fast?  (0=no=interpolated, 1=yes=jaggy)
74  *
75  *    The coordinates (xUL,yUL) are in relation to the *input* image (Y increases
76  *    downward), and locates what will be the upper-left corner in the output
77  *    (before scaling).  The other two points are defined similarly.  If UL is to
78  *    the right of UR, the output image is flipped horizontally.  Vertical flipping
79  *    can be done similarly.
80  *    This rectangle is then scaled to produce outWidth and outHeight pixels.
81  *
82  * Capabilities and Limitations:
83  *
84  *    Crops, white-pads, rotates by any angle, scales, mirrors, and flips vertically.
85  *    Input data may be 1 bit/pixel, or any multiple of 8 bits/pixel.
86  *    Internally, the miniumum amount of memory required for the rotation is
87  *    allocated.  For small angles, internal allocation will be very small.
88  *
89  * Default Input Traits, and Output Traits:
90  *
91  *          trait             default input             output
92  *    -------------------  ---------------------  ------------------------
93  *    iPixelsPerRow         * width of scan        abs(outWidth)
94  *    iBitsPerPixel         * 1, or n*8            same as default input
95  *    iComponentsPerPixel     passed into output   same as default input
96  *    lHorizDPI               passed into output   same as default input
97  *    lVertDPI                passed into output   same as default input
98  *    lNumRows              * height of scan       abs(outHeight)
99  *    iNumPages               passed into output   same as default input
100  *    iPageNum                passed into output   same as default input
101  *
102  *    Above, a "*" by an item indicates it must be valid (not negative).
103  *
104 \******************************************************************************/
105
106 /* Use the #define below if this transform will exist in a dll outside of the */
107 /* image pipeline.  This will allow the functions to be exported.             */
108 /* #define EXPORT_TRANFORM 1                                                  */
109
110 #include "hpip.h"
111 #include "ipdefs.h"
112 #include "string.h"    /* for memset and memcpy */
113 #include "math.h"      /* for sqrt */
114
115
116 #if 0
117     #include "stdio.h"
118     #include <tchar.h>
119     #define PRINT(msg,arg1,arg2) \
120         _ftprintf(stderr, msg, (int)arg1, (int)arg2)
121 #else
122     #define PRINT(msg,arg1,arg2)
123 #endif
124
125 #define CHECK_VALUE 0x4ba1dace
126
127 #ifdef EXPORT_TRANSFORM
128 #define FUNC_STATUS __declspec (dllexport)
129 #else
130 #define FUNC_STATUS static
131 #endif
132
133 typedef struct {
134     IP_IMAGE_TRAITS inTraits;    /* traits of the input image */
135     IP_IMAGE_TRAITS outTraits;   /* traits of the output image */
136
137     // Items from aXformInfo:
138     int      xUL, yUL;           /* UL corner of image */
139     int      xUR, yUR;           /* UR corner of image */
140     int      xLL, yLL;           /* LL corner of image */
141     int      xLR, yLR;           /* LR corner of image (computed) */
142     int      iOutWidth;          /* width of output image */
143     int      iOutHeight;         /* height of output image */
144     BOOL     bRotateFast;        /* rotate fast? (produces worse jaggies) */
145
146     int      hSlopeDx, hSlopeDy; /* input-change for moving right 1 pix in output (16.16) */
147     int      vSlopeDx, vSlopeDy; /* input-change for moving down 1 pix in output (16.16) */
148     int      xLeft, yLeft;       /* current  left-end of row (input coords) (16.16) */
149     int      xRight, yRight;     /* current right-end of row (input coords) (16.16) */
150     BYTE    *pStrip;             /* strip with a portion of input image */
151     BYTE    *pStripAfter;        /* ptr to 1st byte after the strip-buffer */
152     int      stripIndexYTop;     /* row-index of stripYTop in the strip */
153     int      stripYTop;          /* Y-value for topmost row in strip */
154     int      stripYBottom;       /* Y-value for bottommost row in strip */
155     int      stripRows;          /* number of rows allocated in the strip */
156     int      stripLoaded;        /* number of rows actually loaded into the strip */
157     int      stripSize;          /* number of bytes allocated in the strip */
158     int      stripBytesPerRow;   /* bytes/row in strip */
159     int      inBytesPerRow;      /* bytes/row of input image */
160     int      outBytesPerRow;     /* bytes/row of output image */
161     int      bytesPerPixel;      /* bytes/pixel (=1 for bilevel; 1 pixel in each byte) */
162     DWORD    dwRowsSent;         /* number of rows output so far */
163     DWORD    dwInNextPos;        /* file pos for subsequent input */
164     DWORD    dwOutNextPos;       /* file pos for subsequent output */
165     DWORD    dwValidChk;         /* struct validity check value */
166 } ROTATE_INST, *PROTATE_INST;
167
168
169
170 /*****************************************************************************\
171  *
172  * rotate_openXform - Creates a new instance of the transformer
173  *
174  *****************************************************************************
175  *
176  * This returns a handle for the new instance to be passed into
177  * all subsequent calls.
178  *
179  * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
180  *
181 \*****************************************************************************/
182
183 FUNC_STATUS WORD rotate_openXform (
184     IP_XFORM_HANDLE *pXform)   /* out: returned handle */
185 {
186     PROTATE_INST g;
187
188     INSURE (pXform != NULL);
189     IP_MEM_ALLOC (sizeof(ROTATE_INST), g);
190     *pXform = g;
191     memset (g, 0, sizeof(ROTATE_INST));
192     g->dwValidChk = CHECK_VALUE;
193     return IP_DONE;
194
195     fatal_error:
196     return IP_FATAL_ERROR;
197 }
198
199
200
201 /*****************************************************************************\
202  *
203  * rotate_setDefaultInputTraits - Specifies default input image traits
204  *
205  *****************************************************************************
206  *
207  * The header of the file-type handled by the transform probably does
208  * not include *all* the image traits we'd like to know.  Those not
209  * specified in the file-header are filled in from info provided by
210  * this routine.
211  *
212  * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
213  *
214 \*****************************************************************************/
215
216 FUNC_STATUS WORD rotate_setDefaultInputTraits (
217     IP_XFORM_HANDLE  hXform,     /* in: handle for xform */
218     PIP_IMAGE_TRAITS pTraits)    /* in: default image traits */
219 {
220     PROTATE_INST g;
221
222     HANDLE_TO_PTR (hXform, g);
223
224     /* insure that traits we care about are known */
225     INSURE (pTraits->iPixelsPerRow > 0  &&
226             pTraits->lNumRows > 0  &&
227             pTraits->iBitsPerPixel > 0);
228     g->inTraits = *pTraits;   /* a structure copy */
229     return IP_DONE;
230
231     fatal_error:
232     return IP_FATAL_ERROR;
233 }
234
235
236
237 /*****************************************************************************\
238  *
239  * rotate_setXformSpec - Provides xform-specific information
240  *
241 \*****************************************************************************/
242
243 FUNC_STATUS WORD rotate_setXformSpec (
244     IP_XFORM_HANDLE hXform,         /* in: handle for xform */
245     DWORD_OR_PVOID  aXformInfo[])   /* in: xform information */
246 {
247     PROTATE_INST g;
248     int          i;
249
250     HANDLE_TO_PTR (hXform, g);
251
252     i = aXformInfo[IP_ROTATE_UPPER_LEFT].dword;
253     g->xUL         = i >> 16;
254     g->yUL         = i & 0xFFFF;
255
256     i = aXformInfo[IP_ROTATE_UPPER_RIGHT].dword;
257     g->xUR         = i >> 16;
258     g->yUR         = i & 0xFFFF;
259     
260     i = aXformInfo[IP_ROTATE_LOWER_LEFT].dword;
261     g->xLL         = i >> 16;
262     g->yLL         = i & 0xFFFF;
263     
264     i = aXformInfo[IP_ROTATE_OUTPUT_SIZE].dword;
265     g->iOutWidth   = i >> 16;
266     g->iOutHeight  = i & 0xFFFF;
267
268     g->bRotateFast = aXformInfo[IP_ROTATE_FAST].dword == 1;
269
270     INSURE (g->iOutWidth>=0 && g->iOutHeight>=0);
271     g->xLR = g->xUR + (g->xLL - g->xUL);
272     g->yLR = g->yUR + (g->yLL - g->yUL);
273
274     return IP_DONE;
275
276     fatal_error:
277     return IP_FATAL_ERROR;
278 }
279
280
281
282 /*****************************************************************************\
283  *
284  * rotate_getHeaderBufSize - Returns size of input buf needed to hold header
285  *
286 \*****************************************************************************/
287
288 FUNC_STATUS WORD rotate_getHeaderBufSize (
289     IP_XFORM_HANDLE  hXform,         /* in:  handle for xform */
290     DWORD           *pdwInBufLen)    /* out: buf size for parsing header */
291 {
292     /* since input is raw pixels, there is no header, so set it to zero */
293     *pdwInBufLen = 0;
294     return IP_DONE;
295 }
296
297
298
299 /*****************************************************************************\
300  *
301  * rotate_getActualTraits - Parses header, and returns input & output traits
302  *
303 \*****************************************************************************/
304
305 FUNC_STATUS WORD rotate_getActualTraits (
306     IP_XFORM_HANDLE  hXform,         /* in:  handle for xform */
307     DWORD            dwInputAvail,   /* in:  # avail bytes in input buf */
308     PBYTE            pbInputBuf,     /* in:  ptr to input buffer */
309     PDWORD           pdwInputUsed,   /* out: # bytes used from input buf */
310     PDWORD           pdwInputNextPos,/* out: file-pos to read from next */
311     PIP_IMAGE_TRAITS pIntraits,      /* out: input image traits */
312     PIP_IMAGE_TRAITS pOutTraits)     /* out: output image traits */
313 {
314     PROTATE_INST g;
315     int    dxH, dyH, dxV, dyV;
316     double rotWidthSq, rotHeightSq, rotWidth, rotHeight;
317
318     HANDLE_TO_PTR (hXform, g);
319
320     /***** Compute Output Traits (and rotation variables) *****/
321
322     memcpy (&g->outTraits, &g->inTraits, sizeof(IP_IMAGE_TRAITS));
323
324     dxH = g->xUR - g->xUL;
325     dyH = g->yUR - g->yUL;
326     dxV = g->xLL - g->xUL;
327     dyV = g->yLL - g->yUL;
328
329     rotWidthSq  = dxH*dxH + dyH*dyH;
330     rotWidth    = sqrt (rotWidthSq);
331     rotHeightSq = dxV*dxV + dyV*dyV;
332     rotHeight   = sqrt (rotHeightSq);
333     INSURE (rotWidth>0.0 && rotHeight>0.0);
334
335     if (g->iOutWidth == 0)
336         g->iOutWidth = (int)(rotWidth+0.5);
337     if (g->iOutHeight == 0)
338         g->iOutHeight = (int)(rotHeight+0.5);
339
340     g->outTraits.iPixelsPerRow = g->iOutWidth;
341     g->outTraits.lNumRows      = g->iOutHeight;
342
343     g->bytesPerPixel = g->inTraits.iBitsPerPixel / 8;
344     if (g->bytesPerPixel == 0)
345         g->bytesPerPixel = 1;   /* bi-level is expanded to one byte/pixel */
346
347     g->inBytesPerRow  = (g->inTraits.iPixelsPerRow*g->inTraits.iBitsPerPixel + 7) / 8;
348     g->outBytesPerRow = (g->iOutWidth             *g->inTraits.iBitsPerPixel + 7) / 8;
349
350     g->hSlopeDx = (dxH << 16) / g->iOutWidth;
351     g->hSlopeDy = (dyH << 16) / g->iOutWidth;
352     g->vSlopeDx = (dxV << 16) / g->iOutHeight;
353     g->vSlopeDy = (dyV << 16) / g->iOutHeight;
354
355     /* we start outputting at the upper-left corner of output-image */
356     /* do NOT add 0x8000 because when we're not scaling or rotating,
357      * it causes each pair of rows and each pair of columns to be
358      * averaged together, losing sharpness. */
359     g->xLeft  = g->xUL << 16;
360     g->yLeft  = g->yUL << 16;
361     g->xRight = g->xUR << 16;
362     g->yRight = g->yUR << 16;
363
364     /* set-up the strip-buffer */
365     g->stripBytesPerRow = g->inTraits.iPixelsPerRow * g->bytesPerPixel;
366     g->stripIndexYTop   = 0;
367     g->stripYTop        = 0;
368     g->stripYBottom     = -1;
369     g->stripLoaded      = 0;
370     if (g->vSlopeDy < 0)   /* we'll proceed *upward* thru the input image */
371         g->stripRows = IP_MAX(g->yUL, g->yUR) - IP_MIN(g->yLL, g->yLR) + 1;
372     else   /* normal case of proceeding *downward* */
373         g->stripRows = IP_MAX(g->yUL, g->yUR) - IP_MIN(g->yUL, g->yUR) + 1;
374     g->stripRows += 2;  /* allocate extra row on top & bottom for interpolation */
375     g->stripSize = g->stripRows * g->stripBytesPerRow;
376     IP_MEM_ALLOC (g->stripSize, g->pStrip);
377     g->pStripAfter = g->pStrip + g->stripSize;
378
379     /***** Return Values *****/
380
381     /* Since there is no header, we'll report no usage of input */
382     *pdwInputUsed    = 0;
383     *pdwInputNextPos = 0;
384
385     *pIntraits  = g->inTraits;   /* structure copies */
386     *pOutTraits = g->outTraits;
387
388     return IP_DONE | IP_READY_FOR_DATA;
389
390     fatal_error:
391     return IP_FATAL_ERROR;
392 }
393
394
395
396 /****************************************************************************\
397  *
398  * rotate_getActualBufSizes - Returns buf sizes needed for remainder of job
399  *
400 \****************************************************************************/
401
402 FUNC_STATUS WORD rotate_getActualBufSizes (
403     IP_XFORM_HANDLE hXform,          /* in:  handle for xform */
404     PDWORD          pdwMinInBufLen,  /* out: min input buf size */
405     PDWORD          pdwMinOutBufLen) /* out: min output buf size */
406 {
407     PROTATE_INST g;
408
409     HANDLE_TO_PTR (hXform, g);
410
411     *pdwMinInBufLen  = g->inBytesPerRow;
412     *pdwMinOutBufLen = g->outBytesPerRow;
413     return IP_DONE;
414
415     fatal_error:
416     return IP_FATAL_ERROR;
417 }
418
419
420
421 /*****************************************************************************\
422  *
423  * rotate_convert - Converts one row
424  *
425 \*****************************************************************************/
426
427
428
429 /* ExpandBilevelRow - Expands 8 bits/pixel (input) to 1 byte/pixel (output) */
430 static void ExpandBilevelRow (
431     PBYTE pDest,
432     PBYTE pSrc,
433     int   nPixels)
434 {
435     BYTE mask, inbyte=0;
436
437     mask = 0;
438
439     while (nPixels > 0) {
440         if (mask == 0) {
441             mask = 0x80u;
442             inbyte = *pSrc++;
443         }
444         *pDest++ = inbyte & mask ? 0 : 255;
445         mask >>= 1;
446         nPixels -= 1;
447     }
448 }
449
450
451
452 FUNC_STATUS WORD rotate_convert (
453     IP_XFORM_HANDLE hXform,
454     DWORD           dwInputAvail,     /* in:  # avail bytes in in-buf */
455     PBYTE           pbInputBuf,       /* in:  ptr to in-buffer */
456     PDWORD          pdwInputUsed,     /* out: # bytes used from in-buf */
457     PDWORD          pdwInputNextPos,  /* out: file-pos to read from next */
458     DWORD           dwOutputAvail,    /* in:  # avail bytes in out-buf */
459     PBYTE           pbOutputBuf,      /* in:  ptr to out-buffer */
460     PDWORD          pdwOutputUsed,    /* out: # bytes written in out-buf */
461     PDWORD          pdwOutputThisPos) /* out: file-pos to write the data */
462 {
463     // We need another row if the Y of either endpoint of the current rotated row
464     // is below the bottom of the strip.
465     #define NEED_MORE \
466         ((g->yLeft >>16) >= g->stripYBottom  ||  (g->yRight>>16) >= g->stripYBottom)
467
468     PROTATE_INST g;
469     PBYTE        pSrc, pDest;
470
471     HANDLE_TO_PTR (hXform, g);
472
473     *pdwInputUsed = *pdwOutputUsed = 0;
474     *pdwInputNextPos  = g->dwInNextPos;
475     *pdwOutputThisPos = g->dwOutNextPos;
476
477     /**** Check if we were told to flush ****/
478
479     if (pbInputBuf == NULL) {
480         PRINT (_T("rotate_convert: Told to flush.\n"), 0, 0);
481         dwInputAvail = (DWORD)g->inBytesPerRow;
482         if ((long)g->dwRowsSent >= g->outTraits.lNumRows)
483             return IP_DONE;
484     }
485
486     /**** Initial load of strip-buffer ****/
487
488     if (g->stripLoaded < g->stripRows) {
489         if (dwInputAvail == 0)
490             return IP_READY_FOR_DATA;
491         INSURE (dwInputAvail >= (DWORD)g->inBytesPerRow);
492         pDest = g->pStrip + g->stripBytesPerRow*g->stripLoaded;
493         if (g->inTraits.iBitsPerPixel == 1)
494             ExpandBilevelRow (pDest, pbInputBuf, g->inTraits.iPixelsPerRow);
495         else if (pbInputBuf == NULL)
496             memcpy (pDest, pDest - g->stripBytesPerRow, g->stripBytesPerRow);
497         else
498             memcpy (pDest, pbInputBuf, g->stripBytesPerRow);
499         g->stripLoaded += 1;
500         g->stripYBottom += 1;
501         if (g->stripLoaded == 1) {
502             /* first row is *above* top for interpolation; replicate it */
503             memcpy (g->pStrip+g->stripBytesPerRow, g->pStrip, g->stripBytesPerRow);
504             g->stripLoaded += 1;
505             g->stripYBottom += 1;
506         }
507         if (pbInputBuf == NULL)
508             return 0;   /* wait for next call to replicate another row and start rotation */
509         *pdwInputUsed   = (DWORD)g->inBytesPerRow;
510         g->dwInNextPos += (DWORD)g->inBytesPerRow;
511         *pdwInputNextPos = g->dwInNextPos;
512         return ((g->stripLoaded<g->stripRows || NEED_MORE) ? IP_READY_FOR_DATA : 0)
513                | IP_CONSUMED_ROW;
514     }
515
516     /**** Load next row into strip-buffer ****/
517
518     if (NEED_MORE)
519     {
520         /* we need to load a row into the strip-buffer (a wrapping load) */
521         if (dwInputAvail == 0)
522             return IP_READY_FOR_DATA;
523         INSURE (dwInputAvail >= (DWORD)g->inBytesPerRow);
524         pDest = g->pStrip + g->stripBytesPerRow*g->stripIndexYTop;
525         if (pbInputBuf == NULL)  // then white-fill the row
526             memset (pDest, 255, g->stripBytesPerRow);
527         else if (g->inTraits.iBitsPerPixel == 1)
528             ExpandBilevelRow (pDest, pbInputBuf, g->inTraits.iPixelsPerRow);
529         else
530             memcpy (pDest, pbInputBuf, g->stripBytesPerRow);
531         g->stripIndexYTop = (g->stripIndexYTop + 1) % g->stripRows;
532         g->stripYTop    += 1;
533         g->stripYBottom += 1;
534         if (pbInputBuf != NULL) {
535             *pdwInputUsed   = (DWORD)g->inBytesPerRow;
536             g->dwInNextPos += (DWORD)g->inBytesPerRow;
537             *pdwInputNextPos = g->dwInNextPos;
538         }
539     }
540
541     /**** Output a row ****/
542
543     if ((long)g->dwRowsSent >= g->outTraits.lNumRows)
544     {
545         /* we've outputted all rows; discard any further input */
546         g->stripYBottom = -100000;   /* forces strip-loading logic to keep loading */
547     }
548     else if (! NEED_MORE)
549     {
550         int      rowIndex, iPix;
551         int      xcur,ycur, xint,yint;
552         unsigned xfrac,yfrac;
553         PBYTE    pNextRow;
554         BYTE     mask;
555
556         INSURE (dwOutputAvail >= (DWORD)g->outBytesPerRow);
557
558         pDest = pbOutputBuf;
559         xcur = g->xLeft;
560         ycur = g->yLeft;
561
562         /* These two lines set-up bi-level packing: */
563         *pDest = 0;
564         mask = 0x80u;
565
566         for (iPix=0; iPix<g->outTraits.iPixelsPerRow; iPix++)
567         {
568             xint =  xcur >> 16;
569             yint = (ycur >> 16) - g->stripYTop;
570
571             /* below, unsigned makes negatives huge (therefore not in strip) */
572             /* also, we check stripRows-1; the -1 allows the extra interpolation-row */
573             if ((unsigned)yint < (unsigned)(g->stripRows-1)  &&
574                 (unsigned)xint < (unsigned)g->inTraits.iPixelsPerRow)
575             {
576                 /**** We are inside the strip ****/
577
578                 /* Compute address of pixel in the strip */
579                 rowIndex = yint + g->stripIndexYTop;
580                 if (rowIndex >= g->stripRows)
581                     rowIndex -= g->stripRows;
582                 pSrc = (rowIndex*g->inTraits.iPixelsPerRow + xint) * g->bytesPerPixel
583                        + g->pStrip;
584
585                 /**** Output a pixel ****/
586
587                 /* The byte at ptrSrcCur is the upper-left byte out of a 2x2 group.
588                  * These four bytes are interpolated together based on xfrac and yfrac.
589                  */
590                 #define INTERPOLATE_BYTE(ptrOut,ptrSrcCur,ptrSrcNex,bpp) { \
591                     int xtop, xbot, val; \
592                     xtop = (ptrSrcCur)[0]*(256-xfrac) + (ptrSrcCur)[bpp]*xfrac; \
593                     xbot = (ptrSrcNex)[0]*(256-xfrac) + (ptrSrcNex)[bpp]*xfrac; \
594                     val = xtop*(256-yfrac) + xbot*yfrac; \
595                     val = (val + 0x8000) >> 16;  /* the add rounds result of shift */ \
596                     *(ptrOut) = (BYTE)val; \
597                 }
598
599                 #define INTERPOLATE_WORD(ptrOut,ptrSrcCur,ptrSrcNex,bpp) { \
600                     WORD *wptrOut    = (PWORD)(ptrOut   ); \
601                     WORD *wptrSrcCur = (PWORD)(ptrSrcCur); \
602                     WORD *wptrSrcNex = (PWORD)(ptrSrcNex); \
603                     unsigned xtop, xbot, val; \
604                     xtop = ((wptrSrcCur)[0]*(0x10000-xfrac)>>8) + ((wptrSrcCur)[bpp]*xfrac>>8); \
605                     xtop = (xtop+128) >> 8; /* the add rounds result of shift */ \
606                     xbot = ((wptrSrcNex)[0]*(0x10000-xfrac)>>8) + ((wptrSrcNex)[bpp]*xfrac>>8); \
607                     xbot = (xbot+128) >> 8; \
608                     val = (xtop*(0x10000-yfrac)>>8) + (xbot*yfrac>>8); \
609                     val = (val+128) >> 8; \
610                     *(wptrOut) = (WORD)val; \
611                 }
612
613                 pNextRow = pSrc + g->stripBytesPerRow;
614                 if (pNextRow >= g->pStripAfter)
615                     pNextRow -= g->stripSize;  /* next row wrapped to top of strip */
616
617                 xfrac = (xcur>>8) & 0x0000ff;
618                 yfrac = (ycur>>8) & 0x0000ff;
619
620                 if (g->bytesPerPixel == 3) {
621                     if (g->bRotateFast) {
622                         pDest[0] = pSrc[0];
623                         pDest[1] = pSrc[1];
624                         pDest[2] = pSrc[2];
625                     } else {
626                         /* interpolate to eliminate jaggies */
627                         INTERPOLATE_BYTE (pDest+0, pSrc+0, pNextRow+0, 3)
628                         INTERPOLATE_BYTE (pDest+1, pSrc+1, pNextRow+1, 3)
629                         INTERPOLATE_BYTE (pDest+2, pSrc+2, pNextRow+2, 3)
630                     }
631                     pDest += 3;
632                 } else if (g->bytesPerPixel == 1) {
633                     BYTE byt;
634
635                     if (g->bRotateFast)
636                         byt = pSrc[0];
637                     else
638                         INTERPOLATE_BYTE (&byt, pSrc, pNextRow, 1)
639
640                     if (g->inTraits.iBitsPerPixel == 8)
641                         *pDest++ = byt;
642                     else {
643                         /* bi-level: pack 8 pixels per byte */
644                         if (byt < 128)
645                             *pDest |= mask;
646                         mask >>= 1;
647                         if (mask == 0) {
648                             mask = 0x80u;
649                             pDest += 1;
650                             *pDest = 0;
651                         }
652                     }
653                 } else if (g->bytesPerPixel==2 || g->bytesPerPixel==6) {
654                     /* 16-bit grayscale or 48-bit color */
655                     if (g->bRotateFast) {
656                         memcpy (pDest, pSrc, g->bytesPerPixel);
657                     } else {
658                         xfrac = xcur & 0x00ffff;
659                         yfrac = ycur & 0x00ffff;
660                         INTERPOLATE_WORD (pDest+0, pSrc+0, pNextRow+0, g->inTraits.iComponentsPerPixel)
661                         if (g->bytesPerPixel == 6) {
662                             INTERPOLATE_WORD (pDest+2, pSrc+2, pNextRow+2, 3)
663                             INTERPOLATE_WORD (pDest+4, pSrc+4, pNextRow+4, 3)
664                         }
665                     }
666
667                     pDest += g->bytesPerPixel;
668                 } else {
669                     /* unsupported bits per pixel */
670                     INSURE (FALSE);
671                 }
672             }
673             else   /* current pos is outside strip, so output a white pixel (padding) */
674             {
675                 if (g->inTraits.iBitsPerPixel == 1) {
676                     mask >>= 1;
677                     if (mask == 0) {
678                         mask = 0x80u;
679                         pDest += 1;
680                         *pDest = 0;
681                     }
682                 } else {
683                     memset (pDest, 255, g->bytesPerPixel);
684                     pDest += g->bytesPerPixel;
685                 }
686             }
687
688             /* Advance to next pixel in input image */
689             xcur += g->hSlopeDx;
690             ycur += g->hSlopeDy;
691         }  /* end of for-each-pixel loop */
692
693         *pdwOutputUsed    = (DWORD)g->outBytesPerRow;
694         *pdwOutputThisPos = g->dwOutNextPos;
695         g->dwOutNextPos  += (DWORD)g->outBytesPerRow;
696
697         g->dwRowsSent += 1;
698
699         g->xLeft  += g->vSlopeDx;
700         g->yLeft  += g->vSlopeDy;
701         g->xRight += g->vSlopeDx;
702         g->yRight += g->vSlopeDy;
703     } 
704
705     return (*pdwInputUsed !=0 ? IP_CONSUMED_ROW   : 0) |
706            (*pdwOutputUsed!=0 ? IP_PRODUCED_ROW   : 0) |
707            (NEED_MORE         ? IP_READY_FOR_DATA : 0);
708
709     fatal_error:
710     return IP_FATAL_ERROR;
711 }
712
713
714
715 /*****************************************************************************\
716  *
717  * rotate_insertedData - client inserted into our output stream
718  *
719 \*****************************************************************************/
720
721 FUNC_STATUS WORD rotate_insertedData (
722     IP_XFORM_HANDLE hXform,
723     DWORD           dwNumBytes)
724 {
725     fatalBreakPoint ();
726     return IP_FATAL_ERROR;   /* must never be called (can't insert data) */
727 }
728
729
730
731 /*****************************************************************************\
732  *
733  * rotate_newPage - Tells us to flush this page, and start a new page
734  *
735 \*****************************************************************************/
736
737 FUNC_STATUS WORD rotate_newPage (
738     IP_XFORM_HANDLE hXform)
739 {
740     PROTATE_INST g;
741
742     HANDLE_TO_PTR (hXform, g);
743     /* todo: return fatal error if convert is called again? */
744     return IP_DONE;   /* can't insert page-breaks, so ignore this call */
745
746     fatal_error:
747     return IP_FATAL_ERROR;
748
749 }
750
751
752
753 /*****************************************************************************\
754  *
755  * rotate_closeXform - Destroys this instance
756  *
757 \*****************************************************************************/
758
759 FUNC_STATUS WORD rotate_closeXform (IP_XFORM_HANDLE hXform)
760 {
761     PROTATE_INST g;
762
763     HANDLE_TO_PTR (hXform, g);
764
765     if (g->pStrip != NULL)
766         IP_MEM_FREE (g->pStrip);
767     g->dwValidChk = 0;
768     IP_MEM_FREE (g);       /* free memory for the instance */
769
770     return IP_DONE;
771
772     fatal_error:
773     return IP_FATAL_ERROR;
774 }
775
776
777
778 /*****************************************************************************\
779  *
780  * rotateTbl - Jump-table for transform driver
781  *
782 \*****************************************************************************/
783
784 #ifdef EXPORT_TRANSFORM
785 __declspec (dllexport)
786 #endif
787 IP_XFORM_TBL rotateTbl = {
788     rotate_openXform,
789     rotate_setDefaultInputTraits,
790     rotate_setXformSpec,
791     rotate_getHeaderBufSize,
792     rotate_getActualTraits,
793     rotate_getActualBufSizes,
794     rotate_convert,
795     rotate_newPage,
796     rotate_insertedData,
797     rotate_closeXform
798 };
799
800
801
802 /*****************************************************************************\
803  *
804  * ipGetXformTable - Returns pointer to the jump table
805  *
806 \*****************************************************************************/
807
808 #ifdef EXPORT_TRANSFORM
809 EXPORT(WORD) rotateGetXformTable (LPIP_XFORM_TBL pXform)
810 {
811     if (pXform == NULL)
812         return IP_FATAL_ERROR;
813
814     *pXform = clrmapTbl;
815     return IP_DONE;
816 }
817 #endif
818
819 /* End of File */