Source/BlendUtils.c

/*--------------------------------------------------------------------------------------
//
//  File:       BlendProcs.c
//
//  Contents:   Procedures that render blended fills.
//
//
//  By Georgiann ("George") Delaney
//  ©Ê1989 - 1990, Apple Computer, Inc.
//
//--------------------------------------------------------------------------------------*/
 
 
 
 
#pragma segment BlendSeg
 
 
#include "MacHeaders.h"
 
 
 
/*--------------------------------------------------------------------------------------*/
/*  Constants   */
 
#define kGrayPatternRsrc    50
#define kNumGrayPatterns    50
 
 
 
/*--------------------------------------------------------------------------------------*/
/*  Macro definitions  */
 
#define  RECT_BLEND_BAND_COUNT(span,bandWidth)          (((span-bandWidth)/2)/bandWidth)
#define  RECT_BLEND_BAND_INC(span,bandCount,bandWidth)  (((span-bandWidth)/2)/bandCount)
#define  RECT_BLEND_STEP(span,bandWidth)                (65535/(((span/2)-(bandWidth/2))/bandWidth))
 
#define  LINEAR_BLEND_BAND_COUNT(span,bandWidth)        (span/bandWidth)
#define  LINEAR_BLEND_STEP(span,bandWidth)              (65535/(span/bandWidth))
 
 
#define  MIN(x,y)       (((x) < (y)) ? (x) : (y))
#define  MAX(x,y)       (((x) > (y)) ? (x) : (y))
 
 
 
/*--------------------------------------------------------------------------------------*/
void  HLSRectBlend(Rect *boundRect, short saturation)
/*  
//  This procedure fills the specified rect with a concentric rectangular HLS blend 
//  at the specified saturation.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    HSVColor    theHSV;
    short       blendStep;
    short       i;
    short       span;
    Rect        tempRect;
    
 
    tempRect  = *boundRect;
    
                /*  calculate the diameter of the specified rect  */
    span      = MIN((tempRect.right-tempRect.left)/2, (tempRect.bottom - tempRect.top)/2);
 
                /*  calculate the color increment for each band  */
    blendStep = 65536 / span;   
 
                /*  save off the current RGB  */
    GetForeColor(&holdRGB);
    
                /*  set the initial color  */
    theHSV.value        = 65535;
    theHSV.saturation   = saturation;
    theHSV.hue          =  0;
    
                /*  Generate the blend by converting the hls (hsv in mac terms) to 
                    its RGB equivalent.  Set the forecolor to this RGB value.  Paint
                    the rect.  Then increment the HLS value and band rect for the next
                    iteration   
                */
    for (i=0; i<span; i++)  {
        HSV2RGB(&theHSV,&theRGB);
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theHSV.hue += blendStep;
        tempRect.top    +=1;
        tempRect.left   +=1;
        tempRect.right  -=1;
        tempRect.bottom -=1;
        }
        
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  HLSVLinearBlend(Rect *boundRect, short saturation)
/*  
//  This procedure fills the specified rect with a vertical HLS blend 
//  at the specified saturation.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    HSVColor    theHSV;
    short       blendStep;
    short       height;
    Rect        tempRect;
    short       i;
    
 
    tempRect = *boundRect;
            
                /*  calculate the vertical distance the blend is to cover  */
    height   = tempRect.bottom - tempRect.top;
    
                /*  calculate the color increment for each band  */
    blendStep = 65535 / height; 
            
                /*  save off the current RGB  */
    GetForeColor(&holdRGB);
 
                /*  set the initial color  */
    theHSV.value        = 65535;
    theHSV.saturation   = saturation;
    theHSV.hue          =  0;
    
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.bottom = tempRect.top + 1;
    
                /*  Generate the blend by converting the hls (hsv in mac terms) to 
                    its RGB equivalent.  Set the forecolor to this RGB value.  Paint
                    the rect.  Then increment the HLS value and offset the band rect for 
                    the next iteration
                */
    for (i=0; i<height; i++)  {
        HSV2RGB(&theHSV,&theRGB);
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theHSV.hue += blendStep;
        tempRect.top    +=1;
        tempRect.bottom +=1;
        }
 
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  HLSHLinearBlend(Rect *boundRect, short saturation)
/*  
//  This procedure fills the specified rect with a horizontal HLS blend 
//  at the specified saturation.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    HSVColor    theHSV;
    short       blendStep;
    short       width;
    Rect        tempRect;
    short       i;
    
 
    tempRect = *boundRect;
    
                /*  calculate the horizontal distance the blend is to cover  */
    width    = tempRect.right-tempRect.left;
    
                /*  calculate the color increment for each band  */
    blendStep = 65535 / width;  
            
                /*  save off the current RGB  */
    GetForeColor(&holdRGB);
 
                /*  set the initial color  */
    theHSV.value        = 65535;
    theHSV.saturation   = saturation;
    theHSV.hue          =  0;
    
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.right = tempRect.left + 1;
    
                /*  Generate the blend by converting the hls (hsv in mac terms) to 
                    its RGB equivalent.  Set the forecolor to this RGB value.  Paint
                    the rect.  Then increment the HLS value and offset the band rect for 
                    the next iteration
                */
    for (i=0; i<width; i++)  {
        HSV2RGB(&theHSV,&theRGB);
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theHSV.hue     += blendStep;
        tempRect.left  +=1;
        tempRect.right +=1;
        }
 
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  GrayRectBlend(Rect *boundRect)
/*  
//  This procedure fills the specified rect with a concentric rectangular gray scale blend 
//  from black to white.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    short       blendStep;
    short       i;
    short       span;
    Rect        tempRect;
    
 
    tempRect     = *boundRect;
    
                /*  calculate the diameter of the specified rect  */
    span      = MIN((tempRect.right-tempRect.left)/2, (tempRect.bottom - tempRect.top)/2);
 
                /*  calculate the color increment for each band  */
    blendStep = 65536 / span;   
 
                /*  save off the current RGB color  */
    GetForeColor(&holdRGB);
    
                /*  set the initial color  */
    theRGB.red = theRGB.green = theRGB.blue = 0;
    
                /*  Generate the blend by seting the forecolor to the RGB value.  Paint
                    the rect.  Then increment the RGB value and band rect for the next
                    iteration
                */
    for (i=0; i<span; i++)  {
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theRGB.red = theRGB.green = theRGB.blue = theRGB.red + blendStep;
        tempRect.top    +=1;
        tempRect.left   +=1;
        tempRect.right  -=1;
        tempRect.bottom -=1;
        }
        
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  GrayVLinearBlend(Rect *boundRect)
/*      
//  This procedure fills the specified rect with a vertical gray scale blend 
//  from black to white.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    short       blendStep;
    short       i;
    short       height;
    Rect        tempRect;
    
 
    tempRect  = *boundRect;
 
                /*  calculate the vertical distance the blend is to cover  */
    height    = tempRect.bottom - tempRect.top;
 
                /*  calculate the color increment for each band  */
    blendStep = 65535 / height; 
 
                /*  save off the current RGB color  */
    GetForeColor(&holdRGB);
 
                /*  set the initial color  */
    theRGB.red = theRGB.green = theRGB.blue = 0;
    
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.bottom = tempRect.top + 1;
    
                /*  Generate the blend by setting the forecolor to this RGB value.  Paint
                    the rect.  Then increment the RGB value and offset the band rect for 
                    the next iteration
                */
    for (i=0; i<height; i++)  {
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theRGB.red = theRGB.green = theRGB.blue = theRGB.red + blendStep;
        tempRect.top    +=1;
        tempRect.bottom +=1;
        }
 
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  GrayHLinearBlend(Rect *boundRect)
/*  
//  This procedure fills the specified rect with a horizontal gray scale blend 
//  from black to white.  The band size used for this fill is 1 pixel.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    RGBColor    holdRGB,theRGB;
    short       blendStep;
    short       i;
    short       width;
    Rect        tempRect;
    
 
    tempRect  = *boundRect;
 
                /*  calculate the horizontal distance the blend is to cover  */
    width     = tempRect.right-tempRect.left;
 
                /*  calculate the color increment for each band  */
    blendStep = 65535 / width;  
 
                /*  save off the current RGB color  */
    GetForeColor(&holdRGB);
 
                /*  set the initial color  */
    theRGB.red = theRGB.green = theRGB.blue = 0;
    
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.right = tempRect.left + 1;
    
                /*  Generate the blend by setting the forecolor to this RGB value.  Paint
                    the rect.  Then increment the RGB value and offset the band rect for 
                    the next iteration
                */
    for (i=0; i<width; i++)  {
        RGBForeColor(&theRGB);
        PaintRect(&tempRect);
        theRGB.red = theRGB.green = theRGB.blue = theRGB.red + blendStep;
        tempRect.left  +=1;
        tempRect.right +=1;
        }
 
                /*  restore the original RGB  */
    RGBForeColor(&holdRGB);
}
 
/*--------------------------------------------------------------------------------------*/
void  GrayPatRectBlend(Rect *boundRect)
/*  
//  This procedure fills the specified rect with a concentric rectangular gray scale pattern blend
//  from black to white.  The band size used for this fill is calculated by dividing the shorter 
//  the horizontal and vertical diameters of the specified rect by the number of gray patterns in 
//  the gray pattern resource.  It is assumed that the desired port as well as any desired clipping
//  has already been set.
*/
 
{
    register    i;
    double      bandWidth;
    double      distance;
    Rect        origRect,tempRect;
    Pattern     thePat;
        
 
    origRect = tempRect = *boundRect;
    
                /*  calculate the width of each band  */
    bandWidth = (double)(MIN((tempRect.right-tempRect.left)/2, (tempRect.bottom-tempRect.top)/2)) / (double)(kNumGrayPatterns+1);   
    distance  = bandWidth;
    
                /*  There is a pattern resource consisting of 64 shades of gray in MacLib.rsrc 
                //  that gives a pretty good approximation of a gray ramp from white to black.
                //  The following loop performs the actual imaging of the ramp's 64 gray bands.
                */
    for (i=kNumGrayPatterns; i>=1; i--)  {
        GetIndPattern(&thePat, kGrayPatternRsrc, i);  
        FillRect(&tempRect,thePat);
        
        distance  += bandWidth;
        tempRect.top    = origRect.top    + distance;
        tempRect.left   = origRect.left   + distance;
        tempRect.right  = origRect.right  - distance;
        tempRect.bottom = origRect.bottom - distance;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  GrayPatVLinearBlend(Rect *boundRect)
/*  
//  This procedure fills the specified rect with a vertical gray scale pattern blend
//  from black to white.  The band size used for this fill is calculated by dividing the width
//  of the specified blend by the number of gray patterns in the gray pattern resource.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    register    i;
    double      theTop;
    double      bandHeight;
    double      distance;
    Rect        tempRect;
    Pattern     thePat;
    
 
    tempRect  = *boundRect;
    
                /*  silly, but saves a pointer dereference in the blend loop  */
    theTop    = (double)tempRect.top;       
 
                /*  calculate the width of each band  */
    bandHeight = ((double)(tempRect.bottom-tempRect.top)) / (double)kNumGrayPatterns;   
 
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.bottom = tempRect.top + bandHeight;
    distance        = bandHeight;
    
                /*  There is a pattern resource consisting of 64 shades of gray in MacLib.rsrc 
                //  that gives a pretty good approximation of a gray ramp from white to black.
                //  The following loop performs the actual imaging of the ramp's 64 gray bands.
                */
    for (i=kNumGrayPatterns; i>1; i--)  {
        GetIndPattern(&thePat, kGrayPatternRsrc, i);  
        FillRect(&tempRect,thePat);
 
        distance        += bandHeight;
        tempRect.top     = tempRect.bottom;
        tempRect.bottom  = (short)(theTop + distance);
        }
 
    tempRect.bottom  = boundRect->bottom;
    GetIndPattern(&thePat,kGrayPatternRsrc,1);
    FillRect(&tempRect,thePat);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  GrayPatHLinearBlend(Rect *boundRect)
/*  
//  This procedure fills the specified rect with a horizontal gray scale pattern blend
//  from black to white.  The band size used for this fill is calculated by dividing the width
//  of the specified blend by the number of gray patterns in the gray pattern resource.  It is 
//  assumed that the desired port as well as any desired clipping has already been set.
*/
{
    register    i;
    double      farLeft;
    double      bandWidth;
    double      distance;
    Rect        tempRect;
    Pattern     thePat;
    
 
    tempRect  = *boundRect;
    
                /*  silly, but saves a pointer dereference in the blend loop  */
    farLeft   = (double)tempRect.left;      
 
                /*  calculate the width of each band  */
    bandWidth = ((double)(tempRect.right-tempRect.left)) / (double)kNumGrayPatterns;    
 
                /*  Initialize the rectangle that specifies the first band of the blend   */
    tempRect.right = tempRect.left + bandWidth;
    distance       = bandWidth;
    
                /*  There is a pattern resource consisting of 64 shades of gray in MacLib.rsrc 
                //  that gives a pretty good approximation of a gray ramp from white to black.
                //  The following loop performs the actual imaging of the ramp's 64 gray bands.
                */
    for (i=kNumGrayPatterns; i>1; i--)  {
        GetIndPattern(&thePat, kGrayPatternRsrc, i);  
        FillRect(&tempRect,thePat);
 
        distance       += bandWidth;
        tempRect.left   = tempRect.right;
        tempRect.right  = (short)(farLeft + distance);
        }
 
    tempRect.right  = boundRect->right;
    GetIndPattern(&thePat,kGrayPatternRsrc,1);
    FillRect(&tempRect,thePat);
}