home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-12-20 | 13.7 KB | 375 lines | [TEXT/CWIE] |
-
-
-
-
- // Includes
-
- #include "rotate.h"
- #include "FnAssert.h"
- #include "Colors.h"
- #include <math.h>
- #include <algobase.h>
-
- const short twoHunderedFiftySixOfColorsDepth = 8;
-
-
- ///////////////////////////////////////////////////////////////////////////////
- // Rotate input image about its center by angle ang (in radians)
- // input has height h and width w. The output is stored in OUT.
- // We assume that 0 <= ang <= π/2.
- //
- // inputPixMap input image
- // h height
- // w width
- // ang angle in radians
- // bkGndColor the background color of the image
- // outputGWorld ouput GWorld containing rotated image
- //
- // We pass in a PixMap to be rotated and pass back a new PixMap
- // (allocated within this routine) that is rotated by the specified angle.
-
- QDErr rotate(const PixMapHandle inputPixMap, short h, short w, double ang, RGBColor bkGndColor, GWorldPtr &outputGWorld)
- {
- GWorldPtr pass1GWorld; // offscreen world
- GWorldPtr pass2GWorld; // offscreen world
-
- unsigned char * src; // pointer to the begin of a row (in scanline order)
- unsigned char * dst; // ditto
-
- /*
- the dimensions of the rotated image as it is processed are:
- (h)(w) -> (h)(wmax) -> (newh)(wmax) -> (newh)(neww).
- +1 will be added to dimensions due to the last fractional pixel.
- Temporary buffer TMP is used to hold intermediate image.
- */
-
- // precompute most of the const we use
- const double sine = sin(ang);
- const double tangent = tan(ang / 2.0);
- const short wmax = w + (h * tangent) + 1; // width of intermediate image
- const short newh = (w * sine) + (h * cos(ang)) + 1; // final image height
- const short neww = (h * sine) + (w * cos(ang)) + 1; // final image width
- const Rect pass1Bounds = {0, 0, h, wmax}; // init the bounds to size of dst PixMap for 1st pass
- const Rect pass2Bounds = {0, 0, newh, wmax}; // init the bounds to size of dst PixMap for 2nd pass
- const Rect outputBounds = {0, 0, newh, neww}; // init the bounds to size of dst PixMap for final pass
-
-
- // prepare for 1st pass:
- // alloc the 1st pass tmp GWorld and save results of failure
- QDErr theErr = NewGWorld(&pass1GWorld, twoHunderedFiftySixOfColorsDepth, &pass1Bounds, 0, nil, 0);
- ASSERT(theErr == noErr, "alloc pass1GWorld failed!");
-
- // go get the rowBytes from the structure --
- short inputRowBytes = GetRowBytes(inputPixMap);
- PixMapHandle pass1PixMap = GetGWorldPixMap(pass1GWorld);
- short pass1RowBytes = GetRowBytes(pass1PixMap);
-
- LockPixels(inputPixMap); // lock em down so they don't move while working with em
- LockPixels(pass1PixMap);
- Ptr inputPixMapBaseAddr = GetPixBaseAddr(inputPixMap); // always get the addr via this function
- Ptr pass1PixMapBaseAddr = GetPixBaseAddr(pass1PixMap);
- EraseGWorld(pass1GWorld, bkGndColor);
-
- // 1st Pass: skew x (horiz scanlines)
- for(int scanLineNum = 0; scanLineNum < h; scanLineNum++) // visit each row
- {
- src = (unsigned char *)(inputPixMapBaseAddr + (scanLineNum * inputRowBytes)); // input scanline pointer
- dst = (unsigned char *)(pass1PixMapBaseAddr + (scanLineNum * pass1RowBytes)); // output scanline pointer
- skew(src, w, wmax, scanLineNum * tangent, bkGndColor, true, inputRowBytes, pass1RowBytes, dst);// skew row
- }
- UnlockPixels(inputPixMap); // not working with these so unlock 'em
-
- // prepare for 2nd pass:
- // alloc the 2nd pass tmp GWorld and save results of failure
- theErr = NewGWorld(&pass2GWorld, twoHunderedFiftySixOfColorsDepth, &pass2Bounds, 0, nil, 0);
- ASSERT(theErr == noErr, "alloc pass2GWorld failed!");
-
- PixMapHandle pass2PixMap = GetGWorldPixMap(pass2GWorld);
- LockPixels(pass2PixMap); // lock em down so they don't move while working with em
- Ptr pass2PixMapBaseAddr = GetPixBaseAddr(pass2PixMap); // always get the addr via this function
- short pass2RowBytes = GetRowBytes(pass2PixMap);
- EraseGWorld(pass2GWorld, bkGndColor);
-
- // 2nd Pass: skew y (vert scanlines) - use pass2GWorld for intermediate image
- const double offTop = (w - 1) * sine; // offset from top of image
- for(int colNum = 0; colNum < wmax; colNum++) // visit each column
- {
- src = (unsigned char *)(pass1PixMapBaseAddr + colNum); // input scanline pointer
- dst = (unsigned char *)(pass2PixMapBaseAddr + colNum); // output scanline pointer
- skew(src, h, newh, offTop - (colNum * sine), bkGndColor, false, pass1RowBytes, pass2RowBytes, dst);// skew column
- }
- UnlockPixels(pass1PixMap); // not working with these so unlock 'em
- DisposeGWorld(pass1GWorld); // done with first tmp buffer so cleanup
-
-
- // prepare for 3rd pass:
- // alloc the 3rd pass tmp GWorld and save results of failure
- theErr = NewGWorld(&outputGWorld, twoHunderedFiftySixOfColorsDepth, &outputBounds, 0, nil, 0);
- ASSERT(theErr == noErr, "alloc outputGWorld failed!");
-
- PixMapHandle outputPixMap = GetGWorldPixMap(outputGWorld); // go get the PixMap from the GWorld
- short outputRowBytes = GetRowBytes(outputPixMap);
-
- LockPixels(outputPixMap); // lock em down so they don't move while working with em
- Ptr outputPixMapBaseAddr = GetPixBaseAddr(outputPixMap); // always get the addr via this function
- EraseGWorld(outputGWorld, bkGndColor);
-
- // 3rd Pass: skew x (horizontal scanlines)
- for(int scanLineNum = 0; scanLineNum < newh; scanLineNum++) // visit each row
- {
- src = (unsigned char *)(pass2PixMapBaseAddr + (scanLineNum * pass2RowBytes)); // input scanline pointer
- dst = (unsigned char *)(outputPixMapBaseAddr + (scanLineNum * outputRowBytes)); // output scanline pointer
- skew(src, wmax, neww, (scanLineNum - offTop) * tangent, bkGndColor, true, pass2RowBytes, outputRowBytes, dst); // skew row
- }
- UnlockPixels(pass2PixMap); // not working with these so unlock 'em
- DisposeGWorld(pass2GWorld); // done with first tmp buffer so cleanup
-
- if ( theErr != noErr)
- return theErr;
- else
- return noErr;
-
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////
- // Skew scanline in src (length len) into dst (length nlen)
- // starting at position start. The distance between each scanline
- // pixel is offset. offset = 1 for rows; offset = width for columns
- //
- // src pointer to beginning of current source scanline
- // len length of the scanline in the source
- // nlen length of the scanline in the dest
- // bkGndColor the color we erase to
- // row flag indicating processing rows or columns
- // srcRowBytes rowBytes of the source image
- // dstRowBytes rowBytes of the dst image
- // src pointer to beginning of current dest scanline
-
- void skew(unsigned char * src, int len, int nlen, double start, RGBColor bkGndColor, Boolean row, short srcRowBytes, short dstRowBytes, unsigned char * dst)
- {
- int i;
- double f, g, w1, w2; // pixel weighting factors for interpolation (linear)
- char indexBkGndColor = (char)(LoWord(Color2Index(&bkGndColor))); // get the background color index - this is what we stuff in memory
- RGBColor temp0Color, temp1Color;
- long longIndex;
-
- // process left end of output; either prepare for clipping or add padding
- const int istart = (int) start; // integer index
- if(istart < 0)
- {
- if(row)
- {
- src -= istart; // advance input pointer for clipping
- }
- else
- { // doing a column so we must advance by rowBytes
- src -= (srcRowBytes * istart);// advance input pointer for clipping
- }
- }
-
- int lim = min(len + istart, nlen); // find index for right edge (valid range)
-
- for(i = 0; i < istart; i++) // visit all null output pixels at left edge
- {
- *dst = indexBkGndColor; // pad with bkground color
- if(row)
- {
- dst += 1; // advance output pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- dst += dstRowBytes; // advance output pointer
- }
- }
-
- f = (((start - istart) < 0) ? (-(start - istart)) : (start - istart)); // weight for right straddle (abs val)
- g = 1.0 - f; // weight for left straddle
-
- if(f == 0.0) // simple integer shift; no interpolation
- {
- for(;i < lim; i++) // visit all pixels in valid range
- {
- *dst = *src; // copy input to output
- if(row)
- {
- src += 1; // advance input pointer
- dst += 1; // advance output pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- src += srcRowBytes; // advance input pointer by rowBytes since this is a column
- dst += dstRowBytes; // advance output pointer by rowBytes since this is a column
- }
- }
- }
- else // fractional shift: interpolate
- {
- if(start > 0.0)
- {
- w1 = f; // weight for left pixel
- w2 = g; // weight for right pixel
- *dst = src[0]; // first pixel -- so stuff the value to avoid garbage with out of bounds interpolation
-
- if(row)
- {
- dst += 1; // advance output pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- dst += dstRowBytes; // advance output pointer
- }
- i++; // increment index
- }
- else
- {
- w1 = g; // weight for left pixel
- w2 = f; // weight for right pixel
- if(lim < nlen) lim--;
- }
-
- for(;i < lim; i++) // visit all pixels in valid range
- {
- if(row)
- {
- // src[0] is left (top) pixel, and src[offset] is right (bottom) pixel
- Index2Color((long)src[0], &temp0Color); // convert the 8 bit color to an RGB color
- Index2Color((long)src[1], &temp1Color); // ditto
- longIndex = Color2Index(&(RGBColorsPLUS(RGBColorsPERCENT(temp0Color, w1), RGBColorsPERCENT(temp1Color, w2))));
- *dst = LoWord(longIndex); // grab the loword of the index -- we could just cast it but this is safer and it avoids a compiler warning
-
- dst += 1; // advance output pointer
- src += 1; // advance input pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- Index2Color((long)src[0], &temp0Color); // convert the 8 bit color to an RGB color
- Index2Color((long)src[srcRowBytes], &temp1Color); // ditto - (use srcRowBytes since it is a column)
- longIndex = Color2Index(&(RGBColorsPLUS(RGBColorsPERCENT(temp0Color, w1), RGBColorsPERCENT(temp1Color, w2))));
- *dst = LoWord(longIndex); // grab the loword of the index -- we could just cast it but this is safer and it avoids a compiler warning
-
- dst += dstRowBytes; // advance output pointer by rowBytes since this is a column
- src += srcRowBytes; // advance input pointer by rowBytes since this is a column
- }
- }
-
- if(i < nlen)
- {
- *dst = indexBkGndColor; // set it to background
- if(row)
- {
- dst += 1; // advance output pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- dst += dstRowBytes; // advance output pointer
- }
- i++; // increment output index
- }
- }
- for(;i < nlen; i++){ // visit all remaining pixels at right edge
- *dst = indexBkGndColor; // pad with bkground color
- if(row)
- {
- dst += 1; // advance output pointer
- }
- else // doing a column so we must advance by rowBytes
- {
- dst += dstRowBytes; // advance output pointer
- }
- }
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////
- // This function returns the addr of the pixel.
- // Note: If pixelSize < 8 then you must compute a bit offset into
- // the byte (or better yet set multiple bits at a time).
- //
- // thePixMap handle to the PixMap
- // thePixelRow the row we are in (first row is zero)
- // thePixelColumn the col we are in (first column is zero)
- // theBitOffset the bit offset if pixelSize (from left edge)
- //
-
- const short bitsPerByte = 8;
-
- Ptr GetPixelAddr(const PixMapHandle thePixMap, const short thePixelRow, const short thePixelColumn, short &bitOffset)
- {
- // get general PixMap attributes:
- Ptr thePixMapPtr = GetPixBaseAddr( thePixMap ); // always get the addr via this function
- short theRowBytes = ((**thePixMap).rowBytes & 0x3fff); // mask off top to flag bits and get rowBytes
- Rect theBoundRect = (**thePixMap).bounds; // get actual bounds
-
- // now we can calculate the actual addr of the pixel
- Ptr pixAddr = (Ptr)(thePixMapPtr + // start at the baseAddr
- (thePixelRow - theBoundRect.top) * theRowBytes + // account for the number of rows
- ((thePixelColumn - theBoundRect.left) * (**thePixMap).pixelSize) / bitsPerByte); // and the amount that we are from left edge
-
- // compute the bit offset if we are less than 8 bits per pixel:
- if( (**thePixMap).pixelSize < twoHunderedFiftySixOfColorsDepth )
- {
- long numBits = ((thePixelColumn - theBoundRect.left) * bitsPerByte);
- bitOffset = numBits % bitsPerByte;
- }
-
- return pixAddr;
- }
-
- ///////////////////////////////////////////////////////////////////////////////
- // This function returns the rowBytes of a PixMap.
- // Note: The top 2 bits are flag bits -- these are stripped off
- // before returning the actual rowBytes.
- //
- // thePixMap handle to the PixMap
- //
-
- short GetRowBytes(const PixMapHandle thePixMap)
- {
- return ((**thePixMap).rowBytes & 0x3fff);
- }
-
- ///////////////////////////////////////////////////////////////////////////////
- // This function fills the GWorld with the a specified color
- // Note: a side effect of this routine is that
- // the bkackground color is set to the color passed in.
- // All future calls to Erase will erase to this color unless
- // it is changed
- //
- // theGWorld ptr to the GWorld
- // theColor the color that we want to erase to
- //
-
- void EraseGWorld(const GWorldPtr theGWorld, const RGBColor theColor)
- {
- // Store the current port and device before switching to the offscreen world.
- CGrafPtr currentPort;
- GDHandle currentDevice;
- GetGWorld(¤tPort, ¤tDevice);
-
- // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
- SetGWorld(theGWorld, nil);
-
- // Set the background color before we erase
- RGBBackColor(&theColor);
-
- // Get the PixMap
- PixMapHandle thePixMap = GetGWorldPixMap(theGWorld);
- const Rect theBounds = (**thePixMap).bounds;
-
- // Erase the PixMap's bounding box in the GWorld.
- EraseRect(&theBounds);
-
- // Set the port and device back to the original
- SetGWorld(currentPort, currentDevice);
- }
-
-
- // ***** end-of-file ****************************************************************************
-
-
-
-
-
-
-