home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / Vertigo / RenderEngine / DrawingFunctions.cp next >
Encoding:
Text File  |  2000-06-24  |  18.6 KB  |  711 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //    File:        DrawingFunctions.cp
  4. //
  5. //    Project:    MacHack 2000 - Vertigo!
  6. //    Authors:    Darrin Cardani, Drew Thaler, Ed Wynne
  7. //
  8. //    Date:        06/23/2000 (written entirely during the conference!)
  9. //
  10. ///////////////////////////////////////////////////////////////////////////////
  11.  
  12. #define DISABLE_LOCAL_CALLTRACE            0
  13. #define DISABLE_LOCAL_DEBUG                0
  14. #include "DebugUtils.h"
  15.  
  16. #include "DrawingFunctions.h"
  17. #include "DrawingUtils.h"
  18.  
  19. #include <Quickdraw.h>
  20. #include <QDOffscreen.h>
  21.  
  22.  
  23. // A simple enum that defines the maximum "fuzz" space around a region.
  24. enum { kRegionBorder = 24 };
  25.  
  26. #define    SWAPSHORTS(x,y)                do { register SInt16 swap = x; x = y; y = swap; } while(false)
  27. #define FINDNEXTSCANLINE()                                    \
  28.         do {                                                \
  29.             if (delta < 0)                                    \
  30.             {                                                \
  31.                 parse = scanlineparser - 2;                    \
  32.                                                             \
  33.                 while (*parse != 0x7FFF)                    \
  34.                     parse--;                                \
  35.                                                             \
  36.                 if (*parse == 0x7FFF) parse++;                \
  37.             }                                                \
  38.                                                             \
  39.             if (parse == rgnEnd)    doneParsing = true;        \
  40.                                                             \
  41.             scanlineparser = parse;                            \
  42.         } while (false)
  43.  
  44.  
  45. #define kMaxInversionPoints            4096
  46. SInt16        gInversionStack[kMaxInversionPoints];
  47. UInt16        gInversionStackPtr;
  48.  
  49. typedef struct MyRegion MyRegion;
  50. struct MyRegion {
  51.     unsigned short         rgnSize;                    /*size in bytes*/
  52.     Rect                 rgnBBox;                    /*enclosing rectangle*/
  53.     SInt16                rgnData[1];
  54. };
  55.  
  56.  
  57. void BlurImageHorz (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, const Rect* prInArea);
  58. void BlurImageVert (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, SInt32 iInOffset, const Rect* prInArea);
  59. void OffsetImage (GWorldPtr gwpInImage, SInt32 iInOffset, GWorldPtr gwpOutImage);
  60.  
  61.  
  62.  
  63. // ------------------------------------------------------------
  64. //    DrawDropShadows
  65. // ------------------------------------------------------------
  66. // Renders window drop shadows onscreen, into a scratch GWorld.  
  67.  
  68. void RenderDropShadows(const GWorldPtr screenGWorld,
  69.                         GWorldPtr dropShadowWorld, 
  70.                         RgnHandle maskRgn,
  71.                         RegionList &windowRegions)
  72. {
  73.     // How much are the shadows offset?
  74.     enum { kShadowOffset = 16 };
  75.     
  76.     // Do we want to cheat for rectangles?  We get faster, but the output's not as nice...
  77.     enum { cheatOnRectangles = false };
  78.     
  79.     // Fuzziness value at which we judge a region to be approximately rectangular
  80.     //    (a common case which we optimize).  Set to 0 if you want exact drop shadows.
  81.     enum { kApproximatelyARectangleFuzziness = 2 };
  82.     
  83.     // Colors for the shadows.  The larger the values get the darker the shadow will be.
  84.     //    This color table starts from the edge (where the shadow is lightest) and goes
  85.     //    in to the middle (where the shadow is darkest).
  86.     static const RGBColor    shadowColors[kShadowOffset] = {
  87.                                 {0x0800,0x0800,0x0800},
  88.         {0x1000,0x1000,0x1000}, {0x1800,0x1800,0x1800},
  89.         {0x2000,0x2000,0x2000}, {0x2800,0x2800,0x2800},
  90.         {0x3000,0x3000,0x3000}, {0x3800,0x3800,0x3800},
  91.         {0x4000,0x4000,0x4000}, {0x4800,0x4800,0x4800},
  92.         {0x5000,0x5000,0x5000}, {0x5800,0x5800,0x5800},
  93.         {0x6000,0x6000,0x6000}, {0x6800,0x6800,0x6800},
  94.         {0x7000,0x7000,0x7000}, {0x7800,0x7800,0x7800},
  95.         {0x8000,0x8000,0x8000}};
  96.     
  97.     // First thing to do: copy the entire screen over to the drop shadow world.
  98.     //    (Clipped to the mask that we care about, of course).
  99.     CopyGWorldToGWorld(screenGWorld,dropShadowWorld,maskRgn);
  100.     
  101.     // Next, iterate through the window regions (which we keep in back-to-front
  102.     //    order) and draw their drop shadows into the shadow world.
  103.     StSetGWorld    setPort(dropShadowWorld);
  104.     StRegion    shadowRgn, tempRgn, clipRgn, oldClip;
  105.     Rect        boundsRect, tempRect;
  106.     for (int i=0; i<windowRegions.GetCount(); ++i)
  107.     {
  108.         // Calculate the shadow region for the window, which is
  109.         //    simply the region offset by a certain amount.
  110.         shadowRgn = windowRegions[i];
  111.         OffsetRgn(shadowRgn,kShadowOffset,kShadowOffset);
  112.         
  113.         // Calculate the clipping region for this window's shadow by
  114.         //    subtracting out all the windows including and above the current one.
  115.         clipRgn = shadowRgn;
  116.         if (maskRgn != NULL) clipRgn &= maskRgn;
  117.         for (int j=i; j<windowRegions.GetCount(); ++j)
  118.             clipRgn -= windowRegions[j];
  119.         if (clipRgn.IsEmpty())
  120.             continue;
  121.         
  122.         StClipRgnState    setClip(clipRgn);
  123.         
  124.         // First of all - is the window close to rectangular?  We can
  125.         //    totally cheat if it is.
  126.         boundsRect = tempRect = windowRegions[i][0]->rgnBBox;
  127.         tempRect.top += kApproximatelyARectangleFuzziness; tempRect.left += kApproximatelyARectangleFuzziness;
  128.         tempRect.right -= kApproximatelyARectangleFuzziness; tempRect.bottom -= kApproximatelyARectangleFuzziness;
  129.         tempRgn = tempRect;
  130.         tempRgn &= windowRegions[i];
  131.         if ((cheatOnRectangles) && (tempRgn == tempRect))
  132.         {
  133.             // Yes, it's rectangular (the common case which we
  134.             //    really want to optimize).  This could
  135.             //    be way more optimal (that's left as an exercise to
  136.             //    the reader) ... but it's at least better than
  137.             //    a bunch of FrameRgns.
  138.             register short    bottomy = boundsRect.bottom + kShadowOffset - 1;
  139.             register short    topy = boundsRect.top + kShadowOffset;
  140.             register short    leftx = boundsRect.left + kShadowOffset;
  141.             register short    rightx = boundsRect.right + kShadowOffset - 1;
  142.             
  143.             PenMode(subPin);
  144.             for (int i=0; i<kShadowOffset; ++i)
  145.             {
  146.                 RGBForeColor(&shadowColors[i]);
  147.                 MoveTo(leftx,boundsRect.bottom);
  148.                 LineTo(leftx,bottomy);
  149.                 Move(1,0);
  150.                 LineTo(rightx,bottomy);
  151.                 Move(0,-1);
  152.                 LineTo(rightx,topy);
  153.                 Move(-1,0);
  154.                 LineTo(boundsRect.right,topy);
  155.                 
  156.                 leftx += 1;
  157.                 topy += 1;
  158.                 bottomy -= 1;
  159.                 rightx -= 1;
  160.             }
  161.             PenMode(normal);
  162.         }
  163.         else
  164.         {
  165.             // Calculate a nonstandard drop shadow region.  We offset the
  166.             //    window by 8 pixels down and right, then need to clip
  167.             //    out all window regions above and including the current one.
  168.             shadowRgn = windowRegions[i];
  169.             OffsetRgn(shadowRgn,kShadowOffset,kShadowOffset);
  170.             
  171.             // Round off the edges.  This makes rectangular window shadows
  172.             //    softer and more realistic.
  173.             OpenRgn();
  174.             FrameRoundRect(&shadowRgn.Bounds(),kShadowOffset,kShadowOffset);
  175.             CloseRgn(tempRgn);
  176.             shadowRgn &= tempRgn;
  177.             
  178.             // Okay, we've got the drop shadow region.  We're already 
  179.             //    clipped, so just draw it.
  180.             PenMode(subPin);
  181.             for (int i=0; i<kShadowOffset; ++i)
  182.             {
  183.                 RGBForeColor(&shadowColors[i]);
  184.                 FrameRgn(shadowRgn);
  185.                 InsetRgn(shadowRgn,1,1);
  186.             }
  187.             PenMode(normal);
  188.         }
  189.     }
  190. }
  191.  
  192.  
  193.  
  194. // ------------------------------------------------------------
  195. //    CopyRegionForRedShifting
  196. // ------------------------------------------------------------
  197.  
  198. void CopyRegionForRedShifting(const GWorldPtr dropShadowWorld,
  199.                                 GWorldPtr preShiftWorld,
  200.                                 RgnHandle rgn)
  201. {
  202.     // Create a region that's a little bigger than the source.
  203.     StRegion    bigger(rgn);
  204.     InsetRgn(bigger,-kRegionBorder,-kRegionBorder);
  205.     
  206.     // Subtract out the original to get a border area.
  207.     StRegion    border(bigger);
  208.     border -= rgn;
  209.     
  210.     // Erase the border to the shadow color.
  211.     StSetGWorld    setPort(preShiftWorld);
  212.     RGBColor    shadowColor = {0x8000,0x8000,0x8000};
  213.     RGBBackColor(&shadowColor);
  214.     EraseRgn(border);
  215.     
  216.     // Grab the left edge of the border - we're going to make that white
  217.     //    because otherwise it looks like the shadow extends to the left.
  218.     StRegion    leftBorder(border);
  219.     OffsetRgn(leftBorder,kRegionBorder,0);
  220.     leftBorder -= border;
  221.     BackColor(whiteColor);
  222.     EraseRgn(leftBorder);
  223.     
  224.     // Now copy in the source data.
  225.     CopyGWorldToGWorld(dropShadowWorld,preShiftWorld,rgn);
  226. }
  227.  
  228.  
  229.  
  230. // ------------------------------------------------------------
  231. //    RedShiftAndBlur
  232. // ------------------------------------------------------------
  233.  
  234. void RedShiftAndBlur(const GWorldPtr preShiftWorld,
  235.                         GWorldPtr scratchWorld,
  236.                         GWorldPtr postShiftWorld,
  237.                         RgnHandle blitRgn,
  238.                         int depth)
  239. {
  240.     dprintf("RedShiftAndBlur - depth = %d\n", depth);
  241. /*
  242.     {
  243.         StRegion    bigger(blitRgn);
  244.         InsetRgn(bigger,-depth,-1);
  245.         bigger &= postShiftWorld->portRect;
  246.         CopyRgn(bigger,blitRgn);
  247.         
  248.         StSetGWorld    setPort(postShiftWorld);
  249.         CopyGWorldToGWorld(preShiftWorld,postShiftWorld,blitRgn);
  250.         
  251.         ForeColor(redColor);
  252.         FrameRgn(blitRgn);
  253.         return;
  254.     }
  255. */    
  256. /*
  257.     // Erase the scratch world (plus a bit) to the shadow color.
  258.     {
  259.         StSetGWorld    setPort(scratchWorld);
  260.         StRegion    bigger(blitRgn);
  261.         
  262.         InsetRgn(bigger,-kRegionBorder,-kRegionBorder);
  263.  
  264.         RGBColor    shadowColor = {0x8000,0x8000,0x8000};
  265.         RGBBackColor(&shadowColor);
  266.         EraseRgn(bigger);
  267.     }
  268. */    
  269.     // Now, when copying back into the output, we need to copy a bigger
  270.     //    region than we started with, because the red-shift expands
  271.     //    the image (by the depth amount, in fact).  If we were
  272.     //    really cool and not so rushed, we'd intelligently overlay
  273.     //    this onto the background... for now we just copy over.
  274.     StRegion    bigger(blitRgn);
  275.     InsetRgn(bigger,-depth,-1);
  276.     bigger &= postShiftWorld->portRect;
  277.     CopyRgn(bigger,blitRgn);
  278.     
  279.     // Break the red-shift up into rectangle sized chunks.
  280.     Rect    &srcRect = (**blitRgn).rgnBBox;
  281.     Rect    &dstRect = (**blitRgn).rgnBBox;
  282.     
  283.     SInt16            *rgnData, *rgnEnd;
  284.     SInt16            *scanlineparser, *parse, delta;
  285.     SInt16            y,x1,x2,topScanLine,botScanLine;
  286.     Boolean            doneParsing = false;
  287.     MyRegion*        maskPtr;
  288.     Rect            blitDst;
  289.  
  290.     SInt32        i,j;
  291.     
  292.         // *** hack to fix rectangular regions -- it seems that anything with rgnSize == 0x0A
  293.         //    is either a rect rgn or an empty rgn... the catch is, the actual region data is junk
  294.         //    and you're only supposed to use the bounding box.  Catch that case here.
  295.     
  296.     if ( (**blitRgn).rgnSize == 10 )
  297.     {
  298.         blitDst = (**blitRgn).rgnBBox;
  299.         
  300.             // don't blit if it's an empty region
  301.         if ( blitDst.right > blitDst.left && blitDst.bottom > blitDst.top )
  302.             RedShiftAndBlurRect(preShiftWorld,scratchWorld,postShiftWorld,&blitDst,depth);
  303.         
  304.         return;
  305.     }
  306.     
  307.         // Set up the parsing variables
  308.     
  309.     gInversionStackPtr = 0;
  310.     maskPtr = (MyRegion*) (*blitRgn);    // note -- we're not locking the handle,
  311.                                         // because we never call the toolbox again.
  312.     
  313.     rgnData = (SInt16*) maskPtr->rgnData;
  314.     rgnEnd = (SInt16*) ((UInt32) maskPtr + maskPtr->rgnSize) - 1;
  315.     scanlineparser = parse = rgnData;
  316.     delta = +1;
  317.     
  318.         // Now we just need to parse the region into rect-sized chunks and handle
  319.         //    them individually.
  320.     
  321.     while (!doneParsing)
  322.     {
  323.         
  324.             // Start walking the region data.  scanlineparser points to the
  325.             //    beginning of the current scan line, and parse is used as a walk pointer
  326.             //    to extract data. 
  327.         
  328.         y = *(parse++);
  329.         x1 = *(parse++);
  330.         
  331.         while (x1 != 0x7FFF)
  332.         {
  333.             x2 = *(parse++);
  334.             
  335.                 // Need to insertion-sort x1 and x2 into the inversion list.  We're
  336.                 //    guaranteed that x1 < x2, which helps a bit, but it gets more complex
  337.                 //    because if we find a point that's EQUAL to x1 or x2, the two cancel
  338.                 //    each other out and both have to be removed.  There's probably a way
  339.                 //    to do this with a single loop, but for now we'll just loop twice.
  340.             
  341.                 // step 1 -- insertion sort
  342.             
  343.             for (i=0; i<gInversionStackPtr; ++i)
  344.             {
  345.                     // bubble the values up so x1 and x2 always contain the largest values so far
  346.                 
  347.                 if (gInversionStack[i] > x1)
  348.                     SWAPSHORTS( x1, gInversionStack[i] );
  349.                 
  350.                 if (x1 > x2)
  351.                     SWAPSHORTS( x1, x2 );
  352.                 
  353.             }
  354.             gInversionStack[gInversionStackPtr++] = x1;
  355.             gInversionStack[gInversionStackPtr++] = x2;
  356.             
  357.                 // step 2 -- remove redundancies
  358.             
  359.             for (i=0; i<gInversionStackPtr-1; ++i)
  360.             {
  361.                 if ( gInversionStack[i] == gInversionStack[i+1] )
  362.                 {
  363.                     for (j=i+2;j<gInversionStackPtr;++j)
  364.                         gInversionStack[j-2] = gInversionStack[j];
  365.                     
  366.                     gInversionStackPtr -= 2;
  367.                     i--;
  368.                 }
  369.             }
  370.             
  371.             x1 = *(parse++);
  372.         }
  373.         
  374.         // Now we've got our inversion list for the current scan line,
  375.         //    describing what needs to be blitted.  Get the next scan line's Y coord
  376.         //    and build a bunch of rects for blitting.
  377.         
  378.         topScanLine = y;
  379.         FINDNEXTSCANLINE();
  380.         botScanLine = (*parse);
  381.  
  382.             // skip blit step if we have nothing to blit
  383.         
  384.         if (gInversionStackPtr == 0)
  385.             continue;
  386.         
  387.         if (botScanLine == 0x7FFF)    // we *shouldn't* ever have this happen, because
  388.             break;                    //    gInversionStackPtr should be 0 and we should break
  389.                                     //    above with doneParsing = true, but just in case...
  390.         
  391.         if (topScanLine > botScanLine)
  392.             SWAPSHORTS( topScanLine, botScanLine );
  393.         
  394.         blitDst.top = topScanLine;
  395.         blitDst.bottom = botScanLine;
  396.             
  397.         // blit rects left to right
  398.         for ( i=0; i<gInversionStackPtr-1; i+=2 )    
  399.         {                                            
  400.             blitDst.left = gInversionStack[i];
  401.             blitDst.right = gInversionStack[i+1];
  402.             
  403.             RedShiftAndBlurRect(preShiftWorld,scratchWorld,postShiftWorld,&blitDst,depth);
  404.         }
  405.     }    
  406. }
  407.  
  408.  
  409. // ------------------------------------------------------------
  410. //    RedShiftAndBlurRect
  411. // ------------------------------------------------------------
  412.  
  413. void RedShiftAndBlurRect(const GWorldPtr preShiftWorld,
  414.                         GWorldPtr scratchWorld,
  415.                         GWorldPtr postShiftWorld,
  416.                         const Rect *inRect,
  417.                         int depth)
  418. {
  419.     BlurImageHorz(preShiftWorld,scratchWorld,inRect);
  420.     BlurImageVert(scratchWorld,postShiftWorld,depth,inRect);
  421. }
  422.  
  423.  
  424. // ------------------------------------------------------------
  425. //    CopyIntoComposite
  426. // ------------------------------------------------------------
  427.  
  428. void CopyIntoComposite(const GWorldPtr srcWorld,
  429.                         GWorldPtr compositeWorld,
  430.                         RgnHandle rgn)
  431. {
  432.     CopyGWorldToGWorld(srcWorld,compositeWorld,rgn);
  433. }
  434.  
  435.  
  436.  
  437. #pragma mark -
  438.  
  439.  
  440.  
  441. /*
  442.     Function:    BlurImageHorz
  443.     
  444.     Inputs:        gwpInImage - the image to be blurred
  445.     
  446.     Outputs:    gwpOutImage - the blurred image
  447.     
  448.     Purpose:    Performs a horizontal blur on the image to simulate
  449.                 a cheap depth of field. You should call BlurImageVert after calling this
  450.     
  451. */
  452.  
  453. void BlurImageHorz (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, const Rect* prInArea)
  454. {
  455.     PixMapHandle    pmhInImage = GetGWorldPixMap (gwpInImage);
  456.     UInt8            *pSrc = NULL, *pSrc2 = NULL, *pSrc3 = NULL;
  457.     SInt32            iSrcRowBytes = 0;
  458.     SInt32            iSrcBump = 0;
  459.     
  460.     PixMapHandle    pmhOutImage = GetGWorldPixMap (gwpOutImage);
  461.     UInt8*            pDst = NULL;
  462.     SInt32            iDstRowBytes = 0;
  463.     SInt32            iDstBump = 0;
  464.     
  465.     SInt32            height = 0, width = 0;
  466.     SInt32            iWidth = 0;
  467.     
  468.     iWidth = prInArea->right - prInArea->left;
  469.     height = prInArea->bottom - prInArea->top;
  470.     
  471.     //LockPixels (pmhInImage);
  472.     pSrc            = (UInt8*)GetPixBaseAddr (pmhInImage);
  473.     iSrcRowBytes    = GetPixRowBytes (pmhInImage);
  474.     iSrcBump        = iSrcRowBytes - (iWidth * 4);
  475.     pSrc            = pSrc + (prInArea->top * iSrcRowBytes) + (prInArea->left * 4);
  476.  
  477.     //LockPixels (pmhOutImage);
  478.     pDst            = (UInt8*)GetPixBaseAddr (pmhOutImage);
  479.     iDstRowBytes    = GetPixRowBytes (pmhOutImage);
  480.     iDstBump        = iDstRowBytes - (iWidth * 4);
  481.     pDst            = pDst + (prInArea->top * iDstRowBytes) + (prInArea->left * 4);
  482.     
  483.     while (height-- > 0) {
  484.     
  485.         width = iWidth - 2;
  486.         
  487.         pSrc++;
  488.         pDst++;
  489.         *pDst++ = *pSrc++;
  490.         *pDst++ = *pSrc++;
  491.         *pDst++ = *pSrc++;
  492.         pSrc2 = pSrc + 4;
  493.         pSrc3 = pSrc2 + 4;
  494.  
  495.         // Skip Alpha Channels
  496.         pSrc++;
  497.         pSrc2++;
  498.         pSrc3++;
  499.         pDst++;
  500.         
  501.         while (width-- > 0) {
  502.             
  503.             // Handle red channel
  504.             *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
  505.             pSrc++;
  506.             pSrc2++;
  507.             pSrc3++;
  508.             pDst++;
  509.             
  510.             // Handle green channel
  511.             *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
  512.             pSrc++;
  513.             pSrc2++;
  514.             pSrc3++;
  515.             pDst++;
  516.             
  517.             // Handle blue channel
  518.             *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
  519.             pSrc+=2;
  520.             pSrc2+=2;
  521.             pSrc3+=2;
  522.             pDst+=2;
  523.         }
  524.         
  525.         *pDst++ = *pSrc++;
  526.         *pDst++ = *pSrc++;
  527.         *pDst++ = *pSrc++;
  528.  
  529.         pSrc += iSrcBump;
  530.         pDst += iDstBump;
  531.     }
  532. }
  533.  
  534.  
  535. /*
  536.     Function:    BlurImageVert
  537.     
  538.     Inputs:        gwpInImage - the image to be blurred
  539.     
  540.     Outputs:    gwpOutImage - the blurred image
  541.     
  542.     Purpose:    Performs a vertical blur on the image to simulate
  543.                 a cheap depth of field. You should call BlurImageHorz before calling this
  544.     
  545. */
  546.  
  547. void BlurImageVert (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, SInt32 iInOffset, const Rect* prInArea)
  548. {
  549.     PixMapHandle    pmhInImage = GetGWorldPixMap (gwpInImage);
  550.     UInt8            *pSrc = NULL, *pSrc2 = NULL, *pSrc3 = NULL;
  551.     SInt32            iSrcRowBytes = 0;
  552.     SInt32            iSrcBump = 0;
  553.     
  554.     PixMapHandle    pmhOutImage = GetGWorldPixMap (gwpOutImage);
  555.     UInt8*            pDst = NULL;
  556.     SInt32            iDstRowBytes = 0;
  557.     SInt32            iDstBump = 0;
  558.     
  559.     SInt32            height = 0, width = 0;
  560.     SInt32            iWidth;
  561.     
  562.     UInt32            *pSrc32 = NULL, *pDst32 = NULL;
  563.     SInt32            iOffset = iInOffset * 4;
  564.     SInt32            absiInOffset = (iInOffset < 0) ? -iInOffset:iInOffset;
  565.     SInt32            iTemp = 0;
  566.     
  567.     iWidth = prInArea->right - prInArea->left;
  568.     height = prInArea->bottom - prInArea->top - 2;
  569.     
  570.     //LockPixels (pmhInImage);
  571.     pSrc            = (UInt8*)GetPixBaseAddr (pmhInImage);
  572.     iSrcRowBytes    = GetPixRowBytes (pmhInImage);
  573.     iSrcBump        = iSrcRowBytes - (iWidth * 4) - 1;
  574.     pSrc            = pSrc + (prInArea->top * iSrcRowBytes) + (prInArea->left * 4);
  575.     pSrc2            = pSrc + iSrcRowBytes;
  576.     pSrc3            = pSrc2 + iSrcRowBytes;
  577.  
  578.     //LockPixels (pmhOutImage);
  579.     pDst            = (UInt8*)GetPixBaseAddr (pmhOutImage);
  580.     iDstRowBytes    = GetPixRowBytes (pmhOutImage);
  581.     iDstBump        = iDstRowBytes - (iWidth * 4) - 1;
  582.     pDst            = pDst + (prInArea->top * iDstRowBytes) + (prInArea->left * 4);
  583.     
  584.     // Handle the first row
  585.     width = iWidth;
  586.     while (width-- > 0) {
  587.         *pSrc++;
  588.         *pDst++;
  589.         *pDst = *(pSrc + iOffset);
  590.         pSrc++;
  591.         pDst++;
  592.         
  593.         *pDst = *(pSrc - iOffset);
  594.         pSrc++;
  595.         pDst++;
  596.         
  597.         *pDst = *(pSrc - iOffset);
  598.         pSrc++;
  599.         pDst++;
  600.     }
  601.     pDst += iDstBump + 1;
  602.     pSrc += iSrcBump + 1;
  603.     pSrc2 += iSrcRowBytes;
  604.     pSrc3 += iSrcRowBytes;
  605.     
  606.     // Do the rest of the image (except the last row)
  607.     while (height-- > 0) {
  608.         
  609.         width = iWidth + 1;
  610.         
  611.         // Skip Alpha channels
  612.         pSrc++;
  613.         pSrc2++;
  614.         pSrc3++;
  615.         pDst++;
  616.         
  617.         iTemp = absiInOffset;
  618.         while (iTemp-- > 0) {
  619.             width--;
  620.             // Handle red channel
  621.             *pDst = (*(pSrc + iOffset) + (*(pSrc2 + iOffset) * 2) + *(pSrc3 + iOffset)) >> 2;
  622.             pSrc    += 4;
  623.             pSrc2    += 4;
  624.             pSrc3    += 4;
  625.             pDst++;
  626.             
  627.             // Handle green channel
  628.             *pDst    = 0;
  629.             pDst++;
  630.  
  631.             // Handle blue channel
  632.             *pDst    = 0;
  633.  
  634.             pDst    += 2;
  635.         }
  636.  
  637.         while (width-- > absiInOffset) {
  638.             
  639.             // Handle red channel
  640.             *pDst = (*(pSrc + iOffset) + (*(pSrc2 + iOffset) * 2) + *(pSrc3 + iOffset)) >> 2;
  641.             pSrc++;
  642.             pSrc2++;
  643.             pSrc3++;
  644.             pDst++;
  645.             
  646.             // Handle green channel
  647.             *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
  648.             pSrc++;
  649.             pSrc2++;
  650.             pSrc3++;
  651.             pDst++;
  652.             
  653.             // Handle blue channel
  654.             *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
  655.             pSrc+=2;
  656.             pSrc2+=2;
  657.             pSrc3+=2;
  658.             pDst+=2;
  659.         }
  660.         
  661.         while (width-- > 0) {
  662.             // Handle red channel
  663.             *pDst = 0;
  664.             pSrc++;
  665.             pSrc2++;
  666.             pSrc3++;
  667.             pDst++;
  668.             
  669.             // Handle green channel
  670.             *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
  671.             pSrc++;
  672.             pSrc2++;
  673.             pSrc3++;
  674.             pDst++;
  675.             
  676.             // Handle blue channel
  677.             *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
  678.             pSrc+=2;
  679.             pSrc2+=2;
  680.             pSrc3+=2;
  681.             pDst+=2;
  682.         }
  683.  
  684.         pSrc += iSrcBump;
  685.         pSrc2 += iSrcBump;
  686.         pSrc3 += iSrcBump;
  687.         pDst += iDstBump;
  688.     }
  689.     
  690.     // Handle the last row
  691.     width = iWidth;
  692.     while (width-- > 0) {
  693.         *pSrc++;
  694.         *pDst++;
  695.         *pDst = *(pSrc + iOffset);
  696.         pSrc++;
  697.         pDst++;
  698.         
  699.         *pDst = *(pSrc - iOffset);
  700.         pSrc++;
  701.         pDst++;
  702.         
  703.         *pDst = *(pSrc - iOffset);
  704.         pSrc++;
  705.         pDst++;
  706.     }
  707. }
  708.  
  709.  
  710.  
  711.