MoofWars/TTileCollection.cp

/*
    File:       TTileCollection.cp
 
    Contains:   A TileCollection is a set of tiles that can be used to draw a grid.  
                Essentially, this class performs the same function that the TGraphicCollection
                class does, but it is designed just to draw 32x32x8 tiles.  By making this
                code as specific as possible, this code is more than twice as fast as using
                TGraphics to draw the tiles.
 
    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
                
                1/23/97     Timothy Carroll Added include for Moofwars.h so that MrC will
                            compile
                            
                8/15/96     Timothy Carroll Initial Release
                
 
*/
 
#include <Memory.h>
#include <Resources.h>
 
 
#include "Moofwars.h"
#include "TTileCollection.h"
#include "Scaling.h"
 
#include "GridTilesFormat.h"
 
 
const int kTileSize = 1024;
const int kTileGroupSize = (kTileSize+1)*8;
 
/*************************************************************************************
 
Internal Declarations
 
We will hold the list of created TTileCollection objects in a handle.  This handle is 
automatically created the first time we load a TTileCollection and is deallocated when
the list is empty.
 
All of the allocation and deallocation is handled by the static NewCollection call.
 
NewCollection has a few utility routines it calls on to manage the handle.  Mainly they do
the searching, insertion and deletion from the list.
*************************************************************************************/
 
static Handle gTileCollectionList = NULL;
static unsigned long gNumberItemsInList = 0;
 
 
 
static OSErr InsertCollectionIntoList (TTileCollection *theCollection, UInt32 index);
static OSErr DeleteCollectionFromList (UInt32 index);
static OSErr SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index);
 
/*************************************************************************************
    InsertCollectionIntoList
    
    This routine creates the handle if necessary, and otherwise inserts the TTileCollection
    into the list at the index location given.
    
    Assumptions:
        theCollection must be a legitimate TTileCollection -- no NULL parameters.
        index must be no more than 1 larger than gNumberItemsInList. ?????
*************************************************************************************/
 
OSErr InsertCollectionIntoList (TTileCollection *theCollection, UInt32 index)
{
    OSErr theErr = noErr;
 
#if qDebugging
    if (index > gNumberItemsInList)
        SIGNAL_ERROR ("\pAttempting to insert collection at invalid index")
    if (theCollection == NULL)
        SIGNAL_ERROR ("\pAttempting to insert a null collection into the list") 
    
#endif
    
    if (gTileCollectionList == NULL)
    {
        gNumberItemsInList = 1;
        
        gTileCollectionList = NewHandleClear (sizeof (TTileCollection *));
        theErr = MemError();
        
        FAIL_OSERR (theErr, "\pCouldn't allocate a new handle of information")
        FAIL_NIL (gTileCollectionList, "\pCouldn't allocate a new handle of information")
        
    }
    else
    {
        gNumberItemsInList++;
        SetHandleSize(gTileCollectionList,gNumberItemsInList*sizeof(TTileCollection *));
        
        theErr = MemError();
        FAIL_OSERR (theErr, "\pCouldn't resize the list handle")
        FAIL_NIL (gTileCollectionList, "\pCouldn't resize the list handle")
        
        // Shift the data to make room
        BlockMoveData(  (*(TTileCollection ***)gTileCollectionList)+index,
                        (*(TTileCollection ***)gTileCollectionList)+index+1,
                        (gNumberItemsInList - index-1) * sizeof(TTileCollection *));
    }
    
    // finally, set the new object in place
            *((*(TTileCollection ***) gTileCollectionList)+index) = theCollection;
 
    return noErr;
    
    error:
    
    
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
}
 
 
/*************************************************************************************
    DeleteCollectionFromList
    
    This routine removes the collection from the list and destroys it.  If there are no more items
    in the list then we dispose of the handle.
    
    Assumptions:
    index must be within the list.
*************************************************************************************/
OSErr DeleteCollectionFromList (UInt32 index)
{
    OSErr theErr;
    
    gNumberItemsInList--;
    
    //  slide remaining elements up
    
    BlockMoveData(  (*(TTileCollection ***)gTileCollectionList)+index+1,
                    (*(TTileCollection ***)gTileCollectionList)+index,
                    (gNumberItemsInList - index) * sizeof(TTileCollection *));
 
    //  cut back the storage and dispose of the handle if we have no items left
    
    
    if (gNumberItemsInList> 0)
    {
        SetHandleSize((Handle) gTileCollectionList,gNumberItemsInList*sizeof(TTileCollection *));
        theErr = MemError();
        FAIL_OSERR (theErr, "\pCouldn't resize the list handle")
        FAIL_NIL (gTileCollectionList, "\pCouldn't resize the list handle")
    }
    
    else
    {
        DisposeHandle (gTileCollectionList);
        gTileCollectionList = NULL;
    }
    
    
    return noErr;
    error:
    
    
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
}
 
 
/*************************************************************************************
    SearchCollectionList
    
    This routine searches through the list and attempts to find an existing TTileCollection with
    that resID.  If it finds one with that resID, then it returns the index to that collection and
    sets found to true.  If it doesn't find that resource, then it sets found to false AND sets the
    index to where it should be inserted into the list.
*************************************************************************************/
OSErr SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index)
{   
    UInt32 low = 0;
    UInt32 high = gNumberItemsInList;
    UInt32 tempIndex;
    TTileCollection *theItem;
    
    OSErr theErr = noErr;
    
    *found = false;
    
    while (low < high)
        {
        tempIndex = (low+high) >> 1;
        
        theItem = (*(TTileCollection ***)gTileCollectionList)[tempIndex];
        
        FAIL_NIL (theItem, "\pBad TTile object")
        
        if (resID < theItem->GetResID())
            high = tempIndex;                   //  element is below "high"
        else if (resID == theItem->GetResID())
            {
            *found = true;
            *index = tempIndex;
            return noErr;
            }
        else
            low = tempIndex+1;                  //  element is above "low"
        }
    
    // use final calculations to put insert in the right place in the list
    *index = (low+high) >> 1;
    return noErr;
    
    error:
    
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
 
 
}
/*************************************************************************************
    TTileCollection::NewCollection
    
    This routine merely uses the routines we created above to properly create and load
    the TTileCollections.
*************************************************************************************/
TTileCollection
*TTileCollection::NewCollection (SInt16 resID)
{
    unsigned long       listIndex;
    Boolean             tableAlreadyExists;
    TTileCollection *newCollection = NULL;
    OSErr               theErr;
    
    theErr = SearchCollectionList (resID, &tableAlreadyExists, &listIndex);
    FAIL_OSERR(theErr,"\pCouldn't search the collection list in TTileCollection::NewCollection")
    
    if (tableAlreadyExists)
    {
        // find the existing collection and add 1 to the ref count.
        newCollection = (*(TTileCollection ***)gTileCollectionList)[listIndex];
        newCollection->AddReference();
    }
    else
    {
        // create a new collection
        newCollection = new TTileCollection(resID);
        theErr = newCollection->CreateCollection ();
        FAIL_OSERR(theErr,"\pCouldn't create new TTileCollection")
        theErr = InsertCollectionIntoList(newCollection, listIndex);
        FAIL_OSERR(theErr, "\pCouldn't add new TTileCollection to list")
        newCollection->AddReference();
    }
    
    return newCollection;
    error:
        if (newCollection != NULL)
            delete newCollection;
        return NULL;
}
 
 
/*************************************************************************************
    TTileCollection::AddReference
*************************************************************************************/
 
void
TTileCollection::AddReference (void)
   {
    fReferenceCount++;
   }
   
   
/*************************************************************************************
    TTileCollection::DisposeReference
*************************************************************************************/
 
void 
TTileCollection::DisposeReference (void)
{
    fReferenceCount--;
    if (fReferenceCount == 0)
    {
        UInt32              listIndex;
        Boolean             tableEntry;
        OSErr               theErr;
 
        theErr = SearchCollectionList (fResID, &tableEntry, &listIndex);
                
        FAIL_OSERR (theErr, "\pFailed to search the CollectionList")
        FAIL_FALSE (tableEntry, "\pFailed to find an existing TTileCollection in list")
                
        theErr = DeleteCollectionFromList(listIndex);
        FAIL_OSERR (theErr, "\pFailed to delete Collection from the list")
                
        delete this;
    }
        
    error:
    return;
}
   
 
/*************************************************************************************
    TTileCollection::TTileCollection
    
    All of the actual work is done in CreateCollection
*************************************************************************************/
 
TTileCollection::TTileCollection (SInt16 resID)
{
    fResID = resID;
    fReferenceCount = 0;
    fNumberOfTiles = 0;
    fTiles = NULL;
}
 
 
/*************************************************************************************
    TTileCollection::~TTileCollection
    
    All of the actual work is done in DeleteCollection
*************************************************************************************/
 
TTileCollection::~TTileCollection (void)
{
    if (fTiles != NULL)
        {
        OSErr theErr = DestroyCollection();
        FAIL_OSERR (theErr, "\pFailed to destroy the TTileCollection data")
        }
        
    error:
    return;
}
 
 
/*************************************************************************************
    TTileCollection::CreateCollection
    
    This routine uses the resource number we passed into the contructor to load the TTileCollection
    from the 'SptA' resource we created for it.  Note that we don't make any assumptions about
    the resource file being used, so the correct resource file already needs to be opened for this
    call to work.
*************************************************************************************/
 
OSErr TTileCollection::CreateCollection(void)
{
    OSErr   theErr = noErr;
    TileCollectionResHeader **tiles;
    int     loop;
    Ptr     srcPtr, destPtr;
    
    // Load the 'TILE' resource and check to make sure we can read the header information.
    
    tiles = (TileCollectionResHeader **) Get1Resource (TileCollectionResType, fResID);
    theErr = ResError();
    
    FAIL_OSERR (theErr, "\pFailed to load the TILE resource.")
    FAIL_NIL (tiles, "\pFailed to load the TILE resource.")
 
    if ( (**tiles).version != 0) SIGNAL_ERROR ("\pInvalid version number in TILE resource")
    if ( (**tiles).depth != 8) SIGNAL_ERROR ("\pInvalid Pixel Depth in TILE resource")
    fNumberOfTiles = (**tiles).numTiles;
    
    fTiles = NewHandle (fNumberOfTiles * kTileGroupSize);
    theErr = MemError();
    
    FAIL_OSERR (theErr, "\pFailed to allocate memory for the tiles")
    FAIL_NIL (fTiles, "\pFailed to allocate memory for the tiles")
    
    srcPtr = (Ptr)(*tiles) + sizeof (TileCollectionResHeader);
    destPtr = (*fTiles);
    
    fTileOffset = (fNumberOfTiles * kTileSize) +1;
    for (loop = 0; loop < 8; loop++)
    {
        BlockMoveData (srcPtr, destPtr, kTileSize*fNumberOfTiles);
        destPtr += fTileOffset;
    }
    
    // Completed successfully, cleanup and exit with no error;
    goto cleanup;
    
    error:
    if (theErr == noErr)
        theErr = paramErr;
    
    if (fTiles != NULL)
        DisposeHandle (fTiles);
    fTiles = NULL;
    
    cleanup:
    
    if (tiles != NULL)
        ReleaseResource ((Handle) tiles);       
 
    return theErr;
}
 
 
/*************************************************************************************
    TTileCollection::DestroyCollection
    
    Throw away all of the objects that we're created.  We do check for null TTile objects here,
    and properly skip null objects that might not have been finished from the create calls.
*************************************************************************************/
 
OSErr
TTileCollection::DestroyCollection (void)
{
    if (fTiles != NULL)
        DisposeHandle (fTiles);
    fTiles = NULL;
    
    return noErr;
}
 
 
 
/*************************************************************************************
    TTileCollection::LockCollection
 
*************************************************************************************/
 
OSErr
TTileCollection::LockCollection (void)
{
    OSErr theErr = noErr;
    
    MoveHHi (fTiles);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't move fTiles handle high")
    HLock (fTiles);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't lock fTiles handle")
        
    error:
    return theErr;
}
 
 
/*************************************************************************************
    TTileCollection::UnlockCollection
 
*************************************************************************************/
 
OSErr
TTileCollection::UnlockCollection (void)
{
    OSErr theErr = noErr;
    
    HUnlock (fTiles);
    theErr = MemError();
    FAIL_OSERR (theErr, "\pCouldn't unlock fTiles handle")
    
    error:
    return theErr;
}
                   
 
/*************************************************************************************
    TTileCollection::CopyImageUnclipped
    
    This is a set of optimized image copying routines that take advantage of alignment for some serious
    speed improvements.  These routines take the destination pointer and assume that the image being
    copied is completely on the screen.  If your routine can't determine which of these routines to
    call, call CopyImageClip instead.
    
    Now for a more detailed discussion of these routines and why we actually go about this.  The main
    advantage to these routines is that their specialization allows them to be much faster than the
    generic copying loop in the TGraphic class.
    
    The fastest way to move memory on PPC is usually to load and store float doubles, which are 64 bits
    wide.  The problem is that if you decide to move doubles, you need to guarantee alignment to an
    8 byte boundary, or your code will run slow on a 603 and 604 processor.  For the generic sprite
    blitter, it was faster to just move longs and let the hardware deal with address misalignment.  But
    for tiles, we can make copies of the same data at different alignments and use the correct data
    when drawing to the screen.  In fact, we could do this for sprites too.  The main advantage is that
    because we know the width of the tile, we can write a blitter loop that has no logic in it other
    than loads and stores.  At one point, I did attempt to write a variable length blitter that did the
    same thing, but for small sprites, the overhead of checking for the correct sprite was always more
    than just drawing with longs.
    
    In any case, each of these blitters performs every load and every store on an exact boundary, with
    no misalignments.  This results in much faster code in general, and also lets us load data into
    the floats and stores.
    
    Future possibilities:
    
    * The cache touch instructions might be useful to make sure that the source data is loaded well ahead
      of each time through the loop.  This can help performance, and is also possible to do for the
      destination if the destination is cacheable.  I haven't experimented with this yet, but probably
      will in the future.
      
    * Currently the largest number of loads for a single line is 7 (CopyImageUnclipped1).  Because the
      next line starts immediately after the previous line in memory, we can probably replace 3 of the
      loads (load char, load char, load short) with a single load long, and use shifts to get the correct
      data.  This won't do much to reduce the number of stores, but might improve performance slightly.
    
*************************************************************************************/
 
 
/***************************************
TTileCollection::CopyImageUnclipped0
****************************************/
void
TTileCollection::CopyImageUnclipped0 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3, double4;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
        // load all the data into the registers
 
        double1 = *((double *) srcPtr);
        double2 = *((double *) srcPtr+1);
        double3 = *((double *) srcPtr+2);
        double4 = *((double *) srcPtr+3);
 
        // dump all the data out to the destination.
 
        *((double *) destPtr)   = double1;
        *((double *) destPtr+1) = double2;
        *((double *) destPtr+2) = double3;
        *((double *) destPtr+3) = double4;
 
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
    
    error:
    return;
}
 
 
 
/***************************************
TTileCollection::CopyImageUnclipped1
****************************************/
void
TTileCollection::CopyImageUnclipped1 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned char  char1,char2;
    register unsigned short short1;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
 
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + fTileOffset);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
        // load all the data into the registers.  We're one byte misaligned, so we need to load 7 bytes before
        // we hit the next 8-byte boundary.
        
        char1   = *((unsigned char *)                     srcPtr);
        short1  = *((unsigned short *) ((unsigned char *) srcPtr+1));
        long1   = *((unsigned long *)  ((unsigned char *) srcPtr+3));
        double1 = *((double *)         ((unsigned char *) srcPtr+7));
        double2 = *((double *)         ((unsigned char *) srcPtr+15));
        double3 = *((double *)         ((unsigned char *) srcPtr+23));
        char2   = *((unsigned char *)  ((unsigned char *) srcPtr+31));
        
        srcPtr += 32;
        
        // dump all the data out to the destination.
        
        *((unsigned char *)  ((unsigned char *) destPtr))    = char1;
        *((unsigned short *) ((unsigned char *) destPtr+1))  = short1;
        *((unsigned long *)  ((unsigned char *) destPtr+3))  = long1;
        *((double *)         ((unsigned char *) destPtr+7))  = double1;
        *((double *)         ((unsigned char *) destPtr+15)) = double2;
        *((double *)         ((unsigned char *) destPtr+23)) = double3;
        *((unsigned char *)  ((unsigned char *) destPtr+31)) = char2;
        
        destPtr += rowBytesLocal;
    }
 
    error:
    return;
}
 
 
/***************************************
TTileCollection::CopyImageUnclipped2
****************************************/
void
TTileCollection::CopyImageUnclipped2 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned short short1, short2;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
        SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
 
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + fTileOffset*2);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
 
    // load all the data into the registers
 
        short1  = *((unsigned short *)                    srcPtr);
        long1   = *((unsigned long *)  ((unsigned char *) srcPtr+2));
        double1 = *((double *)         ((unsigned char *) srcPtr+6));
        double2 = *((double *)         ((unsigned char *) srcPtr+14));
        double3 = *((double *)         ((unsigned char *) srcPtr+22));
        short2  = *((unsigned short *) ((unsigned char *) srcPtr+30));
        
        // dump all the data out to the destination.
        *((unsigned short *)  ((unsigned char *) destPtr))    = short1;
        *((unsigned long *)   ((unsigned char *) destPtr+2))  = long1;
        *((double *)          ((unsigned char *) destPtr+6))  = double1;
        *((double *)          ((unsigned char *) destPtr+14)) = double2;
        *((double *)          ((unsigned char *) destPtr+22)) = double3;
        *((unsigned short *)  ((unsigned char *) destPtr+30)) = short2;
        
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
    
    error:
    return;
}
 
 
 
/***************************************
TTileCollection::CopyImageUnclipped3
****************************************/
void 
TTileCollection::CopyImageUnclipped3 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned char  char1,char2;
    register unsigned short short1;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + fTileOffset*3);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
 
        // load all the data into the registers
        
        char1   = *((unsigned char *)                    srcPtr);
        long1   = *((unsigned long *)  ((unsigned char *)srcPtr+1));
        double1 = *((double *)         ((unsigned char *)srcPtr+5));
        double2 = *((double *)         ((unsigned char *)srcPtr+13));
        double3 = *((double *)         ((unsigned char *)srcPtr+21));
        char2   = *((unsigned char *)  ((unsigned char *)srcPtr+29));
        short1  = *((unsigned short *) ((unsigned char *)srcPtr+30));
        
        // dump all the data out to the destination.
        *((unsigned char *) ((unsigned char *) destPtr))    = char1;
        *((unsigned long *) ((unsigned char *) destPtr+1))  = long1;
        *((double *)        ((unsigned char *) destPtr+5))  = double1;
        *((double *)        ((unsigned char *) destPtr+13)) = double2;
        *((double *)        ((unsigned char *) destPtr+21)) = double3;
        *((unsigned char *) ((unsigned char *) destPtr+29)) = char2;
        *((unsigned short *)((unsigned char *) destPtr+30)) = short1;
        
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
    
    error:
    return;
}
 
 
 
/***************************************
TTileCollection::CopyImageUnclipped4
****************************************/
void
TTileCollection::CopyImageUnclipped4 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned long  long1, long2;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    //srcPtr =  (unsigned char *)  ((*fTiles) + alignment * fTileOffset + kTileSize*index);
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + 4*fTileOffset);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
        long1   = *((unsigned long *) ((unsigned char *)srcPtr));
        double1 = *((double *)        ((unsigned char *)srcPtr+4));
        double2 = *((double *)        ((unsigned char *)srcPtr+12));
        double3 = *((double *)        ((unsigned char *)srcPtr+20));
        long2   = *((unsigned long *) ((unsigned char *)srcPtr+28));
        
        // dump all the data out to the destination.
        *((unsigned long *) ((unsigned char *) destPtr))    = long1;
        *((double *)        ((unsigned char *) destPtr+4))  = double1;
        *((double *)        ((unsigned char *) destPtr+12)) = double2;
        *((double *)        ((unsigned char *) destPtr+20)) = double3;
        *((unsigned long *) ((unsigned char *) destPtr+28)) = long2;
        
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
 
 
    error:
    return;
}
 
 
 
/***************************************
TTileCollection::CopyImageUnclipped5
****************************************/
void
TTileCollection::CopyImageUnclipped5 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned char  char1,char2;
    register unsigned short short1;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + 5*fTileOffset);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
 
                // load all the data into the registers
        char1   = *((unsigned char *)  ((unsigned char *) srcPtr+0));
        short1  = *((unsigned short *) ((unsigned char *) srcPtr+1));
        double1 = *((double *)         ((unsigned char *) srcPtr+3));
        double2 = *((double *)         ((unsigned char *) srcPtr+11));
        double3 = *((double *)         ((unsigned char *) srcPtr+19));
        long1   = *((unsigned long *)  ((unsigned char *) srcPtr+27));
        char2   = *((unsigned char *)  ((unsigned char *) srcPtr+31));
 
        *((unsigned char *) ((unsigned char *) destPtr+0))  = char1;
        *((unsigned short *)((unsigned char *) destPtr+1))  = short1;
        *((double *)        ((unsigned char *) destPtr+3))  = double1;
        *((double *)        ((unsigned char *) destPtr+11)) = double2;
        *((double *)        ((unsigned char *) destPtr+19)) = double3;
        *((unsigned long *) ((unsigned char *) destPtr+27)) = long1;
        *((unsigned char *) ((unsigned char *) destPtr+31)) = char2;
    
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
 
    error:
    return;
}
 
/***************************************
TTileCollection::CopyImageUnclipped6
****************************************/
void
TTileCollection::CopyImageUnclipped6 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned short short1, short2;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    //srcPtr =  (unsigned char *)  ((*fTiles) + alignment * fTileOffset + kTileSize*index);
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index + 6*fTileOffset);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
 
        short1  = *((unsigned short *) ((unsigned char *) srcPtr+0));
        double1 = *((double *)         ((unsigned char *) srcPtr+2));
        double2 = *((double *)         ((unsigned char *) srcPtr+10));
        double3 = *((double *)         ((unsigned char *) srcPtr+18));
        long1   = *((unsigned long *)  ((unsigned char *) srcPtr+26));
        short2  = *((unsigned short *) ((unsigned char *) srcPtr+30));
        
        *((unsigned short *)((unsigned char *) destPtr+0))  = short1;
        *((double *)        ((unsigned char *) destPtr+2))  = double1;
        *((double *)        ((unsigned char *) destPtr+10)) = double2;
        *((double *)        ((unsigned char *) destPtr+18)) = double3;
        *((unsigned long *) ((unsigned char *) destPtr+26)) = long1;
        *((unsigned short *)((unsigned char *) destPtr+30))  = short2;
        
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
 
    error:
    return;
}
 
 
/***************************************
TTileCollection::CopyImageUnclipped7
****************************************/
void
TTileCollection::CopyImageUnclipped7 (UInt32 index, unsigned char *destPtr)
{
    register double double1, double2, double3;
    register unsigned char  char1,char2;
    register unsigned short short1;
    register unsigned long  long1;
 
    unsigned char   *srcPtr;        // the current position in the sprite data
    int             loop;
    int             rowBytesLocal;  // used to hold the rowbytes value to eliminate a load.
 
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#endif
    //srcPtr =  (unsigned char *)  ((*fTiles) + alignment * fTileOffset + kTileSize*index);
    srcPtr =  (unsigned char *)  ((*fTiles)  + kTileSize*index+ 7*fTileOffset);
    rowBytesLocal = gRowBytes;
 
    for (loop = 0; loop < 32; loop++)
    {
 
        char1   = *((unsigned char *)  ((unsigned char *) srcPtr+0));
        double1 = *((double *)         ((unsigned char *) srcPtr+1));
        double2 = *((double *)         ((unsigned char *) srcPtr+9));
        double3 = *((double *)         ((unsigned char *) srcPtr+17));
        long1   = *((unsigned long *)  ((unsigned char *) srcPtr+25));
        short1  = *((unsigned short *) ((unsigned char *) srcPtr+29));
        char2   = *((unsigned char *)  ((unsigned char *) srcPtr+31));
            
        *((unsigned char *) ((unsigned char *) destPtr+0))  = char1;
        *((double *)        ((unsigned char *) destPtr+1))  = double1;
        *((double *)        ((unsigned char *) destPtr+9))  = double2;
        *((double *)        ((unsigned char *) destPtr+17)) = double3;
        *((unsigned long *) ((unsigned char *) destPtr+25)) = long1;
        *((unsigned short *)((unsigned char *) destPtr+29)) = short1;
        *((unsigned char *) ((unsigned char *) destPtr+31)) = char2;
 
        srcPtr += 32;
        destPtr += rowBytesLocal;
    }
 
    error:
    return;
}
 
 
/*************************************************************************************
    TTileCollection::CopyImageClipped
    
    CopyImageClipped also draws the image, but always checks to make sure that it is inside the
    designated drawing area set in the clipping rectangle for our drawing code.  For the moment, this
    version of the code always does the drawing using simple long moves without regard to alignment.
        
*************************************************************************************/
 
void 
TTileCollection::CopyImageClipped (UInt32 index, SInt32 top, SInt32 left)
{
#if qDebugging
    if (gDestPixMap == NULL)
        SIGNAL_ERROR ("\pAttempting to draw to a NULL destination pixmap")
                
    if (EmptyRect (&gClipRect))
        SIGNAL_ERROR ("\pEmpty Clipping Region")
        
    if  (index >= fNumberOfTiles)
        // We're outside of the bounds of the Tiles table here.  Debugger time! :-)
            SIGNAL_ERROR("\ptried to index a TTile that was out of bounds");
#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    = top;
    destBottom = top+32;
    destLeft   = left;
    destRight  = left+32;
    
    // 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)
    {
        UInt32 clipTop, clipBottom, clipLeft, clipRight;
        unsigned char   *srcPtr;                        // the current position in the tile data
        unsigned char   *destPtr;                       // the current position in the destination pixmap
        unsigned long   blitwidth, blitheight;
        unsigned long   yLoop;
        unsigned long   sourceOffset, destOffset;
        
        // Calculate clipping rectangle.
        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;
 
        // calculate source and destination pointers
        srcPtr =  ( unsigned char * )( *fTiles ) + kTileSize*index;
        srcPtr += 32*clipTop+clipLeft;
        
        destPtr = gDestBaseAddr + (top + clipTop) * gRowBytes + left + clipLeft;
        
        // how much do we actually need to draw?
        blitwidth = clipRight - clipLeft;
        blitheight = clipBottom - clipTop;
        sourceOffset = 32-blitwidth;
        destOffset = gRowBytes-blitwidth;
        
        for (yLoop = 0; yLoop < blitheight; yLoop++)
        {
            register unsigned long sixteenblits, blitloop;
            sixteenblits = blitwidth >> 4;
            for ( blitloop = 0; blitloop < sixteenblits; blitloop++)
            {
                register unsigned long temp1, temp2, temp3, temp4;
                temp1 = ((unsigned long *) srcPtr)[0];
                temp2 = ((unsigned long *) srcPtr)[1];
                temp3 = ((unsigned long *) srcPtr)[2];
                temp4 = ((unsigned long *) srcPtr)[3];
                ((unsigned long *) destPtr)[0] = temp1;
                ((unsigned long *) destPtr)[1] = temp2;
                ((unsigned long *) destPtr)[2] = temp3;
                ((unsigned long *) destPtr)[3] = temp4;
                srcPtr += 16;
                destPtr += 16;
            }
            // move any remaining data, up to 15 bytes total
            if (blitwidth & 0x8)
            {
                register unsigned long temp1, temp2;
                temp1 = ((unsigned long *) srcPtr)[0];
                temp2 = ((unsigned long *) srcPtr)[1];
                ((unsigned long *) destPtr)[0] = temp1;
                ((unsigned long *) destPtr)[1] = temp2;
                srcPtr += 8;
                destPtr += 8;
            }
            if (blitwidth & 0x4)
            {
                register unsigned long temp1;
                temp1 = *((unsigned long *) srcPtr);
                srcPtr +=4;
                *((unsigned long *) destPtr)  = temp1;
                destPtr +=4;
            }
            if (blitwidth & 0x2)
            {
                register unsigned short temp1;
                temp1 = *((unsigned short *) srcPtr);
                srcPtr +=2;
                *((unsigned short *) destPtr)  = temp1;
                destPtr +=2;
            }
            if (blitwidth & 0x1)
                *destPtr++ = *srcPtr++;
        srcPtr += sourceOffset;
        destPtr += destOffset;  
        }
 
    }
    else
    {
        // Unclipped, so calculate the destination pointer and alignment and call the appropriate unclipped case.
        unsigned char   *destPtr;                       // the current position in the destination pixmap
        long            alignment;
        
        
        destPtr = gDestBaseAddr + top * gRowBytes + left;
        alignment = ((long) destPtr) & 0x07;
        switch (alignment)
        {
            case 0: CopyImageUnclipped0 (index, destPtr); break;
            case 1: CopyImageUnclipped1 (index, destPtr); break;
            case 2: CopyImageUnclipped2 (index, destPtr); break;
            case 3: CopyImageUnclipped3 (index, destPtr); break;
            case 4: CopyImageUnclipped4 (index, destPtr); break;
            case 5: CopyImageUnclipped5 (index, destPtr); break;
            case 6: CopyImageUnclipped6 (index, destPtr); break;
            case 7: CopyImageUnclipped7 (index, destPtr); break;
        }
    }
 
error:
    return;
}