Tim's Libraries/TGraphic.cp

/*
    File:       TGraphic.cp
 
    Contains:   The TGraphic class encapsulates custom blitting of a graphic to an offscreen
                environment, such as a GWorld or a DrawSprocket buffer.  The current blitters
                are refined versions of the RLE sprites in "Tips of the Mac Gaming Gurus".
                The core blitters do not do any work to align data to proper boundaries, so
                this code currently does not use doubles to move data (this would be
                horribly slow on the 603/604 chips).
 
                In addition to the drawing code, this class also encorporates hit testing
                graphics to each other or a single point, and using just the mask data to do
                an erase function. It also has some generalized loading and unloading code.
                
    Notes:      Currently the encoders are only designed for 8-bit images, and assume that
                the same color table is being used between the TGraphic encoder and whatever
                the destination GWorld will be.  The blitters don't really care and should
                work in anything >= 8 bits in depth, although they haven't been tested in
                anything other than 8 bits.
 
                Currently, this class is definitely not intended to be subclassed, so
                all the methods are non-virtual.
                
    Written by: Timothy Carroll 
 
    Copyright:  Copyright © 1996-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/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
                11/5/97     Replaced naked c-types with standard types from types.h.
 
                9/18/97     CreateGraphic and the utility functions that it was based on
                            were cleaned up.  Now, the utility functions are responsible
                            for cleaning up if they have an error.  Fixed a few bugs in
                            the CIcon code as well.
 
                9/18/97     Updated code to Universal Headers 3.0.  Cleaned up some of the
                            comments.  Revised code to use OSStatus everywhere.
            
                5/22/97     Writes to resource file now call UpdateResFile.
 
                5/21/97     The list that keeps the TGraphics was never being initialized.
                            Perhaps the compiler automatically initializes the variables to
                            NULL, otherwise I would have expected it to explode.  We now
                            explictly initialize the list variables.
            
                5/21/97     No major changes or bugs fixed, but the list management code has
                            been tidied up.
            
                5/19/97     WriteToPICTResource could fail to restore the ports properly 
                            if QuickDraw returned an error.  This is fixed now.
            
                4/2/97      RELEASE OF 1.01
 
                4/2/97      LoadFromGraphicResource called ReleaseResource twice because I
                            duplicated some code to the cleanup section. Onyx's Spotlight
                            spotted it. Buy it!
 
 
                2/24/97     TGraphic relies on an application to allocate a color table. This
                            handle is now declared inside TGraphic.h
 
                2/24/97     Assert macros were moved to a separate file, which we explictly
                            include.  MrC can build it now.
 
                1/23/97     If garbage were fit into the blitter, it is possible that we
                            could draw before destPtr was initialized.  This could never
                            happen with a valid sprite, but the debug build now initializes
                            the pointer to DEADBEEF to force a bus error.  MrC's optimizer
                            spotted this case in the code.
 
*/
 
 
#include <Memory.h>
#include <Resources.h>
 
#include <Icons.h>
#include "TGraphic.h"
#include "Scaling.h"
 
 
/*************************************************************************************
Internal Declarations
 
We define a number of constants used by the encoders and decoders.  Each TGraphic
consists of a number of drawing commands, each of which is a 32-bit unsigned int with
an 8-bit operator and a 24-bit value, followed by zero or more bytes of associated data.
 
Currently, each command is aligned to a 4-byte boundary, so a change in format would
be required to use 8-byte floats.
*************************************************************************************/
 
enum
{
// An EndShape token must appear at the end of all TGraphics.  The value is ignored.
    kEndShapeToken  = 0L,
 
// Each line must be preceded by a LineStart token, even lines with no drawing. The
// data field is the offset to the next line, used for clipping and hit testing.
    kLineStartToken = 1L,
 
// This is the only token that currently has additional data.  The value field is the
// number of bytes of pixel data, and the op-code is followed by that data.  The data
// is padded to a 4-byte boundary.
    kDrawPixelsToken = 2L,
 
// This is the "draw" equivalent in the hit mask.  We encode the hit mask separately
// to allow for certain pixels to be drawn but not hit tested.
    kHitTestPixelsToken = 2L,
 
// Skips a range of pixels, without drawing.
    kSkipPixelsToken = 3L,
 
// The actual colors tested against in the hit mask.  Alpha blending could be added,
// but should be a different opcode to allow opaque pixels to be drawn quickly.
    kClearColorIndex = 0,
    kMaskColorIndex = 255
};
 
// This macro could be modified to support almost any alignment necessary.
#define ALIGN_TO_NEXT_LONG(x)  ( ((UInt32) x + 3) & ~3)
 
 
// Standard declarations for our resource type and format.
 
#define kTGraphicResourceType 'Gfic'
 
struct TGraphicResHeader
{
    UInt32 version;
    SInt16 depth;
    UInt16 flags;
    Rect   bounds;
    UInt32 imageSize;
    UInt32 maskSize;
};
typedef TGraphicResHeader **TGraphicResHeaderHandle;
 
 
// Preferred depth for the encoder.
#define kPreferredDepth 8
 
 
/*************************************************************************************
TGraphic Allocation and List Management
 
TGraphic objects are allocated and maintained in a list internally by this class.
Each object is reference counted, to reduce the amount of time spent loading and
preparing TGraphics.  This is particularly important when you are loading sprites
from PICT resources, since processing the sprites can take a long time.
 
For truly high performance apps, you can preload all the TGraphics once, and use
them, so that no time in the game loop actually gets spent preparing the sprites.
 
The list is sorted by the TGraphic's resource ID, and uses standard C array indexing.
The list is searched using a binary search. 
*************************************************************************************/
 
static Handle gTGraphicList = NULL;
static UInt32 gNumberGraphics = 0;
 
static OSStatus InsertGraphicIntoList (TGraphic *theGraphic, UInt32 index);
static OSStatus DeleteGraphicFromList (UInt32 index);
static OSStatus SearchGraphicList (SInt16 resID, Boolean *found, UInt32 *index);
 
 
/*************************************************************************************
InsertGraphicIntoList
 
This routine creates the list if it hasn't been initialized.  Otherwise, it just
inserts the TGraphic into the spot pointed to by the index.  If an error occurs, the
list is left in an indeterminate state.
*************************************************************************************/
 
OSStatus InsertGraphicIntoList (TGraphic *theGraphic, UInt32 index)
{
    OSStatus theErr = noErr;
 
#if qDebugging
    if (theGraphic == NULL)
        SIGNAL_ERROR ("\pError: Attempted to insert a NULL TGraphic in list.")
    if ((index < 0) || (index > gNumberGraphics))
        SIGNAL_ERROR ("\pError: Attempted to insert TGraphic at invalid index.")
#endif
    
    // If we don't have a list, allocate one.
    if (gTGraphicList == NULL)
    {
        gNumberGraphics = 1;
    
        gTGraphicList = NewHandleClear (sizeof (TGraphic *));
        theErr = MemError();
        FAIL_OSERR (theErr, "\pERROR: Couldn't allocate TGraphic list.")
        FAIL_NIL (gTGraphicList, "\pERROR: Couldn't allocate TGraphic list.")
    }
    else
    // if we do have a list, expand it by one node
    {
        gNumberGraphics++;
        
        SetHandleSize(gTGraphicList,gNumberGraphics*sizeof(TGraphic *));
        theErr = MemError();
        FAIL_OSERR (theErr, "\pERROR: Couldn't resize TGraphic list")
        FAIL_NIL (gTGraphicList, "\pERROR: Couldn't resize TGraphic list")
        
        // Make room in the list for a node at the correct index.
        BlockMoveData(  (*(TGraphic ***)gTGraphicList)+index,
                        (*(TGraphic ***)gTGraphicList)+index+1,
                        (gNumberGraphics - index-1) * sizeof(TGraphic *));
    }
    
    // Copy the TGraphic into the list.
    *((*(TGraphic ***) gTGraphicList)+index) = theGraphic;
 
    // We succeeded, cleanup and exit.
    goto cleanup;
    
error:
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    return theErr;
}
 
 
/*************************************************************************************
DeleteGraphicFromList
    
This routine removes the graphic from the list.  If the list is then empty,
it disposes of the handle.
*************************************************************************************/
OSStatus DeleteGraphicFromList (UInt32 index)
{
    OSStatus theErr = noErr;
    
#if qDebugging
    if ((index < 0) || (index > gNumberGraphics-1))
        SIGNAL_ERROR ("\pError: Attempted to delete outside of the list.")
#endif
    
    gNumberGraphics--;
    
    // If there are elements in the list, slide them up and shrink the handle
    if (gNumberGraphics > 0)
    {
        BlockMoveData(  (*(TGraphic ***)gTGraphicList)+index+1,
                        (*(TGraphic ***)gTGraphicList)+index,
                        (gNumberGraphics - index) * sizeof(TGraphic *));
 
        SetHandleSize((Handle) gTGraphicList,gNumberGraphics*sizeof(TGraphic *));
        theErr = MemError();
        FAIL_OSERR (theErr, "\pCouldn't resize the list handle")
        FAIL_NIL (gTGraphicList, "\pCouldn't resize the list handle")
    }
    else
    // Otherwise, the list is empty and we can dispose of it.
    {
        DisposeHandle (gTGraphicList);
        gTGraphicList = NULL;
    }
    
    // We succeeded, cleanup and exit.
    goto cleanup;
    
error:
    if (theErr == noErr)
        theErr = paramErr;
cleanup:
    return theErr;
}
 
 
/*************************************************************************************
SearchGraphicList
 
This routine searches for a TGraphic with the resID parameter.  If it doesn't find
one, the index it returns is the location where the new element should be inserted
into the list.
*************************************************************************************/
OSStatus SearchGraphicList (SInt16 resID, Boolean *found, UInt32 *index)
{   
    OSStatus theErr = noErr;
    UInt32 low = 0;
    UInt32 high = gNumberGraphics;
    UInt32 tempIndex;
    SInt16 tempID;
    TGraphic *theItem = NULL;
    
    // For an empty list, we fall through immediately.
    while (low < high)
    {
        tempIndex = (low+high) >> 1;
        theItem = (*(TGraphic ***)gTGraphicList)[tempIndex];
#if qDebugging
        FAIL_NIL (theItem, "\pERROR: List items should never be NULL!")
#endif
        tempID = theItem->GetResID();
        if (resID == tempID)
        {
        // We found the object, return it
            *found = true;
            *index = tempIndex;
            goto cleanup;
        }
        else if (resID < tempID)
        // element must be below the current index
            high = tempIndex;
        else
        // element must be above the current index
            low = tempIndex+1;
    }
    
    // We fell through, so we didn't find the item in the list.
    *found = false;
    *index = (low+high) >> 1;
    goto cleanup;
    
error:
    if (theErr == noErr)
        theErr = paramErr;
    
cleanup:
    return theErr;
}
 
/*************************************************************************************
TGraphic::NewGraphic
 
This uses the list management code to create new graphics when needed, and to manage
the reference counts.
*************************************************************************************/
TGraphic
*TGraphic::NewGraphic (SInt16 resID)
{
    OSStatus    theErr = noErr;
    UInt32      listIndex;
    Boolean     graphicAlreadyExists;
    TGraphic    *newGraphic = NULL;
    
    theErr = SearchGraphicList (resID, &graphicAlreadyExists, &listIndex);
    FAIL_OSERR(theErr,"\pERROR: Failed to search the graphic list")
    
    if (graphicAlreadyExists)
    {
        // Bump the reference count on the existing graphic
        newGraphic = (*(TGraphic ***)gTGraphicList)[listIndex];
        newGraphic->AddReference();
    }
    else
    {
        // Create a new graphic and insert it into the list
        newGraphic = new TGraphic(resID);
        theErr = newGraphic->CreateGraphic();
        FAIL_OSERR(theErr,"\pERROR: Failed to create the new TGraphic.")
        theErr = InsertGraphicIntoList(newGraphic, listIndex);
        FAIL_OSERR(theErr, "\pERROR: Failed to insert TGraphic into list")
        newGraphic->AddReference();
    }
    
    // We succeeded, cleanup and return the TGraphic.
    goto cleanup;
    
error:
    if (newGraphic != NULL)
    {
        delete newGraphic;
        newGraphic = NULL;
    }
    
cleanup:    
    return newGraphic;
}
 
 
/*************************************************************************************
TGraphic::AddReference
*************************************************************************************/
 
void
TGraphic::AddReference (void)
   {
    fReferenceCount++;
   }
   
   
/*************************************************************************************
TGraphic::DisposeReference
*************************************************************************************/
 
void 
TGraphic::DisposeReference (void)
{
    fReferenceCount--;
    
    // If no one cares about us, remove us from the list and delete ourself.
    
    if (fReferenceCount == 0)
    {
        UInt32              listIndex;
        Boolean             tableEntry;
        OSStatus            theErr = noErr;
 
        theErr = SearchGraphicList (fResID, &tableEntry, &listIndex);
#if qDebugging
        FAIL_OSERR (theErr, "\pERROR: List search reported an error")
        FAIL_FALSE (tableEntry, "\pERROR: TGraphic should have been in the list!")
#endif              
        theErr = DeleteGraphicFromList(listIndex);
        FAIL_OSERR (theErr, "\pFailed to delete graphic from the list")
 
        delete this;
    }
        
error:
    return;
}
 
   
   
/*************************************************************************************
TGraphic::TGraphic
 
Just puts some sane values in the member variables.
*************************************************************************************/
TGraphic::TGraphic (SInt16 resID)
{
    fResID = resID;
    fReferenceCount = 0;
    fImage = NULL;
    fHitMask = NULL;
    
}
 
 
/*************************************************************************************
TGraphic::~TGraphic
 
If we have real, allocated graphics, we should dispose of them.
*************************************************************************************/
TGraphic::~TGraphic (void)
{
    DestroyGraphic();
}
 
 
/*************************************************************************************
TGraphic::CreateGraphic
 
Attempt to load the graphic from a resource.  The preferred format is a pre-encoded
sprite, since this loads very quickly, but if we don't find one, we'll use another
format we recognize.
 
This is useful because you can use other graphics formats while under development.
*************************************************************************************/
OSStatus TGraphic::CreateGraphic(void)
{
    OSStatus theErr = noErr;
 
    theErr = LoadFromGraphicResource();
    if (theErr == noErr) goto cleanup;
    
    theErr = LoadFromPICTResource ();
    if (theErr == noErr) goto cleanup;
 
    theErr = LoadFromCIconResource ();
    if (theErr == noErr) goto cleanup;
    
    theErr = LoadFromICN8Resource ();
    if (theErr == noErr) goto cleanup;
    
error:
 
    SIGNAL_ERROR ("\pError: No resource was found!")
    if (theErr == noErr)
        theErr = paramErr;
 
cleanup:
    return theErr;
    
}
 
 
/*************************************************************************************
TGraphic::DestroyGraphic
    
All this needs to do is dispose of the image handle we created earlier.
*************************************************************************************/
 
OSStatus
TGraphic::DestroyGraphic (void)
{
    if (fImage != NULL)
        DisposeHandle (fImage);
    if (fHitMask != NULL)
        DisposeHandle (fHitMask);
        
    fImage = NULL;
    fHitMask = NULL;
    
    return noErr;
}
 
 
 
/*************************************************************************************
TGraphic::LoadFromGraphicResource
    
This routine loaded a previously encoded sprite.
*************************************************************************************/
OSStatus TGraphic::LoadFromGraphicResource(void)
{
    Handle      graphicResource = NULL;
    OSStatus    theErr = noErr;
    Size        imageSize = 0, maskSize = 0;
    TGraphicResHeaderHandle header;
    
// STEP 1 -- determine if the resource exists and try to load it.
    graphicResource = Get1Resource (kTGraphicResourceType, fResID);
    theErr = ResError();
    
    if ((theErr != noErr) || (graphicResource == NULL))
        goto error;
 
// STEP 2 -- Parse the resource header
    
    header = (TGraphicResHeaderHandle) graphicResource;
    
    // This code only parses version 0 formats.
    if ((**header).version != 0L)
        goto error;
        
    fBitDepth = (**header).depth;
    fFlags    = (**header).flags;
    fBounds   = (**header).bounds;
 
    imageSize = (**header).imageSize;
    maskSize =  (**header).maskSize;
 
// Step 3 -- Allocate the image and mask data and copy from the resource.   
    fImage = NewHandle(imageSize);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to allocate the image handle")
    FAIL_NIL   (fImage, "\pFailed to allocate the image handle")
    
    fHitMask = NewHandle (maskSize);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to allocate the mask handle")
    FAIL_NIL   (fHitMask, "\pFailed to allocate the mask handle")
    
    BlockMoveData(((*graphicResource)+sizeof(TGraphicResHeader)), *fImage, imageSize);
    BlockMoveData(((*graphicResource)+sizeof(TGraphicResHeader)+imageSize), *fHitMask, maskSize);
    
// We're done, cleanup and exit!
    goto cleanup;
    
error:
    
    DestroyGraphic();
    
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    if (graphicResource != NULL)
        ReleaseResource (graphicResource);
    
    return theErr;
}
 
 
 
/*************************************************************************************
TGraphic::LoadFromPICTResource
    
This routine reads a specially formatted pict, with image, drawing mask and hit mask
going from left to right.
*************************************************************************************/
OSStatus
TGraphic::LoadFromPICTResource (void)
{
    PicHandle       thePicture = NULL;
    OSStatus        theErr = noErr;
    GWorldPtr       graphicGWorld = NULL;
    PixMapHandle    graphicPixMap = NULL;
    Rect            pictRect;
    
// STEP 1 -- Attempt to load the PICT
    thePicture = (PicHandle) Get1Resource ('PICT', fResID);
    theErr = ResError();
    
    if ((theErr != noErr) || (thePicture == NULL))
        goto error;
 
// STEP 2 -- Normalize the picture rectangle, and use it to build a GWorld.
 
    pictRect = (**thePicture).picFrame;
    
    pictRect.right -= pictRect.left;  pictRect.left = 0;
    pictRect.bottom -= pictRect.top;  pictRect.top = 0;
    
    if (pictRect.right % 3 != 0)
        SIGNAL_ERROR ("\pImproper size pict to load as a graphic")
 
    theErr = NewGWorld(&graphicGWorld, kPreferredDepth, &pictRect, gAppColorTable, NULL, keepLocal);
    FAIL_OSERR (theErr, "\pCouldn't allocate GWorld for encoding")
    
    graphicPixMap  = GetGWorldPixMap(graphicGWorld);
    FAIL_NIL (graphicPixMap, "\pCouldn't get the GWorld's PixMap")
    FAIL_FALSE ( LockPixels(graphicPixMap), "\pCouldn't lock GWorld PixMap")
    
    // Erase the GWorld and draw our picture
    {
        CGrafPtr savePort;
        GDHandle saveDevice;
        
        GetGWorld (&savePort, &saveDevice);
        SetGWorld (graphicGWorld, NULL);
    
        EraseRect (&pictRect);
        DrawPicture (thePicture, &pictRect);
    
        SetGWorld (savePort, saveDevice);
    }
 
// STEP 4 -- feed the GWorld to the encoder.
    
    pictRect.right /=3;
    theErr = EncodeGraphic (graphicPixMap, &pictRect);
    
    if (theErr == noErr)
        goto cleanup;
 
error:
    DestroyGraphic();
        
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    if (thePicture != NULL)
        ReleaseResource ((Handle)thePicture);
    
    if (graphicGWorld != NULL)
        DisposeGWorld (graphicGWorld);
    
    return theErr;
 
}
 
 
 
/*************************************************************************************
TGraphic::LoadFromICN8Resource
 
This routine reads an ICL8 resource and uses the mask as both the drawing and hit
mask.  It isn't quite as versatile as the PICT format, but is probably easier to
find tools to build sprites with.
*************************************************************************************/
OSStatus
TGraphic::LoadFromICN8Resource (void)
{
    OSStatus        theErr = noErr;
    GWorldPtr       graphicGWorld = NULL;
    PixMapHandle    graphicPixMap = NULL;
    Rect            graphicRect = {0,0,32,96};
    Rect            iconRect = {0,0,32,32};
    Handle          theIconSuite = NULL;
    RgnHandle       theMask = NULL;
 
// STEP 1 -- Attempt to load the icon
    theErr = GetIconSuite(&theIconSuite, fResID, svAllAvailableData);
 
    if ((theErr != noErr) || (theIconSuite == NULL))
        goto error;
 
// STEP 2 -- Create a region from the IconSuite's mask
    
    theMask = NewRgn();
    theErr = QDError();
    FAIL_OSERR (theErr, "\pError: Failed to create the mask region")
    FAIL_NIL (theMask, "\pError: Failed to create the mask region")
    
    theErr = IconSuiteToRgn (theMask, &iconRect, atNone, theIconSuite);
    FAIL_OSERR (theErr, "\pError: Failed to load the mask data into the region")
    OffsetRgn (theMask, -(**theMask).rgnBBox.left, -(**theMask).rgnBBox.top);
 
// STEP 3 -- Create a GWorld the size of 3 ICL8s.
 
    theErr = NewGWorld(&graphicGWorld, kPreferredDepth, &graphicRect, gAppColorTable, NULL, keepLocal);
    FAIL_OSERR (theErr, "\pCouldn't allocate GWorld for encoding")
    
    graphicPixMap  = GetGWorldPixMap(graphicGWorld);
    FAIL_NIL (graphicPixMap, "\pCouldn't get the GWorld's PixMap")
    FAIL_FALSE ( LockPixels(graphicPixMap), "\pCouldn't lock GWorld PixMap")
    
// STEP 4 -- Erase the GWorld and draw our icons
    {
        CGrafPtr savePort;
        GDHandle saveDevice;
        
        GetGWorld (&savePort, &saveDevice);
        SetGWorld (graphicGWorld, NULL);
    
        EraseRect (&graphicRect);
        theErr = PlotIconSuite(&iconRect, atNone, ttNone, theIconSuite);  // icon
        
        OffsetRgn(theMask, 32, 0);
        FillRgn (theMask, &qd.black); // drawing mask
        OffsetRgn(theMask, 32, 0);
        FillRgn (theMask, &qd.black); // hit mask
 
        SetGWorld (savePort, saveDevice);
    }
 
// STEP 5 -- feed the GWorld to the encoder.
    
    graphicRect.right /=3;
    theErr = EncodeGraphic (graphicPixMap, &graphicRect);
    
    if (theErr == noErr)
        goto cleanup;
 
error:
    DestroyGraphic();
        
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    if (theMask != NULL)
        DisposeRgn (theMask);
    if (theIconSuite != NULL)
        DisposeIconSuite (theIconSuite, true);
    if (graphicGWorld != NULL)
        DisposeGWorld (graphicGWorld);
    
    return theErr;
 
}
 
 
/*************************************************************************************
TGraphic::LoadFromCIconResource
 
This is much like LoadFromICN8Resource, but loads from a CIcon instead.
*************************************************************************************/
OSStatus
TGraphic::LoadFromCIconResource (void)
{
    OSStatus        theErr = noErr;
    GWorldPtr       graphicGWorld = NULL;
    PixMapHandle    graphicPixMap = NULL;
    Rect            graphicRect, iconRect;
    CIconHandle     theCIcon = NULL;
    RgnHandle       theMask = NULL;
    
// STEP 1 -- Load the CIcon resource
    theCIcon = GetCIcon (fResID);
    theErr = ResError();
    
    if ((theCIcon == NULL) || (theErr != noErr))
        goto error;
 
// STEP 2 -- Create a region from the Icon's mask
 
    theMask = NewRgn();
    theErr = QDError();
    FAIL_OSERR (theErr, "\pError: Failed to create the mask region")
    FAIL_NIL (theMask, "\pError: Failed to create the mask region")
    
    // Fill in the baseAddr of the BitMap.
    ( (**theCIcon).iconMask ).baseAddr = ( char* )(**theCIcon).iconMaskData;
    
    theErr = BitMapToRegion (theMask, &(**theCIcon).iconMask);
    FAIL_OSERR (theErr, "\pFailed to load the mask data into the region")
    OffsetRgn (theMask, -(**theMask).rgnBBox.left, -(**theMask).rgnBBox.top);
 
 
// STEP 3 -- Build a GWorld the size of 3 CIcons
 
    iconRect = (**theCIcon).iconPMap.bounds;
    iconRect.right -= iconRect.left; iconRect.left = 0;
    iconRect.bottom -= iconRect.top; iconRect.top = 0;
    
    graphicRect = iconRect;
    graphicRect.right *= 3;
 
    theErr = NewGWorld(&graphicGWorld, kPreferredDepth, &graphicRect, gAppColorTable, NULL, keepLocal);
    FAIL_OSERR (theErr, "\pCouldn't allocate GWorld for encoding")
    
    graphicPixMap  = GetGWorldPixMap(graphicGWorld);
    FAIL_NIL (graphicPixMap, "\pCouldn't get the GWorld's PixMap")
    FAIL_FALSE ( LockPixels(graphicPixMap), "\pCouldn't lock GWorld PixMap")
    
// STEP 4 -- Erase the GWorld and draw our icons
    {
        CGrafPtr savePort;
        GDHandle saveDevice;
        
        GetGWorld (&savePort, &saveDevice);
        SetGWorld (graphicGWorld, NULL);
    
        EraseRect (&graphicRect);
        
        PlotCIcon (&iconRect, theCIcon); // image
        OffsetRgn(theMask, iconRect.right, 0); 
        FillRgn (theMask, &qd.black); // draw mask
        OffsetRgn(theMask, iconRect.right, 0); 
        FillRgn (theMask, &qd.black); // hit mask
 
        SetGWorld (savePort, saveDevice);
    }
 
// STEP 5 -- feed the GWorld to the encoder.
    
    graphicRect.right /=3;
    theErr = EncodeGraphic (graphicPixMap, &graphicRect);
    
    if (theErr == noErr)
        goto cleanup;
 
error:
    DestroyGraphic();
        
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    if (theMask != NULL)
        DisposeRgn (theMask);
    if (theCIcon != NULL)
        DisposeCIcon (theCIcon);
    if (graphicGWorld != NULL)
        DisposeGWorld (graphicGWorld);
    
    return theErr;
 
}
 
 
 
/*************************************************************************************
TGraphic::EncodeGraphic
 
Takes a previously prepared pixmap and compresses it into our format.
*************************************************************************************/
OSStatus    TGraphic::EncodeGraphic (PixMapHandle theGraphic, Rect *encodeRect)
{
    OSStatus theErr = noErr;
    
    UInt16      shapeHeight;        // the height of the shape
    UInt16      shapeWidth;         // the width of the shape
    
    UInt32      rowBytes;           // the rowbytes of our source pixmap
    UInt8       *baseAddr;          // the base address of the source pixmap
    
    UInt8       *dataPtr;           // points to where we are saving/encoding the data
    UInt8       *sourcePtr;         // points to the graphics data we are saving in our image
    UInt8       *maskPtr;           // points to whichever mask we are currently encoding
    
    UInt8       *sourceStartPtr;    // points to the beginning of a source row.
 
    UInt32      yCounter;           // a counter to scan the shape vertically
    UInt32      xCounter;           // a counter to scan the shape horizontally
 
    Size        newSize;            // used to calculate the size of the new handle.
 
    // We preset the values to make the optimizer happy
    UInt8       *runTokenPtr = NULL;    // where is the token for the current run
    UInt32      runCounter = 0;     // how long is the current run? 
 
// STEP 1 -- precalculate the size of the shape.
// Allocate enough memory to hold the RLE encoded shapes.
 
    shapeHeight = encodeRect->bottom - encodeRect->top;
    shapeWidth = encodeRect->right - encodeRect->left;
    
    // initialize the standard member data for our shape.
    fBounds.left = 0;
    fBounds.top = 0;
    fBounds.right = shapeWidth;
    fBounds.bottom = shapeHeight;
    fFlags = 0;
    fBitDepth = (**theGraphic).pixelSize;
    
    if (fBitDepth != 8)
        SIGNAL_ERROR ("\pError: This encoder only supports 8 bit images")
        
    //the memory totals are for the worse case shape.
    
    fImage = NewHandle (8*shapeHeight * shapeWidth + 4*shapeHeight +4);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to allocate a handle for the shape data")
    FAIL_NIL (fImage, "\pFailed to allocate a handle for the shape data")
    
    fHitMask = NewHandle (4*shapeHeight * shapeWidth + 4*shapeHeight +4);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to allocate a handle for the hit mask data")
    FAIL_NIL (fHitMask, "\pFailed to allocate a handle for the hit mask data")
    
    // Get some of the standard data we'll need when encoding the pixels
    
    baseAddr = (UInt8  *)GetPixBaseAddr( theGraphic );
    rowBytes = (*theGraphic)->rowBytes & 0x3fff;
 
 
// STEP 2 -- Encode the shape data.  We use a separate mask rather than using
// chroma keying.  This was mostly done to allow more flexibility in the
// image design, but the other way works very similar.
    
    HLock (fImage);
    dataPtr = (UInt8 *) (*fImage);
    
    sourceStartPtr = baseAddr + rowBytes * encodeRect->top + encodeRect->left;
    
    // scan the shape row by row
    for( yCounter = 0; yCounter < shapeHeight; yCounter++ )
    {
        // store where the line starts, so we can fill in the lineStart token.
        UInt8   *lineStartPtr = dataPtr;
        
        // Flags to determine which run we are currently in.  Initially false,
        // we don't start in a run.
        UInt8   drawRunFlag = false;                // are we in a draw pixels run?
        UInt8   skipRunFlag = false;                // are we in a skip pixels run?
        
        // reserve space for the lineStart token.
        dataPtr += sizeof( UInt32 );
        
        // move to the start of the row and begin scanning the pixels of the row.
        sourcePtr = sourceStartPtr;
        maskPtr = sourceStartPtr + shapeWidth;
            
        for( xCounter = 0; xCounter < shapeWidth; xCounter++ )
        {
            if ( *maskPtr == kClearColorIndex )
            {
                // Finish the draw run if we are in one.
                if ( drawRunFlag )
                {
                    drawRunFlag = false;
                                    
                    // create the draw token, and pad the line to a multiple of four.
                    *( (UInt32 *)runTokenPtr ) = ( kDrawPixelsToken << 24 ) + runCounter;
                                    
                    *( (UInt32 *)dataPtr ) = 0L;
                    dataPtr = (UInt8 *) ALIGN_TO_NEXT_LONG(dataPtr);
                }
                            
                // Start a new skip run, or continue the existing one.
                if ( skipRunFlag )
                {
                    runCounter++;
                }
                else
                {
                    // start a new skip run
                    skipRunFlag = true;
                    runCounter = 1;
                }
            }
            else
            {
                // Finish the skip run, if we're in one.
                if ( skipRunFlag )
                {
                    skipRunFlag = false;
                    
                    // create the skip token
                    *( ( UInt32 * )dataPtr ) = ( kSkipPixelsToken << 24 ) + runCounter;
                    dataPtr += sizeof( UInt32 );
                }
                
                // Start a new draw run, or continue the existing one.
                if ( drawRunFlag )
                {
                    // continue it
                    runCounter++;
                                    
                    // copy the pixel
                    *dataPtr = *sourcePtr;
                    dataPtr++;
                }
                else
                {
                    // start one
                    drawRunFlag = true;
                    runCounter = 1;
                                    
                    // save the location of the token (so we can fill it in later)
                    runTokenPtr = dataPtr;
                    dataPtr += sizeof( UInt32 );
                                    
                    // copy the pixel
                    *dataPtr = *sourcePtr;
                    dataPtr++;
                }
            }
                    
            // move to the next pixel
            sourcePtr++;
            maskPtr++;
        }
        
        // no need to write a skip at the end of a line, but we do need to finish
        // up a draw run if that's what we were working on.
        if( drawRunFlag )
        {
            // create the draw token
            *((UInt32 *) runTokenPtr) = ( kDrawPixelsToken << 24 ) + runCounter;
                                    
            // clear and pad to a mulitple of four
            *((UInt32 *)dataPtr ) = 0L;
            dataPtr = (UInt8 *) ALIGN_TO_NEXT_LONG(dataPtr);
        }
        
        // finish the line start token, and move to the next row.
        *( (UInt32 *)lineStartPtr ) = ( kLineStartToken << 24 ) + ( dataPtr - ( lineStartPtr + 4 ) );
        sourceStartPtr += rowBytes;
    }
    
    // create the end of shape token
    *((UInt32 *) dataPtr) = kEndShapeToken << 24;
    dataPtr += sizeof( UInt32 );
 
    // Resize the handle to match the real size of the shape
    HUnlock( fImage );
    newSize = dataPtr - (UInt8 *)( *fImage );
    SetHandleSize( fImage, newSize );
    theErr = MemError();
    FAIL_OSERR (theErr, "\p Failed to resize the handle")
    
// STEP 3-- Encode the Mask data.  Unlike the previous encoder, we don't have to copy any pixel
// data.
 
    HLock (fHitMask);
    dataPtr = (UInt8 *)( *fHitMask );
    
    sourceStartPtr = baseAddr + rowBytes * encodeRect->top + encodeRect->left + shapeWidth+shapeWidth;
    
    // scan the shape row by row
    for( yCounter = 0; yCounter < shapeHeight; yCounter++ )
    {
        // we need to store where this line starts so we can fill in the lineStart token later.
        UInt8   *lineStartPtr = dataPtr;
        UInt8   drawRunFlag = false;                // are we in a draw pixels run?
        UInt8   skipRunFlag = false;                // are we in a skip pixels run?
        
        // reserve a place for the line start token.
        dataPtr += sizeof( UInt32 );
        maskPtr = sourceStartPtr;
            
        // scan each pixel of the mask.
        for( xCounter = 0; xCounter < shapeWidth; xCounter++ )
        {
            // is this pixel clear?
            if ( *maskPtr == kClearColorIndex )
            {
                // are we in a draw run?
                if ( drawRunFlag )
                {
                    // end the draw run
                    drawRunFlag = false;
                                    
                    // create the draw token
                    *( (UInt32 *)dataPtr ) = ( kDrawPixelsToken << 24 ) + runCounter;
                    dataPtr += sizeof( UInt32 );
                }
                            
                // are we in a skip run
                if ( skipRunFlag )
                {
                // continue it
                    runCounter++;
                }
                else
                {
                    // start one
                    skipRunFlag = true;
                    runCounter = 1;
                }
            }
            else
            {
            // are we in a skip run
                if ( skipRunFlag )
                {
                    // end the skip run
                    skipRunFlag = false;
                    // create the skip token
                    *( (UInt32 *)dataPtr ) = ( kSkipPixelsToken << 24 ) + runCounter;
                    dataPtr += sizeof( UInt32 );
                }
                            
                // are we in a draw run
                if ( drawRunFlag )
                {
                    // continue it
                    runCounter++;
                }
                else
                {
                    // start one
                    drawRunFlag = true;
                    runCounter = 1;
                }
            }
                    
            // move to the next byte
                    maskPtr++;
        }
        
        // if we're finishing up a draw run, we need to write out the token
 
        if( drawRunFlag )
        {
            *( (UInt32 *)dataPtr ) = ( kDrawPixelsToken << 24 ) + runCounter;
            dataPtr += sizeof( UInt32 );
        }
            
        // create the line start token
        *( (UInt32 *)lineStartPtr ) = ( kLineStartToken << 24 ) + ( dataPtr - ( lineStartPtr + 4 ) );
            
        // move the row start to the next row
        sourceStartPtr += rowBytes;
    }
    
    // create the end of shape token
    *( (UInt32 *)dataPtr ) = kEndShapeToken << 24;
    dataPtr += sizeof( UInt32 );
    
    
    
    // Resize the handle to match the real size of the shape
    HUnlock( fHitMask );
    newSize = dataPtr - (UInt8 *)( *fHitMask );
    SetHandleSize( fHitMask, newSize );
    theErr = MemError();
    FAIL_OSERR (theErr, "\p Failed to resize the handle")
 
    goto cleanup;
 
error:
    
    if (theErr == noErr)
        theErr = paramErr;
        
cleanup:
    // cleanup is actually the responsibility of the calling function.
    return theErr;
}
    
    
 
/*************************************************************************************
TGraphic::WriteToGraphicResource
    
This takes a previously created graphic and writes out our custom resource format.
*************************************************************************************/
OSStatus
TGraphic::WriteToGraphicResource (void)
{
    Handle  graphicResource = NULL;
    OSStatus    theErr;
    Size imageSize;
    Size maskSize;
    
    imageSize = GetHandleSize(fImage);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't obtain image handle size")
    
    maskSize = GetHandleSize (fHitMask);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't obtain mask handle size")
    
    graphicResource = NewHandle (sizeof(TGraphicResHeader) + imageSize + maskSize);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't allocate handle to hold new resource")
    FAIL_NIL (graphicResource, "\pCouldn't allocate handle to hold new resource")
    
    // Now to fill in the header and block move all of the data over into the new resource.
    
    (**(TGraphicResHeaderHandle)graphicResource).version   = 0L;
    (**(TGraphicResHeaderHandle)graphicResource).depth     = fBitDepth;
    (**(TGraphicResHeaderHandle)graphicResource).flags     = fFlags;
    (**(TGraphicResHeaderHandle)graphicResource).bounds    = fBounds;
    (**(TGraphicResHeaderHandle)graphicResource).imageSize = imageSize;
    (**(TGraphicResHeaderHandle)graphicResource).maskSize  = maskSize;
    
    BlockMoveData(*fImage, ((*graphicResource)+sizeof(TGraphicResHeader)), imageSize);
    BlockMoveData(*fHitMask, ((*graphicResource)+sizeof(TGraphicResHeader)+imageSize), maskSize);
    
    // **TO DO:  Check to see if there is already a resource with that ID!
    
    // Add the resource to the file.
    AddResource( graphicResource, kTGraphicResourceType, fResID, "\p" );
    theErr = ResError();
    FAIL_OSERR (theErr, "\pFailed to add the TGraphic resource to the file")
    
    WriteResource( graphicResource );
    theErr = ResError();
    FAIL_OSERR (theErr, "\pError: Failed to write the resource")
    
    UpdateResFile (CurResFile());
    theErr = ResError();
    
    ReleaseResource( graphicResource );
    
    return theErr;
    
error:
    if (graphicResource != NULL)
        DisposeHandle (graphicResource);
    if (theErr == noErr)
        theErr = paramErr;
        
    return theErr;
}
 
 
 
/*************************************************************************************
TGraphic::WriteToPICTResource
    
This takes a previously created graphic and writes out a PICT with the 3 images.
*************************************************************************************/
OSStatus
TGraphic::WriteToPICTResource (void)
{   
    Rect            pictRect = fBounds;
    OSStatus        theErr = NULL;
    GWorldPtr       graphicGWorld = NULL;
    PixMapHandle    graphicPixMap = NULL;
    PicHandle       thePicture = NULL;
        
    // Create a locked GWorld with the proper depth, dimensions and color table.
    pictRect.right += (pictRect.right+pictRect.right);
    
    theErr = NewGWorld(&graphicGWorld, kPreferredDepth, &pictRect, gAppColorTable, NULL, keepLocal);
    FAIL_OSERR (theErr, "\pCouldn't allocate GWorld for encoding")
    graphicPixMap  = GetGWorldPixMap(graphicGWorld);
    FAIL_NIL (graphicPixMap, "\pCouldn't get the GWorld's PixMap")
    FAIL_FALSE ( LockPixels(graphicPixMap), "\pCouldn't lock GWorld PixMap")
    
    // Erase the GWorld to white and draw the graphics into it.
    // HACK alert -- this code knows way too much about the information saved off in scaling.cp.
    // The real way to do this is to have some sort of structure analogous to a grafPort, and
    // switch the existing destination that way.  Was slated for 1.1, but if that doesn't
    // happen, I'll move it back into this code.
    
    {
        CGrafPtr savePort;
        GDHandle saveDevice;
        
        Rect            saveRect            = gClipRect;
        PixMapHandle    saveDestPixMap      = gDestPixMap;
        PixMapHandle    saveBackPixMap      = gBackPixMap;
        UInt8           *saveDestBaseAddr   = gDestBaseAddr;
        UInt8           *saveBackBaseAddr   = gBackBaseAddr;
        UInt32          saveRowBytes        = gRowBytes;
 
        SInt32          top, left;
        OpenCPicParams  pictParams;
                
        GetGWorld (&savePort, &saveDevice);
        SetGWorld (graphicGWorld, NULL);
        
        ClipRect (&pictRect);
        EraseRect (&pictRect);
                
        SetDestinationBuffer (graphicPixMap, NULL);
 
        // finally, we draw the three images
        top = fBounds.top;
        left = fBounds.left;
        
        GraphicUnclipped (top, left);
        left += fBounds.right;
        DrawMaskUnclipped (top, left);
        left += fBounds.right;
        HitMaskUnclipped (top, left);
        
        // restore everything 
        
        gClipRect = saveRect;
        gDestPixMap = saveDestPixMap;
        gBackPixMap = saveBackPixMap;
        gDestBaseAddr = saveDestBaseAddr;
        gBackBaseAddr = saveBackBaseAddr;
        gRowBytes = saveRowBytes;
        
        // create the pict
        pictParams.srcRect = pictRect;
        pictParams.hRes = 0x00480000;
        pictParams.vRes = 0x00480000;
        pictParams.version = -2;
        pictParams.reserved1 = 0;
        pictParams.reserved2 = 0;
        
        thePicture = OpenCPicture (&pictParams);
        
        ClipRect(&pictRect);
        CopyBits ((BitMap *) *graphicPixMap, (BitMap *) *graphicPixMap, 
                    &pictRect, &pictRect, srcCopy, NULL);
        
        ClosePicture();
        theErr = QDError();
        
        SetGWorld (savePort, saveDevice);
    }
    
    FAIL_NIL (thePicture, "\p Couldn't open the new picture")
    FAIL_OSERR (theErr, "\pFailed to create new picture")
    
    // Add the PICT to the resource fork.
    
    AddResource( (Handle) thePicture, 'PICT', fResID, "\p" );
    theErr = ResError();
    FAIL_OSERR (theErr, "\pFailed to add the PICT resource to the file")
    
    WriteResource( (Handle) thePicture );
    FAIL_OSERR (theErr, "\pError: Failed to write the resource")
    
    UpdateResFile (CurResFile());
    theErr = ResError();
    FAIL_OSERR (theErr, "\pError: Failed to update the resource fork")
    
    goto cleanup;
 
error:
    if (theErr == noErr)
        theErr = paramErr;
        
 
cleanup:
    if (graphicGWorld != NULL)
        DisposeGWorld (graphicGWorld);
    
    if (thePicture != NULL)
    {
        SInt8   memState = HGetState((Handle)thePicture);
        if (memState & 0x20)
            ReleaseResource ((Handle) thePicture);
        else
            DisposeHandle ((Handle) thePicture);
    }
    
    return theErr;
 
}
 
 
 
/*************************************************************************************
TGraphic::LockGraphic
 
Move all fo the data high and lock it down in memory.
*************************************************************************************/
OSStatus
TGraphic::LockGraphic (void)
{
    OSStatus theErr;
    MoveHHi (fImage);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to move the draw data high")
    HLock (fImage);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to lock the draw data")
 
    MoveHHi (fHitMask);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to move the mask data high")
    HLock (fHitMask);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to lock the mask data")   
    return noErr;
    
    error:
    return theErr;
}
 
 
/*************************************************************************************
TGraphic::UnlockGraphic
    
Unlock the previously locked memory.
*************************************************************************************/
 
OSStatus
TGraphic::UnlockGraphic (void)
{
    OSStatus theErr;
    HUnlock (fImage);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to unlock the draw data")
    HUnlock (fHitMask);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pFailed to unlock the draw data") 
    return noErr;
    
    error:
    
    return theErr;
}
 
 
 
 
 
 
/*************************************************************************************
TGraphic::CopyImage
 
This routine checks against the clipping rectangle and dispatches to the correct
utility routine.
*************************************************************************************/
void
TGraphic::CopyImage (SInt32 top, SInt32 left, Boolean useBackground)
{
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
        
    if (useBackground && (gBackPixMap == NULL))
        SIGNAL_ERROR ("\pAttepting to draw from an NULL background pixmap.")
        
    if (EmptyRect (&gClipRect))
        SIGNAL_ERROR ("\pEmpty Clipping Region")
#endif
    // We'll do all our calculations in variables rather than use a structure.
    // hopefully this should speed things up
    SInt32 destTop, destBottom, destLeft, destRight;
    
    destTop    = fBounds.top + top;
    destBottom = fBounds.bottom + top;
    destLeft   = fBounds.left + left;
    destRight  = fBounds.right + left;
    
    // determine if the spite needs to be drawn at all
    if  (destTop >= gClipRect.bottom || destBottom <= gClipRect.top ||
         destLeft >= gClipRect.right || destRight <= gClipRect.left )
        // no need to draw, goodbye
        return;
    
    // determine if the sprite will be clipped
    if  (destTop < gClipRect.top || destBottom > gClipRect.bottom ||
         destLeft < gClipRect.left || destRight > gClipRect.right)
    {
        // calculate the clipped bounding box in object local coordinates
        UInt32 clipTop, clipBottom, clipLeft, clipRight;
                
        clipTop    = destTop < gClipRect.top       ? gClipRect.top - destTop  : 0;
        clipBottom = destBottom > gClipRect.bottom ? gClipRect.bottom-destTop : destBottom-destTop;
        clipLeft   = destLeft < gClipRect.left     ? gClipRect.left-destLeft  : 0;
        clipRight  = destRight > gClipRect.right   ? gClipRect.right-destLeft : destRight-destLeft;
 
        if (useBackground)
            BackgroundClipped (destTop, destLeft, clipLeft, clipRight, clipTop, clipBottom);
        else
            GraphicClipped(destTop, destLeft, clipLeft, clipRight, clipTop, clipBottom );
    }
    else
    {
        if (useBackground)
            BackgroundUnclipped (destTop, destLeft);
        else
            GraphicUnclipped(destTop, destLeft );
    }
 
error:
    return;
}
 
 
/*************************************************************************************
TGraphic::HitTest
    
This code walks the mask and returns true if the point intersects inside the mask.
 
We make sure the point is inside the rectangle.  If it in, we find the line in the
shape that corresponds to that point and walk it, looking for lines inside the mask.
 
Note that on the right side, we test greater than or equal, and on the left we only
test less than.  This is because the point that corresponds to a coord goes down and
to the left.  Think about it and you'll understand. :-)
*************************************************************************************/
Boolean
TGraphic::HitTest (SInt32 v, SInt32 h)
{   
    if( v >= fBounds.bottom || v < fBounds.top ||
        h >= fBounds.right || h < fBounds.left )
    {
        return false;
    }
    
    // Our standard things to read the sprite
    UInt8 *dataPtr;
    UInt32 tokenOp;
    UInt32 tokenData;
    
    // We use these to count where we are in the image, to move to our point.
        
    UInt32 yCount = 0;
    UInt32 xCount = 0; 
    
    // Move to the start of our hit testing data
    dataPtr = (UInt8 *)(*fHitMask);
 
    // Do a quick walk to the line which holds the point.
    while (v > 0)
    {
        v--;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
            
        dataPtr += sizeof( UInt32 );
        dataPtr += tokenData;
    }
    
    // Skip the newLine token
    dataPtr += sizeof( UInt32 );
        
    // Start reading draw commands.  Any end or newLine tokens mean we're done.
    // We'll exit out of the While loop via a return.
    do 
    {
        // get a token
        tokenOp = ( *( (UInt32 *)dataPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 );
            
        // depending on the token, we take an action
        
        // Note that line starts will be very common to begin with, so we'll move them up to the top
        switch( tokenOp )
        {
            case kHitTestPixelsToken:
 
    // We can only generate a hit in a draw command.  If our point is between
    // xCount and xCount+tokenData, then it is on a drawn line, so return true.
                if ((xCount <= h) && (xCount+tokenData>h))
                    return true;
                else if (xCount > h)
                    return false;
                xCount += tokenData;
                
                break;
                    
            case kSkipPixelsToken:
                    
                xCount += tokenData;
                        
                if (xCount > h)
                    return false;
                            
                break;
                    
            case kLineStartToken:
            case kEndShapeToken:
                return false;
#if qDebugging      
            default:
                SIGNAL_ERROR ("\pInvalid TGraphic drawing operation")
                break;
#endif
        }
    } while ( true );
 
error:
return false;
}
 
 
 
/*************************************************************************************
TGraphic::Intersect
    
This compares two TGraphic objects and two points in the SAME coordinate system
and returns true if any two drawing commands intersect in the two objects.
*************************************************************************************/
 
 
Boolean TGraphic::Intersect (TGraphic *object1, TGraphic *object2, SInt32 v1, SInt32 h1, SInt32 v2, SInt32 h2)
{
    SInt32      differenceX, differenceY;
    
    SInt32      object1right, object1bottom;
    SInt32      object2top, object2bottom, object2left, object2right;
    
    UInt32      xIntersectLeft, xIntersectRight, yHeight;
    
    Boolean     intersect = false;
    
    // First, we sort the two objects in order of the x coordinate.
    
    if (h2 < h1)
    {
        SInt32 tempNum;
        TGraphic *tempGraphic;
        
        tempGraphic = object1; object1 = object2; object2 = tempGraphic;
 
        tempNum = h2; h2 = h1; h1 = tempNum;
        tempNum = v2; v2 = v1; v1 = tempNum;
    }
 
    // Next we calculate the bounding boxes of the two graphics and check to see if they overlap.
    // if they don't we can drop out and exit.
    
    differenceX = h2-h1;
    differenceY = v2-v1;
    // create two rectangles based on the object locations.
    
    object1right = (object1->fBounds).right;
    object1bottom = (object1->fBounds).bottom;
    
    object2left = differenceX;
    object2right = object2->fBounds.right + differenceX;
    object2top = differenceY;
    object2bottom = object2->fBounds.bottom + differenceY;
 
    if (object2bottom <= 0 || object2top >= object1bottom ||
        object2left >= object1right)
        goto done;
 
    // Next we want to do some calculations that we'll use to parse the list.  Basically, we are
    // determining the coordinates we're going to use for to search for an intersection.
    
    xIntersectLeft = object2left;
    xIntersectRight = object1right > object2right  ? object2right  :  object1right;
    
    {
        UInt32 yTop, yBottom;
    
        yTop    = object2top    > 0             ? object2top     :     0;
        yBottom = object1bottom > object2bottom ? object2bottom  :     object1bottom;
 
        yHeight = yBottom-yTop; // the number of row's we'll need to test.
    }
            
// These will hold whichever command we are looking at.
    UInt32 tokenOp;
    UInt32 tokenData;
    
    // These will hold our current pointers to commands for both objects.
    UInt8 *dataPtr1, *dataPtr2;
    
    // We'll precalculate the pointers for the next line, to make it easier to change
    // both objects at the same time.
    
    UInt8 *nextDataPtr1, *nextDataPtr2;
    
    // Whenever we hit an end of line, we need to drop out and make sure both data pointers are incremented.
        
    UInt8 lineDone = false;
    
    // We occasionally need to drop out of just the inner loop -- we'll set this to true.
    
    UInt8 innerObjectDone = false;
    
    // These count where we're at in each of the two object's scanlines.
    
    UInt32 xCount1 = 0, xCount2 = 0;
 
 
 
    // move to the start of each shape's data.
    dataPtr1 = (UInt8 *)(*(object1->fHitMask));
    dataPtr2 = (UInt8 *)(*(object2->fHitMask));
 
    // One of the two objects will probably need to be advanced in the vertical coordinate to put us
    // in the correct position.  We'll count out a number of skips equal to the previous y difference
    // we calculated.
    // now we need to skip lines until we get to the correct starting location for each shape.
    // With the current encoder, all objects start with startline commands.  So we'll cound out a number
    // of skips equal to the intersection rect's top Y coordinate.
    if (differenceY > 0)
        do
        {
            tokenData = ( *( (UInt32 *)dataPtr1 ) ) & 0x00ffffff;
            
            dataPtr1 += sizeof( UInt32 );
            dataPtr1 += tokenData;
        }
        while (--differenceY > 0);
    else
        while (differenceY++ < 0)
        {
            tokenData = ( *( (UInt32 *)dataPtr2 ) ) & 0x00ffffff;
            
            dataPtr2 += sizeof( UInt32 );
            dataPtr2 += tokenData;
        }
    // Now we're ready to begin.  For the most part, we loop inside object1 looking for a draw command
    // inside the intersected area.  If we find one, then we run a second loop inside this, looking
    // for a draw commmand in object2 that intersects the one from object1.  If we find one, we return
    // true.  If we hit the end of the shape, or the intersected area, we return false.
    
    // If we hit the end of a particular line, then we drop out of both loops and advance the pointers
    // appropriately, and run the next line.
    
    do 
    {
    // First, we retrieve the start of line command for each of the objects -- from this we can
    // calculate the pointers for the next scanline, which we use when we drop out of the search
    // loops.  
        tokenOp = ( *( (UInt32 *)dataPtr1 ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr1 ) ) & 0x00ffffff;
            
        dataPtr1 += sizeof( UInt32 );
        nextDataPtr1 = dataPtr1+ tokenData;
        
        tokenOp = ( *( (UInt32 *)dataPtr2 ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr2 ) ) & 0x00ffffff;
            
        dataPtr2 += sizeof( UInt32 );
        nextDataPtr2 = dataPtr2+ tokenData;
        
        // reset our booleans and our horizontal positions.
        lineDone = false;
        xCount1 = 0;
        xCount2 = xIntersectLeft;
        
 
        
        while (!lineDone)
        {
        // Loop in object1 looking for a draw command inside the intersected area.  If we hit the end
        // of our intersected area or a new line token, we'll drop out of the loop.
        // Get the next operation
            tokenOp = ( *( (UInt32 *)dataPtr1 ) ) >> 24;
            tokenData = ( *( (UInt32 *)dataPtr1 ) ) & 0x00ffffff;
            
            dataPtr1 += sizeof( UInt32 );
        
            switch (tokenOp)
            {
                case kHitTestPixelsToken:
                    // kDrawPixels is the ugly token to parse, because if the drawing command
                    // is inside the intersected area, we have to run ANOTHER loop for object 2.
                    {
                    UInt32 x1RightDraw = xCount1+tokenData;
                    UInt32 x1LeftDraw = xCount1;
                    // skip to next command
                    
                    // if we're still to the left of our intersection, skip to next set
                    if (x1RightDraw <= xIntersectLeft)
                        break;
                        
                    // pin left side to intersection
                    if (x1LeftDraw < xIntersectLeft)
                        x1LeftDraw = xIntersectLeft;
                        
                    // pin to right side to intersection
                    if (x1RightDraw > xIntersectRight)
                        x1RightDraw = xIntersectRight;
                    
                    // Now we loop inside of object 2 until we find an intersecting draw command
                    // or skip past object1's draw area.
                    innerObjectDone = false;
                    
                    while (!(lineDone || innerObjectDone)) 
                    {
                        tokenOp = ( *( (UInt32 *)dataPtr2 ) ) >> 24;
                        tokenData = ( *( (UInt32 *)dataPtr2 ) ) & 0x00ffffff;
                        
                        switch (tokenOp)
                        {
                            case kHitTestPixelsToken:
                                // Now we can finally check for an intersection!!
                                    
                                if (!(((xCount2 <= x1LeftDraw) && (xCount2+tokenData <= x1LeftDraw)) ||
                                      ((xCount2 >= x1RightDraw) && (xCount2+tokenData > x1RightDraw))))
                                {
                                    intersect = true;
                                    goto done;
                                }
                                    
                                // if we are to the right of object1's drawing command, then we drop out of
                                // the object2 loop.  Note that in this case we do not advance to the next
                                // draw command in object2, as we might need to check the next drawing command
                                // in object1 against it.
                                    
                                if (xCount2+tokenData >= x1RightDraw)
                                {
                                    innerObjectDone = true;
                                }
                                else
                                {
                                    xCount2 += tokenData;
                                    dataPtr2 += sizeof( UInt32 );
                                }
                                break;
                                
                            case kSkipPixelsToken:
                                xCount2 += tokenData;
                                dataPtr2 += sizeof( UInt32 );
                                if (xCount2 > xIntersectRight)
                                    innerObjectDone = true;
                                break;
                                
                            case kLineStartToken:
                                lineDone = true;
                                break;
                            case kEndShapeToken:
                                goto done;
                                break;
#if qDebugging
                            default:
                                Debugger();
                                break;
#endif
                        } // end inner switch
                    } // end inner while loop
                    
                    // and make sure we exit if were at the end of the line in object1.
                    if (xCount1 >= xIntersectRight)
                        lineDone = true;
                    }
                    break;
                    
                case kSkipPixelsToken:
                    xCount1 += tokenData;
                    if (xCount1 >= xIntersectRight)
                        lineDone = true;
                    break;
                case kLineStartToken:
                    lineDone = true;
                    break;
                case kEndShapeToken:
                    goto done;
                    break;
#if qDebugging
                default:
                    Debugger();
                    break;
#endif
            }
        }           
        // skip to the next line for both and increment the count.
        dataPtr1 = nextDataPtr1;
        dataPtr2 = nextDataPtr2;
    } while (--yHeight > 0);
    
    
    done: 
    // If we got this far, we've dropped out of the bottom of the intersect area. 
    return intersect;
}
 
 
 
/*************************************************************************************
TGraphic::GraphicClipped
*************************************************************************************/
    
void
TGraphic::GraphicClipped (SInt32 top, SInt32 left, UInt32 clipLeft, UInt32 clipRight,
                          UInt32 clipTop, UInt32 clipBottom)
 
// This routine is almost identical to the one from Tips of the Mac Game Programming Gurus.
{
    UInt8   *rowStart;      // the pointer to the start of this row
    UInt8   *srcPtr;        // the current position in the sprite data
    UInt8   *destPtr;       // the current position in the destination pixmap
    UInt32  miscCounter;    // a counter for various purposes
    UInt32  extraCounter;   // a counter for right clippling purposes ( how much extra was there? )
    UInt32  tokenOp;        // the op code from the token
    UInt32  tokenData;      // the data from the token
    UInt32  yCount;         // how many lines do we have left to scan.
    UInt32  xCount;         // where are we in this line?
    
    // Determine the actual clipping area we're going to need to draw, we do these as UInt32s because
    // that's what we'll use when drawing.
        
    // set up the initial count of rows we'll want to search
    yCount = clipBottom-clipTop;
 
    // determine characteristics about the pixmap
    rowStart = gDestBaseAddr + (top + clipTop) * gRowBytes + left;
 
    // move to the start of the image data.  We'll do a quick skip of any initial pixel rows that we don't
    // need to worry about.
    srcPtr = (UInt8 *)( *fImage);
 
    // We do this to get rid of an optimizer error.  Normally, the destPtr and xCount get
    // set as part of the first token that gets interpreted by the loop.
    
#if qDebugging
    destPtr = (UInt8 *)0xDDDDDDDD;
#else
    destPtr = rowStart;
#endif  
    xCount = 0;  
 
    while (clipTop > 0)
    {
        clipTop--;
        tokenData = ( *( (UInt32 *)srcPtr ) ) & 0x00ffffff;
        srcPtr += sizeof( UInt32 ) + tokenData;
    }
    
    // start the loop
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)srcPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)srcPtr ) ) & 0x00ffffff;
        srcPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
            case kDrawPixelsToken:
                miscCounter = tokenData;
                extraCounter = 0;
                        
                // if we need to, clip to the left
                if( xCount < clipLeft )
                {
                    // if this run does not appear at all, don't draw it
                    if ( miscCounter < clipLeft - xCount )
                    {
                        destPtr += miscCounter;
                        xCount += miscCounter;
                        miscCounter = ALIGN_TO_NEXT_LONG(miscCounter);                  
                        srcPtr += miscCounter;
 
                        break;
                    }
                    else
                    {
                        // if it does, skip to where we can draw
                        miscCounter -= clipLeft - xCount;
                        destPtr += clipLeft - xCount;
                        srcPtr += clipLeft - xCount;
                        xCount = clipLeft;
                    }
                }
                        
                // if we need to, clip to the right
                if ( xCount + miscCounter > clipRight )
                {
                    // if this run does not appear at all, skip it
                    if ( xCount > clipRight )
                    {
                        // once xCount is greater than xRight, no drawing commands will ever
                        // be issued on that line, so I changed the "gurus" code, which was actually
                        // wasting a couple of instructions setting variables that aren't needed.
                        //destPtr += miscCounter;
                        //xCount += miscCounter;
                        miscCounter = ALIGN_TO_NEXT_LONG(miscCounter);                  
                        srcPtr += miscCounter;
                        break;
                    }
                    else
                    {
                        // if it does, setup to draw what we can
                        extraCounter = (xCount + miscCounter) - clipRight;
                        miscCounter -= extraCounter;
                    }
                }
                
                // adjust xCount for the run
                xCount += miscCounter;
                        
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits, blitloop;
                sixteenblits = miscCounter >> 4;
                for ( blitloop = 0; blitloop < sixteenblits; blitloop++)
                {
                        register UInt32 temp1, temp2, temp3, temp4;
                        temp1 = ((UInt32 *) srcPtr)[0];
                        temp2 = ((UInt32 *) srcPtr)[1];
                        temp3 = ((UInt32 *) srcPtr)[2];
                        temp4 = ((UInt32 *) srcPtr)[3];
                        ((UInt32 *) destPtr)[0] = temp1;
                        ((UInt32 *) destPtr)[1] = temp2;
                        ((UInt32 *) destPtr)[2] = temp3;
                        ((UInt32 *) destPtr)[3] = temp4;
                        srcPtr += 16;
                        destPtr += 16;
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    register UInt32 temp1, temp2;
                    temp1 = ((UInt32 *) srcPtr)[0];
                    temp2 = ((UInt32 *) srcPtr)[1];
                    ((UInt32 *) destPtr)[0] = temp1;
                    ((UInt32 *) destPtr)[1] = temp2;
                    srcPtr += 8;
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    register UInt32 temp1;
                    temp1 = *((UInt32 *) srcPtr);
                    srcPtr +=4;
                    *((UInt32 *) destPtr)  = temp1;
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    register UInt16 temp1;
                    temp1 = *((UInt16 *) srcPtr);
                    srcPtr +=2;
                    *((UInt16 *) destPtr)  = temp1;
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *srcPtr++;
        
                }
                        
                // adjust for right clipping
                destPtr += extraCounter;
                srcPtr += extraCounter;
                xCount += extraCounter;
                        
                // adjust for the padding
                srcPtr = (UInt8 *) ALIGN_TO_NEXT_LONG(srcPtr);  
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                xCount += tokenData;
                break;
                        
            case kLineStartToken:
                // check to see if we're done drawing
                if (yCount == 0)
                    return;
                else
                {
                    // set all the counters up
                    yCount--;
                    xCount = 0;
                    // set up the destination pointer
                    destPtr = rowStart;
                    rowStart += gRowBytes;
                }
                break;
                        
            case kEndShapeToken:
                // exit.
                return;
                break;
#if qDebugging                      
            default:
                // we should never get here
                Debugger();
                break;
#endif
        }
    } while (true);
}
 
 
/*************************************************************************************
TGraphic::GraphicUnclipped
*************************************************************************************/
 
void 
TGraphic::GraphicUnclipped (SInt32 top, SInt32 left)
{
// This routine is almost identical to the one from Tips of the Mac Game Programming Gurus.
 
    UInt8 *rowStart;        // the pointer to the start of this row
    UInt8 *srcPtr;          // the current position in the sprite data
    UInt8 *destPtr;         // the current position in the destination pixmap
    UInt32 miscCounter;     // a counter for various purposes
    UInt32 tokenOp;         // the op code from the token
    UInt32 tokenData;       // the data from the token
 
 
// If we are debugging and we have an invalid graphic, then destPtr might never be set
// up correctly.  This will force us to attempt to write to a bad location in memory.
    
    // determine characteristics about the pixmap
    rowStart = gDestBaseAddr + top * gRowBytes + left;
    
    // move to the start of the image data.
    srcPtr = (UInt8 *)( *fImage );
    
#if qDebugging
    destPtr = (UInt8 *) 0xDDDDDDDD;
#else
    destPtr = rowStart;
#endif  
    
    // Start looping
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)srcPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)srcPtr ) ) & 0x00ffffff;
        srcPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
            case kDrawPixelsToken:
                miscCounter = tokenData;
                
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits, blitloop;
                sixteenblits = miscCounter >> 4;
                for ( blitloop = 0; blitloop < sixteenblits; blitloop++)
                {
                        register UInt32 temp1, temp2, temp3, temp4;
                        temp1 = ((UInt32 *) srcPtr)[0];
                        temp2 = ((UInt32 *) srcPtr)[1];
                        temp3 = ((UInt32 *) srcPtr)[2];
                        temp4 = ((UInt32 *) srcPtr)[3];
                        ((UInt32 *) destPtr)[0] = temp1;
                        ((UInt32 *) destPtr)[1] = temp2;
                        ((UInt32 *) destPtr)[2] = temp3;
                        ((UInt32 *) destPtr)[3] = temp4;
                        srcPtr += 16;
                        destPtr += 16;
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    register UInt32 temp1, temp2;
                    temp1 = ((UInt32 *) srcPtr)[0];
                    temp2 = ((UInt32 *) srcPtr)[1];
                    ((UInt32 *) destPtr)[0] = temp1;
                    ((UInt32 *) destPtr)[1] = temp2;
                    srcPtr += 8;
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    register UInt32 temp1;
                    temp1 = *((UInt32 *) srcPtr);
                    srcPtr +=4;
                    *((UInt32 *) destPtr)  = temp1;
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    register UInt16 temp1;
                    temp1 = *((UInt16 *) srcPtr);
                    srcPtr +=2;
                    *((UInt16 *) destPtr)  = temp1;
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *srcPtr++;
        
                }
                
                // adjust for the padding
                srcPtr = (UInt8 *) ALIGN_TO_NEXT_LONG(srcPtr);  
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                break;
                        
            case kLineStartToken:
                // set up the destination pointer
                destPtr = rowStart;
                rowStart += gRowBytes;
                break;
                        
            case kEndShapeToken:
                // exit the routine
                return;
                break;
#if qDebugging
            default:
                // we should never get here
                Debugger();
                break;
#endif
        }
    }   while( true );
}
 
 
/*************************************************************************************
TGraphic::BackgroundClipped
    
The Background routines work similarly to the standard graphic drawing routines,
but we keep an additional pointer to the destination graphics world, and we draw from
it, not the graphic data.  Later, we might optimize this routine further as we know
the two are always in sync.
*************************************************************************************/
 
 
void
TGraphic::BackgroundClipped (SInt32 top, SInt32 left, UInt32 clipLeft, UInt32 clipRight, 
                             UInt32 clipTop, UInt32 clipBottom)
{
    UInt8 *sourceRowStart;  // the pointer to the start of this row in the source
    UInt8 *destRowStart;    // the pointer to the start of this row in the dest
    UInt8 *srcPtr;          // the current position in the source pixmap
    UInt8 *destPtr;         // the current position in the destination pixmap
    UInt8 *dataPtr;         // the current position in the sprite data;
    UInt32 miscCounter;     // a counter for various purposes
    UInt32 extraCounter;    // a counter for right clippling purposes ( how much extra was there? )
    UInt32 tokenOp;         // the op code from the token
    UInt32 tokenData;       // the data from the token
    UInt32 yCount;          // how many lines do we still have to draw
    UInt32 xCount;          // where are we in this line?
 
    // determine characteristics about the pixmap
    sourceRowStart = gBackBaseAddr + (top + clipTop) * gRowBytes + left;
    destRowStart   = gDestBaseAddr + (top + clipTop) * gRowBytes + left;
    
    // move to the start of the image data.  We'll do a quick skip of any initial pixel
    // rows that we don't need to worry about.
    dataPtr = (UInt8 *)( *fImage);
    
    yCount = clipBottom - clipTop;
 
#if qDebugging
    srcPtr =  (UInt8 *) 0xDDDDDDDD;
    destPtr = (UInt8 *) 0xDDDDDDDD;
#else
    srcPtr = sourceRowStart;
    destPtr = destRowStart;
#endif  
    xCount = 0;
    
    while (clipTop > 0)
    {
        clipTop--;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 ) + tokenData;
    }
    
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)dataPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
            case kDrawPixelsToken:
                miscCounter = tokenData;
                extraCounter = 0;
                        
                // advance the data pointer for free
                tokenData = ALIGN_TO_NEXT_LONG (tokenData);
                dataPtr += tokenData;
                        
                // if we need to, clip to the left
                if (xCount < clipLeft )
                {
                    // if this run does not appear at all, don't draw it
                    if ( miscCounter < clipLeft - xCount )
                    {
                        destPtr += miscCounter;
                        srcPtr += miscCounter;
                        xCount += miscCounter;
                        break;
                    }
                    else
                    {
                    // if it does, skip to where we can draw
                        miscCounter -= clipLeft - xCount;
                        destPtr += clipLeft - xCount;
                        srcPtr += clipLeft - xCount;
                        xCount = clipLeft;
                    }
                }
                        
                // if we need to, clip to the right
                if ( xCount + miscCounter > clipRight )
                {
                    // if this run does not appear at all, skip it
                    if ( xCount > clipRight )
                        // once xCount is greater than xRight, no drawing commands will ever
                        // be issued on that line, so I changed the "gurus" code, which was actually
                        // wasting a couple of instructions setting variables that aren't needed.
                        //destPtr += miscCounter;
                        //srcPtr += miscCounter;
                        //xCount += miscCounter;
                        break;
                    else
                    {
                        // if it does, setup to draw what we can
                        extraCounter = (xCount + miscCounter) - clipRight;
                        miscCounter -= extraCounter;
                    }
                }
                        
                // adjust xCount for the run
                xCount += miscCounter;
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits, blitloop;
                sixteenblits = miscCounter >> 4;
                for ( blitloop = 0; blitloop < sixteenblits; blitloop++)
                {
                        register UInt32 temp1, temp2, temp3, temp4;
                        temp1 = ((UInt32 *) srcPtr)[0];
                        temp2 = ((UInt32 *) srcPtr)[1];
                        temp3 = ((UInt32 *) srcPtr)[2];
                        temp4 = ((UInt32 *) srcPtr)[3];
                        ((UInt32 *) destPtr)[0] = temp1;
                        ((UInt32 *) destPtr)[1] = temp2;
                        ((UInt32 *) destPtr)[2] = temp3;
                        ((UInt32 *) destPtr)[3] = temp4;
                        srcPtr += 16;
                        destPtr += 16;
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    register UInt32 temp1, temp2;
                    temp1 = ((UInt32 *) srcPtr)[0];
                    temp2 = ((UInt32 *) srcPtr)[1];
                    ((UInt32 *) destPtr)[0] = temp1;
                    ((UInt32 *) destPtr)[1] = temp2;
                    srcPtr += 8;
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    register UInt32 temp1;
                    temp1 = *((UInt32 *) srcPtr);
                    srcPtr +=4;
                    *((UInt32 *) destPtr)  = temp1;
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    register UInt16 temp1;
                    temp1 = *((UInt16 *) srcPtr);
                    srcPtr +=2;
                    *((UInt16 *) destPtr)  = temp1;
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *srcPtr++;
        
                }
                            
                // adjust for right clipping
                destPtr += extraCounter;
                srcPtr += extraCounter;
                xCount += extraCounter;
                        
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                srcPtr += tokenData;
                        
                xCount += tokenData;
                break;
                        
            case kLineStartToken:
                // check to see if we're done drawing
                if (yCount == 0)
                    return;
                else
                {
                    // set all the counters up
                    yCount--;
                    xCount = 0;
                    // set up the source and destination pointers
                    destPtr = destRowStart;
                    srcPtr = sourceRowStart;
                    destRowStart += gRowBytes;
                    sourceRowStart += gRowBytes;
                }
                break;
            case kEndShapeToken:
                return;
                break;
#if qDebugging
            default:
            // we should never get here
            Debugger();
            break;
#endif
        }
    } while (true);
}
 
/*************************************************************************************
TGraphic::BackgroundUnclipped
    
The Background routines work similarly to the standard graphic drawing routines, but
we keep an additional pointer to the destination graphics world, and we draw from it,
not the graphic data.  Later, we might optimize this routine further as we know the
two are always in sync.
*************************************************************************************/
 
void 
TGraphic::BackgroundUnclipped (SInt32 top, SInt32 left)
{
    UInt8 *destRowStart;                // the pointer to the start of this row
    UInt8 *sourceRowStart;
    UInt8 *srcPtr;                      // the current position in the source pixmap
    UInt8 *destPtr;                     // the current position in the destination pixmap
    UInt8 *dataPtr;                     // the current position in the sprite data
    UInt32 miscCounter;                 // a counter for various purposes
    UInt32 tokenOp;                     // the op code from the token
    UInt32 tokenData;                   // the data from the token
    
    // determine characteristics about the pixmap
    destRowStart   = gDestBaseAddr + top * gRowBytes + left;
    sourceRowStart = gBackBaseAddr + top * gRowBytes + left;
    
    // move to the start of the graphic data
    dataPtr = (UInt8 *)( *fImage );
 
#if qDebugging
    srcPtr  = (UInt8 *) 0xDDDDDDDD;
    destPtr = (UInt8 *) 0xDDDDDDDD;
#else
    srcPtr = sourceRowStart;
    destPtr = destRowStart;
#endif  
    
    // loop until we are done
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)dataPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
        case kDrawPixelsToken:
            miscCounter = tokenData;
            // adjust the data pointer
            tokenData = ALIGN_TO_NEXT_LONG (tokenData);
            dataPtr += tokenData;
    
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits, blitloop;
                sixteenblits = miscCounter >> 4;
                for ( blitloop = 0; blitloop < sixteenblits; blitloop++)
                {
                        register UInt32 temp1, temp2, temp3, temp4;
                        temp1 = ((UInt32 *) srcPtr)[0];
                        temp2 = ((UInt32 *) srcPtr)[1];
                        temp3 = ((UInt32 *) srcPtr)[2];
                        temp4 = ((UInt32 *) srcPtr)[3];
                        ((UInt32 *) destPtr)[0] = temp1;
                        ((UInt32 *) destPtr)[1] = temp2;
                        ((UInt32 *) destPtr)[2] = temp3;
                        ((UInt32 *) destPtr)[3] = temp4;
                        srcPtr += 16;
                        destPtr += 16;
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    register UInt32 temp1, temp2;
                    temp1 = ((UInt32 *) srcPtr)[0];
                    temp2 = ((UInt32 *) srcPtr)[1];
                    ((UInt32 *) destPtr)[0] = temp1;
                    ((UInt32 *) destPtr)[1] = temp2;
                    srcPtr += 8;
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    register UInt32 temp1;
                    temp1 = *((UInt32 *) srcPtr);
                    srcPtr +=4;
                    *((UInt32 *) destPtr)  = temp1;
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    register UInt16 temp1;
                    temp1 = *((UInt16 *) srcPtr);
                    srcPtr +=2;
                    *((UInt16 *) destPtr)  = temp1;
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *srcPtr++;
        
                }
    
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                srcPtr += tokenData;    
                break;
                    
            case kLineStartToken:
                // set up the source and destination
                destPtr = destRowStart;
                srcPtr = sourceRowStart;
                destRowStart += gRowBytes;
                sourceRowStart += gRowBytes;
                break;
                        
            case kEndShapeToken:
                // signal a loop exit
                return;
                break;
#ifdef qDebugging
            default:
                // we should never get here
                Debugger();
                break;
#endif
        }
    } while (true);
}
 
 
/*************************************************************************************
TGraphic::DrawMaskUnclipped
    
This version of the drawing loop draws black anywhere the draw mask is active.
Useful for writing a PICT out to disk with the mask information.
*************************************************************************************/
 
void 
TGraphic::DrawMaskUnclipped (SInt32 top, SInt32 left)
{
    UInt8 *destRowStart;                // the pointer to the start of this row
    UInt8 *destPtr;                     // the current position in the destination pixmap
    UInt8 *dataPtr;                     // the current position in the sprite data
    UInt32 miscCounter;                 // a counter for various purposes
    UInt32 tokenOp;                     // the op code from the token
    UInt32 tokenData;                   // the data from the token
    UInt32 indexSmear;
    UInt32 indexSmear2;
    
    indexSmear = kMaskColorIndex;
    indexSmear |= (kMaskColorIndex << 8);
    indexSmear |= (indexSmear << 16);
    
    indexSmear2 = indexSmear;
    
    // determine characteristics about the pixmap
    destRowStart = gDestBaseAddr + top * gRowBytes + left;
    
    // move to the start of the graphic data
    dataPtr = (UInt8 *)( *fImage );
    
#if qDebugging
    destPtr = (UInt8 *) 0xDDDDDDDD;
#else
    destPtr = (UInt8 *) destRowStart;
#endif  
 
    // loop until we are done
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)dataPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
        case kDrawPixelsToken:
            miscCounter = tokenData;
            // adjust the data pointer
            tokenData = ALIGN_TO_NEXT_LONG (tokenData);
            dataPtr += tokenData;
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits, blitloop;
                sixteenblits = miscCounter >> 4;
                for (blitloop = 0; blitloop < sixteenblits; blitloop++)
                {
                    ((UInt32 *) destPtr)[0] = *((UInt32 *) &indexSmear);
                    ((UInt32 *) destPtr)[1] = *((UInt32 *) &indexSmear);
                    ((UInt32 *) destPtr)[2] = *((UInt32 *) &indexSmear);
                    ((UInt32 *) destPtr)[3] = *((UInt32 *) &indexSmear);
                    destPtr += 16;
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    ((UInt32 *) destPtr)[0] = *((UInt32 *) &indexSmear);
                    ((UInt32 *) destPtr)[1] = *((UInt32 *) &indexSmear);
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    *((UInt32 *) destPtr)  = *((UInt32 *) &indexSmear);
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    *((UInt16 *) destPtr) = *((UInt16 *) &indexSmear);
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *((UInt8 *) &indexSmear);
        
                }
    
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                break;
                    
            case kLineStartToken:
                // set up the source and destination
                destPtr = destRowStart;
                destRowStart += gRowBytes;
                break;
                        
            case kEndShapeToken:
                // signal a loop exit
                return;
                break;
#ifdef qDebugging
            default:
                // we should never get here
                Debugger();
                break;
#endif
        }
    } while (true);
}
 
/*************************************************************************************
TGraphic::HitMaskUnclipped
    
Same as DrawMaskUnclipped, but using the fHitMask data instead.
*************************************************************************************/
 
void 
TGraphic::HitMaskUnclipped (SInt32 top, SInt32 left)
{
    UInt8 *destRowStart;                // the pointer to the start of this row
    UInt8 *destPtr;                     // the current position in the destination pixmap
    UInt8 *dataPtr;                     // the current position in the sprite data
    UInt32 miscCounter;                 // a counter for various purposes
    UInt32 tokenOp;                     // the op code from the token
    UInt32 tokenData;                   // the data from the token
    UInt32 indexSmear;
    UInt32 indexSmear2;
    
    indexSmear = kMaskColorIndex;
    indexSmear |= (kMaskColorIndex << 8);
    indexSmear |= (indexSmear << 16);
    
    indexSmear2 = indexSmear;
    
    // determine characteristics about the pixmap
    destRowStart = gDestBaseAddr + top * gRowBytes + left;
    
    // move to the start of the graphic data
    dataPtr = (UInt8 *)( *fHitMask );
 
#if qDebugging
    destPtr = (UInt8 *) 0xDDDDDDDD;
#else
    destPtr = destRowStart;
#endif  
    
    // loop until we are done
    do
    {
        // get a token
        tokenOp = ( *( (UInt32 *)dataPtr ) ) >> 24;
        tokenData = ( *( (UInt32 *)dataPtr ) ) & 0x00ffffff;
        dataPtr += sizeof( UInt32 );
            
        // depending on the token
        switch( tokenOp )
        {
        case kDrawPixelsToken:
            miscCounter = tokenData;
    
                // This blit loop is optimized to move 32 bytes or less.  On 604 machines, using an 8 byte move
                // is faster in those rare cases where you can get them to align, and way slower when they don't.
                // because of the rarity of these cases, this blit loop doesn't do anything fancy with doubles.
                
                
                {
                register UInt32 sixteenblits;
                sixteenblits = miscCounter >> 4;
                if (sixteenblits)
                {
                    do
                    {
                        ((UInt32 *) destPtr)[0] = *((UInt32 *) &indexSmear);
                        ((UInt32 *) destPtr)[1] = *((UInt32 *) &indexSmear);
                        ((UInt32 *) destPtr)[2] = *((UInt32 *) &indexSmear);
                        ((UInt32 *) destPtr)[3] = *((UInt32 *) &indexSmear);
                        destPtr += 16;
                    }  while (--sixteenblits != 0);
                }
                // move any remaining data, up to 15 bytes total
                if (miscCounter & 0x8)
                {
                    ((UInt32 *) destPtr)[0] = *((UInt32 *) &indexSmear);
                    ((UInt32 *) destPtr)[1] = *((UInt32 *) &indexSmear);
                    destPtr += 8;
                }
                if (miscCounter & 0x4)
                {
                    *((UInt32 *) destPtr)  = *((UInt32 *) &indexSmear);
                    destPtr +=4;
                }
                if (miscCounter & 0x2)
                {
                    *((UInt16 *) destPtr) = *((UInt16 *) &indexSmear);
                    destPtr +=2;
                }
                if (miscCounter & 0x1)
                    *destPtr++ = *((UInt8 *) &indexSmear);
        
                }
 
                break;
                        
            case kSkipPixelsToken:
                destPtr += tokenData;
                break;
                    
            case kLineStartToken:
                // set up the source and destination
                destPtr = destRowStart;
                destRowStart += gRowBytes;
                break;
                        
            case kEndShapeToken:
                // signal a loop exit
                return;
                break;
#ifdef qDebugging
            default:
                // we should never get here
                Debugger();
                break;
#endif
        }
    } while (true);
}