out.c

/*
    File:       out.c
 
    Contains:   This application demonstrates one method to                 
                produce animation on the Macintosh using QuickDraw's        
                palette manager animation routines.  The app simply         
                creates an offscreen image, copies it to the                
                screen's window, then calls AnimatePalette()                
                every cycle through the eventloop.  To provide the          
                fastest possible animation, the image is only               
                created and copied once from the offscreen GWorld.          
                The actual animation is produced by shifting the            
                color entries within its colortable forward or              
                backward one index.  Also the cTable is divided             
                and shifted in two separate groups to simulate              
                objects moving at different speeds.         
 
    Written by: EL  
 
    Copyright:  Copyright © 1991-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/16/1999   KG              Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
#include "out.h"
 
/*******************************/
/* Global Variable Definitions */
/*******************************/
 
WindowPtr       gWindow;                /* Main Display Window */
CTabHandle      gCTable;                /* Window & offscreen Colortable */
GWorldPtr       gGWorld;                /* Offscreen GWorld */
PixMapHandle    gPixMap;                /* PixMap of the GWorld */
PaletteHandle   gPalette;               /* Window & offscreen Palette */
 
int             gCurrentPat = 1;        /* Current Test Pattern */
int             gCurrentMove = STOP;    /* Current State of Animation */
int             gCurrentDir = FORWARD;  /* Current Direction to Shift Palette Colors */
int             gCurrentColor = COLOR;  /* Current Colors: mixed or gray */
 
void main(void)
{
    //MaxApplZone();            /* Increase application heap. */
 
    initMac();              /* Do standard mac initializations. */
    initVariables();        /* Initialize the colortable. */
    
    createWindow();
    createPalette();
    createGWorld();
 
    createImage();          /* Create the current image offscreen. */
    drawImage();            /* Copybits the offscreen image into the window. */
 
    pollEvents();           /* Handle any events & update the palette. */
}
 
void initMac()
{
    /*********************************/
    /* Standard Initialization Calls */
    /*********************************/
    
    /*InitGraf( &qd.thePort );
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs( nil );*/
    InitCursor();
    FlushEvents( 0, everyEvent );
    
    setUpMenus();
}
 
void initVariables()
{
    /********************************************************************/
    /* Allocate memory for the colortable.  Since ColorTable already    */
    /*  contains one ColorSpec, allocate (TOTALCOLORS - 1) ColorSpec's. */
    /*  One ColorSpec is required for each entry.                       */
    /********************************************************************/
        
    gCTable = (CTabHandle)NewHandle( sizeof( ColorTable ) +
                ((TOTALCOLORS - 1) * sizeof( ColorSpec )));
}
 
void createWindow()
{
    Rect wBounds;
    BitMap  bitMap;
    int top, left;
    
    GetQDGlobalsScreenBits(&bitMap);
    
    top = (((bitMap.bounds.bottom - bitMap.bounds.top) - WHEIGHT) / 2);
    left = (((bitMap.bounds.right - bitMap.bounds.left) - WWIDTH) / 2);
 
    /*****************************************************************/ 
    /* Create and center the destination window in the main monitor. */
    /*****************************************************************/
    
    //SetRect( &wBounds, WLEFT, WTOP, WLEFT + WWIDTH, WTOP + WHEIGHT );
    SetRect( &wBounds, left, top, left + WWIDTH, top + WHEIGHT );
    gWindow = NewCWindow( 0L, &wBounds, "\pOut of This GWorld", true, noGrowDocProc,
                            (WindowPtr)-1L, true, 0L );
    
    /**************************************************************/
    /* Display the main window and assign it as the current port. */
    /**************************************************************/
    
    ShowWindow( gWindow );
    //SetPort( gWindow );
    SetPortWindowPort( gWindow );
}
 
void createPalette()
{
    /********************************************************************/
    /* Allocate a palette for color animation.  Using pmExplicit as the */
    /*  usage, tells QD that the index values in the palette are more   */
    /*  important than the rgb values stored at that index.  This is    */
    /*  necessary since the animation in this application is simulated  */
    /*  by shifting the colors at each palette index.                   */
    /********************************************************************/
    
    gPalette = NewPalette( TOTALCOLORS, nil, pmAnimated + pmExplicit, 0 );
    
    /************************************************/
    /* Define the rgb values at each palette index. */
    /************************************************/
    
    defineColorPalette();
    
    /******************************************/
    /* Attach the palette to the main window. */
    /******************************************/
    
    SetPalette( gWindow, gPalette, true );
}
 
void createGWorld()
{
    int         i;
    Rect        gBounds;
    Rect        tempRect1;
    //CGrafPtr  currentPort;
    //GDHandle  currentDevice;
    
    /*************************************************/
    /* Define the size of the GWorld's bounding box. */
    /*************************************************/
    
    /*SetRect( &gBounds, 0, 0, gWindow->portRect.right - gWindow->portRect.left,
                            gWindow->portRect.bottom - gWindow->portRect.top );*/
    GetPortBounds(GetWindowPort(gWindow), &tempRect1);
    
    SetRect( &gBounds, 0, 0, tempRect1.right - tempRect1.left, tempRect1.bottom - tempRect1.top );
 
    /********************************************************************************/
    /* Convert the palette to a colortable before explicitly setting bit 14 in      */
    /*  the ctFlags field of the colortable.  This tells the colortable to refer    */
    /*  to the palette indexes instead of its rgb values.  Again, this is           */
    /*  necessary since the animation is performed by altering the palette.         */
    /********************************************************************************/
        
    Palette2CTab( gPalette, gCTable );
    (**gCTable).ctFlags |= 0x4000;
 
    /************************************************************************/
    /* Make sure each value field of the colortable is assigned to the      */
    /*  corresponding entry in the palette and make sure the ctSeed field   */
    /*  is given a unique value.                                            */
    /************************************************************************/
    
    for (i = 0; i < TOTALCOLORS; i++)
        (**gCTable).ctTable[i].value = i;
    (**gCTable).ctSeed = GetCTSeed();
    
    /*************************************************************************/
    /* Allocate a new GWorld for the offscreen drawing and store its PixMap. */
    /*************************************************************************/
    
    NewGWorld( &gGWorld, 8, &gBounds, gCTable, nil, 0 );
    gPixMap = GetGWorldPixMap( gGWorld );
}
 
void updatePalette()
{
    /******************************************************************************/
    /* Convert the altered colortable to a palette, then animate all its entries. */
    /******************************************************************************/
 
    Palette2CTab( gPalette, gCTable );
    AnimatePalette( gWindow, gCTable, 0, 0, TOTALCOLORS );
}
 
void defineColorPalette()
{
    int i;
    
    gCurrentColor = COLOR;
 
    /***********************************************************************/
    /* Define the first 8 palette entries with primary & secondary colors. */
    /***********************************************************************/
    
    setRGB( 0, 0, 0, 0 );           /* black */
    setRGB( 1, 0, 255, 0 );         /* green */
    setRGB( 2, 0, 255, 255 );       /* cyan */
    setRGB( 3, 0, 0, 255 );         /* blue */
    setRGB( 4, 255, 0, 255 );       /* magenta */
    setRGB( 5, 255, 0, 0 );         /* red */
    setRGB( 6, 255, 255, 0 );       /* yellow */
    setRGB( 7, 255, 255, 255 );     /* white */
 
    /*******************************************************************/
    /* Define shades of the previous colors for the remaining entries. */
    /*******************************************************************/
 
    for (i = 8; i < 48; i++)                                        /* greens */
        setRGB( i, 0, 10 + ((i - 8) * 6), 0 );
        
    for (i = 48; i < 88; i++)                                       /* blues */
        setRGB( i, 0, 0, 10 + ((i - 48) * 6) );
        
    for (i = 88; i < 128; i++)                                      /* reds */
        setRGB( i, 10 + ((i - 88) * 6), 0, 0 );
    
    for (i = 128; i < 168; i++)                                     /* magentas */
        setRGB( i, 10 + ((i - 128) * 6), 0, 10 + ((i - 128) * 6) );
    
    for (i = 168; i < 208; i++)                                     /* yellows */
        setRGB( i, 10 + ((i - 168) * 6), 10 + ((i - 168) * 6), 0 );
    
    for (i = 208; i < 248; i++)                                     /* cyans */
        setRGB( i, 0, 10 + ((i - 208) * 6), ((i - 208) * 6) );
}
 
void defineGrayPalette()
{
    int i;
    int shade;
 
    gCurrentColor = GRAY;
    
    /*******************************************************/
    /* Define all the palette entries with shades of gray. */
    /*******************************************************/
 
    for (i = 1; i < 7; i++)
    {
        shade = i * 42;
        setRGB( i, shade, shade, shade );
    }
    
    for (i = 8, shade = 10; i < 248; i++)
    {
        setRGB( i, shade, shade, shade );
        shade += 12;
        
        if ((i - 8) % 20 == 0)
            shade = 10;
    }
}
 
void setRGB( index, r, g, b )
int index;
int r, g, b;
{
    RGBColor aColor;
    
    /***********************************************************************/
    /* Set the rgb values for the palette color stored at the index value. */
    /***********************************************************************/
    
    aColor.red = r * SUN2MAC;       /* All colors were defined using    */
    aColor.green = g * SUN2MAC;     /*  SUN computer raster values.     */
    aColor.blue = b * SUN2MAC;
    
    SetEntryColor( gPalette, index, &aColor );
}
 
void setColor( index )
int index;
{
    RGBColor aColor;
    
    /********************************************************/
    /* Get the rgb values stored at the index value in the  */
    /*  palette, then use it as the foreground color.       */
    /********************************************************/
                    
    GetEntryColor( gPalette, index, &aColor );
    RGBForeColor( &aColor );
}
 
void createImage()
{
    CGrafPtr    currentPort;
    GDHandle    currentDevice;
    RGBColor    aColor;
    
    /******************************************************************************/
    /* Store the current port and device before switching to the offscreen world. */
    /******************************************************************************/
    
    GetGWorld( &currentPort, &currentDevice );
    
    /****************************************************************************/
    /* Switch to the offscreen GWorld then lock the offscreen buffer in memory. */
    /****************************************************************************/
 
    SetGWorld( gGWorld, nil );
    LockPixels( gPixMap );
    
    /************************************************/
    /* Set the background color to black then erase */
    /*  the PixMap's bounding box in the GWorld.    */
    /************************************************/
    
    GetEntryColor( gPalette, 0, &aColor );
    RGBBackColor( &aColor );
    EraseRect( &(**gPixMap).bounds );
 
    /*************************************************************/
    /* Draw a 1 pixel wide white border along the PixMap's edge. */
    /*************************************************************/
    
    drawWindowBorder();
 
    /*************************************************************************/
    /* Depending on the current test pattern, create an image in the PixMap. */
    /*************************************************************************/
 
    if (gCurrentPat == 1)
        createColorScale();
    else if (gCurrentPat == 2)
        createColorWheels();
    else if (gCurrentPat == 3)
        createColorRings();
    else if (gCurrentPat == 4)
        createColorGears();
    else if (gCurrentPat == 5)
        createColorCurves();
    else if (gCurrentPat == 6)
        createColorBalls();
    else if (gCurrentPat == 7)
        createColorWave();
    else if (gCurrentPat == 8)
        createColorText();
    else
        doAbout();              /* Treat the About message as a test pattern. */
    
    /********************************************************************/
    /* After drawing the image, unlock the offscreen buffer in memory   */
    /*  and set the port and device back to the window's.               */
    /********************************************************************/
 
    UnlockPixels( gPixMap );
    SetGWorld( currentPort, currentDevice );
}
 
void drawWindowBorder()
{
    /********************************************************************/
    /* Set the foreground color to white before drawing a 1 pixel wide  */
    /*  frame around the PixMap's bounding box.                         */
    /********************************************************************/
    
    setColor( 7 );
    PenSize( 1, 1 );
    MoveTo( 0, 0 );
    LineTo( 0, (**gPixMap).bounds.bottom - 1 );
    LineTo( (**gPixMap).bounds.right - 1, (**gPixMap).bounds.bottom - 1 );
    LineTo( (**gPixMap).bounds.right - 1, 0 );
}
 
void drawImage()
{
    Rect    tempRect1;
    
    /***********************************************************/
    /* Transfer the contents of the PixMap to the main window. */
    /***********************************************************/
 
    /*CopyBits( (BitMap*)*gPixMap, &gWindow->portBits, &(**gPixMap).bounds,
                &gWindow->portRect, srcCopy, 0l);*/
    CopyBits( (BitMap *)*gPixMap, GetPortBitMapForCopyBits(GetWindowPort(gWindow)), &(**gPixMap).bounds,
                GetPortBounds(GetWindowPort(gWindow), &tempRect1), srcCopy, 0L);
}
 
void animateCTable()
{
    static int      counter = 0;
    RGBColor        aColor;
 
    /**************************************************************************/
    /* Convert the Palette to a colortable before shifting the color entries. */
    /**************************************************************************/
    
    Palette2CTab( gPalette, gCTable );  
 
    /************************************************************************/
    /* Depending on the current direction, shift 239 palette entries either */
    /*  ahead 1 or behind 1 index.  Store the first or last entry before    */
    /*  shifting to avoid overwriting its rgb values.                       */
    /************************************************************************/
 
    if (gCurrentDir == FORWARD)
    {
        aColor = (**gCTable).ctTable[247].rgb;
        BlockMove( &(**gCTable).ctTable[8].value, &(**gCTable).ctTable[9].value,
                    239 * sizeof(ColorSpec) );
        (**gCTable).ctTable[8].rgb = aColor;
    }
    else
    {
        aColor = (**gCTable).ctTable[8].rgb;
        BlockMove( &(**gCTable).ctTable[9].value, &(**gCTable).ctTable[8].value,
                    239 * sizeof(ColorSpec) );
        (**gCTable).ctTable[247].rgb = aColor;
    }
    
    /**************************************************************/
    /* Shift the first 5 colortable entries once every 6 updates. */
    /**************************************************************/
    
    if (counter == 0)
    {
        aColor = (**gCTable).ctTable[1].rgb;
        BlockMove( &(**gCTable).ctTable[2].value, &(**gCTable).ctTable[1].value,
                    5 * sizeof(ColorSpec) );
        (**gCTable).ctTable[6].rgb = aColor;
    }
 
    /********************************************************/
    /* Update the window's colors by passing the new        */
    /*  colortable as the srcCTab field of AnimatePalette.  */
    /********************************************************/
    
    AnimatePalette( gWindow, gCTable, 0, 0, TOTALCOLORS );
    
    /**********************************************/
    /* Increment the counter by 1 on each update. */
    /**********************************************/
    
    counter = (counter + 1) % 6;
}
 
void doAbout()
{
    Rect        rect;
    PicHandle   thePict;
    int         width, height;
    
    /*******************************************************/
    /* Load the About picture stored in the resource fork. */
    /*******************************************************/
 
    thePict = (PicHandle)GetResource( 'PICT', 128 );
    
    /************************************************/
    /* Define the rect used to enclose the picture. */
    /************************************************/
 
    width = (**thePict).picFrame.right - (**thePict).picFrame.left;
    height = (**thePict).picFrame.bottom - (**thePict).picFrame.top;
 
    SetRect( &rect, (WWIDTH - width) / 2, (WHEIGHT - height) / 2, 
                    ((WWIDTH - width) / 2) + width, ((WHEIGHT - height) / 2 ) + height );
 
    /*********************************/
    /* Draw the picture in the rect. */
    /*********************************/
 
    DrawPicture( thePict, &rect );
    ReleaseResource( (Handle)thePict );
}
 
void cleanUp()
{
    /*****************************************************************************/
    /* Free the memory set aside for the GWorld and main window before exitting. */
    /*****************************************************************************/
 
    DisposeGWorld( gGWorld );
    DisposeWindow( gWindow );
    ExitToShell();
}