/** ** $Header: /roq/libim/imvfbrotate.c 1 11/02/99 4:38p Zaphod $ ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) ** a division of General Atomics, San Diego, California, USA ** ** Users and possessors of this source code are hereby granted a ** nonexclusive, royalty-free copyright and design patent license to ** use this code in individual software. License is not granted for ** commercial resale, in whole or in part, without prior written ** permission from SDSC. This source is provided "AS IS" without express ** or implied warranty of any kind. ** ** For further information contact: ** E-Mail: info@sds.sdsc.edu ** ** Surface Mail: Information Center ** San Diego Supercomputer Center ** P.O. Box 85608 ** San Diego, CA 92186-9784 ** (619) 534-5000 **/ #define HEADER " $Header: /roq/libim/imvfbrotate.c 1 11/02/99 4:38p Zaphod $ " /** ** FILE ** imvfbrotate.c - Rotate a VFB by a certain angle ** ** PROJECT ** libim - SDSC image manipulation library ** ** DESCRIPTION ** imvfbrotate.c contains code to rotate an image. ** It also has the code to shear an image in the x or y direction ** ** PUBLIC CONTENTS ** d =defined constant ** f =function ** m =defined macro ** t =typedef/struct/union ** v =variable ** ? =other ** ** ImVfbRotate f rotate the vfb ** ImVfbXShear f shear the vfb in the x direction ** ImVfbYShear f shear the vfb in the y direction ** ** PRIVATE CONTENTS ** none ** ** HISTORY ** $Log: /roq/libim/imvfbrotate.c $ * * 1 11/02/99 4:38p Zaphod ** Revision 1.10 1995/06/30 22:12:11 bduggan ** added some casts ** ** Revision 1.9 1995/06/29 00:28:04 bduggan ** updated copyright year ** ** Revision 1.8 1995/06/16 09:01:05 bduggan ** added some casts ** ** Revision 1.7 94/10/03 11:29:57 nadeau ** Updated to ANSI C and C++ compatibility. ** Removed all use of register keyword. ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) ** Changed all float arguments to double. ** Added forward declarations. ** Added misc. casts to passify SGI and DEC compilers. ** Changed all macros and defined constants to have names ** starting with IM. ** Updated comments. ** Updated indenting on some code. ** Updated copyright message. ** ** Revision 1.6 92/12/03 01:56:03 nadeau ** Total rewrite. ** ** Revision 1.5 92/10/19 14:07:23 groening ** *** empty log message *** ** ** Revision 1.4 92/09/17 14:50:14 vle ** Added optional include for M_PI declaration to make some ** compilers happy. ** ** Revision 1.3 92/09/03 16:41:14 groening ** Added more error checks. ** ** Revision 1.2 92/09/02 11:17:02 vle ** Updated copyright notice. ** ** Revision 1.1 92/09/02 11:13:57 groening ** Initial revision ** **/ /** ** CODE CREDITS ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1992. **/ #include #ifndef M_PI //#include #define M_PI 3.1415926 #endif #include "iminternal.h" /* * FUNCTION * ImVfbXShear - shear a vfb in the X-direction * * DESCRIPTION * implements this formula x1 = x - tan(degree/2)*y * */ ImVfb * /* Returns resized VFB */ #ifdef __STDC__ ImVfbXShear( ImVfb *srcVfb, double degree, ImVfb *dstVfb) #else ImVfbXShear( srcVfb, degree, dstVfb) ImVfb *srcVfb; /* VFB to resize */ double degree; /* Amount to rotate */ ImVfb *dstVfb; /* Result VFB */ #endif { ImVfbPtr pdst; /* Destination VFB pointer */ ImVfbPtr psrc; /* Destination VFB pointer */ int i,j; /* generic integer value */ int fields; /* vfb field description */ int dw, dh; /* width an height */ int newX; /* new x position */ int xDif; /* how much wider new vfb is */ double tandegree; /* tangent of degree/2 */ /* * make sure there is a source vfb then make sure dst vfb * has the same fields as the source. */ if ( srcVfb == IMVFBNULL ) { ImErrNo = IMENOVFB ; return (IMVFBNULL); } /* * Floating point modulo 'degree' by 360.0. */ degree *= 2.0; degree -= ((int)(degree / 360.0)) * 360.0; /* * Make sure that a valid shear degree was requested. */ if ( (degree <= -180.0) || (degree >= 180.0) ) { ImErrNo = IMEIMPSHEAR; return (IMVFBNULL); } if ( degree < 0.0 ) degree += 360.0; degree *= -(M_PI/180); /* * figure out the necessary size for the resulting vfb */ if ( degree > -M_PI ) { dw = (int)( 0.5 + ImVfbQWidth(srcVfb) - tan(degree/2)*ImVfbQHeight( srcVfb )); xDif = 0; dh = ImVfbQHeight( srcVfb ); } else { dw = (int)( 0.5 + ImVfbQWidth(srcVfb) + tan(degree/2)*ImVfbQHeight( srcVfb )); xDif = dw-ImVfbQWidth(srcVfb); dh = ImVfbQHeight( srcVfb ); } /* * If the user hasn't given us a destination VFB, make one. * If they have given us a destination VFB, make sure it is usable. */ fields = ImVfbQFields( srcVfb ); if ( dstVfb == IMVFBNEW ) { dstVfb = ImVfbAlloc( dw, dh, fields ); if( dstVfb == IMVFBNULL ) { ImErrNo = IMEMALLOC; return( IMVFBNULL ); } ImVfbClear( fields, 0, dstVfb ); } else { /* make sure that the passed vfb is the correct size */ if ( ImVfbQWidth( dstVfb ) < dw ) { ImErrNo = IMEWIDTH; return ( IMVFBNULL ); } if ( ImVfbQHeight( dstVfb ) < dh ) { ImErrNo = IMEHEIGHT; return ( IMVFBNULL ); } if (( ImVfbQFields(srcVfb) & ImVfbQFields(dstVfb)) != ImVfbQFields(srcVfb)) { ImErrNo = IMEFIELD; return (IMVFBNULL); } } /* * Now shear the vfb */ tandegree = tan( degree / 2 ); psrc = ImVfbQFirst(srcVfb); for (i=0; i= 180.0) ) { ImErrNo = IMEIMPSHEAR; return (IMVFBNULL); } if ( degree < 0.0 ) degree += 360.0; degree *= -(M_PI/180); /* * figure out the necessary size for the resulting vfb */ if ( degree>-M_PI ) { dh = (int)(( 0.5 + ImVfbQHeight(srcVfb) - sin(degree)*ImVfbQWidth( srcVfb ))); dw = ImVfbQWidth( srcVfb ); yDif = dh-ImVfbQHeight(srcVfb); } else { dh = (int)(( 0.5 + ImVfbQHeight(srcVfb) + sin(degree)*ImVfbQWidth( srcVfb ))); dw = ImVfbQWidth( srcVfb ); yDif = 0; } /* * If the user hasn't given us a destination VFB, make one. * If they have given us a destination VFB, make sure it is usable. */ fields = ImVfbQFields( srcVfb ); if ( dstVfb == IMVFBNEW ) { dstVfb = ImVfbAlloc( dw, dh, fields ); if( dstVfb == IMVFBNULL ) { ImErrNo = IMEMALLOC; return( IMVFBNULL ); } ImVfbClear( fields, 0, dstVfb ); } else { /* make sure that passed vfb is the correct size */ if ( ImVfbQWidth( dstVfb ) < dw ) { ImErrNo = IMEWIDTH; return ( IMVFBNULL ); } if ( ImVfbQHeight( dstVfb ) < dh ) { ImErrNo = IMEHEIGHT; return ( IMVFBNULL ); } if (( ImVfbQFields(srcVfb) & ImVfbQFields(dstVfb)) != ImVfbQFields(srcVfb)) { ImErrNo = IMEFIELD; return (IMVFBNULL); } } /* * Now shear the vfb */ psrc = ImVfbQFirst(srcVfb); sindegree = sin( degree ); for (i=0; i= 90.0 ) { if ( (tmpVfb = ImVfb90Rotate( srcVfb, IMVFBNEW )) == IMVFBNULL ) { ImErrNo = IMEMALLOC; return (IMVFBNULL); } rad -= (M_PI/2.0); rotation -= 90.0; if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); srcVfb = tmpVfb; } W = ImVfbQWidth( srcVfb ); H = ImVfbQHeight( srcVfb ); if ( rotation == 0.0 ) { /* * Nothing more to do. Easy! Copy the rotated image into * the destination VFB and return. */ if ( ImVfbCopy( srcVfb , 0, 0, W, H, fieldMask, dstVfb, 0, 0 ) == IMVFBNULL) { /* ImErrNo already set. */ if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); return ( IMVFBNULL ); } if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); return ( dstVfb ); } /* * Compute ultimate image size. */ Wone = (int) (W + 0.5 + H * fabs(tan(rad/2))); Hone = (int) (H + 0.5 + fabs(sin(rad)) * Wone); Wtwo = (int) (0.5 + Wone + Hone*fabs(tan(rad/2))); srcYTop = (int)(0.5 + fabs(tan(rad/2))*(fabs(sin(rad))*H)); srcXLeft = (int) (0.5 + fabs(sin(rad)) * ( (W)*fabs(tan(rad/2)))); srcDX = (int) (0.5 + fabs(sin(rad))*H + fabs (cos(rad)*W)); srcDY = (int) (0.5 + fabs(sin(rad))*W + fabs (cos(rad)*H)); srcDY +=2; if (srcDY>Hone) srcDY=Hone; if (srcYTop>1.0) srcYTop--; rotation /= 2.0; /* * Create a dummy monochrome VFB filled with 1's. */ if ( (dummyVfb = ImVfbAlloc( W, H, IMVFBMONO )) == IMVFBNULL) { /* ImErrNo already set. */ if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); return (IMVFBNULL); } if ( ImVfbFill( dummyVfb, 0, 0, W, H, IMMONO, 1.0, 1.0, IMVFBINSIDE, IMGRADNONE, dummyVfb ) == IMVFBNULL ) { /* ImErrNo already set. */ if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } /* * Shear in X. */ if ( (tmpVfb = ImVfbXShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) { /* ImErrNo already set. */ if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } if ( srcVfb != sorceVfb ) ImVfbFree( srcVfb ); srcVfb = tmpVfb; if ( (tmpVfb = ImVfbXShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( dummyVfb ); dummyVfb = tmpVfb; /* * Shear in Y. */ if ( (tmpVfb = ImVfbYShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( srcVfb ); srcVfb = tmpVfb; if ( (tmpVfb = ImVfbYShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( dummyVfb ); dummyVfb = tmpVfb; /* * Trim off the excess that comes about by increasing the size to * shear in X, then increasing the size to shear in Y. We end up * with a lot of extra "empty" space around the image. By trimming * it off here, we save time later by having fewer pixels to move * about. */ if ( (tmpVfb = ImVfbCopy( srcVfb, 0, srcYTop, Wone, srcDY, fieldMask, IMVFBNEW, 0, 0)) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( srcVfb ); srcVfb = tmpVfb; if ( (tmpVfb = ImVfbCopy( dummyVfb, 0, srcYTop, Wone, srcDY, IMVFBMONO, IMVFBNEW, 0, 0)) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( dummyVfb ); dummyVfb = tmpVfb; /* * Shear in X again. */ if ( (tmpVfb = ImVfbXShear( srcVfb, rotation, IMVFBNEW ) ) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( srcVfb ); srcVfb = tmpVfb; if ( (tmpVfb = ImVfbXShear( dummyVfb, rotation, IMVFBNEW)) == IMVFBNULL) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( dummyVfb ); dummyVfb = tmpVfb; /* * Trim off the excess again. */ if ( (tmpVfb = ImVfbCopy( srcVfb, srcXLeft, 0, srcDX, srcDY, fieldMask, IMVFBNEW, 0, 0 )) == IMVFBNULL ) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( srcVfb ); srcVfb = tmpVfb; if ( (tmpVfb = ImVfbCopy( dummyVfb, srcXLeft, 0, srcDX, srcDY, IMVFBMONO, IMVFBNEW, 0, 0 )) == IMVFBNULL ) { /* ImErrNo already set. */ ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return (IMVFBNULL); } ImVfbFree( dummyVfb ); dummyVfb = tmpVfb; /* * Use the dummyVfb has a mask for the rotated srcVfb to determine * which pixels of the srcVfb to copy to the dstVfb. 1's in the * dummyVfb mean copy. 0's mean don't. */ pSrc = ImVfbQFirst( srcVfb ); pDummy = ImVfbQFirst( dummyVfb ); pDst = ImVfbQFirst( dstVfb ); for ( i = 0; i < ImVfbQHeight( dummyVfb ); i++ ) { for ( j = 0; j < ImVfbQWidth( dummyVfb) ; j++ ) { if ( !ImVfbQMono( dummyVfb, pDummy ) ) { /* Don't copy. */ ImVfbSInc( dummyVfb, pDummy ); ImVfbSInc( srcVfb, pSrc ); ImVfbSInc( dstVfb, pDst ); continue; } /* Do copy. */ if (fieldMask&IMVFBRGB) { ImVfbSRed( dstVfb, pDst, ImVfbQRed( srcVfb, pSrc)); ImVfbSGreen( dstVfb, pDst, ImVfbQGreen( srcVfb, pSrc)); ImVfbSBlue( dstVfb, pDst, ImVfbQBlue( srcVfb, pSrc)); } if (fieldMask&IMVFBZ) ImVfbSZ( dstVfb, pDst, ImVfbQZ( srcVfb, pSrc ) ); if (fieldMask&IMVFBWPROT) ImVfbSWProt( dstVfb, pDst, ImVfbQWProt( srcVfb, pSrc ) ); if (fieldMask&IMVFBIDATA) ImVfbSIData( dstVfb, pDst, ImVfbQIData( srcVfb, pSrc ) ); if (fieldMask&IMVFBFDATA) ImVfbSFData( dstVfb, pDst, ImVfbQFData( srcVfb, pSrc ) ); if (fieldMask&IMVFBMONO) ImVfbSMono( dstVfb, pDst, ImVfbQMono( srcVfb, pSrc ) ); if (fieldMask&IMVFBALPHA) ImVfbSAlpha( dstVfb, pDst, ImVfbQAlpha( srcVfb, pSrc ) ); if (fieldMask&IMVFBINDEX8) ImVfbSIndex8( dstVfb, pDst, ImVfbQIndex8( srcVfb, pSrc ) ); if (fieldMask&IMVFBINDEX16) ImVfbSIndex16( dstVfb, pDst, ImVfbQIndex16( srcVfb, pSrc ) ); ImVfbSInc( dummyVfb, pDummy ); ImVfbSInc( srcVfb, pSrc ); ImVfbSInc( dstVfb, pDst ); } } ImVfbFree( srcVfb ); ImVfbFree( dummyVfb ); return( dstVfb ); } /* * FUNCTION * ImVfb90Rotate - rotate by 90 degrees * * DESCRIPTION * When rotating by exactly 90 degrees counter-clockwise (right-hand * rule), we can do the rotation quicker than by using 3 shears. */ ImVfb * /* Returns rotated VFB */ #ifdef __STDC__ ImVfb90Rotate( ImVfb* sourceVfb, ImVfb* dstVfb ) #else ImVfb90Rotate( sourceVfb, dstVfb ) ImVfb *sourceVfb; /* VFB to rotated */ ImVfb *dstVfb; /* VFB to return rotated */ #endif { ImVfbPtr psrc; /* pointer into source vfb */ ImVfbPtr pdst; /* pointer into destination vfb */ int i,j; /* Counters */ int wDst, hDst; /* Destination width and height */ int fieldMask; /* Fields to deal with */ /* * Get destination width and height... reversed from source's * `cause we're rotating by 90.0. */ hDst = ImVfbQWidth( sourceVfb ); wDst = ImVfbQHeight( sourceVfb ); fieldMask = ImVfbQFields( sourceVfb ); /* * If a destination VFB wasn't given, allocate one at the same * size and depth as the source. Otherwise make sure the desination * is the same size as the source and has at least the same fields * as the source. */ if (dstVfb == IMVFBNEW) { if ( (dstVfb = ImVfbAlloc( wDst, hDst, fieldMask )) ==IMVFBNULL) { ImErrNo = IMEMALLOC; return (IMVFBNULL); } } else { if ( ImVfbQWidth( dstVfb ) != wDst ) { ImErrNo = IMEWIDTH; return (IMVFBNULL); } if ( ImVfbQHeight( dstVfb ) != hDst ) { ImErrNo = IMEHEIGHT; return (IMVFBNULL); } if ( (fieldMask & ImVfbQFields(dstVfb)) != fieldMask ) { ImErrNo = IMEFIELD; return (IMVFBNULL); } } /* * Walk the image and rotate it by 90 into the destination VFB. */ psrc = ImVfbQFirst( sourceVfb ); for ( i = 0; i < wDst; i++ ) { for ( j = hDst-1; j >= 0; j-- ) { pdst = ImVfbQPtr( dstVfb, i, j ); if (fieldMask&IMVFBRGB) { ImVfbSRed (dstVfb, pdst, ImVfbQRed(sourceVfb, psrc)); ImVfbSGreen (dstVfb, pdst, ImVfbQGreen(sourceVfb, psrc)); ImVfbSBlue (dstVfb, pdst, ImVfbQBlue(sourceVfb, psrc)); } if (fieldMask&IMVFBZ) { ImVfbSZ (dstVfb, pdst, ImVfbQZ(sourceVfb, psrc)); } if (fieldMask&IMVFBWPROT) { ImVfbSWProt (dstVfb, pdst, ImVfbQWProt(sourceVfb, psrc)); } if (fieldMask&IMVFBIDATA) { ImVfbSIData (dstVfb, pdst, ImVfbQIData(sourceVfb, psrc)); } if (fieldMask&IMVFBFDATA) { ImVfbSFData (dstVfb, pdst, ImVfbQFData(sourceVfb, psrc)); } if (fieldMask&IMVFBMONO) { ImVfbSMono (dstVfb, pdst, ImVfbQMono(sourceVfb, psrc)); } if (fieldMask&IMVFBALPHA) { ImVfbSAlpha (dstVfb, pdst, ImVfbQAlpha(sourceVfb, psrc)); } if (fieldMask&IMVFBINDEX8) { ImVfbSIndex8 (dstVfb, pdst, ImVfbQIndex8(sourceVfb, psrc)); } if (fieldMask&IMVFBINDEX16) { ImVfbSIndex16 (dstVfb, pdst, ImVfbQIndex16(sourceVfb, psrc)); } ImVfbSInc( sourceVfb, psrc ); } } return (dstVfb); }