CalcMask.c

/*
    File:       CalcCMask & CalcMask.c
 
    Contains:   This snippet shows how to use both CalcCMask or             
                CalcMask to create a mask given a source bitmap             
                image.  As decribed on pages IV-24 and V-72 of              
                Inside Mac, the two routines compute a destination              
                bitmap image with 1's only in the pixels where              
                paint can not leak from any of the outer edges.         
                This is similar to the lasso tool found in many             
                drawing apps.                   
 
    Written by: EL  
 
    Copyright:  Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                08/2000     JM              Carbonized, non-Carbon code is commented out
                                            for demonstration purposes.
                7/8/1999    KG              Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
#ifdef __MWERKS__
 
// includes for MetroWerks CodeWarrior
 
#include "CarbonPrefix.h"
#include <Dialogs.h>
#include <Fonts.h>
#include <QDOffscreen.h>
#include <Resources.h>
 
#else
 
#ifdef __APPLE_CC__
 
// includes for ProjectBuilder
 
#include <Carbon/Carbon.h>
 
#else
 
// includes for MPW
 
#include <Carbon.h>
 
#endif
#endif
 
/* Constant Declarations */
 
#define WWIDTH      176
#define WHEIGHT     106
 
//#define WLEFT     (((qd.screenBits.bounds.right - qd.screenBits.bounds.left) - WWIDTH) / 2)
//#define WTOP      (((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - WHEIGHT) / 2)
 
/* Global Variable Definitions */
 
WindowPtr   gWindow;
 
void initMac();
void createWindow();
void doCalcCMaskExample();
 
void DisposeGrafPort(GrafPtr ptr);
GrafPtr CreateGrafPort(Rect *rect);
 
void doEventLoop();
 
void main(void)
{
    initMac();
    
    createWindow();
    
    doEventLoop();
}
 
void initMac()
{
    /*MaxApplZone();
    
    InitGraf( &qd.thePort );
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs( nil );*/
    InitCursor();
    FlushEvents( 0, everyEvent );
}
 
void createWindow()
{
    Rect rect;
    BitMap  bitMap;
    int top, left;
    
    GetQDGlobalsScreenBits(&bitMap);
    
    left = (((bitMap.bounds.right - bitMap.bounds.left) - WWIDTH) / 2);
    top = (((bitMap.bounds.bottom - bitMap.bounds.top) - WHEIGHT) / 2);
    
    //SetRect( &rect, WLEFT, WTOP, WLEFT + WWIDTH, WTOP + WHEIGHT );
    SetRect( &rect, left, top, left + WWIDTH, top + WHEIGHT );
    
    gWindow = NewCWindow( 0L, &rect, "\pCalc-C-Mask", true, documentProc,
                            (WindowPtr)-1L, true, 0L );
                            
    //SetPort( gWindow );
    SetPortWindowPort( gWindow );
}
 
#define COLOR_VERSION
 
void doCalcCMaskExample()
{
    PicHandle   pict;               /* B/W Pict used to create mask. */
    GWorldPtr   gworld;             /* Gworld used for CopyMask source. */
    //GrafPtr     mask;             /* Mask created by CalcCMask. */
    GrafPtr     sourceForMask;      /* Bitmap of pict image used for creating mask. */
    GrafPtr     oldPort;            // Old port, saved then restored
    RGBColor    seedColor;          /* Color used to determine the mask. */
    Rect        rect;               /* Bounding rect of mask and source. */
    CGrafPtr    currentPort;        /* Saved CGrafPtr for later restore. */
    GDHandle    currentDevice;      /* Saved device for later restore. */
    Rect        tempRect1, tempRect2; // Used for carbonization
    OSErr       err = noErr;
    GWorldPtr   newMask;
    
    GetPort(&oldPort);
    
    /* Load the pict resource to be used for the mask. */
    pict = (PicHandle)GetResource( 'PICT', 128 );
    
    /* Define the bounding rect for the source and mask bitmap. */
    rect = (**pict).picFrame;
    OffsetRect( &rect, -rect.left, -rect.top );
    
    /* Allocate the source bitmap for which the mask will be created from. */
    sourceForMask = CreateGrafPort( &rect );
    
    /* Create the source bitmap's image by using the pict. */
    SetPort( sourceForMask );
    DrawPicture( pict, &rect );
    //SetPort( gWindow );
    SetPortWindowPort( gWindow );
    
    /* Release the memory used by the pict. */
    ReleaseResource( (Handle)pict );
    
    /* Copy the source image to the window to see what the mask was created from. */
    /*CopyBits( &sourceForMask->portBits, &gWindow->portBits,
                &sourceForMask->portRect, &rect, srcCopy, nil );*/
    CopyBits(GetPortBitMapForCopyBits(sourceForMask), GetPortBitMapForCopyBits(GetWindowPort(gWindow)),
            GetPortBounds(sourceForMask, &tempRect1), &rect, srcCopy, nil );
    
    /* Allocate the bitmap for the mask. */
    //mask = CreateGrafPort( &sourceForMask->portRect );
    GetGWorld( &currentPort, &currentDevice );
    NewGWorld(&newMask, 1, GetPortBounds(sourceForMask, &tempRect1), nil, nil, 0);
    SetGWorld( newMask, nil );
    EraseRect( &tempRect1 );
    
    GetPortBounds(sourceForMask, &tempRect1);
    GetPortBounds(newMask, &tempRect2);
    LockPixels(GetPortPixMap(sourceForMask));
    LockPixels(GetGWorldPixMap(newMask));
 
#ifdef COLOR_VERSION
    /* Create a mask from the source bitmap of all colors that match the seedColor. */
    seedColor.red = seedColor.green = seedColor.blue = 0;
    
    /*CalcCMask( &sourceForMask->portBits, &mask->portBits, &sourceForMask->portRect,
                &mask->portRect, &seedColor, nil, 0 );*/
 
    CalcCMask( (BitMap *) *(GetPortPixMap(sourceForMask)), (BitMap *)*(GetPortPixMap(newMask)), &tempRect1, &tempRect2,
     &seedColor, nil, 0);
 
#else
 
    /*CalcMask( &sourceForMask->portBits.baseAddr, &mask->portBits.baseAddr,
                sourceForMask->portBits.rowBytes, mask->portBits.rowBytes,
                sourceForMask->portRect.bottom - sourceForMask->portRect.top,
                sourceForMask->portBits.rowBytes >> 1 );*/
    // This is not recommended since the ports are all color ports under Carbon
    GetPortBounds(sourceForMask, &tempRect1);
    CalcMask( (*(GetPortPixMap(sourceForMask)))->baseAddr, (*(GetGWorldPixMap(newMask)))->baseAddr,
                (*(GetPortPixMap(sourceForMask)))->rowBytes, (*(GetPortPixMap(newMask)))->rowBytes,
                tempRect1.bottom - tempRect1.top, (*(GetPortPixMap(sourceForMask)))->rowBytes >> 1);
#endif
 
    
    UnlockPixels(GetPortPixMap(sourceForMask));
    UnlockPixels(GetPortPixMap(newMask));
    SetGWorld( currentPort, currentDevice );
    
    // Create a gworld of a blue rectangle for the CopyMask source.
    //GetGWorld( &currentPort, &currentDevice );
    err = NewGWorld( &gworld, 8, &rect, nil, nil, 0 );
    //SetPort( (GrafPtr)gworld );
    SetGWorld( gworld, nil );
    ForeColor( blueColor );
    PaintRect( &rect );
    SetGWorld( currentPort, currentDevice );
    
    // Now draw the blue rectangle with its mask. 
    //OffsetRect( &rect, sourceForMask->portRect.right, 0 );
    OffsetRect( &rect, GetPortBounds(sourceForMask, &tempRect1)->right, 0 );
    //CopyMask( (BitMap*)&(*gworld).portPixMap, &mask->portBits, &gWindow->portBits,
    //          &sourceForMask->portRect, &mask->portRect, &rect );
    //SetPortWindowPort(gWindow);
    GetGWorld( &currentPort, &currentDevice );
    SetGWorld(GetWindowPort(gWindow), nil);
    LockPixels(GetPortPixMap(gworld));
    LockPixels(GetPortPixMap(newMask));
    CopyMask( (BitMap*)*(GetPortPixMap(gworld)), (BitMap *)*(GetPortPixMap(newMask)), GetPortBitMapForCopyBits(GetWindowPort(gWindow)),
                GetPortBounds(sourceForMask, &tempRect1), GetPortBounds(newMask, &tempRect2), &rect );
    UnlockPixels(GetPortPixMap(gworld));
    UnlockPixels(GetGWorldPixMap(newMask));
 
    // Release the used memory. 
    DisposeGrafPort( sourceForMask );
    //DisposeGrafPort( mask );
    DisposeGWorld( newMask );
    DisposeGWorld( gworld );
    SetPort( oldPort );
}
 
GrafPtr CreateGrafPort( Rect *bounds )  /* CreateGrafPort originally written by Forrest Tanaka. */
{
    GrafPtr savedPort;      /* Saved GrafPtr for later restore. */
    GrafPtr newPort;        /* New GrafPort. */
    Rect    localBounds;    /* Local copy of bounds. */
    RgnHandle       rgnHandle = NewRgn();   // Used for carbonization
 
    GetPort( &savedPort );
 
    /* Set the top-left corner of bounds to (0,0). */
    localBounds = *bounds;
    OffsetRect( &localBounds, -bounds->left, -bounds->top );
 
    /* Allocate a new GrafPort. */
    //newPort = (GrafPtr)NewPtrClear( sizeof( GrafPort ) );
    newPort = CreateNewPort();
    
    if (newPort != nil)
    {
        /* Initialize the new port and make the current port. */
        //OpenPort( newPort );
        SetPort( newPort);
 
        /* Initialize and allocate the bitmap. */
        /*newPort->portBits.bounds = localBounds;
        newPort->portBits.rowBytes = ((localBounds.right + 15) >> 4) << 1;
        newPort->portBits.baseAddr =  NewPtrClear( newPort->portBits.rowBytes *
                                                    (long)localBounds.bottom );*/
        
        LockPixels(GetPortPixMap(newPort)); 
        
        (*(GetPortPixMap(newPort)))->bounds = localBounds;
        (*(GetPortPixMap(newPort)))->rowBytes = ((localBounds.right + 15) >> 4) <<1;
        (*(GetPortPixMap(newPort)))->baseAddr = NewPtrClear((*(GetPortPixMap(newPort)))->rowBytes *
                                                        (long)localBounds.bottom );
 
        UnlockPixels(GetPortPixMap(newPort));
        
        //if (newPort->portBits.baseAddr != nil)
        if (GetPortBitMapForCopyBits(newPort)->baseAddr != nil)
        {
            /* Clean up the new port. */
            //newPort->portRect = localBounds;
            SetPortBounds(newPort, &localBounds);
            ClipRect( &localBounds );
            //RectRgn( newPort->visRgn, &localBounds );
            RectRgn( GetPortVisibleRegion(newPort, rgnHandle), &localBounds );
            EraseRect( &localBounds );
        }
        else
        {
            /* Allocation failed; deallocate the port. */
            /*ClosePort( newPort );
            DisposePtr( (Ptr)newPort );*/
            DisposePort( newPort );
            newPort = nil;
        }
    }
    
    DisposeRgn(rgnHandle);
    SetPort( savedPort );
    return newPort;
}
 
void DisposeGrafPort( GrafPtr doomedPort )  /* DisposeGrafPort originally written by Forrest Tanaka. */
{
    //ClosePort( doomedPort );
    //DisposePtr( doomedPort->portBits.baseAddr );
    DisposePtr( GetPortBitMapForCopyBits(doomedPort)->baseAddr );
    //DisposePtr( (Ptr)doomedPort );
    DisposePort( doomedPort );
}
void doEventLoop()
{
    EventRecord event;
    WindowPtr   window;
    short       clickArea;
    Rect        screenRect;
 
    for (;;)
    {
        if (WaitNextEvent( everyEvent, &event, 0, nil ))
        {
            if (event.what == mouseDown)
            {
                clickArea = FindWindow( event.where, &window );
                
                if (clickArea == inDrag)
                {
                    //screenRect = (**GetGrayRgn()).rgnBBox;
                    GetRegionBounds(GetGrayRgn(), &screenRect);
                    DragWindow( window, event.where, &screenRect );
                }
                else if (clickArea == inContent)
                {
                    if (window != FrontWindow())
                        SelectWindow( window );
                }
                else if (clickArea == inGoAway)
                    if (TrackGoAway( window, event.where ))
                        return;
            }
            else if (event.what == updateEvt)
            {
                window = (WindowPtr)event.message;  
                //SetPort( window );
                SetPortWindowPort( window );
                
                BeginUpdate( window );
                doCalcCMaskExample();
                EndUpdate( window );
            }
        }
    }
}