1 /* libhpojip -- HP OfficeJet image-processing library. */
3 /* Copyright (C) 1995-2002 Hewlett-Packard Company
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.
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.
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,
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.
32 /* Original author: Mark Overton and others.
34 * Ported to Linux by David Paschal.
37 /******************************************************************************\
39 * xrotate.c - Rotates the image
41 * Mark Overton, Feb 2000
43 ******************************************************************************
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.
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.
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.
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.
63 * Name of Global Jump-Table:
67 * Items in aXformInfo array passed into setXformSpec:
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)
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.
82 * Capabilities and Limitations:
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.
89 * Default Input Traits, and Output Traits:
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
102 * Above, a "*" by an item indicates it must be valid (not negative).
104 \******************************************************************************/
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 */
112 #include "string.h" /* for memset and memcpy */
113 #include "math.h" /* for sqrt */
119 #define PRINT(msg,arg1,arg2) \
120 _ftprintf(stderr, msg, (int)arg1, (int)arg2)
122 #define PRINT(msg,arg1,arg2)
125 #define CHECK_VALUE 0x4ba1dace
127 #ifdef EXPORT_TRANSFORM
128 #define FUNC_STATUS __declspec (dllexport)
130 #define FUNC_STATUS static
134 IP_IMAGE_TRAITS inTraits; /* traits of the input image */
135 IP_IMAGE_TRAITS outTraits; /* traits of the output image */
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) */
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;
170 /*****************************************************************************\
172 * rotate_openXform - Creates a new instance of the transformer
174 *****************************************************************************
176 * This returns a handle for the new instance to be passed into
177 * all subsequent calls.
179 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
181 \*****************************************************************************/
183 FUNC_STATUS WORD rotate_openXform (
184 IP_XFORM_HANDLE *pXform) /* out: returned handle */
188 INSURE (pXform != NULL);
189 IP_MEM_ALLOC (sizeof(ROTATE_INST), g);
191 memset (g, 0, sizeof(ROTATE_INST));
192 g->dwValidChk = CHECK_VALUE;
196 return IP_FATAL_ERROR;
201 /*****************************************************************************\
203 * rotate_setDefaultInputTraits - Specifies default input image traits
205 *****************************************************************************
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
212 * Return value: IP_DONE=success; IP_FATAL_ERROR=misc error.
214 \*****************************************************************************/
216 FUNC_STATUS WORD rotate_setDefaultInputTraits (
217 IP_XFORM_HANDLE hXform, /* in: handle for xform */
218 PIP_IMAGE_TRAITS pTraits) /* in: default image traits */
222 HANDLE_TO_PTR (hXform, g);
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 */
232 return IP_FATAL_ERROR;
237 /*****************************************************************************\
239 * rotate_setXformSpec - Provides xform-specific information
241 \*****************************************************************************/
243 FUNC_STATUS WORD rotate_setXformSpec (
244 IP_XFORM_HANDLE hXform, /* in: handle for xform */
245 DWORD_OR_PVOID aXformInfo[]) /* in: xform information */
250 HANDLE_TO_PTR (hXform, g);
252 i = aXformInfo[IP_ROTATE_UPPER_LEFT].dword;
256 i = aXformInfo[IP_ROTATE_UPPER_RIGHT].dword;
260 i = aXformInfo[IP_ROTATE_LOWER_LEFT].dword;
264 i = aXformInfo[IP_ROTATE_OUTPUT_SIZE].dword;
265 g->iOutWidth = i >> 16;
266 g->iOutHeight = i & 0xFFFF;
268 g->bRotateFast = aXformInfo[IP_ROTATE_FAST].dword == 1;
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);
277 return IP_FATAL_ERROR;
282 /*****************************************************************************\
284 * rotate_getHeaderBufSize - Returns size of input buf needed to hold header
286 \*****************************************************************************/
288 FUNC_STATUS WORD rotate_getHeaderBufSize (
289 IP_XFORM_HANDLE hXform, /* in: handle for xform */
290 DWORD *pdwInBufLen) /* out: buf size for parsing header */
292 /* since input is raw pixels, there is no header, so set it to zero */
299 /*****************************************************************************\
301 * rotate_getActualTraits - Parses header, and returns input & output traits
303 \*****************************************************************************/
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 */
315 int dxH, dyH, dxV, dyV;
316 double rotWidthSq, rotHeightSq, rotWidth, rotHeight;
318 HANDLE_TO_PTR (hXform, g);
320 /***** Compute Output Traits (and rotation variables) *****/
322 memcpy (&g->outTraits, &g->inTraits, sizeof(IP_IMAGE_TRAITS));
324 dxH = g->xUR - g->xUL;
325 dyH = g->yUR - g->yUL;
326 dxV = g->xLL - g->xUL;
327 dyV = g->yLL - g->yUL;
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);
335 if (g->iOutWidth == 0)
336 g->iOutWidth = (int)(rotWidth+0.5);
337 if (g->iOutHeight == 0)
338 g->iOutHeight = (int)(rotHeight+0.5);
340 g->outTraits.iPixelsPerRow = g->iOutWidth;
341 g->outTraits.lNumRows = g->iOutHeight;
343 g->bytesPerPixel = g->inTraits.iBitsPerPixel / 8;
344 if (g->bytesPerPixel == 0)
345 g->bytesPerPixel = 1; /* bi-level is expanded to one byte/pixel */
347 g->inBytesPerRow = (g->inTraits.iPixelsPerRow*g->inTraits.iBitsPerPixel + 7) / 8;
348 g->outBytesPerRow = (g->iOutWidth *g->inTraits.iBitsPerPixel + 7) / 8;
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;
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;
364 /* set-up the strip-buffer */
365 g->stripBytesPerRow = g->inTraits.iPixelsPerRow * g->bytesPerPixel;
366 g->stripIndexYTop = 0;
368 g->stripYBottom = -1;
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;
379 /***** Return Values *****/
381 /* Since there is no header, we'll report no usage of input */
383 *pdwInputNextPos = 0;
385 *pIntraits = g->inTraits; /* structure copies */
386 *pOutTraits = g->outTraits;
388 return IP_DONE | IP_READY_FOR_DATA;
391 return IP_FATAL_ERROR;
396 /****************************************************************************\
398 * rotate_getActualBufSizes - Returns buf sizes needed for remainder of job
400 \****************************************************************************/
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 */
409 HANDLE_TO_PTR (hXform, g);
411 *pdwMinInBufLen = g->inBytesPerRow;
412 *pdwMinOutBufLen = g->outBytesPerRow;
416 return IP_FATAL_ERROR;
421 /*****************************************************************************\
423 * rotate_convert - Converts one row
425 \*****************************************************************************/
429 /* ExpandBilevelRow - Expands 8 bits/pixel (input) to 1 byte/pixel (output) */
430 static void ExpandBilevelRow (
439 while (nPixels > 0) {
444 *pDest++ = inbyte & mask ? 0 : 255;
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 */
463 // We need another row if the Y of either endpoint of the current rotated row
464 // is below the bottom of the strip.
466 ((g->yLeft >>16) >= g->stripYBottom || (g->yRight>>16) >= g->stripYBottom)
471 HANDLE_TO_PTR (hXform, g);
473 *pdwInputUsed = *pdwOutputUsed = 0;
474 *pdwInputNextPos = g->dwInNextPos;
475 *pdwOutputThisPos = g->dwOutNextPos;
477 /**** Check if we were told to flush ****/
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)
486 /**** Initial load of strip-buffer ****/
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);
498 memcpy (pDest, pbInputBuf, g->stripBytesPerRow);
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);
505 g->stripYBottom += 1;
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)
516 /**** Load next row into strip-buffer ****/
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);
530 memcpy (pDest, pbInputBuf, g->stripBytesPerRow);
531 g->stripIndexYTop = (g->stripIndexYTop + 1) % g->stripRows;
533 g->stripYBottom += 1;
534 if (pbInputBuf != NULL) {
535 *pdwInputUsed = (DWORD)g->inBytesPerRow;
536 g->dwInNextPos += (DWORD)g->inBytesPerRow;
537 *pdwInputNextPos = g->dwInNextPos;
541 /**** Output a row ****/
543 if ((long)g->dwRowsSent >= g->outTraits.lNumRows)
545 /* we've outputted all rows; discard any further input */
546 g->stripYBottom = -100000; /* forces strip-loading logic to keep loading */
548 else if (! NEED_MORE)
551 int xcur,ycur, xint,yint;
552 unsigned xfrac,yfrac;
556 INSURE (dwOutputAvail >= (DWORD)g->outBytesPerRow);
562 /* These two lines set-up bi-level packing: */
566 for (iPix=0; iPix<g->outTraits.iPixelsPerRow; iPix++)
569 yint = (ycur >> 16) - g->stripYTop;
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)
576 /**** We are inside the strip ****/
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
585 /**** Output a pixel ****/
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.
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; \
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; \
613 pNextRow = pSrc + g->stripBytesPerRow;
614 if (pNextRow >= g->pStripAfter)
615 pNextRow -= g->stripSize; /* next row wrapped to top of strip */
617 xfrac = (xcur>>8) & 0x0000ff;
618 yfrac = (ycur>>8) & 0x0000ff;
620 if (g->bytesPerPixel == 3) {
621 if (g->bRotateFast) {
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)
632 } else if (g->bytesPerPixel == 1) {
638 INTERPOLATE_BYTE (&byt, pSrc, pNextRow, 1)
640 if (g->inTraits.iBitsPerPixel == 8)
643 /* bi-level: pack 8 pixels per byte */
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);
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)
667 pDest += g->bytesPerPixel;
669 /* unsupported bits per pixel */
673 else /* current pos is outside strip, so output a white pixel (padding) */
675 if (g->inTraits.iBitsPerPixel == 1) {
683 memset (pDest, 255, g->bytesPerPixel);
684 pDest += g->bytesPerPixel;
688 /* Advance to next pixel in input image */
691 } /* end of for-each-pixel loop */
693 *pdwOutputUsed = (DWORD)g->outBytesPerRow;
694 *pdwOutputThisPos = g->dwOutNextPos;
695 g->dwOutNextPos += (DWORD)g->outBytesPerRow;
699 g->xLeft += g->vSlopeDx;
700 g->yLeft += g->vSlopeDy;
701 g->xRight += g->vSlopeDx;
702 g->yRight += g->vSlopeDy;
705 return (*pdwInputUsed !=0 ? IP_CONSUMED_ROW : 0) |
706 (*pdwOutputUsed!=0 ? IP_PRODUCED_ROW : 0) |
707 (NEED_MORE ? IP_READY_FOR_DATA : 0);
710 return IP_FATAL_ERROR;
715 /*****************************************************************************\
717 * rotate_insertedData - client inserted into our output stream
719 \*****************************************************************************/
721 FUNC_STATUS WORD rotate_insertedData (
722 IP_XFORM_HANDLE hXform,
726 return IP_FATAL_ERROR; /* must never be called (can't insert data) */
731 /*****************************************************************************\
733 * rotate_newPage - Tells us to flush this page, and start a new page
735 \*****************************************************************************/
737 FUNC_STATUS WORD rotate_newPage (
738 IP_XFORM_HANDLE hXform)
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 */
747 return IP_FATAL_ERROR;
753 /*****************************************************************************\
755 * rotate_closeXform - Destroys this instance
757 \*****************************************************************************/
759 FUNC_STATUS WORD rotate_closeXform (IP_XFORM_HANDLE hXform)
763 HANDLE_TO_PTR (hXform, g);
765 if (g->pStrip != NULL)
766 IP_MEM_FREE (g->pStrip);
768 IP_MEM_FREE (g); /* free memory for the instance */
773 return IP_FATAL_ERROR;
778 /*****************************************************************************\
780 * rotateTbl - Jump-table for transform driver
782 \*****************************************************************************/
784 #ifdef EXPORT_TRANSFORM
785 __declspec (dllexport)
787 IP_XFORM_TBL rotateTbl = {
789 rotate_setDefaultInputTraits,
791 rotate_getHeaderBufSize,
792 rotate_getActualTraits,
793 rotate_getActualBufSizes,
802 /*****************************************************************************\
804 * ipGetXformTable - Returns pointer to the jump table
806 \*****************************************************************************/
808 #ifdef EXPORT_TRANSFORM
809 EXPORT(WORD) rotateGetXformTable (LPIP_XFORM_TBL pXform)
812 return IP_FATAL_ERROR;