Source/Graphics.c

//¥ ------------------------------------------------------------------------------------------  ¥
//¥
//¥ Copyright © 1996-1999 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.
//¥
//¥     Authors:
//¥         Chris De Salvo
//¥
//¥ ------------------------------------------------------------------------------------------  ¥
 
//¥ ------------------------------  Includes
 
#include <Types.h>
#include <Resources.h>
 
#include <string.h>
 
#include "DrawSprocket.h"
#include "ErrorHandler.h"
#include "Graphics.h"
#include "MemoryHandler.h"
#include "SIResources.h"
#include "Sprite.h"
 
//¥ ------------------------------  Private Definitions
 
#define kFirstIconColor             1
#define kLastIconColor              33
 
#define kFirstBGColor               34
#define kLastBGColor                183
 
#define kFirstSpriteColor           184
#define kLastSpriteColor            254
 
//¥ ------------------------------  Private Types
//¥ ------------------------------  Private Variables
 
static DSpAltBufferReference        gUnderlay;
static CTabHandle                   gGameCLUT;
 
//¥ ------------------------------  Private Functions
 
static void GraphicsUpdateScreenPalette(UInt16 inFirstColor, UInt16 inLastColor);
static void GraphicsCreateUnderlay(void);
static void GraphicsSelectContext(SInt16 modifiers);
static void GraphicsInitAttributes(DSpContextAttributes *inAttributes);
 
//¥ ------------------------------  Public Variables
 
DSpContextReference     gDisplayContext;
DSpContextAttributes    gDisplayAttributes;
GDHandle                gGameGDH;
 
const RGBColor  rgbBlack    = { 0x0000, 0x0000, 0x0000 };
const RGBColor  rgbWhite    = { 0xFFFF, 0xFFFF, 0xFFFF };
const RGBColor  rgbRed      = { 0xFFFF, 0x0000, 0x0000 };
const RGBColor  rgbBlue     = { 0x0000, 0x0000, 0xFFFF };
const RGBColor  rgbYellow   = { 0xFFFF, 0xFFFF, 0x0000 };
const RGBColor  rgbGreen    = { 0x0000, 0xFFFF, 0x0000 };
 
#pragma mark ÑÑÑÑÑ  Startup/Shutdown  ÑÑÑÑÑ
 
//¥ --------------------    GraphicsInit
 
void
GraphicsInit(SInt16 modifiers)
{
CGrafPtr    backBuffer;
OSErr       theErr;
Rect        r = { 40, 40, 100, 100 };
 
    theErr = DSpStartup();
    if (theErr)
        FatalError("GraphicsInit: DSpStartup() failed!!!!");
 
    //¥ CLUT 8 is the standard 8-bit color system color table and is
    //¥ always present
    gGameCLUT = GetCTable(8);
    DetachResource((Handle) gGameCLUT);
    
    //¥ Define and create our drawing context
    GraphicsSelectContext(modifiers);
 
    //¥ Add our various colors to the color table
    GraphicsMergeCLUT(128, kFirstIconColor, kLastIconColor);
    GraphicsMergeCLUT(kPICTGasCloud, kFirstBGColor, kLastBGColor);
    GraphicsMergeCLUT(kCLUTSprites, kFirstSpriteColor, kLastSpriteColor);
 
    //¥ Create our underlay and load a picture into it
    GraphicsCreateUnderlay();
    GraphicsLoadBackground(kPICTGasCloud);
 
    //¥ Swap the buffers once to get the underlay image on the screen
    DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuffer);
    DSpContext_InvalBackBufferRect(gDisplayContext, &backBuffer->portRect);
    GraphicsUpdateScreen();
 
    //¥ Fade up from black and then put the context in the paused state
    //¥ so that the user has access to the menu bar.
    DSpContext_FadeGammaIn(nil, nil);
    GraphicsPaused();
}
 
//¥ --------------------    GraphicsReset
 
void
GraphicsReset(void)
{
    DSpContext_FadeGammaOut(nil, nil);
    GraphicsInactive();
    DSpContext_FadeGammaIn(nil, nil);
 
    DSpContext_Release(gDisplayContext);
    DSpShutdown();
}
 
#pragma mark ÑÑÑÑÑ  Context Management  ÑÑÑÑÑ
 
//¥ --------------------    GraphicsSelectContext
 
static void
GraphicsSelectContext(SInt16 modifiers)
{
OSStatus        theError;
DisplayIDType   displayID;
    
    //¥ Initialize our context description
    GraphicsInitAttributes(&gDisplayAttributes);
 
    //¥ Tell DrawSprocket the exact needs we have for our game
    gDisplayAttributes.displayWidth         = 640;
    gDisplayAttributes.displayHeight        = 480;
    gDisplayAttributes.colorNeeds           = kDSpColorNeeds_Require;
    gDisplayAttributes.backBufferDepthMask  = kDSpDepthMask_8;
    gDisplayAttributes.displayDepthMask     = kDSpDepthMask_8;
    gDisplayAttributes.backBufferBestDepth  = 8;
    gDisplayAttributes.displayBestDepth     = 8;
    gDisplayAttributes.pageCount            = 2;
    gDisplayAttributes.colorTable           = gGameCLUT;
 
    //¥ Find the best context for these attributes
    //¥ This call will check to see if multiple monitors can handle the request
    //¥ If there is more than one valid device it will put up the selection interface
    theError = DSpUserSelectContext(&gDisplayAttributes, 0L, nil, &gDisplayContext);
 
    if (theError)
        FatalError("A suitable display context could not be found.");
 
    if (modifiers & optionKey)
        gDisplayAttributes.contextOptions |= kDSpContextOption_DontSyncVBL;
 
    //¥ Page flipping only works on 7500/7600/8500/8600 machines and only when
    //¥ there is >= 4 MB of VRAM.  There are very few of these machines in
    //¥ existance so turning on this option is not recommended.
    //¥ gDisplayAttributes.contextOptions |= kDSpContextOption_PageFlip;
 
    //¥ Set the blanking color to black
    DSpSetBlankingColor(&rgbBlack);
 
    //¥ Find the DisplayID for this draw context and then find the GDevice
    //¥ that goes along with it.  We need this because if we need to perform
    //¥ QuickDraw operations in a context that isn't on the main monitor then
    //¥ we need to have the proper GDevice set or QuickDraw loses its mind.
    theError =  DSpContext_GetDisplayID(gDisplayContext, &displayID);
    if (theError)
        FatalError("DSpContext_GetDisplayID() had an error.");
 
    theError = DMGetGDeviceByDisplayID(displayID, &gGameGDH, false);
    if (theError)
        FatalError("DMGetGDeviceByDisplayID() had an error.");
 
    //¥ Reserve the draw context so that we get the attributes we want
    theError = DSpContext_Reserve(gDisplayContext, &gDisplayAttributes);
    if (theError)
        FatalError("Could not reserve display context.");
 
    //¥ Fade the screen to black and make our context active
    DSpContext_FadeGammaOut(nil, nil);
    GraphicsActive();
}
 
//¥ --------------------    GraphicsInitAttributes
 
static void
GraphicsInitAttributes(DSpContextAttributes *inAttributes)
{
    if (! inAttributes)
        FatalError("Nil context passed to MyInitAttributes().");
 
    memset(inAttributes, 0, sizeof (DSpContextAttributes));
}
 
//¥ --------------------    GraphicaCreateUnderlay
 
static void
GraphicsCreateUnderlay(void)
{
OSStatus    theError;
 
    //¥ Create a new alt buffer which mimics the onscreen display
    theError = DSpAltBuffer_New(gDisplayContext, true, 0L, &gUnderlay);
    if (theError)
        FatalError("Could not create underlay buffer.");
        
    //¥ Make it the active underlay
    theError = DSpContext_SetUnderlayAltBuffer(gDisplayContext, gUnderlay);
    if (theError)
        FatalError("Could not set underlay buffer.");
}
 
//¥ --------------------    GraphicsGetUnderlayGrafPort
 
void
GraphicsGetUnderlayGrafPort(CGrafPtr *underlay, GDHandle *device)
{
    DSpAltBuffer_GetCGrafPtr(gUnderlay, kDSpBufferKind_Normal, underlay, device);
}
 
#pragma mark ÑÑÑÑÑ  Image Management  ÑÑÑÑÑ
 
//¥ --------------------    GraphicsLoadBackground
 
void
GraphicsLoadBackground(SInt16 whichBG)
{
GrafPtr     oldPort;
GDHandle    oldDevice;
GDHandle    underlayDevice;
PicHandle   thePict;
CGrafPtr    backBuff;
Rect        r;
 
    thePict = GetPicture(whichBG);
    if (! thePict)
        FatalError("Could not load background.");
 
    //¥ Get a CGrafPtr reference to the underlay graphics buffer
    DSpAltBuffer_GetCGrafPtr(gUnderlay, kDSpBufferKind_Normal, &backBuff, &underlayDevice);
    
    GetPort(&oldPort);
    oldDevice = GetGDevice();
    
    SetPort((GrafPtr) backBuff);
    SetGDevice(underlayDevice);
 
    RGBForeColor(&rgbBlack);
    RGBBackColor(&rgbWhite);
    
    //¥ Fill the background with black
    PaintRect(&backBuff->portRect);
 
    HLock((Handle) thePict);
 
    SetRect(&r, 0, 0, 640, 440);
    DrawPicture(thePict, &r);
    KillPictureZ(&thePict);
 
    //¥ Invalidate the entire underlay so that all other back buffers
    //¥ get the new image copied up to them.
    DSpAltBuffer_InvalRect(gUnderlay, &backBuff->portRect);
 
    SetPort(oldPort);
    SetGDevice(oldDevice);
}
 
//¥ --------------------    GraphicsMergeCLUT
 
void
GraphicsMergeCLUT(SInt16 clutID, UInt16 firstColor, UInt16 lastColor)
{
CTabHandle  ct;
UInt32      i;
 
    //¥ Load the appropritate 'CLUT' resource
    ct = GetCTable(clutID);
    if (! ct)
        FatalError("Could not load color table for merge.");
        
    HLock((Handle) ct);
    
    //¥ Copy colors from it to our game color table
    for (i = firstColor; i <= lastColor; i++)
    {
        (**gGameCLUT).ctTable[i].rgb.red = (**ct).ctTable[i].rgb.red;
        (**gGameCLUT).ctTable[i].rgb.green = (**ct).ctTable[i].rgb.green;
        (**gGameCLUT).ctTable[i].rgb.blue = (**ct).ctTable[i].rgb.blue;
    }
    
    HUnlock((Handle) ct);
    ReleaseResource((Handle) ct);
 
    //¥ Make the new game color table the active color environment
    GraphicsUpdateScreenPalette(firstColor, lastColor);
}
 
//¥ --------------------    GraphicsUpdateScreenPalette
 
static void
GraphicsUpdateScreenPalette(UInt16 inFirstColor, UInt16 inLastColor)
{
OSStatus    theError;
    
    //¥ Copy our game color table to our DrawSprocket context.  This also
    //¥ updates the color environment of any buffers that we've allocated with
    //¥ DrawSprocket.
    theError = DSpContext_SetCLUTEntries(gDisplayContext, (**gGameCLUT).ctTable, inFirstColor, inLastColor - inFirstColor);
    if( theError )
        DebugStr("\p error from CLUT ");
}
 
//¥ --------------------    GraphicsSetRectDirty
 
void
GraphicsSetRectDirty(RectPtr r)
{
    DSpContext_InvalBackBufferRect(gDisplayContext, r);
}
 
//¥ --------------------    GraphicsSetUnderlayRectDirty
 
void
GraphicsSetUnderlayRectDirty(RectPtr r)
{
    DSpAltBuffer_InvalRect(gUnderlay, r);
}
 
//¥ --------------------    GraphicsUpdateScreen
 
void
GraphicsUpdateScreen(void)
{
    DSpContext_SwapBuffers(gDisplayContext, nil, nil);
}
 
#pragma mark ÑÑÑÑÑ  Context State  ÑÑÑÑÑ
 
//¥ --------------------    GraphicsActive
 
void
GraphicsActive(void)
{
OSStatus    theErr;
 
    HideCursor();
 
    theErr = DSpContext_SetState(gDisplayContext, kDSpContextState_Active);
    if (theErr)
        DebugStr("\pSetState active failed in GraphicsActive().");
}
 
//¥ --------------------    GraphicsPaused
 
void
GraphicsPaused(void)
{
    DSpContext_SetState(gDisplayContext, kDSpContextState_Paused);
    ShowCursor();
    DrawMenuBar();
}
 
//¥ --------------------    GraphicsInactive
 
void
GraphicsInactive(void)
{
    DSpContext_SetState(gDisplayContext, kDSpContextState_Inactive);
    ShowCursor();
}
 
//¥ --------------------    GraphicsDoUpdateEvent
 
void
GraphicsDoUpdateEvent(void)
{
GrafPtr     savePort;
GDHandle    saveGDH;
 
CGrafPtr    backBuffer;
CGrafPtr    frontBuffer;
 
    DSpContext_GetFrontBuffer(gDisplayContext, &frontBuffer);
    DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuffer);
 
    GetPort(&savePort);
    saveGDH = GetGDevice();
    
    SetPort((GrafPtr) frontBuffer);
    SetGDevice(gGameGDH);
 
    ForeColor(blackColor);
    BackColor(whiteColor);
 
    CopyBits((BitMapPtr) *backBuffer->portPixMap, (BitMapPtr) *frontBuffer->portPixMap,
        &backBuffer->portRect, &frontBuffer->portRect, srcCopy, nil);
 
    SetPort(savePort);
    SetGDevice(saveGDH);
}