Source/BitBlitz.c

/*--------------------------------------------------------------------------------------
//
//  File:       BitBlitz.c
//
//  Contents:   This files contains all of the main routines that comprise the BitBlitz tool.
//              The tool was designed to be a learning/testing tool for the Copybits(), CopyMask(),
//              and CopyDeepMask() QuickDraw calls.  It makes extensive use of GWorlds as well as
//              the above mentioned QuickDraw bit tools.
//
//
//  By Georgiann ("George") Delaney
//  ©Ê1989 - 1990, Apple Computer, Inc.
//
//--------------------------------------------------------------------------------------*/
  
  
#include "MacHeaders.h"
 
#include "WCenter.h"
#include "ColorTools.h"
 
#include "MainStuff.h"
 
#include "BlendUtils.h"
#include "OffscreenUtils.h"
#include "PICTUtils.h"
#include "AlertDlogUtils.h"
#include "AlrtDlogTools.h"
 
#include "BitBlitz.h"
 
 
 
#pragma segment CopyTests
 
 
/*--------------------------------------------------------------------------------------*/
/*  Constants  */
 
#define  AttrbDlogID        300             /*  Attributes dialog constants  */
#define  diDlogTitle        3
#define  diLine1            4
#define  diLine2            5
#define  diLine3            10
#define  diLine4            16
#define  diBitmapButton     6
#define  diGWorldButton     7
#define  diDepthPopUp       8
#define  diContentPopUp     9
#define  diHeightText       14
#define  diWidthText        15
 
#define  TrapDlogID         301             /*  Trap selection dialog contents  */
#define  diTrapLine1         4
#define  diTrapLine2         5
#define  diCopyMaskButton    6
#define  diCopyDeepButton    7
#define  diCopyBitsButton    8
 
#define  Sys7DlogID         302             /*  Need 7.0 dialog constants  */
#define  AboutBoxDlogID     303             
 
 
#define  kDepthMenu         300             /*  Attributes dialog menu constants  */
#define  kContentMenu       301 
 
        
#define  kNone              0               /*  Buffer type constants  */
#define  kBitMap            1
#define  kPixMap            2
#define  kGWorld            3
    
#define  km1bpp             1               /*  Constants for depth menu selecions  */ 
#define  km2bpp             2
#define  km4bpp             3
#define  km8bpp             4
#define  km16bpp            5
#define  km32bpp            6
#define  kmMaxbpp           8
#define  kTitleStrings      4000            /*  String resource constants  */
 
#define  kErrorStrings      4001
#define     kBootMemErr     1
#define     kDepthMemErr    2
#define     kResizeMemErr   3
#define     kPICTReadErr    4
#define  kAttribDlogStrings 4002
 
 
#define kHLSRectBlend       1               
#define kHLSHBlend          2
#define kHLSVBlend          3
#define kGrayRectBlend      5
#define kGrayHBlend         6
#define kGrayVBlend         7
#define kGrayPatRectBlend   9
#define kGrayPatHBlend      10
#define kGrayPatVBlend      11
#define kSolidRGB           13
#define kPICT               15
 
 
 
 
/*--------------------------------------------------------------------------------------*/
/*  Global Variables  */
 
RGBColor    gLastRGB;               /* The last RGB Color selected for a solid fill.                
                                       Used as a starting color for the color picker for selection. */
Point       gZeroPt;                /* Its just what you think - a point initialized to (0,0)       */ 
 
                                    /* Array containing the 8 standard colors   */
long        gColorArray[8]  =  {blackColor, whiteColor, redColor, greenColor, blueColor, cyanColor, magentaColor, yellowColor}; 
 
                                    /* Array containing rgb equivalents of 8 standard colors & gray*/
RGBColor    gRGBArray[9];       
 
/*======================================================================================*/
/*  Initialization Routines     */
 
 
/*--------------------------------------------------------------------------------------*/
Boolean myTrapAvailable(theNumber,theType)
/*
//  This procedure tests to see if the specified trap is available in the current system.
*/
    short       theNumber;
    TrapType    theType;
{
    return NGetTrapAddress(theNumber,theType) != GetTrapAddress(_Unimplemented);
} 
 
/*--------------------------------------------------------------------------------------*/
Boolean HasGWorlds()
/*
//  This procedure tests to see if the GWorlds are available.
*/
{
    #define _GWorldDispatch (short)0xAB1D
 
    return(myTrapAvailable(_GWorldDispatch,ToolTrap));
} 
 
 
/*--------------------------------------------------------------------------------------*/
void InitTestGlobals()
/*
//  This procedure is called during the program's initialization to set the initial values
//  of the app's constants.
*/
{
    gColorAvail   = HasColorQD();
    gGWorldsAvail = HasGWorlds();
    gZeroPt.h     = gZeroPt.v  = 0;
}
 
 
/*--------------------------------------------------------------------------------------*/
void  InitTestWindowAttributes()
/*
//  InitTestWindowAttributes() performs initialization of the global structs defining the  
//  attributes of the source, mask, and destination bit images.
*/
{
    register    i;
    
            /*  initialize all of the common fields  */
    for (i=kSrcWindow; i <= kBitWindow; i++)  {
        gWList[i].window            = nil;
        gWList[i].buffer.gworld     = nil;
        
        if (gGWorldsAvail)  {
            gWList[i].bufferType    = kGWorld;
            gWList[i].bufferDepth   = 0;
            }
        else  {
            gWList[i].bufferType    = kBitMap;
            gWList[i].bufferDepth   = 1;
            }
        }
 
            /*  set initial window contents with respect to the availability of color.  */
    if (gColorAvail)  {
        gWList[kSrcWindow].contentType          = kHLSRectBlend;
        gWList[kSrcWindow].content.saturation   = 65535;
    
        gWList[kMskWindow].contentType          = kGrayVBlend;
    
        gWList[kDstWindow].contentType          = kSolidRGB;
        gWList[kDstWindow].content.RGB.red      = 0xFFFF;
        gWList[kDstWindow].content.RGB.green    = 0xFFFF;
        gWList[kDstWindow].content.RGB.blue     = 0x0000;
        }
    else  {
        gWList[kSrcWindow].contentType          = kGrayPatRectBlend;
        gWList[kMskWindow].contentType          = kGrayPatVBlend;
        gWList[kDstWindow].contentType          = kGrayPatHBlend;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  InitColorSettings()
/*
//  InitColorSettings() initializes the values in the color array.
*/
{
    gRGBArray[0].red   = 0x0000;    gRGBArray[0].green = 0x0000;    gRGBArray[0].blue  = 0x0000;
    gRGBArray[1].red   = 0xFFFF;    gRGBArray[1].green = 0xFFFF;    gRGBArray[1].blue  = 0xFFFF;
    gRGBArray[2].red   = 0xDD6B;    gRGBArray[2].green = 0x08C2;    gRGBArray[2].blue  = 0x06A2;
    gRGBArray[3].red   = 0x0000;    gRGBArray[3].green = 0x8000;    gRGBArray[3].blue  = 0x11B0;
    gRGBArray[4].red   = 0x0000;    gRGBArray[4].green = 0x0000;    gRGBArray[4].blue  = 0xD400;
    gRGBArray[5].red   = 0x0241;    gRGBArray[5].green = 0xAB54;    gRGBArray[5].blue  = 0xEAFF;
    gRGBArray[6].red   = 0xF2D7;    gRGBArray[6].green = 0x0856;    gRGBArray[6].blue  = 0x84EC;
    gRGBArray[7].red   = 0xFC00;    gRGBArray[7].green = 0xF37D;    gRGBArray[7].blue  = 0x052F;
    gRGBArray[8].red   = 0x7FFF;    gRGBArray[8].green = 0x7FFF;    gRGBArray[8].blue  = 0x7FFF;
 
 
    gFGColor.menuIndex      = kBlack;
    gFGColor.rgb            = gRGBArray[kBlack-1];
    
    gBKColor.menuIndex      = kWhite;
    gBKColor.rgb            = gRGBArray[kWhite-1];
 
    gOPColor.menuIndex      = kGray;
    gOPColor.rgb            = gRGBArray[kGray-1];
 
    gHiliteColor.menuIndex  = kBlue;
    gHiliteColor.rgb        = gRGBArray[kBlue-1];
}
 
/*--------------------------------------------------------------------------------------*/
void  InitRgnSettings()
/*
//  InitRgnSettings() initializes all of the region settings to nil.
*/
{
    register    i;
    
    for (i=kClpRgn; i<=kMskRgn; i++)  {
        gRList[i].type = kNoRgn;
        gRList[i].rgn  = nil;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  DisposeRgnSettings()
/*
//  DisposeRgnSettings() disposes any region settings that may have been set by the user.
*/
{
    register    i;
    
    for (i=kClpRgn; i<=kMskRgn; i++)  {
        if (gRList[i].rgn != nil)  {
            DisposeRgn(gRList[i].rgn);
            gRList[i].rgn = nil;
            }
        }
}
 
 
 
/*--------------------------------------------------------------------------------------*/
void  MenuIndex2RGB(ColorAttributes colorSelect, RGBColor *theRGB)
{
    if (colorSelect.menuIndex == kCustomColor)
        *theRGB = colorSelect.rgb;
    else
        *theRGB = gRGBArray[colorSelect.menuIndex-1];
}
 
 
/*--------------------------------------------------------------------------------------*/
void  DrawOval()
{
    Rect    tempRect;
 
    tempRect = gWList[kBitWindow].window->portRect;
    
    InsetRect(&tempRect, 10, 10);
    FrameOval(&tempRect);
}
 
/*--------------------------------------------------------------------------------------*/
void  DrawDiamond()
{
    Rect    tempRect;
    Point   centerPt;
 
    tempRect = gWList[kBitWindow].window->portRect;
    
    InsetRect(&tempRect, 10, 10);
    
    centerPt.h = tempRect.left + ((tempRect.right  - tempRect.left)>>1);
    centerPt.v = tempRect.top  + ((tempRect.bottom - tempRect.top )>>1);
    
    MoveTo(centerPt.h,      tempRect.top);
    LineTo(tempRect.right,  centerPt.v);
    LineTo(centerPt.h,      tempRect.bottom);
    LineTo(tempRect.left,   centerPt.v);
    LineTo(centerPt.h,      tempRect.top);
}
 
/*--------------------------------------------------------------------------------------*/
void  DrawHole()
{
    Rect    tempRect;
 
    tempRect = gWList[kBitWindow].window->portRect;
    
    InsetRect(&tempRect, 10, 10);
    FrameOval(&tempRect);
 
    InsetRect(&tempRect, ((tempRect.right  - tempRect.left)>>2), ((tempRect.bottom - tempRect.top )>>2));
    FrameOval(&tempRect);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  DrawStar()
{
    Rect    tempRect;
    Point   centerPt;
 
    tempRect = gWList[kBitWindow].window->portRect;
    
    InsetRect(&tempRect, 10, 10);
    
    centerPt.h = tempRect.left + ((tempRect.right  - tempRect.left)>>1);
    centerPt.v = tempRect.top  + ((tempRect.bottom - tempRect.top )/3);
    
    MoveTo(centerPt.h,      tempRect.top);
    LineTo(tempRect.right,  tempRect.bottom);
    LineTo(tempRect.left,   centerPt.v);
    LineTo(tempRect.right,  centerPt.v);
    LineTo(tempRect.left,   tempRect.bottom);
    LineTo(centerPt.h,      tempRect.top);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  UpdateRgnSettings()
/*
//  UpdateRgnSettings() updates any region settings that the user has selected after the 
//  corresponding window has been resized.
*/
{
    register    i;
    
    
    DisposeRgnSettings();
    
    for (i=kClpRgn; i<=kMskRgn; i++) 
 
        if (gRList[i].type != kNoRgn)  {
            gRList[i].rgn = NewRgn();
            OpenRgn();
    
            switch(gRList[i].type)  {
                case kDiamondClip:  DrawDiamond();      break;
                case kOvalClip:     DrawOval();         break;
                case kStarClip:     DrawStar();         break;
                case kHoleClip:     DrawHole();         break;
                }
    
            CloseRgn(gRList[i].rgn);
            }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SetEnv()
/*
//  SetEnv() is called to set the color environment and clip regions the user haa designated
//  for use in the copy call.
*/
{
    GrafPtr     thePort;
    
 
    if (gColorAvail)  {
        RGBForeColor(&gFGColor.rgb);
        RGBBackColor(&gBKColor.rgb);
        OpColor     (&gOPColor.rgb);
        HiliteColor (&gHiliteColor.rgb);
        }
    else  {
        ForeColor(gColorArray[gFGColor.menuIndex-1]);
        BackColor(gColorArray[gBKColor.menuIndex-1]);
        }
    
    if (gRList[kClpRgn].rgn != nil)  {
        gHoldClpRgn = NewRgn();
        GetClip(gHoldClpRgn);
        SetClip(gRList[kClpRgn].rgn);
        }
 
    if (gRList[kVisRgn].rgn != nil)  {
        GetPort(&thePort);
        gHoldVisRgn     = thePort->visRgn;
        thePort->visRgn = gRList[kVisRgn].rgn;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  RestoreEnv()
/*
//  RestoreEnv() restores the color and clip settings following the copy call.
*/
{
    GrafPtr     thePort;
    
    if (gColorAvail)  {
        RGBForeColor(&gRGBArray[kBlack-1]);
        RGBBackColor(&gRGBArray[kWhite-1]);
        }
    else  {
        ForeColor(gColorArray[kBlack-1]);
        BackColor(gColorArray[kWhite-1]);
        }
        
 
    if (gHoldClpRgn != nil)  {
        SetClip(gHoldClpRgn);
        DisposeRgn(gHoldClpRgn);
        gHoldClpRgn = nil;
        }
 
    if (gHoldVisRgn != nil)  {
        GetPort(&thePort);
        thePort->visRgn     = gHoldVisRgn;
        }
            
}
 
 
 
/*======================================================================================*/
/*  Routines to handle CopyMode selection   */
 
 
/*--------------------------------------------------------------------------------------*/
short  ModeMenuItem2CopyMode(short theItem)
/*
//  This routine is used to convert a copy mode menu selection to an actual QuickDraw 
//  copy mode.   The reason a switch statement is required here is because the 
//  QuickDraw copy mode constants are not contiguous.
*/
{
    short   theMode;
    
    switch(theItem)  {
        case kSrcCopy:      if (gDither) 
                                 theMode = ditherCopy;
                            else theMode = theItem -1;      
                            break;
        case kSrcOr:
        case kSrcXor:
        case kSrcBic:
        case kNotSrcCopy:
        case kNotSrcOr:
        case kNotSrcXor:
        case kNotSrcBic:    theMode = theItem -1;       break;
 
        case kBlend:        theMode = blend;            break;
        case kAddPin:       theMode = addPin;           break;
        case kAddOver:      theMode = addOver;          break;
        case kAddMax:       theMode = addMax;           break;
        case kAddMin:       theMode = adMin;            break;
        case kSubOver:      theMode = subOver;          break;
        case kSubPin:       theMode = subPin;           break;
        
        case kTransparent:  theMode = transparent;      break;
        
        case kHilite:       theMode = hilite;           break;
        }
    
    return(theMode);
}
 
 
/*--------------------------------------------------------------------------------------*/
short  CopyMode2ModeMenuItem(short theMode)
/*
//  This routine is used to convert a QuickDraw copy mode to a copy mode menu selection.
//  The reason a switch statement is required here is because the QuickDraw copy mode
//  constants are not contiguous.
*/
{
    short  theItem;
    
    switch(theMode)  {
        case ditherCopy:    theItem = srcCopy +1;       break;
        
        case srcCopy:
        case srcOr:
        case srcXor:
        case srcBic:
        case notSrcOr:
        case notSrcCopy:
        case notSrcXor:
        case notSrcBic:     theItem = theMode +1;       break;
 
        case blend:         theItem = kBlend;           break;
        case addPin:        theItem = kAddPin;          break;
        case addOver:       theItem = kAddOver;         break;
        case adMin  :       theItem = kAddMin;          break;
        case addMax:        theItem = kAddMax;          break;
        case subOver:       theItem = kSubOver;         break;
        case subPin:        theItem = kSubPin;          break;
        
        case transparent:   theItem = kTransparent;     break;
        
        case hilite:        theItem = kHilite;          break;
        }
        
    return(theItem);
}
 
  
  
/*======================================================================================*/
/*  Window Attribute PICT handle cleanup.   */
 
 
/*--------------------------------------------------------------------------------------*/
void  DisposePICTInfo(short windowCode)
/*  
//  DisposePICTInfo() tests to determine if the specified window has a PICT handle allocated 
//  for buffer updating and disposes it if it is not already nil.
*/
{
    if ((gWList[windowCode].contentType == kPICT) && (gWList[windowCode].content.pictInfo != nil))  {
        DisposHandle((Handle)gWList[windowCode].content.pictInfo);
        gWList[windowCode].content.pictInfo = nil;
        }
}
 
 
 
/*======================================================================================*/
/*  Offscreen Buffer Maintainance Routines  */
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  CreateWindowBuffer(short windowCode)
/*  
//  CreateWindowBuffer() creates an offscreen buffer of the designated size with respect to the
//  attributes set in the window's attribute information.
*/
{
    Boolean     bufferCreated   = false;
    
    switch(gWList[windowCode].bufferType) {
        case kBitMap:   
            bufferCreated = CreateOSBitmap(&gWList[windowCode].buffer.bitmap, &gWList[windowCode].window->portRect);
            break;
        case kGWorld:               
            bufferCreated = (NewGWorld(&gWList[windowCode].buffer.gworld, gWList[windowCode].bufferDepth, &gWList[windowCode].window->portRect, nil, nil, 0) == noErr);
            break;
        }
    
    return(bufferCreated);
}
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  UpdateWindowBuffer(short windowCode, short depth)    
/* 
//  This routine updates the specified window's offscreen buffer to the desired depth.
*/
{
    Boolean  bufferChanged  = false;
    long     gwError;
    
    if (gWList[windowCode].bufferType == kGWorld) { 
        gWList[windowCode].bufferDepth = depth;
        
        gwError = (UpdateGWorld(&gWList[windowCode].buffer.gworld, depth, &gWList[windowCode].buffer.gworld->portRect, 0, 0, 0) == noErr);
        bufferChanged = (gwError & gwFlagErr);
        }
        
    return(true);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  DisposeWindowBuffer(short windowCode)
/*  
//  DisposeWindowBuffer() determines the type of buffer that is associated with the specified 
//  window and performs the appropriate dispose.
*/
{
    if (gWList[windowCode].buffer.gworld != nil) 
        switch(gWList[windowCode].bufferType) {
            case kBitMap:   DisposeOSBitmap(gWList[windowCode].buffer.bitmap);  break;
            case kGWorld:   DisposeGWorld  (gWList[windowCode].buffer.gworld);  break;
            }
}
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  SetOffscreen(short windowCode, short type, short depth)    
/*
//  Hey, guess what, it does what you expect - sets the specified window's buffer to the
//  specified offscreen type and depth.
*/
{
    Boolean     bufferChanged   = false;    
 
            
    if (type != gWList[windowCode].bufferType) {
        DisposeWindowBuffer(windowCode);
        
        gWList[windowCode].bufferType  = type;
        gWList[windowCode].bufferDepth = depth;
        bufferChanged = CreateWindowBuffer(windowCode);
        if (!bufferChanged)
            OKRsrcAlert(kErrorStrings,kDepthMemErr);
        }
    else  {
        if (depth != gWList[windowCode].bufferDepth)  {
            gWList[windowCode].bufferDepth = depth;
            bufferChanged = UpdateWindowBuffer(windowCode, depth);
            if (!bufferChanged)
                OKRsrcAlert(kErrorStrings,kDepthMemErr);
            }
        }
    
    return(bufferChanged);
}
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  UpdateOffscreen(short windowCode, short type, short depth)
/*
//  UpdateOffscreen() tests to determine if the specified window's offscreen buffer has the 
//  specified type and depth attributes and  performs whatever buffer updating necessary.
*/
{
    Boolean depthChanged = false;
    
 
    depthChanged = SetOffscreen(windowCode,type,depth);
    
                /*  Since the dest and copy window must be the same depth, if the user changes 
                //  the depth of the dest window, the copy window should also be set to the 
                //  new selected depth/
                */
    if ((depthChanged) && (windowCode == kDstWindow))  {
        depthChanged = SetOffscreen(kBitWindow,type,depth);
            
                /*  However, if an error occurs and the copy window can not be set to the
                //  new depth then return the dest window to the original depth so that both are
                //  equal.
                */
        if (!depthChanged) 
            SetOffscreen(kDstWindow,gWList[kBitWindow].bufferType,gWList[kBitWindow].bufferDepth);
        }
        
    if (depthChanged)  {
        DrawBuffer(windowCode);
        DrawBuffer(kBitWindow);
        }
                
    return(depthChanged);
}
 
 
 
/*======================================================================================*/
/*  Attributes Dialog Routines  */
 
 
/*--------------------------------------------------------------------------------------*/
void  SetDepthMenuSelection(DialogPtr  theDlog, short  theItem, short theDepth)
/*
//  This procedure initializes the attributes dialog's depth popup to the depth of the 
//  selected window's offscreen depth.
*/
{
    short       itemType;
    Handle      itemHdl;
    Rect        itemRect;
    
    short       selection;
    MenuHandle  depthMenuHdl;
 
    
    depthMenuHdl = GetMHandle(kDepthMenu);
    if ((!gGWorldsAvail) || (!gColorAvail))  {
        DisableItem(depthMenuHdl, km2bpp);
        DisableItem(depthMenuHdl, km4bpp);
        DisableItem(depthMenuHdl, km8bpp);
        DisableItem(depthMenuHdl, km16bpp);
        DisableItem(depthMenuHdl, km32bpp);
        }
        
    if (!gGWorldsAvail)  
        DisableItem(depthMenuHdl, kmMaxbpp);
 
    GetDItem(theDlog,theItem,&itemType,&itemHdl,&itemRect);
    
    switch(theDepth)  {   
        case 1  : selection = km1bpp;   break;
        case 2  : selection = km2bpp;   break;
        case 4  : selection = km4bpp;   break;
        case 8  : selection = km8bpp;   break;
        case 16 : selection = km16bpp;  break;
        case 32 : selection = km32bpp;  break;
        case 0  : selection = kmMaxbpp; break;
        }
    
    SetCtlValue((ControlHandle)itemHdl,selection);
}
 
 
/*--------------------------------------------------------------------------------------*/
short  GetDepthMenuSelection(DialogPtr  theDlog, short  theItem)
/*
//  This procedure converts the attributes dialog's depth popup selection to its
//  equivalent depth value.
*/
{
    short       itemType;
    Handle      itemHdl;
    Rect        itemRect;
    
    short       selection,depth;
 
 
    GetDItem(theDlog,theItem,&itemType,&itemHdl,&itemRect);
    selection = GetCtlValue((ControlHandle)itemHdl);
    
    switch(selection)  {   
        case km1bpp  : depth = 1;   break;
        case km2bpp  : depth = 2;   break;
        case km4bpp  : depth = 4;   break;
        case km8bpp  : depth = 8;   break;
        case km16bpp : depth = 16;  break;
        case km32bpp : depth = 32;  break;
        case kmMaxbpp: depth = 0;   break;
        }
    
    return(depth);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SetContentMenuSelection(DialogPtr  theDlog, short  theItem, short theContent)
/*
//  This procedure initializes the attributes dialog's content popup to reflect the 
//  selected window's current content type.
*/
{
    short       itemType;
    Handle      itemHdl;
    Rect        itemRect;
    MenuHandle  contentMenuHdl;
 
    contentMenuHdl = GetMHandle(kContentMenu);
    if (!gColorAvail)  {
        DisableItem(contentMenuHdl, kHLSRectBlend);
        DisableItem(contentMenuHdl, kHLSHBlend);
        DisableItem(contentMenuHdl, kHLSVBlend);
        DisableItem(contentMenuHdl, kGrayRectBlend);
        DisableItem(contentMenuHdl, kGrayHBlend);
        DisableItem(contentMenuHdl, kGrayVBlend);
        DisableItem(contentMenuHdl, kSolidRGB);
        }
 
    GetDItem(theDlog,theItem,&itemType,&itemHdl,&itemRect);
    SetCtlValue((ControlHandle)itemHdl,theContent);
}
 
 
/*--------------------------------------------------------------------------------------*/
short  GetContentMenuSelection(DialogPtr  theDlog, short  theItem)
/*
//  This procedure returns the attributes dialog's content popup selection.
*/
{
    short       itemType;
    Handle      itemHdl;
    Rect        itemRect;
 
 
    GetDItem(theDlog,theItem,&itemType,&itemHdl,&itemRect);
    return(GetCtlValue((ControlHandle)itemHdl));
}
 
 
/*--------------------------------------------------------------------------------------*/
pascal Boolean AttrbDlogFilter(DialogPtr theDlog, EventRecord *theEvent, short *theItem)
{
 /*
 //  AttrbDlogFilter() is the dialog filter for the attributes  dialog.
 */
 
                
    IBeamCursorAdjust(theDlog,(ValidTextProcPtr)StdValidDecTextProc);
 
                /*  If we get a key down event, check to see if it is for the OK or Cancel
                //  buttons.  If not, filter it for numeric input only (via EditNumText()).
                */
    if ((theEvent->what == autoKey) || (theEvent->what == keyDown))  {
        short editItem = ((DialogPeek)theDlog)->editField + 1;
        
        if (HandleOkayCancel(theDlog,theEvent,theItem))
            return(true);
 
        EditNumText(theDlog,theEvent,editItem,0L,1000L,decimalOnly);
        }
        
    return(false);
}
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  ChangeWindowAttributes(short windowCode)
/*
//  ChangeWindowAttributes() allows the user to change any of the attributes of the source, 
//  mask, or destination windows.
//
//  This dialog is accessed by selecting the appropriate menu selection or by double-clicking 
//  in the content region of the desired window.
*/
{
    DialogPtr   theDlog;
    GrafPtr     holdPort;
    short       itemHit;
    
    Boolean     okHit           = false;
    Boolean     done            = false;
    Boolean     bufferChanged   = false;
    Boolean     contentChanged  = false;
 
    short       tempBufferType   = gWList[windowCode].bufferType;
    short       tempBufferDepth  = gWList[windowCode].bufferDepth;
    short       tempContentType  = gWList[windowCode].contentType;
    PicHandle   tempPICT         = nil;  
    short       tempSaturation   = 65535;  
    RGBColor    tempRGB;
    
    register    i;
    short       contentSelect;
    SFReply     reply;
    Str255      tempStr;
    
    Point       oldDimen, newDimen;
    
    
                /*  Initialize temporary values that have not already been initialized during
                //  variable declaration.
                */
    if (tempContentType == kSolidRGB) 
        tempRGB = gWList[windowCode].content.RGB;
    else
        tempRGB = gLastRGB;
 
            
                /*  Allocate and center the dialog  */
    CenterWRsrc(DLOG,AttrbDlogID,vThird);
    theDlog = GetNewDialog(AttrbDlogID, nil, (WindowPtr)-1);
    
    reply.good = false;
    
                /*  If the dialog has been allocated successfully - process the user's selections  */
    if (theDlog != nil)  {
    
                /*  Save off the current port.  */
        GetPort(&holdPort);
        SetPort(theDlog);
        
                /*  Dialog item initialization.  */
        SetDlogItemProc(theDlog,diLine1,(ProcPtr)FrameDlogItemRect);
        SetDlogItemProc(theDlog,diLine2,(ProcPtr)FrameDlogItemRect);
        SetDlogItemProc(theDlog,diLine3,(ProcPtr)FrameDlogItemRect);
        SetDlogItemProc(theDlog,diLine4,(ProcPtr)FrameDlogItemRect);
        
                /*  Set the appropriate dialog title  */
        GetIndString (tempStr,kAttribDlogStrings,windowCode+1);
        SetDlogString(theDlog, diDlogTitle, tempStr);
 
        HandleRadioButtons(theDlog,diBitmapButton,diGWorldButton,((tempBufferType == kGWorld) ? diGWorldButton : diBitmapButton));
        SetDepthMenuSelection  (theDlog, diDepthPopUp,  tempBufferDepth);
        SetContentMenuSelection(theDlog, diContentPopUp,tempContentType);
 
        oldDimen.v = gWList[windowCode].window->portRect.bottom - gWList[windowCode].window->portRect.top;
        oldDimen.h = gWList[windowCode].window->portRect.right  - gWList[windowCode].window->portRect.left;
 
        SetDlogShort(theDlog,diHeightText,oldDimen.v);
        SetDlogShort(theDlog,diWidthText, oldDimen.h);
        SelIText    (theDlog,diHeightText, 0, 32767);
    
                /*  Display the dialog  */
        ShowWindow(theDlog);
        
                /*  Process the user's actions  */
        do  {
            ModalDialog((ProcPtr)AttrbDlogFilter, &itemHit);
            
            switch (itemHit)  {
                    case  ok:               okHit = true;       
                    case  cancel:           done  = true;
                                            break;
                
                    case  diBitmapButton:   HandleRadioButtons(theDlog,diBitmapButton,diGWorldButton,itemHit);
                                            tempBufferDepth = 1;
                                            tempBufferType  = kBitMap;
                                            break;
                                            
                    case  diDepthPopUp:
                    case  diGWorldButton:   HandleRadioButtons(theDlog,diBitmapButton,diGWorldButton,diGWorldButton);
                                            tempBufferDepth = GetDepthMenuSelection(theDlog, diDepthPopUp);
                                            tempBufferType  = kGWorld;
                                            break;
                
    
                    case  diContentPopUp:   contentSelect = GetContentMenuSelection(theDlog, diContentPopUp);
                                        
                                            switch(contentSelect) {
                                                case kSolidRGB :    
                                                        GetColor(gZeroPt,"\pSelect a color.",&tempRGB,&tempRGB);
                                                        tempContentType = contentSelect;
                                                        for(i=kSrcWindow; i <= kBitWindow; i++)
                                                            DrawWindow(i);
                                                        break;
                                                
                                                case kPICT: 
                                                        StdGetPictFile(&reply);
                                                        for(i=kSrcWindow; i <= kBitWindow; i++)
                                                            DrawWindow(i);
                                                        if (reply.good)
                                                            tempContentType = contentSelect;
                                                        break;
                                                        
                                                default:  tempContentType = contentSelect;
                                                }
                                            break;
                    }
            }  while ( !done );
 
        GetDlogShort(theDlog,diHeightText, &newDimen.v);
        GetDlogShort(theDlog,diWidthText,  &newDimen.h);
        
        if (newDimen.v < 2)  newDimen.v = 2;
        if (newDimen.h < 2)  newDimen.h = 2;
        
        DisposDialog(theDlog);
        SetPort(holdPort);
        }
        
    if (okHit) {
        SetCursor(*GetCursor(watchCursor));
        
        for(i=kSrcWindow; i <= kBitWindow; i++)
            DrawWindow(i);
            
                /* Reset the offscreen depth if it has been changed  */
        bufferChanged = UpdateOffscreen(windowCode, tempBufferType, tempBufferDepth);
            
                
                /* Reset the content region if a new type has been selected */
        if ((tempContentType != gWList[windowCode].contentType) ||
            (tempContentType == kPICT) || (tempContentType == kSolidRGB)) {
            if (tempContentType == kPICT) {
                if (ReadPICTFile(&reply, &tempPICT) == noErr) {
                    if (gWList[windowCode].contentType == kPICT)
                        DisposePICTInfo(windowCode);
                    gWList[windowCode].contentType       = kPICT;
                    gWList[windowCode].content.pictInfo  = tempPICT;
                    DrawBuffer(windowCode);
                    contentChanged = true;
                    }
                else
                    if (reply.good)
                        OKRsrcAlert(kErrorStrings,kPICTReadErr);
                }
            else  {
                if (gWList[windowCode].contentType == kPICT)
                    DisposePICTInfo(windowCode);
                    
                gWList[windowCode].contentType = tempContentType;
                
                switch(tempContentType)  {
                    case kSolidRGB:     gWList[windowCode].content.RGB = gLastRGB = tempRGB;    break;
                    case kHLSRectBlend: 
                    case kHLSHBlend:    
                    case kHLSVBlend:    gWList[windowCode].content.saturation = tempSaturation; break;
                    }
                    
                DrawBuffer(windowCode);
                contentChanged = true;
                }
            }
        
 
                /*  Resize the window if the size has been altered.  Remember, the source and 
                //  mask windows (when available)  as well as the destination and copy windows
                //  must be the same size so if one is altered its corresponding window must also
                //  be sized.  It therefore follows that if there is not enough memory available to
                //  enlarge both windows then neither should be changed.  
                */
        if ( !EqualPt(oldDimen, newDimen) )  {
            GetPort(&holdPort);
            
            if ((windowCode == kSrcWindow) || (windowCode == kMskWindow)) {
                SetPort(gWList[kSrcWindow].window);
                if (ResizeTestWindow(&newDimen,kSrcWindow)) { 
                    oldDimen.h = gWList[kSrcWindow].window->portRect.right  - gWList[kSrcWindow].window->portRect.left;
                    oldDimen.v = gWList[kSrcWindow].window->portRect.bottom - gWList[kSrcWindow].window->portRect.top;
    
                    SetPort(gWList[kMskWindow].window);
                    if (ResizeTestWindow(&newDimen,kMskWindow))  {
                        SizeWindow(gWList[kMskWindow].window,newDimen.h,newDimen.v,false);
                        SizeWindow(gWList[kSrcWindow].window,newDimen.h,newDimen.v,false);
                        }
                    else {
                        SetPort(gWList[kSrcWindow].window);
                        ResizeTestWindow(&oldDimen,kSrcWindow);
                        }
                    }
                }
    
            if ((windowCode == kDstWindow) || (windowCode == kBitWindow)) {
                SetPort(gWList[kDstWindow].window);
                if (ResizeTestWindow(&newDimen,kDstWindow))  {
                    oldDimen.h = gWList[kDstWindow].window->portRect.right  - gWList[kDstWindow].window->portRect.left;
                    oldDimen.v = gWList[kDstWindow].window->portRect.bottom - gWList[kDstWindow].window->portRect.top;
                    
                    SetPort(gWList[kBitWindow].window);
                    if (ResizeTestWindow(&newDimen,kBitWindow))  {
                        SizeWindow(gWList[kBitWindow].window,newDimen.h,newDimen.v,false);
                        SizeWindow(gWList[kDstWindow].window,newDimen.h,newDimen.v,false);
                        UpdateRgnSettings();
                        }
                    else {
                        SetPort(gWList[kDstWindow].window);
                        ResizeTestWindow(&oldDimen,kDstWindow);
                        }
                    }
                }
                
            SetPort(holdPort);
            bufferChanged = true;
            }
                /*  Update the screen with the new changes  */
        if (bufferChanged || contentChanged)
            DrawBuffer(kBitWindow);
            for(i=kSrcWindow; i <= kBitWindow; i++)
                DrawWindow(i);
        
        InitCursor();
        }
        
    return(okHit);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SelectFGColor (short theItem)
{
    register    i;
    RGBColor    tempRGB;
 
    if (theItem == kCustomColor)  {
        tempRGB = gFGColor.rgb;
        if (GetColor(gZeroPt,"\pSelect a foreground color.",&tempRGB,&tempRGB))  {
            for(i=kSrcWindow; i <= kDstWindow; i++)
                DrawWindow(i);
 
            gFGColor.menuIndex  = kCustomColor;
            gFGColor.rgb        = tempRGB;
 
            DrawBuffer(kBitWindow);
            DrawWindow(kBitWindow);
            }
        }
    else  {
        gFGColor.menuIndex  = theItem;
        gFGColor.rgb        = gRGBArray[theItem-1];
 
        DrawBuffer(kBitWindow);
        DrawWindow(kBitWindow);
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SelectBKColor (short theItem)
{
    register    i;
    RGBColor    tempRGB;
    
    if (theItem == kCustomColor)  {
        tempRGB = gBKColor.rgb;
        if (GetColor(gZeroPt,"\pSelect a background color.",&tempRGB,&tempRGB))  {
            for(i=kSrcWindow; i <= kDstWindow; i++)
                DrawWindow(i);
 
            gBKColor.menuIndex  = kCustomColor;
            gBKColor.rgb        = tempRGB;
 
            DrawBuffer(kBitWindow);
            DrawWindow(kBitWindow);
            }
        }
    else  {
        gBKColor.menuIndex  = theItem;
        gBKColor.rgb        = gRGBArray[theItem-1];
 
        DrawBuffer(kBitWindow);
        DrawWindow(kBitWindow);
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SelectOPColor (short theItem)
{
    register    i;
    RGBColor    tempRGB;
 
 
    if (theItem == kCustomColor)  {
        tempRGB = gOPColor.rgb;
        if (GetColor(gZeroPt,"\pSelect an OpColor.",&tempRGB,&tempRGB))  {
            for(i=kSrcWindow; i <= kDstWindow; i++)
                DrawWindow(i);
 
            gOPColor.menuIndex  = kCustomColor;
            gOPColor.rgb        = tempRGB;
 
            DrawBuffer(kBitWindow);
            DrawWindow(kBitWindow);
            }
        }
    else  {
        gOPColor.menuIndex  = theItem;
        gOPColor.rgb        = gRGBArray[theItem-1];
 
        DrawBuffer(kBitWindow);
        DrawWindow(kBitWindow);
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SelectHiliteColor (short theItem)
{
    register    i;
    RGBColor    tempRGB;
 
 
    if (theItem == kCustomColor)  {
        tempRGB = gHiliteColor.rgb;
        if (GetColor(gZeroPt,"\pSelect a hilite color.",&tempRGB,&tempRGB))  {
            for(i=kSrcWindow; i <= kDstWindow; i++)
                DrawWindow(i);
 
            gHiliteColor.menuIndex  = kCustomColor;
            gHiliteColor.rgb        = tempRGB;
 
            DrawBuffer(kBitWindow);
            DrawWindow(kBitWindow);
            }
        }
    else  {
        gHiliteColor.menuIndex  = theItem;
        gHiliteColor.rgb        = gRGBArray[theItem-1];
 
        DrawBuffer(kBitWindow);
        DrawWindow(kBitWindow);
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  SelectRgn(short rgnCode, short rgnType)
{
 
    gRList[rgnCode].type = rgnType;
    
    if (rgnType == kNoRgn)  {
        if (gRList[rgnCode].rgn != nil)  {
            DisposeRgn(gRList[rgnCode].rgn);
            gRList[rgnCode].rgn = nil;
            }
        }
    else  {
        gRList[rgnCode].rgn = NewRgn();
        OpenRgn();
 
        switch(rgnType)  {
            case kDiamondClip:  DrawDiamond();      break;
            case kOvalClip:     DrawOval();         break;
            case kStarClip:     DrawStar();         break;
            case kHoleClip:     DrawHole();         break;
            }
 
        CloseRgn(gRList[rgnCode].rgn);
        }
    
    DrawBuffer(kBitWindow);
    DrawWindow(kBitWindow);
}
 
 
/*--------------------------------------------------------------------------------------*/
Boolean  SelectTrap()
/*
//  This call allows the user to select which of the two copymas traps he wishes to test. 
*/
{
    DialogPtr   theDlog;
    GrafPtr     holdPort;
    short       itemHit;
    
    Boolean     okHit           = false;
    Boolean     done            = false;
    Boolean     bufferChanged   = false;
    Boolean     contentChanged  = false;
 
    short       tempTrapSelect   = gTrapSelect;
    
    register    i;
    Str255      tempStr;
    
    
 
                /*  Allocate and center the dialog  */
    CenterWRsrc(DLOG,TrapDlogID,vThird);
    theDlog = GetNewDialog(TrapDlogID, nil, (WindowPtr)-1);
    
                /*  If the dialog has been allocated successfully - process the user's selections  */
    if (theDlog != nil)  {
    
                /*  Save off the current port.  */
        GetPort(&holdPort);
        SetPort(theDlog);
        
                /*  Set up user item divider lines */
        SetDlogItemProc(theDlog,diTrapLine1,(ProcPtr)FrameDlogItemRect);
        SetDlogItemProc(theDlog,diTrapLine2,(ProcPtr)FrameDlogItemRect);
        
                /*  Init the radio buttons to reflect current selection.  */
        HandleRadioButtons(theDlog,diCopyMaskButton,diCopyBitsButton,tempTrapSelect+diCopyMaskButton);
    
                /*  Display the dialog  */
        ShowWindow(theDlog);
        
                /*  Process the user's actions  */
        do  {
            ModalDialog((ProcPtr)HandleOkayCancel, &itemHit);
            
            switch (itemHit)  {
                    case  ok:               okHit = true;       
                    case  cancel:           done  = true;
                                            break;
                
                    case  diCopyMaskButton:
                    case  diCopyDeepButton:
                    case  diCopyBitsButton: HandleRadioButtons(theDlog,diCopyMaskButton,diCopyBitsButton,itemHit);
                                            tempTrapSelect  = itemHit - diCopyMaskButton;
                                            break;
                    }
            }  while ( !done );
            
        DisposDialog(theDlog);
        SetPort(holdPort);
        }
        
    if ((okHit) && (gTrapSelect != tempTrapSelect))  {
        for(i=kSrcWindow; i <= kBitWindow; i++)
            DrawWindow(i);
            
        gTrapSelect = tempTrapSelect;
        GetIndString(&tempStr, kTitleStrings, gTrapSelect+4);
        SetWTitle(gWList[kBitWindow].window,tempStr); 
        
        ShowHide(gWList[kMskWindow].window, !(gTrapSelect==kCopyBits));
    
        DrawBuffer(kBitWindow);
        DrawWindow(kBitWindow);
        }
        
    return(okHit);
}
 
 
/*--------------------------------------------------------------------------------------*/
void  NeedSystem7Dlog()
/*
//  This dialog alerts the user that system 7.0 is needed for the app to run. 
*/
{
    DialogPtr   theDlog;
    GrafPtr     holdPort;
    short       itemHit;
    Boolean     done        = false;
    
    
                /*  Allocate and center the dialog  */
    CenterWRsrc(DLOG,Sys7DlogID,vThird);
    theDlog = GetNewDialog(Sys7DlogID, nil, (WindowPtr)-1);
    
                /*  If the dialog has been allocated successfully - process the user's selections  */
    if (theDlog != nil)  {
    
                /*  Save off the current port.  */
        GetPort(&holdPort);
        SetPort(theDlog);
 
        FrameDlogButton (theDlog,ok);
            
                /*  Display the dialog  */
        ShowWindow(theDlog);
        
                /*  Process the user's actions  */
        do  {
        
            ModalDialog((ProcPtr)HandleOkayCancel, &itemHit);
            done = (itemHit == ok);     
                
            }  while ( !done );
            
        DisposDialog(theDlog);
        SetPort(holdPort);
        }
}
 
 
/*--------------------------------------------------------------------------------------*/
void  AboutBitBlitz()
{ 
 
    DialogPtr   theDlog;
    GrafPtr     holdPort;
    short       itemHit;
    Boolean     done            = false;
 
 
 
    CenterWRsrc(DLOG,AboutBoxDlogID,vThird);
    theDlog = GetNewDialog(AboutBoxDlogID, nil, (WindowPtr)-1);
 
 
    if (theDlog != nil)  {
    
                /*  Save off the current port.  */
        GetPort(&holdPort);
        SetPort(theDlog);
        
    
                /*  Display the dialog  */
        ShowWindow(theDlog);
        
        
                /*  Display about box until mouseDown  */
        do  {
            ModalDialog((ProcPtr)HandleOkayCancel, &itemHit);
            done  = (itemHit == 1);
            }  while ( !done );
            
            
            /*  Cleanup before returning  */
        DisposDialog(theDlog);
        SetPort(holdPort);
        }
}
 
 
/*======================================================================================*/
/*  Offscreen buffer imaging routines   */
 
/*--------------------------------------------------------------------------------------*/
void  FillSolid(RGBColor *theRGB, Rect *boundRect)
/*  
//  FillSolid() preserves and restores the current foreground color setting while filling
//  the specified Rect with the desired color.
*/
{
    RGBColor    holdRGB;    
 
    GetForeColor(&holdRGB);
    
    RGBForeColor(theRGB);
    PaintRect(boundRect);
    
    RGBForeColor(&holdRGB);
}
 
 
/*--------------------------------------------------------------------------------------*/  
void  DoCopy()
/*  
//  DoCopy() updates the buffer associated with the copy window.  This update takes 
//  two steps.  Since the contents of the specified destination bitmap are overwritten by 
//  the copy result we keep both a destination window and the copy window.  Before performing 
//  the copy trap, the contents of the destination window's buffer are copied into the copy
//  window's buffer.  This buffer is then passed along with the source and mask window's buffers
//  to the copy routine.  
*/
{
    GrafPtr         holdPort;
    GDHandle        holdDevice;
    BitMap          *srcBits, *mskBits, *dstBits;
    Rect            *srcRect, *mskRect, *dstRect;
    PixMapHandle    srcBuffer, mskBuffer, dstBuffer, bitBuffer;
 
 
            /*  If the user selects to use screen bits for the copy call the buffer pointer
            //  that is passed to the copy call is set to the window's portbits.
            */
    if (gUseScreenBits) {
        srcBits = &gWList[kSrcWindow].window->portBits;
        srcRect = &gWList[kSrcWindow].window->portRect;
 
        if (gWList[kMskWindow].bufferType == kBitMap) {
            mskBits = &gWList[kMskWindow].buffer.bitmap->portBits;
            mskRect = &gWList[kMskWindow].buffer.bitmap->portBits.bounds;
            }
        else  {
            mskBuffer  = GetGWorldPixMap(gWList[kMskWindow].buffer.gworld);
            LockPixels(mskBuffer);
            mskBits    = *mskBuffer;
            mskRect    = &gWList[kMskWindow].buffer.gworld->portRect;
            }
        
            /* The cursor is hidded during the copy so as not to interfere with the image  */
        HideCursor();
        }
        
            /* Otherwise, the offscreen gworld's portbits are passed  */
    else  {
        if (gWList[kSrcWindow].bufferType == kBitMap) {
            srcBits = &gWList[kSrcWindow].buffer.bitmap->portBits;
            srcRect = &gWList[kSrcWindow].buffer.bitmap->portBits.bounds;
            }
        else  {
            srcBuffer  = GetGWorldPixMap(gWList[kSrcWindow].buffer.gworld);
            LockPixels(srcBuffer);
            srcBits    = *srcBuffer;
            srcRect    = &gWList[kSrcWindow].buffer.gworld->portRect;
            }
 
        if (gWList[kMskWindow].bufferType == kBitMap) {
            mskBits = &gWList[kMskWindow].buffer.bitmap->portBits;
            mskRect = &gWList[kMskWindow].buffer.bitmap->portBits.bounds;
            }
        else  {
            mskBuffer  = GetGWorldPixMap(gWList[kMskWindow].buffer.gworld);
            LockPixels(mskBuffer);
            mskBits    = *mskBuffer;
            mskRect    = &gWList[kMskWindow].buffer.gworld->portRect;
            }
        }
 
    switch(gWList[kBitWindow].bufferType) {
        case kBitMap:   
            GetPort(&holdPort);
            SetPort(gWList[kBitWindow].buffer.bitmap);
            
            CopyBits(&gWList[kDstWindow].buffer.bitmap->portBits,           &gWList[kBitWindow].buffer.bitmap->portBits,
                     &gWList[kDstWindow].buffer.bitmap->portBits.bounds,    &gWList[kBitWindow].buffer.bitmap->portBits.bounds, srcCopy, NULL);
            
            if (gUseScreenBits) {
                DrawWindow(kBitWindow);
                dstBits = &gWList[kBitWindow].window->portBits;
                dstRect = &gWList[kBitWindow].window->portRect;
                }
            else  {
                dstBits = &gWList[kBitWindow].buffer.bitmap->portBits;
                dstRect = &gWList[kBitWindow].buffer.bitmap->portBits.bounds;
                }
 
            SetEnv();
            if (gTrapSelect == kCopyMask)
                CopyMask(srcBits, mskBits, dstBits, srcRect, mskRect, dstRect);
            else if (gTrapSelect == kCopyDeepMask) 
                CopyMask2(srcBits, mskBits, dstBits, srcRect, mskRect, dstRect, gCopyMode, gRList[kMskRgn].rgn);
            else 
                CopyBits (srcBits, dstBits, srcRect, dstRect, gCopyMode, gRList[kMskRgn].rgn);
            RestoreEnv();
                                             
            SetPort(holdPort);
            break;
            
        case kGWorld:
            dstBuffer = GetGWorldPixMap(gWList[kDstWindow].buffer.gworld);
            bitBuffer = GetGWorldPixMap(gWList[kBitWindow].buffer.gworld);
 
            LockPixels(dstBuffer);
            LockPixels(bitBuffer);
            
            GetGWorld(&(CGrafPtr)holdPort,&holdDevice);
            SetGWorld((CGrafPtr)gWList[kBitWindow].buffer.gworld,nil);
 
            CopyBits(*dstBuffer,                                  *bitBuffer,
                     &gWList[kDstWindow].buffer.gworld->portRect, &gWList[kBitWindow].buffer.gworld->portRect, srcCopy, nil);
 
            if (gUseScreenBits) {
                SetGWorld((CGrafPtr)holdPort,holdDevice);
 
                GetPort(&holdPort);
                SetPort(gWList[kBitWindow].window);
 
                DrawWindow(kBitWindow);
                dstBits = &gWList[kBitWindow].window->portBits;
                dstRect = &gWList[kBitWindow].window->portRect;
                }
            else  {
                dstBits = *bitBuffer;
                dstRect = &gWList[kBitWindow].buffer.gworld->portRect;
                }
 
            SetEnv();
            if (gTrapSelect == kCopyMask)
                CopyMask(srcBits, mskBits, dstBits, srcRect, mskRect, dstRect);
            else if (gTrapSelect == kCopyDeepMask)
                CopyMask2(srcBits, mskBits, dstBits, srcRect, mskRect, dstRect, gCopyMode, gRList[kMskRgn].rgn);
            else 
                CopyBits (srcBits, dstBits, srcRect, dstRect,  gCopyMode, gRList[kMskRgn].rgn);
            RestoreEnv();
 
            if (gUseScreenBits) {
                SetPort(holdPort);
                
                GetGWorld(&(CGrafPtr)holdPort,&holdDevice);
                SetGWorld((CGrafPtr)gWList[kBitWindow].buffer.gworld,nil);
                CopyBits(dstBits, *bitBuffer, dstRect, &gWList[kBitWindow].buffer.gworld->portRect, srcCopy, nil);
                }
                         
            SetGWorld((CGrafPtr)holdPort,holdDevice);
            UnlockPixels(dstBuffer);
            UnlockPixels(bitBuffer);
            break;
        }
 
 
    if (gUseScreenBits) 
        ShowCursor();
    else {
        if (gWList[kSrcWindow].bufferType == kGWorld)
            UnlockPixels(srcBuffer);
 
        if (gWList[kMskWindow].bufferType == kGWorld)
            UnlockPixels(mskBuffer);
    
        }
}
 
 
/*--------------------------------------------------------------------------------------*/  
void  ImageBuffer(short windowCode)
/*
//  ImageBuffer() sets the current port in accordance with the buffer type, draws the image
//  designated by the contentType, and restores current port to its original setting.
*/
{
    GrafPtr     holdPort;
    GDHandle    holdDevice;
    Rect        bufferRect;
 
 
    switch(gWList[windowCode].bufferType) {
        case kBitMap:   
            GetPort(&holdPort);
            SetPort(gWList[windowCode].buffer.bitmap);
            bufferRect = gWList[windowCode].buffer.bitmap->portBits.bounds;
            break;
            
        case kGWorld:
            LockPixels(GetGWorldPixMap(gWList[windowCode].buffer.gworld));
            GetGWorld(&holdPort,&holdDevice);
            SetGWorld(gWList[windowCode].buffer.gworld,nil);
            bufferRect = gWList[windowCode].buffer.gworld->portRect;
            break;
        }
            
    switch (gWList[windowCode].contentType)  {
        case kHLSRectBlend:     HLSRectBlend        (&bufferRect,gWList[windowCode].content.saturation); break;
        case kHLSHBlend:        HLSHLinearBlend     (&bufferRect,gWList[windowCode].content.saturation); break;
        case kHLSVBlend:        HLSVLinearBlend     (&bufferRect,gWList[windowCode].content.saturation); break;
        case kGrayRectBlend:    GrayRectBlend       (&bufferRect);                                       break;
        case kGrayHBlend:       GrayHLinearBlend    (&bufferRect);                                       break;
        case kGrayVBlend:       GrayVLinearBlend    (&bufferRect);                                       break;
        case kGrayPatRectBlend: GrayPatRectBlend    (&bufferRect);                                       break;
        case kGrayPatHBlend:    GrayPatHLinearBlend (&bufferRect);                                       break;
        case kGrayPatVBlend:    GrayPatVLinearBlend (&bufferRect);                                       break;
        case kSolidRGB:         FillSolid           (&gWList[windowCode].content.RGB,&bufferRect);       break;
        case kPICT:             if (gWList[windowCode].content.pictInfo != nil)
                                    DrawPicture(gWList[windowCode].content.pictInfo,&bufferRect);                       
                                    break;
        }
    
    switch(gWList[windowCode].bufferType) {
        case kBitMap:   SetPort  (holdPort);                
                        break;
        case kGWorld:   SetGWorld(holdPort,holdDevice);     
                        UnlockPixels(GetGWorldPixMap(gWList[windowCode].buffer.gworld));
                        break;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/  
void  DrawBuffer(short windowCode)
/*  
//  DrawBuffer() redraws the specified window's offscreen buffer.
*/
{   
    if (windowCode == kBitWindow) 
        DoCopy();
    else
        ImageBuffer(windowCode);
}
 
 
 
/*======================================================================================*/
/*  Offscreen-to-onscreen transferral   */
 
/*--------------------------------------------------------------------------------------*/
void  DrawWindow(short windowCode)
/*  
//  DrawWindow() draws the current contents of the specified window's buffer to that window.
*/
{
    GrafPtr         holdPort;
    PixMapHandle    windowBuffer;
    
        
                /*  Save the current port setting and set the current port to the specified window  */
    GetPort(&holdPort);
    SetPort(gWList[windowCode].window);
    
                /*  Switch off the current buffer type setting to perform the appropriate copybits  */
    switch(gWList[windowCode].bufferType) {
        case kBitMap:   
            CopyBits(&gWList[windowCode].buffer.bitmap->portBits,           &gWList[windowCode].window->portBits,
                     &gWList[windowCode].buffer.bitmap->portBits.bounds,    &gWList[windowCode].window->portRect, srcCopy, NULL);
            break;
            
        case kGWorld:
            windowBuffer = GetGWorldPixMap(gWList[windowCode].buffer.gworld);
            LockPixels(windowBuffer);
            CopyBits(*windowBuffer,                               &gWList[windowCode].window->portBits,
                     &gWList[windowCode].buffer.gworld->portRect, &gWList[windowCode].window->portRect, srcCopy, NULL);
            UnlockPixels(windowBuffer);
            break;
        }
 
                /*  Restore the current port to its original setting.  */
    SetPort(holdPort);
}
 
 
 
/*======================================================================================*/
/*  Window/Buffer resizing routines  */
 
/*--------------------------------------------------------------------------------------*/
Boolean  ResizeTestWindow(Point *newDimen, short windowCode)
/*  
//  ResizeWindow() sizes the specifed window to the new dimensions.
*/
{
    Rect        tempRect, oldRect;
    Boolean     bufferScaled;
    GrafPtr     tempBits;
    GWorldFlags gwError;
    
    
    switch(gWList[windowCode].bufferType) {
        case kBitMap:           
            oldRect  =  tempRect = gWList[windowCode].buffer.bitmap->portBits.bounds;
            tempRect.bottom      = tempRect.top  + newDimen->v;
            tempRect.right       = tempRect.left + newDimen->h;
    
            DisposeOSBitmap(gWList[windowCode].buffer.bitmap);
            
            bufferScaled     = CreateOSBitmap(&tempBits,&tempRect);
            if (bufferScaled)  
                gWList[windowCode].buffer.bitmap = tempBits;
            else
                CreateOSBitmap(&gWList[windowCode].buffer.bitmap,&oldRect);         
            break;
        
        case kGWorld:
            tempRect         = gWList[windowCode].buffer.gworld->portRect;
            tempRect.bottom  = tempRect.top  + newDimen->v;
            tempRect.right   = tempRect.left + newDimen->h;
            
            gwError          = UpdateGWorld(&gWList[windowCode].buffer.gworld, gWList[windowCode].bufferDepth, &tempRect, nil, nil, clipPix);
            bufferScaled     = ((gwError & gwFlagErr) == 0);
            break;
        }
 
    if (bufferScaled)  {
        if (windowCode != kBitWindow) 
            DrawBuffer(windowCode);
        }
    else
        OKRsrcAlert(kErrorStrings,kResizeMemErr);
        
    return(bufferScaled);
}
 
 
 
/*======================================================================================*/
/*  Test Window allocation/disposal routines  */
 
 
/*--------------------------------------------------------------------------------------*/  
void  CalcWindowRect(Rect *windowRect, short windowCode)
/*  
//  This procedure calculates the window rect for the window designated by windowCode.
//  It essentially divides the screen into 4 equal quadrants and insets the rect defining
//  the window's rect appropriately in order to have it positioned correctly on whatever
//  size screen the application is run.
*/
{
    #define     kTitleBarHeight     20   
    #define     kWindowSep          10      /*  # pixels between windows and space between edge */
                                            /*  of screen and window                            */
 
    Rect        screenRect;
    Point       windowCenter;
 
    screenRect = qd.screenBits.bounds;
    screenRect.top += MBarHeight;
 
    windowCenter.h    = screenRect.left  + (HRectLength(&screenRect) >> 1);
    windowCenter.v    = screenRect.top   + (VRectLength(&screenRect) >> 1);
    
    switch (windowCode)  {
        case kSrcWindow:
            SetRect(windowRect, screenRect.left+kWindowSep, 
                                screenRect.top+kWindowSep+kTitleBarHeight, 
                                windowCenter.h-(kWindowSep/2),  
                                windowCenter.v-(kWindowSep/2));
            break;
            
        case kMskWindow:
            SetRect(windowRect, windowCenter.h+(kWindowSep/2),
                                screenRect.top+kWindowSep+kTitleBarHeight,
                                screenRect.right-kWindowSep,
                                windowCenter.v-(kWindowSep/2));
            break;
            
        case kDstWindow:
            SetRect(windowRect, screenRect.left+kWindowSep,
                                windowCenter.v+(kWindowSep/2)+kTitleBarHeight,
                                windowCenter.h-(kWindowSep/2),
                                screenRect.bottom-kWindowSep);
            break;
            
        case kBitWindow:
            SetRect(windowRect ,windowCenter.h+(kWindowSep/2),
                                windowCenter.v+(kWindowSep/2)+kTitleBarHeight,
                                screenRect.right-kWindowSep,
                                screenRect.bottom-kWindowSep);
            break;
        }
}
 
 
/*--------------------------------------------------------------------------------------*/  
Boolean CreateTestWindows()
{
/*  This function allocates, initializes, and displays the four test windows.  If an error 
//  occurs during this process and error message is displayed.  All memory allocated is 
//  disposed.  And false is returned so that the application will terminate from main. 
*/  
    register    i;
    Rect        tempRect;
    Str255      title;
    
    
    for (i = kSrcWindow; i <= kBitWindow; i++)  {
        CalcWindowRect(&tempRect,i);
        GetIndString(&title, kTitleStrings, i+1);
 
        if (gColorAvail)
            gWList[i].window = NewCWindow(nil,&tempRect,title,true,documentProc,(WindowPtr)-1, false,0);
        else
            gWList[i].window = NewWindow(nil,&tempRect,title,true,documentProc,(WindowPtr)-1, false,0);
        if  (gWList[i].window == nil)
            goto handle_init_error;
            
        if  (!CreateWindowBuffer(i))
            goto handle_init_error;
        
        DrawBuffer(i);      
        DrawWindow(i);
        }
        
    return(true);
 
    handle_init_error:  DisposeTestWindows();
                        OKRsrcAlert(kErrorStrings,kBootMemErr);
                        return(false);
}
 
 
/*--------------------------------------------------------------------------------------*/
void    DisposeTestWindows()
/*
//  This procedure takes care of disposing all memory allocated in association with any 
//  of the app windows before the application quits either normally or in response to an
//  unrecoverable error.
*/
{   
    register i;
    
    for (i = kSrcWindow; i <= kBitWindow; i++)  {
        if ((gWList[i].contentType == kPICT) && (gWList[i].content.pictInfo != nil))
            DisposHandle((Handle)gWList[i].content.pictInfo);
        DisposeWindowBuffer(i); 
        if (gWList[i].window != nil)
            DisposeWindow(gWList[i].window);
        }
}