Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Tim's Libraries/TGraphicCollection.cp
/* |
File: TGraphicCollection.cp |
Contains: A TGraphicCollection is a group of related TGraphic objects. The API is |
very similar to that of TGraphic, but includes an index to determine which |
TGraphic is receiving the command. |
This class is tightly coupled to the TGraphic class, so it has all of the |
same limitations. |
Note that this class isn't currently intended to be subclassed, so all of |
the methods are non-virtual. This might change if at some point we |
determine we need to create a subclass. |
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 Cleaned up to work with 3.x Universal Headers |
and CW Pro. Cleaned up some comments. Made same |
changes to list management code as were made in |
TGraphic. Should revisit making a generic list |
class, if performance isn't killed. |
4/2/97 CreateCollection was calling ReleaseResource twice, |
because I forgot to remove the original call to |
ReleaseResource when I reorganized some of the |
cleanup code. Onyx's Spotlight spotted this one for |
me. |
2/24/97 Explicitly includes the error macros now. We should |
be able to build on other compilers than Metrowerks. |
*/ |
#include <Memory.h> |
#include <Resources.h> |
#include "Error Macros.h" |
#include "TGraphicCollection.h" |
/************************************************************************************* |
TGraphicCollections and List Management |
TGraphicCollections are allocated and maintained in a list internally by this class. |
Each object is reference counted, to minimize the need to load resources more than |
once. |
For optimal performance, pre-load the TGraphicCollection once outside the game loop. |
This should mean that all searches hit inside the list. Theoretically, you can also |
just create one TGraphicCollection, and everybody who needs it gets a copy of the |
handle. Eliminates the need for reference counting, but then you need to make sure |
any references to it are eliminated. |
The list is sorted by the collections's resource ID, and uses standard C array indexing. |
The list is searched using a binary search. |
*************************************************************************************/ |
static Handle gCollectionList = NULL; |
static UInt32 gNumberCollections = 0; |
static OSStatus InsertCollectionIntoList (TGraphicCollection *theCollection, UInt32 index); |
static OSStatus DeleteCollectionFromList (UInt32 index); |
static OSStatus SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index); |
/***************************************************************************** |
InsertCollectionIntoList |
This routine creates the list if it hasn't been initialized. Otherwise, it |
just inserts the collection into the spot pointed to by the index. If an error |
occurs, the list is left in an indeterminate state. |
*****************************************************************************/ |
OSStatus InsertCollectionIntoList (TGraphicCollection *theCollection, UInt32 index) |
{ |
OSStatus theErr = noErr; |
#if qDebugging |
if (theCollection == NULL) |
SIGNAL_ERROR ("\pAttempting to insert a null collection into the list") |
if ((index > gNumberCollections) || (index < 0)) |
SIGNAL_ERROR ("\pAttempting to insert collection at invalid index") |
#endif |
// if we don't have a list allocate one |
if (gCollectionList == NULL) |
{ |
gNumberCollections = 1; |
gCollectionList = NewHandleClear (sizeof (TGraphicCollection *)); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't allocate a new handle of information") |
FAIL_NIL (gCollectionList, "\pCouldn't allocate a new handle of information") |
} |
else |
// if we do have a list, expand it by one node. |
{ |
gNumberCollections++; |
SetHandleSize(gCollectionList,gNumberCollections*sizeof(TGraphicCollection *)); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't resize the list handle") |
FAIL_NIL (gCollectionList, "\pCouldn't resize the list handle") |
// Shift the data to make room |
BlockMoveData( (*(TGraphicCollection ***)gCollectionList)+index, |
(*(TGraphicCollection ***)gCollectionList)+index+1, |
(gNumberCollections - index-1) * sizeof(TGraphicCollection *)); |
} |
// finally, set the new object in place |
*((*(TGraphicCollection ***) gCollectionList)+index) = theCollection; |
goto cleanup; |
error: |
if (theErr == noErr) |
theErr = paramErr; |
cleanup: |
return theErr; |
} |
/***************************************************************************** |
DeleteCollectionFromList |
This routine removes the collection from the list. If the list is then empty, |
it disposes of the handle. If an error occurs, the list is left in an |
indeterminate state. |
*****************************************************************************/ |
OSStatus DeleteCollectionFromList (UInt32 index) |
{ |
OSStatus theErr = noErr; |
#if qDebugging |
if ((index < 0) || (index > gNumberCollections-1)) |
SIGNAL_ERROR ("\pError: Attempted to delete outside of the list.") |
#endif |
gNumberCollections--; |
// slide remaining elements up |
BlockMoveData( (*(TGraphicCollection ***)gCollectionList)+index+1, |
(*(TGraphicCollection ***)gCollectionList)+index, |
(gNumberCollections - index) * sizeof(TGraphicCollection *)); |
// Shrink the handle |
if (gNumberCollections> 0) |
{ |
SetHandleSize((Handle) gCollectionList,gNumberCollections*sizeof(TGraphicCollection *)); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't resize the list handle") |
FAIL_NIL (gCollectionList, "\pCouldn't resize the list handle") |
} |
else |
{ |
DisposeHandle (gCollectionList); |
gCollectionList = NULL; |
} |
// We succeeded, cleanup and exit. |
goto cleanup; |
error: |
if (theErr == noErr) |
theErr = paramErr; |
cleanup: |
return theErr; |
} |
/***************************************************************************** |
SearchCollectionList |
This routine searches the list for an existing collection with that resID. |
If it doesn't find one, the index points to where that resID should be |
inserted into the list. |
*****************************************************************************/ |
OSStatus SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index) |
{ |
OSStatus theErr = noErr; |
UInt32 low = 0; |
UInt32 high = gNumberCollections; |
UInt32 tempIndex; |
SInt16 tempID; |
TGraphicCollection *theItem; |
while (low < high) |
{ |
tempIndex = (low+high) >> 1; |
theItem = (*(TGraphicCollection ***)gCollectionList)[tempIndex]; |
#if qDebugging |
FAIL_NIL (theItem, "\pBad TGraphic object") |
#endif |
tempID = theItem->GetResID(); |
if (resID == tempID) |
{ |
*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; |
} |
/***************************************************************************** |
TGraphicCollection::NewCollection |
This uses the list management routines to create and load a TGraphicCollection. |
*****************************************************************************/ |
TGraphicCollection |
*TGraphicCollection::NewCollection (SInt16 resID) |
{ |
OSStatus theErr = noErr; |
UInt32 listIndex; |
Boolean collectionExists; |
TGraphicCollection *newCollection = NULL; |
theErr = SearchCollectionList (resID, &collectionExists, &listIndex); |
FAIL_OSERR(theErr,"\pError: Failed to search the collection list") |
if (collectionExists) |
{ |
// Bump the reference count on the existing collection. |
newCollection = (*(TGraphicCollection ***)gCollectionList)[listIndex]; |
newCollection->AddReference(); |
} |
else |
{ |
// create a new collection |
newCollection = new TGraphicCollection(resID); |
theErr = newCollection->CreateCollection (); |
FAIL_OSERR(theErr,"\pCouldn't create new TGraphicCollection") |
theErr = InsertCollectionIntoList(newCollection, listIndex); |
FAIL_OSERR(theErr, "\pCouldn't add new TGraphicCollection to list") |
newCollection->AddReference(); |
} |
// We succeeded, cleanup and return the collection. |
goto cleanup; |
error: |
if (newCollection != NULL) |
{ |
delete newCollection; |
newCollection = NULL; |
} |
cleanup: |
return newCollection; |
} |
/***************************************************************************** |
TGraphicCollection::AddReference |
*****************************************************************************/ |
void |
TGraphicCollection::AddReference (void) |
{ |
fReferenceCount++; |
} |
/***************************************************************************** |
TGraphicCollection::DisposeReference |
*****************************************************************************/ |
void |
TGraphicCollection::DisposeReference (void) |
{ |
fReferenceCount--; |
if (fReferenceCount == 0) |
{ |
UInt32 listIndex; |
Boolean tableEntry; |
OSStatus theErr; |
theErr = SearchCollectionList (fResID, &tableEntry, &listIndex); |
FAIL_OSERR (theErr, "\pFailed to search the CollectionList") |
FAIL_FALSE (tableEntry, "\pFailed to find an existing TGraphicCollection in list") |
theErr = DeleteCollectionFromList(listIndex); |
FAIL_OSERR (theErr, "\pFailed to delete Collection from the list") |
delete this; |
} |
error: |
return; |
} |
/***************************************************************************** |
TGraphicCollection::TGraphicCollection |
The constructor just sets a few clean values -- the real work is done later. |
*****************************************************************************/ |
TGraphicCollection::TGraphicCollection (SInt16 resID) |
{ |
fResID = resID; |
fReferenceCount = 0; |
fGraphics = NULL; |
} |
/***************************************************************************** |
TGraphicCollection::~TGraphicCollection |
The destructor just disposes of any data and returns. |
*****************************************************************************/ |
TGraphicCollection::~TGraphicCollection (void) |
{ |
(void) DestroyCollection(); |
} |
/***************************************************************************** |
TGraphicCollection::CreateCollection |
We load the 'SptA' resource, and extract the resource IDs of our TGraphic |
objects we want to load. Assumes those resources are in the current |
resource fork. |
The format of a 'SptA' resource is a 2 byte unsigned integer for the number |
of elements in the list, followed by the resource IDs. In the future, this |
should probably have a version number, so we can change the format. |
*****************************************************************************/ |
OSStatus TGraphicCollection::CreateCollection(void) |
{ |
Handle collectionResource = Get1Resource ('SptA', fResID); |
OSStatus theErr = ResError(); |
UInt32 loop; |
FAIL_OSERR (theErr, "\pResource manager couldn't load the sprite resource") |
FAIL_NIL (collectionResource, "\pResource manager returned a null handle") |
// Find out how many TGraphcs we need to load, and allocate a handle |
// to hold the TGraphic pointers. |
fNumberOfGraphics = *((SInt16 *) (*collectionResource)); |
fGraphics = NewHandleClear (fNumberOfGraphics * sizeof (TGraphic *)); |
FAIL_NIL (fGraphics, "\pCouldn't allocate collection handle") |
// Load each of the TGraphics. |
HLock (fGraphics); |
for (loop = 1; loop <= fNumberOfGraphics; loop++) |
{ |
SInt16 graphicResID = *((SInt16 *) ((*collectionResource)+sizeof (SInt16)*loop)); |
TGraphic *theGraphic = TGraphic::NewGraphic (graphicResID); |
FAIL_NIL (theGraphic, "\pFailed to load graphic for collection") |
*((*(TGraphic ***)fGraphics)+loop-1) = theGraphic; |
} |
HUnlock (fGraphics); |
goto cleanup; |
error: |
if (theErr == noErr) |
theErr = paramErr; |
// We didn't explictly destroy the collection if an error occured, because |
// the object will be deleted anyway. |
cleanup: |
if (collectionResource != NULL) |
ReleaseResource (collectionResource); |
return theErr; |
} |
/***************************************************************************** |
TGraphicCollection::DestroyCollection |
Throw away all of the objects that we're created. We do check for null TGraphic objects here, |
and properly skip null objects that might not have been finished from the create calls. |
*****************************************************************************/ |
OSStatus |
TGraphicCollection::DestroyCollection (void) |
{ |
UInt32 loop; |
if (fGraphics) |
{ |
HLock (fGraphics); |
for (loop = 0; loop < fNumberOfGraphics ; loop++) |
{ |
TGraphic *theGraphic = *((*(TGraphic ***)fGraphics)+loop); |
if (theGraphic != NULL) |
theGraphic->DisposeReference(); |
} |
DisposeHandle (fGraphics); |
fGraphics = NULL; |
} |
return noErr; |
} |
/***************************************************************************** |
TGraphicCollection::LockCollection |
*****************************************************************************/ |
OSStatus |
TGraphicCollection::LockCollection (void) |
{ |
UInt32 loop; |
OSStatus theErr = noErr; |
MoveHHi (fGraphics); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't move fGraphics handle high") |
HLock (fGraphics); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't lock fGraphics handle") |
TGraphic **ptrToGraphics = (TGraphic **) *fGraphics; |
for (loop = 0; loop < fNumberOfGraphics ; loop++) |
{ |
TGraphic *theGraphic = *ptrToGraphics++; |
theGraphic->LockGraphic(); |
} |
return noErr; |
error: |
return theErr; |
} |
/***************************************************************************** |
TGraphicCollection::UnlockCollection |
*****************************************************************************/ |
OSStatus |
TGraphicCollection::UnlockCollection (void) |
{ |
UInt32 loop; |
OSStatus theErr = noErr; |
// Should still be locked from the lock collection call. We should do some testing |
// to make sure we aren't locking and unlocking things multiple times here. |
TGraphic **ptrToGraphics = (TGraphic **) *fGraphics; |
for (loop = 0; loop < fNumberOfGraphics ; loop++) |
{ |
TGraphic *theGraphic = *ptrToGraphics++; |
theGraphic->UnlockGraphic(); |
} |
HUnlock (fGraphics); |
theErr = MemError(); |
FAIL_OSERR (theErr, "\pCouldn't unlock fGraphics handle") |
return noErr; |
error: |
return theErr; |
} |
/***************************************************************************** |
TGraphicCollection::GetBounds |
*****************************************************************************/ |
Rect |
TGraphicCollection::GetBounds (UInt32 index) |
{ |
TGraphic *theGraphic; |
#if qDebugging |
if (index >= fNumberOfGraphics) |
// We're outside of the bounds of the graphics table here. Debugger time! :-) |
SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds"); |
#endif |
theGraphic = *((*(TGraphic ***)fGraphics)+index); |
#if qDebugging |
if (theGraphic == NULL) |
SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object") |
#endif |
return (theGraphic->GetBounds ()); |
#if qDebugging |
error: |
Rect theRect = {0,0,0,0}; |
return theRect; |
#endif |
} |
/***************************************************************************** |
TGraphicCollection::CopyImage |
*****************************************************************************/ |
void |
TGraphicCollection::CopyImage (UInt32 index, SInt32 top, SInt32 left, Boolean useBackground) |
{ |
TGraphic *theGraphic; |
#if qDebugging |
if (index >= fNumberOfGraphics) |
// We're outside of the bounds of the graphics table here. Debugger time! :-) |
SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds"); |
#endif |
theGraphic = *((*(TGraphic ***)fGraphics)+index); |
#if qDebugging |
if (theGraphic == NULL) |
SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object") |
#endif |
theGraphic->CopyImage(top, left, useBackground); |
error: |
return; |
} |
/***************************************************************************** |
TGraphicCollection::HitTest |
*****************************************************************************/ |
Boolean |
TGraphicCollection::HitTest (UInt32 index, SInt32 v, SInt32 h) |
{ |
TGraphic *theGraphic; |
#if qDebugging |
if (index >= fNumberOfGraphics) |
// We're outside of the bounds of the graphics table here. Debugger time! :-) |
SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds"); |
#endif |
theGraphic = *((*(TGraphic ***)fGraphics)+index); |
#if qDebugging |
if (theGraphic == NULL) |
SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object") |
#endif |
return theGraphic->HitTest (v, h); |
error: |
return false; |
} |
/***************************************************************************** |
TGraphicCollection::GetGraphicObject |
*****************************************************************************/ |
TGraphic |
*TGraphicCollection::GetTGraphic (UInt32 index) |
{ |
TGraphic *theGraphic; |
#if qDebugging |
if (index >= fNumberOfGraphics) |
// We're outside of the bounds of the graphics table here. Debugger time! :-) |
SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds"); |
#endif |
theGraphic = *((*(TGraphic ***)fGraphics)+index); |
#if qDebugging |
if (theGraphic == NULL) |
SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object") |
#endif |
return theGraphic; |
error: |
return NULL; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14