home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / Label / rotate.cp < prev    next >
Encoding:
Text File  |  1996-12-20  |  13.7 KB  |  375 lines  |  [TEXT/CWIE]

  1.  
  2.  
  3.  
  4.  
  5. // Includes
  6.  
  7. #include "rotate.h"
  8. #include "FnAssert.h"
  9. #include "Colors.h"
  10. #include <math.h>
  11. #include <algobase.h>
  12.  
  13. const short    twoHunderedFiftySixOfColorsDepth = 8;
  14.  
  15.  
  16. ///////////////////////////////////////////////////////////////////////////////
  17. //    Rotate input image about its center by angle ang (in radians)
  18. //    input has height h and width w. The output is stored in OUT.
  19. //    We assume that 0 <= ang <= π/2.
  20. //    
  21. //    inputPixMap        input image
  22. //    h                 height
  23. //    w                width
  24. //    ang              angle in radians
  25. //    bkGndColor        the background color of the image
  26. //    outputGWorld    ouput GWorld containing rotated image
  27. //    
  28. //    We pass in a PixMap to be rotated and pass back a new PixMap 
  29. //    (allocated within this routine) that is rotated by the specified angle.
  30.  
  31. QDErr rotate(const PixMapHandle inputPixMap, short h, short w, double ang, RGBColor bkGndColor, GWorldPtr &outputGWorld)
  32. {
  33.     GWorldPtr        pass1GWorld;    // offscreen world
  34.     GWorldPtr        pass2GWorld;    // offscreen world
  35.  
  36.     unsigned char * src;    // pointer to the begin of a row (in scanline order)
  37.     unsigned char * dst;    // ditto
  38.  
  39.     /*    
  40.         the dimensions of the rotated image as it is processed are:
  41.         (h)(w) -> (h)(wmax) -> (newh)(wmax) -> (newh)(neww).
  42.         +1 will be added to dimensions due to the last fractional pixel.
  43.         Temporary buffer TMP is used to hold intermediate image.
  44.     */
  45.     
  46.     // precompute most of the const we use
  47.     const double sine = sin(ang);
  48.     const double tangent = tan(ang / 2.0);
  49.     const short wmax = w + (h * tangent) + 1;        // width of intermediate image
  50.     const short newh = (w * sine) + (h * cos(ang)) + 1;    // final image height
  51.     const short neww = (h * sine) + (w * cos(ang)) + 1;    // final image width
  52.     const Rect pass1Bounds = {0, 0, h, wmax};        // init the bounds to size of dst PixMap for 1st pass
  53.     const Rect pass2Bounds = {0, 0, newh, wmax};    // init the bounds to size of dst PixMap for 2nd pass
  54.     const Rect outputBounds = {0, 0, newh, neww};    // init the bounds to size of dst PixMap for final pass
  55.  
  56.  
  57.     // prepare for 1st pass:
  58.     // alloc the 1st pass tmp GWorld and save results of failure
  59.     QDErr theErr = NewGWorld(&pass1GWorld, twoHunderedFiftySixOfColorsDepth, &pass1Bounds, 0, nil, 0);
  60.     ASSERT(theErr == noErr, "alloc pass1GWorld failed!");
  61.     
  62.     // go get the rowBytes from the structure --
  63.     short inputRowBytes = GetRowBytes(inputPixMap);
  64.     PixMapHandle pass1PixMap = GetGWorldPixMap(pass1GWorld);
  65.     short pass1RowBytes = GetRowBytes(pass1PixMap);
  66.  
  67.     LockPixels(inputPixMap);                                    // lock em down so they don't move while working with em
  68.     LockPixels(pass1PixMap);
  69.     Ptr inputPixMapBaseAddr = GetPixBaseAddr(inputPixMap);        // always get the addr via this function
  70.     Ptr pass1PixMapBaseAddr = GetPixBaseAddr(pass1PixMap);
  71.     EraseGWorld(pass1GWorld, bkGndColor);
  72.  
  73.     // 1st Pass: skew x (horiz scanlines)
  74.     for(int scanLineNum = 0; scanLineNum < h; scanLineNum++)    // visit each row
  75.     {
  76.         src = (unsigned char *)(inputPixMapBaseAddr + (scanLineNum * inputRowBytes));    // input scanline pointer
  77.         dst = (unsigned char *)(pass1PixMapBaseAddr + (scanLineNum * pass1RowBytes));    // output scanline pointer
  78.         skew(src, w, wmax, scanLineNum * tangent, bkGndColor, true, inputRowBytes, pass1RowBytes, dst);// skew row
  79.     }
  80.     UnlockPixels(inputPixMap);                                    // not working with these so unlock 'em
  81.  
  82.     // prepare for 2nd pass:
  83.     // alloc the 2nd pass tmp GWorld and save results of failure
  84.     theErr = NewGWorld(&pass2GWorld, twoHunderedFiftySixOfColorsDepth, &pass2Bounds, 0, nil, 0);
  85.     ASSERT(theErr == noErr, "alloc pass2GWorld failed!");
  86.         
  87.     PixMapHandle pass2PixMap = GetGWorldPixMap(pass2GWorld);
  88.     LockPixels(pass2PixMap);                                    // lock em down so they don't move while working with em
  89.     Ptr pass2PixMapBaseAddr = GetPixBaseAddr(pass2PixMap);        // always get the addr via this function
  90.     short pass2RowBytes = GetRowBytes(pass2PixMap);
  91.     EraseGWorld(pass2GWorld, bkGndColor);
  92.  
  93.     // 2nd Pass: skew y (vert scanlines) - use pass2GWorld for intermediate image
  94.     const double offTop = (w - 1) * sine;                        // offset from top of image
  95.     for(int colNum = 0; colNum < wmax; colNum++)                // visit each column
  96.     {
  97.         src = (unsigned char *)(pass1PixMapBaseAddr + colNum);    // input scanline pointer
  98.         dst = (unsigned char *)(pass2PixMapBaseAddr + colNum);    // output scanline pointer
  99.         skew(src, h, newh, offTop - (colNum * sine), bkGndColor, false, pass1RowBytes, pass2RowBytes, dst);// skew column
  100.     }
  101.     UnlockPixels(pass1PixMap);                                    // not working with these so unlock 'em
  102.     DisposeGWorld(pass1GWorld);                                    // done with first tmp buffer so cleanup
  103.  
  104.  
  105.     // prepare for 3rd pass:
  106.     // alloc the 3rd pass tmp GWorld and save results of failure
  107.     theErr = NewGWorld(&outputGWorld, twoHunderedFiftySixOfColorsDepth, &outputBounds, 0, nil, 0);
  108.     ASSERT(theErr == noErr, "alloc outputGWorld failed!");
  109.         
  110.     PixMapHandle outputPixMap = GetGWorldPixMap(outputGWorld);    // go get the PixMap from the GWorld
  111.     short outputRowBytes = GetRowBytes(outputPixMap);
  112.  
  113.     LockPixels(outputPixMap);                                    // lock em down so they don't move while working with em
  114.     Ptr outputPixMapBaseAddr = GetPixBaseAddr(outputPixMap);    // always get the addr via this function
  115.     EraseGWorld(outputGWorld, bkGndColor);
  116.  
  117.     // 3rd Pass: skew x (horizontal scanlines)
  118.     for(int scanLineNum = 0; scanLineNum < newh; scanLineNum++)    // visit each row
  119.     {
  120.         src = (unsigned char *)(pass2PixMapBaseAddr + (scanLineNum * pass2RowBytes));    // input scanline pointer
  121.         dst = (unsigned char *)(outputPixMapBaseAddr + (scanLineNum * outputRowBytes));    // output scanline pointer
  122.         skew(src, wmax, neww, (scanLineNum - offTop) * tangent, bkGndColor, true, pass2RowBytes, outputRowBytes, dst);    // skew row
  123.     }
  124.     UnlockPixels(pass2PixMap);                                    // not working with these so unlock 'em
  125.     DisposeGWorld(pass2GWorld);                                    // done with first tmp buffer so cleanup
  126.  
  127.     if ( theErr != noErr)
  128.         return theErr;
  129.     else
  130.         return noErr;
  131.     
  132. }
  133.     
  134.     
  135. ///////////////////////////////////////////////////////////////////////////////
  136. //    Skew scanline in src (length len) into dst (length nlen)
  137. //    starting at position start. The distance between each scanline
  138. //    pixel is offset. offset = 1 for rows; offset = width for columns
  139. //    
  140. //    src            pointer to beginning of current source scanline
  141. //    len          length of the scanline in the source
  142. //    nlen         length of the scanline in the dest
  143. //    bkGndColor    the color we erase to
  144. //    row            flag indicating processing rows or columns
  145. //    srcRowBytes    rowBytes of the source image
  146. //    dstRowBytes    rowBytes of the dst image
  147. //    src            pointer to beginning of current dest scanline
  148.  
  149. void skew(unsigned char * src, int len, int nlen, double start, RGBColor bkGndColor, Boolean row, short srcRowBytes, short dstRowBytes, unsigned char * dst)
  150. {
  151.     int i;
  152.     double f, g, w1, w2;                // pixel weighting factors for interpolation (linear)
  153.     char indexBkGndColor = (char)(LoWord(Color2Index(&bkGndColor)));    // get the background color index - this is what we stuff in memory
  154.     RGBColor temp0Color, temp1Color;
  155.     long longIndex;
  156.                 
  157.     // process left end of output; either prepare for clipping or add padding
  158.     const int istart = (int) start;        // integer index
  159.     if(istart < 0)
  160.     {
  161.         if(row)
  162.         {
  163.             src -= istart;                // advance input pointer for clipping
  164.         }
  165.         else
  166.         {                                // doing a column so we must advance by rowBytes
  167.             src -= (srcRowBytes * istart);// advance input pointer for clipping
  168.         }
  169.     }
  170.     
  171.     int lim = min(len + istart, nlen);    // find index for right edge (valid range)
  172.     
  173.     for(i = 0; i < istart; i++)            // visit all null output pixels at left edge
  174.     {
  175.         *dst = indexBkGndColor;            // pad with bkground color
  176.         if(row)
  177.         {
  178.             dst += 1;                    // advance output pointer
  179.         }
  180.         else                            // doing a column so we must advance by rowBytes
  181.         {
  182.             dst += dstRowBytes;            // advance output pointer
  183.         }
  184.     }
  185.     
  186.     f = (((start - istart) < 0) ? (-(start - istart)) : (start - istart));    // weight for right straddle (abs val)
  187.     g = 1.0 - f;                        // weight for left straddle
  188.     
  189.     if(f == 0.0)                        // simple integer shift; no interpolation
  190.     {
  191.         for(;i < lim; i++)                // visit all pixels in valid range
  192.         {
  193.             *dst = *src;                // copy input to output
  194.             if(row)
  195.             {
  196.                 src += 1;                // advance input pointer
  197.                 dst += 1;                // advance output pointer
  198.             }
  199.             else                        // doing a column so we must advance by rowBytes
  200.             {
  201.                 src += srcRowBytes;        // advance input pointer by rowBytes since this is a column
  202.                 dst += dstRowBytes;        // advance output pointer by rowBytes since this is a column
  203.             }
  204.         }
  205.     }
  206.     else                                // fractional shift: interpolate
  207.     {
  208.         if(start > 0.0)
  209.         {
  210.             w1 = f;                        // weight for left pixel
  211.             w2 = g;                        // weight for right pixel
  212.             *dst = src[0];                // first pixel -- so stuff the value to avoid garbage with out of bounds interpolation
  213.  
  214.             if(row)
  215.             {
  216.                 dst += 1;                // advance output pointer
  217.             }
  218.             else                        // doing a column so we must advance by rowBytes
  219.             {
  220.                 dst += dstRowBytes;        // advance output pointer
  221.             }
  222.             i++;                        // increment index
  223.         }
  224.         else
  225.         {
  226.             w1 = g;                        // weight for left pixel
  227.             w2 = f;                        // weight for right pixel
  228.             if(lim < nlen) lim--;
  229.         }
  230.         
  231.         for(;i < lim; i++)                // visit all pixels in valid range
  232.         {
  233.             if(row)
  234.             {
  235.                 // src[0] is left (top) pixel, and src[offset] is right (bottom) pixel                
  236.                 Index2Color((long)src[0], &temp0Color);    // convert the 8 bit color to an RGB color
  237.                 Index2Color((long)src[1], &temp1Color);    // ditto
  238.                 longIndex = Color2Index(&(RGBColorsPLUS(RGBColorsPERCENT(temp0Color, w1), RGBColorsPERCENT(temp1Color, w2))));
  239.                 *dst = LoWord(longIndex);    // grab the loword of the index -- we could just cast it but this is safer and it avoids a compiler warning
  240.  
  241.                 dst += 1;                // advance output pointer
  242.                 src += 1;                // advance input pointer
  243.             }
  244.             else                        // doing a column so we must advance by rowBytes
  245.             {
  246.                 Index2Color((long)src[0], &temp0Color);                // convert the 8 bit color to an RGB color
  247.                 Index2Color((long)src[srcRowBytes], &temp1Color);    // ditto - (use srcRowBytes since it is a column)
  248.                 longIndex = Color2Index(&(RGBColorsPLUS(RGBColorsPERCENT(temp0Color, w1), RGBColorsPERCENT(temp1Color, w2))));
  249.                 *dst = LoWord(longIndex);    // grab the loword of the index -- we could just cast it but this is safer and it avoids a compiler warning
  250.  
  251.                 dst += dstRowBytes;        // advance output pointer by rowBytes since this is a column
  252.                 src += srcRowBytes;        // advance input pointer by rowBytes since this is a column
  253.             }
  254.         }
  255.         
  256.         if(i < nlen)
  257.         {
  258.             *dst = indexBkGndColor;        // set it to background
  259.             if(row)
  260.             {
  261.                 dst += 1;                // advance output pointer
  262.             }
  263.             else                        // doing a column so we must advance by rowBytes
  264.             {
  265.                 dst += dstRowBytes;        // advance output pointer
  266.             }
  267.             i++;                        // increment output index
  268.         }
  269.     }
  270.     for(;i < nlen; i++){                // visit all remaining pixels at right edge
  271.         *dst = indexBkGndColor;            // pad with bkground color
  272.         if(row)
  273.         {
  274.             dst += 1;                    // advance output pointer
  275.         }
  276.         else                            // doing a column so we must advance by rowBytes
  277.         {
  278.             dst += dstRowBytes;            // advance output pointer
  279.         }
  280.     }
  281. }
  282.  
  283.  
  284. ///////////////////////////////////////////////////////////////////////////////
  285. //    This function returns the addr of the pixel.
  286. //    Note:     If pixelSize < 8 then you must compute a bit offset into 
  287. //            the byte (or better yet set multiple bits at a time).
  288. //
  289. //    thePixMap        handle to the PixMap
  290. //    thePixelRow     the row we are in (first row is zero)
  291. //    thePixelColumn    the col we are in (first column is zero)
  292. //    theBitOffset    the bit offset if pixelSize (from left edge)
  293. //
  294.  
  295. const short bitsPerByte = 8;
  296.  
  297. Ptr GetPixelAddr(const PixMapHandle thePixMap, const short thePixelRow, const short thePixelColumn, short &bitOffset)
  298. {
  299.     // get general PixMap attributes:
  300.     Ptr thePixMapPtr = GetPixBaseAddr( thePixMap );            // always get the addr via this function
  301.     short theRowBytes = ((**thePixMap).rowBytes & 0x3fff);    // mask off top to flag bits and get rowBytes
  302.     Rect theBoundRect = (**thePixMap).bounds;                // get actual bounds
  303.  
  304.     // now we can calculate the actual addr of the pixel
  305.     Ptr    pixAddr = (Ptr)(thePixMapPtr +                                         // start at the baseAddr
  306.                         (thePixelRow - theBoundRect.top) * theRowBytes +     // account for the number of rows
  307.                         ((thePixelColumn - theBoundRect.left) * (**thePixMap).pixelSize) / bitsPerByte); // and the amount that we are from left edge
  308.  
  309.     // compute the bit offset if we are less than 8 bits per pixel:
  310.     if( (**thePixMap).pixelSize < twoHunderedFiftySixOfColorsDepth )
  311.     {
  312.         long numBits = ((thePixelColumn - theBoundRect.left) * bitsPerByte);
  313.         bitOffset = numBits % bitsPerByte;
  314.     }
  315.     
  316.     return pixAddr;
  317. }
  318.  
  319. ///////////////////////////////////////////////////////////////////////////////
  320. //    This function returns the rowBytes of a PixMap.
  321. //    Note:     The top 2 bits are flag bits -- these are stripped off
  322. //            before returning the actual rowBytes.
  323. //
  324. //    thePixMap        handle to the PixMap
  325. //
  326.  
  327. short GetRowBytes(const PixMapHandle thePixMap)
  328.     return ((**thePixMap).rowBytes & 0x3fff); 
  329. }
  330.  
  331. ///////////////////////////////////////////////////////////////////////////////
  332. //    This function fills the GWorld with the a specified color
  333. //    Note:     a side effect of this routine is that
  334. //            the bkackground color is set to the color passed in.
  335. //            All future calls to Erase will erase to this color unless
  336. //            it is changed
  337. //
  338. //    theGWorld        ptr to the GWorld
  339. //    theColor        the color that we want to erase to
  340. //
  341.  
  342. void EraseGWorld(const GWorldPtr theGWorld, const RGBColor theColor)
  343.     // Store the current port and device before switching to the offscreen world.
  344.     CGrafPtr    currentPort;
  345.     GDHandle    currentDevice;
  346.     GetGWorld(¤tPort, ¤tDevice);
  347.  
  348.     // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  349.     SetGWorld(theGWorld, nil);
  350.  
  351.     // Set the background color before we erase
  352.     RGBBackColor(&theColor);
  353.  
  354.     // Get the PixMap
  355.     PixMapHandle thePixMap = GetGWorldPixMap(theGWorld);
  356.     const Rect theBounds = (**thePixMap).bounds;
  357.  
  358.     // Erase the PixMap's bounding box in the GWorld.
  359.     EraseRect(&theBounds);
  360.  
  361.     // Set the port and device back to the original        
  362.     SetGWorld(currentPort, currentDevice);
  363. }
  364.  
  365.  
  366. // *****  end-of-file  ****************************************************************************
  367.  
  368.  
  369.  
  370.  
  371.  
  372.  
  373.