home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-11-05 | 17.5 KB | 625 lines | [TEXT/CWIE] |
- /*****************************************************************************
- TGraphicCollection.cp
-
- Author: Timothy Carroll
- Apple Developer Technical Support
- timc@apple.com
-
- Copyright © 1996, 1997 Apple Computer, Inc., All Rights Reserved
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample 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 Code, but that you've made changes.
-
-
- Description
- -----------
-
- 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.
-
-
- Modification History
- --------------------
-
- 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;
- }