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/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); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14