IconDimming.c

/*
    File:       IconDimming.c
 
    Contains:   In System 7, selected non-open files are shown as           
                dimmed icons on the desktop.  This snippet shows            
                two different ways to achieve the same results in           
                an application.                                             
                                                                                    
                The first method uses a custom color search                 
                procedure in place of the current device's to               
                create the dimming effect.  Once the image has              
                been copied to the destination, the custom                  
                search proc is then removed.                                
                                                                                
                In the second method the RGB components of the              
                icon's colortable entries are all dimmed before             
                the image is copied into the destination.  The              
                dimming algorithm used in this method simply            
                darkens each RGB component in half then takes               
                the two smaller components and darkens them                 
                in half again.  In System 7, this is similar                
                to what the Finder does.
 
    Written by: Edgar Lee   
 
    Copyright:  Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/9/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include<MacTypes.h>
#include<Quickdraw.h>
#include<Windows.h>
#include<Dialogs.h>
#include<TextEdit.h>
#include<Fonts.h>
#include<Menus.h>
#include<Events.h>
#include<Resources.h>
 
/* Constant Declarations */
 
#define WWIDTH      480
#define WHEIGHT     180
 
#define WLEFT       (((qd.screenBits.bounds.right - qd.screenBits.bounds.left) - WWIDTH) / 2)
#define WTOP        (((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - WHEIGHT) / 2)
 
typedef struct
{
    PixMap  pixmap;     /* Pixmap used to store the icl8 pixel image. */
    BitMap  mask;       /* Bitmap used to store the 1 bit mask image of the icl8. */
}
MyIconType;
 
/* Global Variable Definitions */
 
WindowPtr   gWindow;
 
void initMac();
void createWindow();
void loadIconResource();
void doEventLoop();
 
void drawIconUnchanged();
void drawIconUsingSearchProc();
void drawIconUsingCTable();
 
void DimColor( RGBColor *aColor );
long CompareComponents( unsigned short component0, unsigned short component1 );
 
static pascal Boolean SearchProc();
 
void main()
{
    MyIconType  icon;
    
    initMac();
    
    createWindow();
    loadIconResource( &icon );
 
    doEventLoop( &icon );
}
 
void initMac()
{
    MaxApplZone();
    
    InitGraf( &qd.thePort );
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs( nil );
    InitCursor();
    FlushEvents( 0, everyEvent );
}
 
void createWindow()
{
    Rect rect;
    
    SetRect( &rect, WLEFT, WTOP, WLEFT + WWIDTH, WTOP + WHEIGHT );
    
    gWindow = NewCWindow( 0L, &rect, "\pIconDimming", true, noGrowDocProc,
                            (WindowPtr)-1L, true, 0L );
    SetPort( gWindow );
    
    TextFont( kFontIDGeneva );
    TextSize( 9 );
}
 
void loadIconResource( icon )
MyIconType  *icon;
{
    Handle      icnHandle;      /* Handle to the icon bitmap used for the mask. */
    Handle      iclHandle;      /* Handle to the icl8 resource. */
    char        depth;          /* Depth of the icl8 image. */
    Rect        rect;           /* Bounding rect for the image. */
    
    SetRect( &rect, 0, 0, 32, 32 );
    depth = 8;
    
    /* Create the mask. */
    
    icnHandle = GetResource( 'ICN#', 129 );
    
    HLock( icnHandle );
    HNoPurge( icnHandle );
    
    (*icon).mask.baseAddr = *icnHandle + (4 * 32);
    (*icon).mask.rowBytes = 4;
    (*icon).mask.bounds = rect;
    
    /* Create a pixmap for the icl8 pixel image. */
    
    iclHandle = GetResource( 'icl8', 129 );
    
    HLock( iclHandle );
    HNoPurge( iclHandle );
    
    (*icon).pixmap.baseAddr = *iclHandle;
    (*icon).pixmap.rowBytes = ((32 * depth) / 8) | 0x8000;
    (*icon).pixmap.bounds = rect;
    (*icon).pixmap.pmVersion = 0;
    (*icon).pixmap.packType = 0;
    (*icon).pixmap.packSize = 0;
    (*icon).pixmap.hRes = 72;
    (*icon).pixmap.vRes = 72;
    (*icon).pixmap.pixelSize = depth;
    (*icon).pixmap.planeBytes = 0;
    (*icon).pixmap.pmReserved = 0;
    (*icon).pixmap.pixelType = 0;
    (*icon).pixmap.cmpCount = 1;
    (*icon).pixmap.cmpSize = depth;
    (*icon).pixmap.pmTable = GetCTable( depth );
    
    /* Give a unique seed for the pixmap's colortable.  Note that this is       */
    /*  necessary for two reasons.  (1) The pixmap's colortable is ignored by   */
    /*  copybits or copymask if the source ctable's ctseed value matches that   */
    /*  of the  destination's.  (2) Matching ctseed values prevent any custom   */
    /*  searchProcs from being called.                                          */
    
    (**(*icon).pixmap.pmTable).ctSeed = GetCTSeed();
}
 
void drawIconUnchanged( icon )
MyIconType  *icon;
{
    Str255      title = "\pOriginal 'icl8'";
    Rect        rect;
 
    /* Draw the original icl8 image unchanged. */
    
    SetRect( &rect, 0, 0, 160, 160 );
    CopyMask( (BitMap *)&(*icon).pixmap, &(*icon).mask, &gWindow->portBits,
                &(*icon).pixmap.bounds, &(*icon).mask.bounds, &rect );
                
    MoveTo( (rect.right - rect.left - StringWidth( title )) / 2 + rect.left,
            rect.bottom + 15 );
    DrawString( title );
}
 
void drawIconUsingSearchProc( icon )
MyIconType  *icon;
{
    Str255      title = "\p'icl8' using SearchProc Dimming";
    Rect        rect;
    GWorldPtr   gworld;         /* Offscreen used for the custom search procedure. */
    CGrafPtr    currentPort;    /* Saved CGrafPort for later restore. */
    GDHandle    currentDevice;  /* Saved gdDevice for later restore. */
    
    GetGWorld( &currentPort, &currentDevice );
    
    /* Install the custom search proc to an offscreen pixmap instead of the */
    /*  screen's in order to be courteous to other apps.                    */
        
    NewGWorld( &gworld, 8, &(*icon).pixmap.bounds, GetCTable( 8 ), nil, 0 );
    HLock( (Handle)(*gworld).portPixMap );
        
    SetGWorld( gworld, nil );
    AddSearch( NewColorSearchProc(SearchProc) );
 
    CopyBits( (BitMapPtr)&(*icon).pixmap, (BitMapPtr)&(*gworld).portPixMap, &(*icon).pixmap.bounds,
                &(**(*gworld).portPixMap).bounds, srcCopy, nil );
    
    DelSearch( NewColorSearchProc(SearchProc) );
    SetGWorld( currentPort, currentDevice );
    
    /* Now copy the dimmed pixmap image to the window. */
    
    rect.left = (*icon).pixmap.bounds.right * 5;
    rect.top = (*icon).pixmap.bounds.top;
    rect.right = (*icon).pixmap.bounds.right * 5 + rect.left;
    rect.bottom = (*icon).pixmap.bounds.bottom * 5;
 
    CopyMask( (BitMapPtr)(*(*gworld).portPixMap), &(*icon).mask, &gWindow->portBits,
                &(**(*gworld).portPixMap).bounds, &(*icon).mask.bounds, &rect );
    
    DisposeGWorld( gworld );
    
    MoveTo( (rect.right - rect.left - StringWidth( title )) / 2 + rect.left,
            rect.bottom + 15 );
    DrawString( title );
}
 
static pascal Boolean SearchProc( color, position )
RGBColor    *color;
long        *position;
{
    #pragma unused(position)
    /* Darken the RGB components in half.  Note that this routine could just    */
    /*  have easily called DimColor; however, I wanted to show the difference   */
    /*  between just dimming the components in half to what System 7 does.      */
        
    color->red >>= 1;
    color->green >>= 1;
    color->blue >>= 1;
    
    return false;
}
 
void drawIconUsingCTable( icon )
MyIconType  *icon;
{
    Str255      title = "\p'icl8' using Colortable Dimming";
    Rect        rect;
    short       index;          /* Index into the dimmed colortable */
    CTabHandle  dimmedClut;     /* Holds dimmed version of iconÕs colortable */
    CTabHandle  savedClut;      /* Saves the iconÕs original colortable */
    
    /* Save the iconÕs color table and make a copy of it */
    dimmedClut = savedClut = (*icon).pixmap.pmTable;
    HandToHand( &(Handle)dimmedClut );
 
    /* Dim each of the colors in the copy of the color table */
    for (index = 0; index <= (**dimmedClut).ctSize; ++index)
        DimColor( &(**dimmedClut).ctTable[index].rgb );
 
    /* Install the dimmed copy of the color table */
    (*icon).pixmap.pmTable = dimmedClut;
    
    rect.left = (*icon).pixmap.bounds.right * 10;
    rect.top = (*icon).pixmap.bounds.top;
    rect.right = (*icon).pixmap.bounds.right * 5 + rect.left;
    rect.bottom = (*icon).pixmap.bounds.bottom * 5;
    
    /* Now copy the dimmed pixmap image to the window. */
    
    CopyMask( (BitMap *)&(*icon).pixmap, &(*icon).mask, &gWindow->portBits,
                &(*icon).pixmap.bounds, &(*icon).mask.bounds, &rect );
    
    (*icon).pixmap.pmTable = savedClut;
    DisposeCTable( dimmedClut );
 
    MoveTo( (rect.right - rect.left - StringWidth( title )) / 2 + rect.left,
            rect.bottom + 15 );
    DrawString( title );
}
 
/******************************************************************************\
* DimColor - Dim one color for selection
*       written by Forrest Tanaka
* This routine dims the color thatÕs passed in the aColor parameter so that
* itÕs suitable to use for making an icon look selected.  It works by dimming
* all components by half, and then dimming the smaller two components by half
* again.  By keeping the largest component dimmed by only half, we keep the
* saturation of the color approximately what it was before it was dimmed.
\******************************************************************************/
 
void DimColor( 
    RGBColor *aColor)
{
    unsigned short *biggest; /* Pointer to the biggest component */
 
    /* Dim all components by half */
    aColor->red >>= 1;
    aColor->green >>= 1;
    aColor->blue >>= 1;
 
    /* If aColor isnÕt nearly gray, dim smallest 2 components again by half */
    if (CompareComponents( aColor->red, aColor->green ) != 0 ||
            CompareComponents( aColor->red, aColor->blue ) != 0)
    {
        /* Point to the larger of red and green */
        if (CompareComponents( aColor->red, aColor->green ) > 0)
            biggest = &aColor->red;
        else
            biggest = &aColor->green;
 
        /* Point to the larger of previous test and blue */
        if (CompareComponents( aColor->blue, *biggest ) > 0)
            biggest = &aColor->blue;
 
        /* Dim smaller two components by half */
        if (&aColor->red != biggest)
            aColor->red >>= 1;
        if (&aColor->green != biggest)
            aColor->green >>= 1;
        if (&aColor->blue != biggest)
            aColor->blue >>= 1;
    }
}
 
/******************************************************************************\
* CompareComponents - Compare two components for difference and equality
*       written by Forrest Tanaka
* This routine compares two components of an RGBColor that are passed in the
* component0 and component1 parameters.  If theyÕre within 512 units of each
* other, then zero is returned.  Otherwise, the positive difference is returned
* if component0 is greater than component1, or the negative of the difference
* is returned if component1 is greater than component0.
\******************************************************************************/
 
long CompareComponents(
    unsigned short component0, /* First component to check */
    unsigned short component1) /* Second component to check */
{
    long difference; /* Difference between components */
 
    /* Calculate the difference between the components */
    difference = (unsigned long)component0 - (unsigned long)component1;
 
    /* If the difference is within 512 units, then consider the components the same */
    if ((difference < 512) && (difference > -512))
        return 0;
    else
        return difference;
}
 
void doEventLoop( icon )
MyIconType  *icon;
{
    EventRecord event;
    WindowPtr   window;
    short       clickArea;
    Rect        screenRect;
 
    for (;;)
    {
        if (WaitNextEvent( everyEvent, &event, 0, nil ))
        {
            if (event.what == mouseDown)
            {
                clickArea = FindWindow( event.where, &window );
                
                if (clickArea == inDrag)
                {
                    screenRect = (**GetGrayRgn()).rgnBBox;
                    DragWindow( window, event.where, &screenRect );
                }
                else if (clickArea == inContent)
                {
                    if (window != FrontWindow())
                        SelectWindow( window );
                }
                else if (clickArea == inGoAway)
                    if (TrackGoAway( window, event.where ))
                        return;
            }
            else if (event.what == updateEvt)
            {
                window = (WindowPtr)event.message;  
                SetPort( window );
                
                BeginUpdate( window );
                
                drawIconUnchanged( icon );
                drawIconUsingSearchProc( icon );
                drawIconUsingCTable( icon );
                
                EndUpdate( window );
            }
        }
    }
}