GWLayers.c

/*
** Apple Macintosh Developer Technical Support
**
** Program:     DTS.Lib
** File:        GWLayers.c
** Written by:  Eric Soldan and Forrest Tanaka
**
** Copyright © 1989-1993 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. */
 
/* This is an implementation of off-screen GWorld handling.  This particular
** implementation uses GWorlds in a hierarchical manner, with each layer in
** the hierarchy having its own tasks to handle at its specific level.
** The advantage to this is that it can conform to many applications.  Each
** application may need a different number of layers, and each layer may
** need to perform a different kind of operation.  By having an unlimited
** number of layers, and by having each layer handle its own application
** specific tasks, off-screen GWorld handling can be standardized.
**
** A common use for off-screen stuff is to move an object around in a 
** window over a background.  To accomplish this, we need 3 layers.
** These are:
**
** 1) The window layer.  This layer transfers a rectangle of pixels from
**    the middle layer into the window layer, once the middle layer is ready.
**    The rectangle transferred would be large enough to cover the old
**    location and the new location, thus moving the piece without having
**    to turn it off in the old location as a separate step.  This gives a
**    very smooth appearance when moving an object.
** 2) A middle layer that is used to apply the object being moved to the
**    background plus removing the object from the old location.  Once these
**    two tasks are done, the off-screen work area is ready to be transferred
**    to the window layer.
** 3) A background image against which the object moves.  This is used to
**    restore the middle layer at the location where the object being moved
**    was last at.
**
** The top layer object relates to the window, and therefore we don't need an
** off-screen GWorld for it.  A call to create this layer might look like the below:
**
** err = NewLayer(&windowLayer,   Layer object handle is returned here.
**                nil,            Top layer, so there is no above layer.
**                nil,            Uses default layer procedure.
**                window,         Window used by the layer object.
**                0,              Desired depth (0 for screen depth).
**                0);             Custom layer init data, if any.
**
** If NewLayer succeeds, the layer object handle is returned in windowLayer.
** If it fails, nil is returned in windowLayer, plus an error is returned.
** If windowLayer is successfully created, then we can proceed to create the
** next two layers.  In the case below, we are creating an off-screen layer
** that has a pixmap the same size and depth as windowLayer.  If this is
** what we want for the middle layer, then we can again use the default
** LayerProc for the kLayerInit message.  All we need to do is to call the
** default layerProc with a kLayerInit message.  We want the standard
** action for initialization, but we want our own update action.  That's
** why we have a custom layerProc for the middle layer.  The call would look
** something like the below:
**
** err = NewLayer(&middleLayer,     Layer object handle is returned here.
**                windowLayer,      Layer above this layer.
**                MiddleLayerProc,  Custom layerProc.
**                nil,              Create a pixmap for layer.
**                0,                Pixmap created as same size/depth as above layer.
**                0);
**
** The background layer would be created similarly.  When you are finished with
** the layers, you can dispose of them one at a time with DisposeLayer, or you
** can dispose of all of them in the layer chain with DisposeThisAndBelowLayers.
**
** Inserting a layer is done by position, and not by which layer it goes above
** or below.  The reason for this is that the layer positions are most likely
** absolute, and therefore it is better to indicate their position with an
** absolute number instead of always relating it to some other layer.  If it
** is necessary to insert a layer specifically above or below some other layer,
** it would be done as follows:
**      InsertLayer(newLayer, aboveLayer, GetLayerPosition(aboveLayer) + 1);
**      InsertLayer(newLayer, belowLayer, GetLayerPosition(belowLayer));
**
** The sample applications DTS.Draw and Kibitz uses the off-screen layer code.
** For a sample usage, see the file Window2.c in DTS.Draw, or Offscreen.c in Kibitz.
*/
 
 
 
/*****************************************************************************/
 
 
 
#ifndef __ERRORS__
#include <Errors.h>
#endif
 
#ifndef __GESTALTEQU__
#include <GestaltEqu.h>
#endif
 
#ifndef __GWLAYERS__
#include "GWLayers.h"
#endif
 
#ifndef __RESOURCES__
#include <Resources.h>
#endif
 
#ifndef __TRAPS__
#include <Traps.h>
#endif
 
#ifndef __WINDOWS__
#include <Windows.h>
#endif
 
#define kQDOriginal 0
 
static OSErr    DefaultLayerInit(LayerObj theLayer);
static OSErr    DefaultLayerUpdate(LayerObj theLayer);
static OSErr    DefaultLayerDispose(LayerObj theLayer);
 
static OSErr    MakeLayerWorld(GWorldPtr *layerWorld, LayerObj theLayer, Rect bnds);
static void     KillLayerWorld(LayerObj theLayer);
static void     SmartGetGWorld(CGrafPtr *port, GDHandle *gdh);
static void     SmartSetGWorld(CGrafPtr port, GDHandle gdh);
static short    GetQDVersion(void);
static short    GetSystemVersion(void);
static short    NumToolboxTraps(void);
static Boolean  TrapExists(short theTrap);
static TrapType GetTrapType(short theTrap);
 
 
 
/*****************************************************************************/
/*****************************************************************************/
 
#ifdef applec
#pragma segment ATGGWLayers
#endif
 
/*****************************************************************************/
/*****************************************************************************/
 
 
 
OSErr   NewLayer(LayerObj *newLayer, LayerObj aboveLayer, LayerProc theProc,
                 GrafPtr basePort, short depth, unsigned long theData)
{
    OSErr       err;
    LayerRecPtr lptr;
    CGrafPtr    scratchPort;
    GDHandle    baseGDevice;
 
    *newLayer = (LayerObj)NewHandleClear(sizeof(LayerRec));
    err = MemError();
    if (err) return(err);
        /* If not enough memory for layer object, return nil and error. */
 
    SmartGetGWorld(&scratchPort, &baseGDevice);
    if (!theProc)
        theProc = DefaultLayerProc;
            /* If layer proc is nil, then they want the default behavior. */
 
    lptr = **newLayer;
    lptr->layerPort     = basePort;
    lptr->layerGDevice  = baseGDevice;
    lptr->layerDepth    = depth;
    lptr->xferMode      = srcCopy;
    lptr->layerProc     = theProc;
    lptr->layerData     = theData;
        /* Layer object is now initialized, except for layers that need a GWorld
        ** created.  This will occur when the layer proc is called with an
        ** initialization message.  (All fields not explicitly set are 0.) */
 
    InsertLayer(*newLayer, aboveLayer, GetLayerPosition(aboveLayer) + 1);
        /* Connect the layer to the layer chain.  The default initialization
        ** behavior may need this, as it may create a GWorld of the same size
        ** as the above layer.  If it isn't connected to the layer chain, then
        ** there is no above layer. */
 
    err = (*theProc)(*newLayer, kLayerInit);
    if (err) {
        DisposeLayer(*newLayer);
        *newLayer = nil;
            /* There wasn't enough memory to create the off-screen GWorld, so
            ** dispose of the layer object.  Since we failed, we need to return
            ** nil and the error. */
    }
 
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
void    DetachLayer(LayerObj theLayer)
{
    LayerObj    aboveLayer, belowLayer;
 
    if (theLayer) {
        aboveLayer = (*theLayer)->aboveLayer;
        belowLayer = (*theLayer)->belowLayer;
        if (aboveLayer)
            (*aboveLayer)->belowLayer = belowLayer;
        if (belowLayer)
            (*belowLayer)->aboveLayer = aboveLayer;
        (*theLayer)->aboveLayer = (*theLayer)->belowLayer = nil;
    }
}
 
 
 
/*****************************************************************************/
 
 
 
OSErr   DisposeLayer(LayerObj theLayer)
{
    OSErr   err;
 
    err = noErr;
    if (theLayer) {
        err = (*((*theLayer)->layerProc))(theLayer, kLayerDispose);
        DetachLayer(theLayer);
        DisposeHandle((Handle)theLayer);
    }
 
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
OSErr   DisposeThisAndBelowLayers(LayerObj theLayer)
{
    OSErr   err, err2;
 
    err = noErr;
    if (theLayer) {
        err2 = DisposeThisAndBelowLayers((*theLayer)->belowLayer);
        err  = DisposeLayer(theLayer);
        if (!err)
            err = err2;
    }
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
short   GetLayerPosition(LayerObj theLayer)
{
    short   pos;
 
    if (!theLayer) return(0);
 
    for (pos = 0; (theLayer = (*theLayer)->aboveLayer) != nil; ++pos) {};
    return(pos);
}
 
 
 
/*****************************************************************************/
 
 
 
LayerObj    GetTopLayer(LayerObj theLayer)
{
    for (; (*theLayer)->aboveLayer; theLayer = (*theLayer)->aboveLayer) {};
    return(theLayer);
}
 
 
 
/*****************************************************************************/
 
 
 
LayerObj    GetBottomLayer(LayerObj theLayer)
{
    for (; (*theLayer)->belowLayer; theLayer = (*theLayer)->belowLayer) {};
    return(theLayer);
}
 
 
 
/*****************************************************************************/
 
 
 
void    InsertLayer(LayerObj theLayer, LayerObj referenceLayer, short pos)
{
    LayerObj    aboveLayer, belowLayer;
    short       i;
 
    if (theLayer) {
        if (theLayer == referenceLayer) {
            /* If theLayer layer is the same as referenceLayer... */
 
            belowLayer = (*theLayer)->belowLayer;
            if (belowLayer)
                referenceLayer = belowLayer;
            aboveLayer = (*theLayer)->aboveLayer;
            if (aboveLayer)
                referenceLayer = aboveLayer;
                    /* Try to make the reference layer not the same as theLayer.
                    ** If it is the same as theLayer, then when theLayer is
                    ** removed from the old hierarchy, we lose the ability to re-add
                    ** it to the hierarchy in a new location. */
        }
 
        DetachLayer(theLayer);
            /* Remove layer from its old hierarchy, if any. */
 
        if (!referenceLayer) return;
            /* If there isn't a valid alternative reference, then theLayer
            ** IS the hierarchy and no action is taken. */
 
        aboveLayer = nil;
        belowLayer = GetTopLayer(referenceLayer);
            /* aboveLayer now nil.  belowLayer now is top layer.  These
            ** are the correct values if the layer being added is to be
            ** the new top layer.  This will be the case if pos is 0.
            ** We now walk the linked list pos number of times to get the
            ** correct position.  We also terminate if we reach the end
            ** of the linked list, no matter what pos is.  This will allow
            ** values of pos that are too big to insert the layer at the
            ** end of the linked list. */
 
        for (i = 0; ((belowLayer) && (i != pos)); ++i) {
            aboveLayer = belowLayer;
            belowLayer = (*belowLayer)->belowLayer;
        }
            /* We now have correct values for aboveLayer and belowLayer.  Note that
            ** these values may be nil, which would be correct. */
        (*theLayer)->aboveLayer = aboveLayer;
        if (aboveLayer)
            (*aboveLayer)->belowLayer = theLayer;
        (*theLayer)->belowLayer = belowLayer;
        if (belowLayer)
            (*belowLayer)->aboveLayer = theLayer;
    }
}
 
 
 
/*****************************************************************************/
 
 
 
/*****************************************************************************/
 
 
 
OSErr   UpdateLayer(LayerObj theLayer)
{
    OSErr   err;
 
    err = noErr;
    if (theLayer) {
        err = UpdateLayer((*theLayer)->belowLayer);
            /* Handle the updates from the bottom up. */
        if (!err)
            err = (*((*theLayer)->layerProc))(theLayer, kLayerUpdate);
                /* Chain possible errors through each level of recursion. */
    }
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
Rect    GetEffectiveSrcRect(LayerObj theLayer)
{
    Rect    srcRect;
 
    if (!theLayer)
        SetRect(&srcRect, 0, 0, 0, 0);
    else {
        srcRect = (*theLayer)->srcRect;
        if (EmptyRect(&srcRect))
            srcRect = ((*theLayer)->layerPort)->portRect;
    }
    return(srcRect);
}
 
 
 
/*****************************************************************************/
 
 
 
Rect    GetEffectiveDstRect(LayerObj theLayer)
{
    Rect    dstRect;
 
    if (!theLayer)
        SetRect(&dstRect, 0, 0, 0, 0);
    else {
        dstRect = (*theLayer)->dstRect;
        if (EmptyRect(&dstRect))
            dstRect = ((*theLayer)->layerPort)->portRect;
    }
    return(dstRect);
}
 
 
 
/*****************************************************************************/
 
 
 
OSErr   DefaultLayerProc(LayerObj theLayer, short message)
{
    OSErr   err;
 
    err = noErr;
    if (theLayer) {
        switch (message) {      /* Dispatch to the correct default behavior. */
            case kLayerInit:
                err = DefaultLayerInit(theLayer);
                break;
            case kLayerDispose:
                err = DefaultLayerDispose(theLayer);
                break;
            case kLayerUpdate:
                err = DefaultLayerUpdate(theLayer);
                break;
            default:
                break;
        }
    }
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
Rect    UpdateUpdateRects(LayerObj theLayer)
{
    Rect    lastUpdate, thisUpdate, dstRect;
 
    if (theLayer) {
        lastUpdate = (*theLayer)->lastUpdate;
        (*theLayer)->lastUpdate = thisUpdate = (*theLayer)->thisUpdate;
        SetRect(&((*theLayer)->thisUpdate), 0, 0, 0, 0);
 
        if ((*theLayer)->includeLastUpdate) {
            (*theLayer)->includeLastUpdate = false;
            if (EmptyRect(&lastUpdate))
                lastUpdate = thisUpdate;
            if (EmptyRect(&thisUpdate))
                thisUpdate = lastUpdate;
            UnionRect(&thisUpdate, &lastUpdate, &thisUpdate);
                /* We are going to update the last and current update rects.
                ** This will allow the appearance of movement for a foreground
                ** object.  The old location is cleared, plus the new location
                ** is updated. */
            dstRect = GetEffectiveDstRect(theLayer);
            SectRect(&thisUpdate, &dstRect, &thisUpdate);
        }
    }
    else SetRect(&thisUpdate, 0, 0, 0, 0);
 
    return(thisUpdate);
}
 
 
 
/*****************************************************************************/
 
 
 
void    InvalLayer(LayerObj theLayer, Rect invalRect, Boolean includeLastUpdate)
{
    Rect        thisUpdate, srcRect, dstRect;
    LayerObj    belowLayer;
    short       ow, oh;
    long        dw, dh, sw, sh;
 
    if (theLayer) {
        belowLayer = (*theLayer)->belowLayer;
        dstRect    = GetEffectiveDstRect(theLayer);
 
        SectRect(&dstRect, &invalRect, &invalRect);
        if (!EmptyRect(&invalRect)) {               /* If there is something to invalidate... */
            thisUpdate = (*theLayer)->thisUpdate;   /* There may be a prior unhandled update... */
            if (EmptyRect(&thisUpdate))
                thisUpdate = invalRect;             /* UnionRect doesn't */
            UnionRect(&thisUpdate, &invalRect, &(*theLayer)->thisUpdate);   /* like empty rects. */
 
            if (belowLayer) {
                /* If we have a below layer, then pass the update down.  The effectiveSrcRct
                ** rect for the below layer may be a different size than the effectiveDstRct.
                ** If this is the case, we want to scale invalRect to invalidate a proportional
                ** area in the below layer. */
 
                srcRect = GetEffectiveSrcRect(belowLayer);
 
                dw = dstRect.right  - dstRect.left;     /* Calculate widths and heights for */
                dh = dstRect.bottom - dstRect.top;      /* srcRect and dstRect. */
                sw = srcRect.right  - srcRect.left;
                sh = srcRect.bottom - srcRect.top;
 
                OffsetRect(&invalRect, -dstRect.left, -dstRect.top);
                    /* We want to align the upper-left corner of the srcRect and dstRect
                    ** so that the scaling also aligns the invalRect into the correct
                    ** place in the below layer's effectiveSrcRect.  invalRect is now
                    ** positioned relative to a dstRect with a upper-left corner of 0,0. */
 
                if (dw != sw) {     /* Width dstRect different than srcRect. */
                    ow = invalRect.right  - invalRect.left;
                    invalRect.left  = (short)((invalRect.left  * sw) / dw);
                    invalRect.right = (short)((invalRect.right * sw) / dw);
                    if ((((invalRect.right  - invalRect.left) * dw) / sw) != ow)
                        ++invalRect.right;
                            /* We can possibly lose a fraction of a pixel on the right edge when
                            ** scaling the invalRect.  It won't hurt if we inval just a bit too
                            ** much, whereas invalidating too little is a bad thing. */
                }
 
                if (dh != sh) {     /* Height dstRect different than srcRect. */
                    oh = invalRect.bottom - invalRect.top;
                    invalRect.top    = (short)((invalRect.top    * sh) / dh);
                    invalRect.bottom = (short)((invalRect.bottom * sh) / dh);
                    if ((((invalRect.bottom - invalRect.top ) * dh) / sh) != oh)
                        ++invalRect.bottom;
                }
 
                OffsetRect(&invalRect, srcRect.left, srcRect.top);
                    /* Displace the new invalRect correctly relative to the srcRect. */
            }
        }
 
        if (includeLastUpdate)
            (*theLayer)->includeLastUpdate = true;
                /* If requested to update last position as well, flag it. */
 
        InvalLayer(belowLayer, invalRect, includeLastUpdate);
            /* Invalidate the below layer with the new (possibly scaled) invalRect. */
    }
}
 
 
/*****************************************************************************/
 
 
 
void    SetLayerWorld(LayerObj theLayer)
{
    CGrafPtr    keepPort;
    GDHandle    keepGDevice;
 
    /* This is a convenient call for setting a GWorld, while remembering what
    ** the previous GWorld was.  This should be balanced with a call to
    ** ResetLayerWorld.  A count of how many times this is called is kept
    ** so that the old GWorld is cached only if SetLayerWorld is currently
    ** in balance with ResetLayerWorld.  This keeps the oldest kept GWorld
    ** from being overwritten by subsequent calls. */
 
    if (theLayer) {
        if (!(*theLayer)->cachedCount++) {
            SmartGetGWorld(&keepPort, &keepGDevice);
            (*theLayer)->cachedPort    = keepPort;
            (*theLayer)->cachedGDevice = keepGDevice;
        }
        SmartSetGWorld((CGrafPtr)(*theLayer)->layerPort, (*theLayer)->layerGDevice);
        LockLayerWorld(theLayer);
    }
}
 
 
 
/*****************************************************************************/
 
 
 
void    ResetLayerWorld(LayerObj theLayer)
{
    /* This is used to undo a call to SetLayerWorld.  Calls to ResetLayerWorld
    ** should be balanced with previous calls to SetLayerWorld. */
 
    if (theLayer) {
        UnlockLayerWorld(theLayer);
        if (!--(*theLayer)->cachedCount)
            SmartSetGWorld((*theLayer)->cachedPort, (*theLayer)->cachedGDevice);
    }
}
 
 
 
/*****************************************************************************/
 
 
 
void    LockLayerWorld(LayerObj theLayer)
{
    Handle  bitmap;
 
    /* This is a convenient way to lock down the pixels for a layer's GWorld.
    ** A locked count is kept to make sure that the GWorld is locked only the
    ** first time this is called.  Calls to LockLayerWorld will most likely
    ** be balanced by calls to UnlockLayerWorld, but not necessarily.  It may
    ** be desirable to keep a GWorld call locked.  In this case, right after
    ** creating the layer (and indirectly its GWorld), call LockLayerWorld.
    ** This will initially lock it.  Subsequent calls would be balanced, and
    ** therefore there will always be one more LockLayerWorld call than
    ** UnlockLayerWorld calls.  This will keep it locked. */
 
    if (theLayer) {
        if ((*theLayer)->layerOwnsPort) {
            if (!(*theLayer)->lockedCount++) {
                bitmap = (*theLayer)->layerBitmap;
                if (bitmap) {
                    HLock(bitmap);
                    (*theLayer)->layerPort->portBits.baseAddr = *bitmap;
                }
                else
                    LockPixels(GetGWorldPixMap((GWorldPtr)(*theLayer)->layerPort));
            }
        }
    }
}
 
 
 
/*****************************************************************************/
 
 
 
void    UnlockLayerWorld(LayerObj theLayer)
{
    /* This undoes what LockLayerWorld does.  Calls to UnlockLayerWorld will
    ** generally be balanced with calls to LockLayerWorld. */
 
    if (theLayer) {
        if ((*theLayer)->layerOwnsPort) {
            if (!--(*theLayer)->lockedCount) {
                if ((*theLayer)->layerBitmap)
                    HUnlock((*theLayer)->layerBitmap);
                else
                    UnlockPixels(GetGWorldPixMap((GWorldPtr)(*theLayer)->layerPort));
            }
        }
    }
}
 
 
 
/*****************************************************************************/
 
 
 
RgnHandle   ScreenDepthRegion(short depth)
{
    RgnHandle       retRgn, tmpRgn;
    GDHandle        device;
    PixMapHandle    pmap;
    Rect            rct;
    GrafPtr         mainPort;
 
    retRgn = NewRgn();
 
    if (GetQDVersion() == kQDOriginal) {
        if (depth == 1) {
            GetWMgrPort(&mainPort);
            rct = mainPort->portRect;
            RectRgn(retRgn, &rct);
        }
    }
    else {
        tmpRgn = NewRgn();
        for (device = GetDeviceList(); device; device = GetNextDevice(device)) {
            if (
                (TestDeviceAttribute(device, screenDevice)) &&
                (TestDeviceAttribute(device, screenActive))
            ) {
                pmap = (*device)->gdPMap;
                if ((*pmap)->pixelSize >= depth) {
                    rct = (*device)->gdRect;
                    RectRgn(tmpRgn, &rct);
                    UnionRgn(retRgn, tmpRgn, retRgn);
                }
            }
        }
        DisposeRgn(tmpRgn);
    }
 
    return(retRgn);
}
 
 
 
/*****************************************************************************/
 
 
 
CIconHandle ReadCIcon(short iconID)
{
    Handle  hndl;
 
    if (TrapExists(_PlotCIcon))
        return(GetCIcon(iconID));
 
    hndl = GetResource('cicn', iconID);
    DetachResource(hndl);
    return((CIconHandle)hndl);
}
 
 
 
/*****************************************************************************/
 
 
 
void    KillCIcon(CIconHandle icon)
{
    if (!icon) return;
 
    if (TrapExists(_PlotCIcon))
        DisposeCIcon(icon);
    else
        DisposeHandle((Handle)icon);
}
 
 
 
/*****************************************************************************/
 
 
 
void    DrawCIcon(CIconHandle icon, Rect destRect)
{
    WindowPtr   curPort;
    short       depth;
 
    if (!icon) return;
 
    if (TrapExists(_PlotCIcon)) {
        depth = 8;
        if (GetSystemVersion() < 0x0700) {
            depth = 1;
            GetPort(&curPort);
            if (curPort->portBits.rowBytes & 0x8000)
                depth = (*(((CGrafPtr)curPort)->portPixMap))->pixelSize;
        }
    }
    else depth = 1;
 
    if (depth > 1)
        PlotCIcon(&destRect, icon);
    else
        DrawCIconByDepth(icon, destRect, 1, true);
}
 
 
 
/*****************************************************************************/
 
 
 
void    DrawCIconNoMask(CIconHandle icon, Rect destRect)
{
    Rect    iconRect;
    char    oldMask[128], *mptr;
    short   maskSize, i;
 
    if (!icon) return;
 
    mptr = (Ptr)(*icon)->iconMaskData;
    iconRect = (*icon)->iconPMap.bounds;
    maskSize = (iconRect.bottom - iconRect.top) * (*icon)->iconMask.rowBytes;
    for (i = 0; i < maskSize; ++i) {
        oldMask[i] = mptr[i];
        mptr[i] = 0xFF;
    }
    DrawCIcon(icon, destRect);
    mptr = (Ptr)(*icon)->iconMaskData;
    for (i = 0; i < maskSize; ++i) mptr[i] = oldMask[i];
}
 
 
 
/*****************************************************************************/
 
 
 
void    DrawCIconByDepth(CIconHandle icon, Rect destRect, short depth, Boolean useMask)
{
    GrafPtr     curPort;
    char        savedIconState;
    char        savedDataState;
    short       offset;
    BitMapPtr   bmap;
    Rect        iconRect;
 
    if (!icon) return;
 
    GetPort(&curPort);
 
    if (!depth) {
        if (!(curPort->portBits.rowBytes & 0x8000))
            depth = 1;
        else
            depth = (*(((CGrafPtr)curPort)->portPixMap))->pixelSize;
    }
 
    savedIconState = HGetState((Handle)icon);       /* Lock down things. */
    HLock((Handle)icon);
    if (depth > 1) {
        savedDataState = HGetState((*icon)->iconData);
        HLock((*icon)->iconData);
        (*icon)->iconPMap.baseAddr = *((*icon)->iconData);
            /* Point the icon's pixMap at the color icon data. */
    }
 
    iconRect = (*icon)->iconPMap.bounds;
        /* Find out the dimensions of the icon. */
 
    (*icon)->iconMask.baseAddr = (Ptr)(*icon)->iconMaskData;
        /* Point the mask's bitMap at the mask data. */
 
    offset  = iconRect.bottom - iconRect.top;
    offset *= (*icon)->iconMask.rowBytes;
    (*icon)->iconBMap.baseAddr = (*icon)->iconMask.baseAddr + offset;
        /* Point the icon's bitMap at the b/w icon data. */
 
    bmap = (depth == 1) ? (BitMapPtr)&((*icon)->iconBMap) : (BitMapPtr)&((*icon)->iconPMap);
    if (useMask)
        CopyMask(bmap, &((*icon)->iconMask), &curPort->portBits, &iconRect, &iconRect, &destRect);
    else
        CopyBits(bmap, &curPort->portBits, &iconRect, &destRect, srcCopy, nil);
 
    HSetState((Handle)icon, savedIconState);        /* Unlock things. */
    if (depth > 1)
        HSetState((*icon)->iconData, savedDataState);
}
 
 
 
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
 
 
 
static OSErr    DefaultLayerInit(LayerObj theLayer)
{
    LayerObj    aboveLayer;
    GWorldPtr   layerWorld;     /* GWorld for this layer. */
    Rect        parentRect;     /* Rectangle of parent in global coordinates. */
    GrafPtr     parentPort;     /* Parent layer's GrafPort. */
    GDHandle    parentGDevice;  /* Parent layer's GDevice. */
    CGrafPtr    keepPort;       /* Saved GrafPort. */
    GDHandle    keepGDevice;    /* Saved GDevice. */
    Point       org;
    OSErr       err;
    short       depth;
 
    err = noErr;
    if (theLayer) {
        if (!(*theLayer)->layerPort) {
 
            aboveLayer = (*theLayer)->aboveLayer;
            if (aboveLayer) {
                /* The default behavior is to create a GWorld the same size
                ** as the above layer, if there is one.  If there isn't an above
                ** layer and we were expected to create a GWorld, we have problems.
                ** This situation can't be resolved and is handled as a paramErr. */
 
                if (!((*theLayer)->layerDepth))
                    (*theLayer)->layerDepth = (*aboveLayer)->layerDepth;
 
                SmartGetGWorld(&keepPort, &keepGDevice);        /* Keep the GWorld. */
 
                parentPort    = (*aboveLayer)->layerPort;
                parentGDevice = (*aboveLayer)->layerGDevice;
                    /* Grab the parent layer's GrafPort and GDevice. */
    
                SmartSetGWorld((CGrafPtr)parentPort, parentGDevice);
                parentRect = GetEffectiveDstRect(aboveLayer);
                    /* The default behavior is to use the portRect of the above
                    ** port.  This behavior can be overridden if desired by setting
                    ** dstRect.  dstRect is initialized to be empty, but if
                    ** it is specifically set, then this layer should map into
                    ** just the dstRect and not the portRect.  This is useful if
                    ** the off-screen image is to be displayed in only a portion
                    ** of a window. */
 
                org.h = parentRect.left;
                org.v = parentRect.top;
 
                LocalToGlobal(((Point *)&parentRect) + 0);
                LocalToGlobal(((Point *)&parentRect) + 1);
                    /* Put the parent layer's destination rect in global coordinates. */
    
                if (GetQDVersion())
                    err = NewGWorld(&layerWorld, (*theLayer)->layerDepth, &parentRect, nil, nil, 0);
                        /* Create the GWorld for this layer.  It will be created with the
                        ** requested depth.  If the requested depth is 0, then it will be
                        ** created with a depth great enough for the deepest monitor the
                        ** parentRect intersects. */
                else
                    err = MakeLayerWorld(&layerWorld, theLayer, parentRect);
                        /* Create a bitmap for those systems without GWorlds. */
 
                if (err == noErr) {
                    (*theLayer)->layerOwnsPort = true;
                    SetPort((*theLayer)->layerPort = (GrafPtr)layerWorld);
                        /* Save the new GWorld in the layer object. */
                    SetOrigin(org.h, org.v);
                        /* Set the origin so that this GWorld maps directly into the
                        ** area to be copied into (dstRect or portRect) for the
                        ** above layer. */
 
                    if (!((*theLayer)->layerDepth)) {
                        if (((GrafPtr)layerWorld)->portBits.rowBytes & 0x8000)
                            depth = (*(((CGrafPtr)layerWorld)->portPixMap))->pixelSize;
                        else
                            depth = 1;
                        (*theLayer)->layerDepth = depth;
                    }
                }
 
                SmartSetGWorld(keepPort, keepGDevice);      /* Restore the kept GWorld. */
            }
            else {
                err = paramErr;
                    /* We were expected to create an off-screen GWorld of the
                    ** same size as the above layer, but we didn't have an above
                    ** layer.  This is an error.  The parameters passed to NewLayer
                    ** were inappropriate for the situation, so return a paramErr. */
            }
        }
    }
 
    return(err);
}
 
 
 
/*****************************************************************************/
 
 
 
static OSErr    DefaultLayerUpdate(LayerObj theLayer)
{
    LayerObj    belowLayer;
    GrafPtr     belowPort, thisPort;
    GDHandle    thisGDevice;
    CGrafPtr    keepPort;
    GDHandle    keepGDevice;
    Rect        thisUpdate, belowRect, thisRect;
    short       xfer;
    RgnHandle   rgn;
 
    /* The default update behavior is to copy the area to be updated from the
    ** below layer into the indicated layer.  We only need to update layer if
    ** there is a below layer.  The bottom-most layer update doesn't do anything.
    ** As a default, the bottom-most layer is considered background and does not
    ** get updated. */
 
    if (theLayer) {
        belowLayer = (*theLayer)->belowLayer;
        if (belowLayer) {
            /* Get this layer's GWorld and below layer's port. */
            thisPort    = (*theLayer)->layerPort;
            thisGDevice = (*theLayer)->layerGDevice;
            belowPort   = (*belowLayer)->layerPort;
 
            /* Save current GWorld and set the parent's GWorld. */
            SmartGetGWorld(&keepPort, &keepGDevice);
            SmartSetGWorld((CGrafPtr)thisPort, thisGDevice);
 
            thisUpdate = UpdateUpdateRects(theLayer);
 
            rgn = NewRgn();
            RectRgn(rgn, &thisUpdate);
 
            belowRect = GetEffectiveSrcRect(belowLayer);
            thisRect  = GetEffectiveDstRect(theLayer);
 
                /* As a default behavior, we CopyBits the below layer into this layer. */
            LockLayerWorld(belowLayer);
            LockLayerWorld(theLayer);
            xfer = (*theLayer)->xferMode;
            CopyBits(&belowPort->portBits, &thisPort->portBits, &belowRect, &thisRect, xfer, rgn);
            UnlockLayerWorld(theLayer);
            UnlockLayerWorld(belowLayer);
            DisposeRgn(rgn);
 
            SmartSetGWorld(keepPort, keepGDevice);      /* Restore to the kept GWorld. */
        }
    }
    return(noErr);
}
 
 
 
/*****************************************************************************/
 
 
 
static OSErr    DefaultLayerDispose(LayerObj theLayer)
{
    GWorldPtr   theWorld;
 
    if (theLayer) {
        if ((*theLayer)->layerOwnsPort) {
            theWorld = (GWorldPtr)(*theLayer)->layerPort;
            if (theWorld) {
                if ((*theLayer)->layerBitmap)
                    KillLayerWorld(theLayer);
                else
                    DisposeGWorld(theWorld);
            }
        }
    }
 
    return(noErr);
}
 
 
 
/*****************************************************************************/
 
 
 
static OSErr    MakeLayerWorld(GWorldPtr *layerWorld, LayerObj theLayer, Rect bnds)
{
    GrafPtr oldPort;
    GrafPtr newPort;
    Handle  bitmap;
    OSErr   err;
 
    OffsetRect(&bnds, -bnds.left, -bnds.top);   /* Make sure upper-left is 0,0. */
 
    GetPort(&oldPort);      /* Need this to restore the port after OpenPort. */
 
    newPort = (GrafPtr)NewPtr(sizeof(GrafPort));        /* Allocate the grafPort. */
    err = MemError();
    if (err)
        return(err);        /* Failed to allocate the off-screen port. */
 
    /* The call to OpenPort does the following:
    ** 1) allocates space for visRgn (set to screenBits.bounds) and clipRgn (set wide open)
    ** 2) sets portBits to screenBits
    ** 3) sets portRect to screenBits.bounds, etc. (see IM I-163,164)
    ** 4) side effect: does a SetPort(&offScreen) */
 
    OpenPort(newPort);
    SetPort(oldPort);
 
        /* Now make bitmap the size of the bounds that caller supplied. */
 
    newPort->portRect = bnds;
    newPort->portBits.bounds = bnds;
    RectRgn(newPort->visRgn, &bnds);
 
    SetRectRgn(newPort->clipRgn, -32000, -32000, 32000, 32000);
        /* Avoid wide-open clipRgn, to be safe. */
 
    /* rowBytes is size of row, it must be rounded up to an even number of bytes. */
    newPort->portBits.rowBytes = ((bnds.right - bnds.left + 15) >> 4) << 1;
 
    bitmap = NewHandle(newPort->portBits.rowBytes * (long)(bnds.bottom - bnds.top));
    err = MemError();
    if (err) {
        ClosePort(newPort);         /* Dump the visRgn and clipRgn. */
        DisposePtr((Ptr)newPort);   /* Dump the GrafPort. */
        return(err);
    }
 
    (*theLayer)->layerBitmap = bitmap;
    *layerWorld              = (GWorldPtr)newPort;
 
    return(noErr);
}
 
 
 
/*****************************************************************************/
 
 
 
static void KillLayerWorld(LayerObj theLayer)
{
    DisposeHandle((*theLayer)->layerBitmap);
    (*theLayer)->layerBitmap = nil;
 
    ClosePort((*theLayer)->layerPort);
    DisposePtr((Ptr)(*theLayer)->layerPort);
    (*theLayer)->layerPort = nil;
}
 
 
 
 
/*****************************************************************************/
 
 
 
static void SmartGetGWorld(CGrafPtr *port, GDHandle *gdh)
{
    if (GetQDVersion())
        GetGWorld(port, gdh);
    else {
        *gdh = nil;
        GetPort((GrafPtr *)port);
    }
}
 
 
 
/*****************************************************************************/
 
 
 
static void SmartSetGWorld(CGrafPtr port, GDHandle gdh)
{
    if (GetQDVersion())
        SetGWorld(port, gdh);
    else
        SetPort((GrafPtr)port);
}
 
 
 
/*****************************************************************************/
 
 
 
static short    GetQDVersion()
{
    static long gestaltResult = -1;
 
    if (gestaltResult == -1) {
        if (Gestalt(gestaltQuickdrawVersion, &gestaltResult))
            gestaltResult = 0;
    }
 
    return((gestaltResult >> 8) & 0xFF);
}
 
 
 
/*****************************************************************************/
 
 
 
static short    GetSystemVersion()
{
    static long gestaltResult;
 
    if (!gestaltResult) {
        if (Gestalt(gestaltSystemVersion, &gestaltResult))
            gestaltResult = 0;
    }
 
    return(gestaltResult);
}
 
 
 
/*****************************************************************************/
 
 
 
static Boolean  TrapExists(short theTrap)
{
    TrapType    theTrapType;
 
    theTrapType = GetTrapType(theTrap);
    if ((theTrapType == ToolTrap) && ((theTrap &= 0x07FF) >= NumToolboxTraps()))
        theTrap = _Unimplemented;
 
    return(NGetTrapAddress(_Unimplemented, ToolTrap) != NGetTrapAddress(theTrap, theTrapType));
}
 
 
 
/*****************************************************************************/
 
 
 
static short    NumToolboxTraps(void)
{
    if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
        return(0x200);
    else
        return(0x400);
}
 
 
 
/*****************************************************************************/
 
 
 
static TrapType GetTrapType(short theTrap)
{
    /* OS traps start with A0, Tool with A8 or AA. */
    if ((theTrap & 0x0800) == 0)                    /* per D.A. */
        return(OSTrap);
    else
        return(ToolTrap);
}