TweetFollow Us on Twitter

Palette Animation
Volume Number:4
Issue Number:5
Column Tag:Macintosh II

Palette Animation

By Steven C. Sheets, Contributing Editor

PalFun: an Example of Palette Manipulation

Last month’s column explained the basics of the Palette Manager, the new Toolbox calls designed to handle the color environment of the Mac //. To fully understand this month’s column, the reader will have to have either read the previous article and/or have worked with the Palette Manager on his own. Hopefully any new ideas/examples will be explained this month, but the reader must have a certain level of knowledge of Macintosh // Color to start with (this author hates 2-part TV Shows which spend most of the second part rehashing the events of the first half). This month’s article will explain the idea of Palette Animation, or how to directly manipulate colors in the Palette to change shapes on the Macintosh // screen. The first portion of this article will describe the concepts involved in Palette Animation. The second section will describe special effect techniques that can be used with Palette Animation. The last section is an explanation of this month’s sample program, PalFun.

Palette Animation: Changing the Color Environment

Imagine a sample Macintosh // screen, using the information described last month. The color screen has several windows on it, each one having a Palette and a group of colors that it wants to draw with. The top most window gets first choice of colors. Based on how many colors it wants and the tolerance of each color (ie. how picky it is in finding a closely matching color), the Palette Manager has changed certain colors in the CLUT (Color Look Up Table) of the video card. If there are any entries in the CLUT unused (ie. not reserved by someone), the next lower Window gets it’s choice of Colors, and so on, and so on.

At some point, the number of available entries on the video card will run out. If there are any windows with Palette that have not gotten a chance to allocate entries, they will just have to make do (ie. use the given colors in the color environment). If there are several windows, this may happen to the bottom most window, or it may happen to the top most window if there are not very many entries in the CLUT of the video card.

Back to imagining the sample Macintosh // screen with multiple windows. Imagine that each one has a certain shade of Red it wants to display. The first window get it’s exact Red $FFFF,$0,$0 (ie. the Palette Manager changes the CLUT of the video card so that some entry on it has this value). The second window happens to need the exact shade of Red $FFFF,$0,$0. Since the second window is more than happy to use the shade allocated by the first window, no entries are use by the second window to describe this red. The third window wants it’s own shade of Red $CCCC,$0,$0, so it is allocated a color entry (there are still enough left). Finally the last window wants a Red shade of $EEEE,$0,$0, but there are no color entries left unallocated, so it does not change the Color environment.

When each Window tries to draw it’s own shade of Red, it has to use the Colors available on the card (assume no other shades of Red on the video card in this example). Windows one and two use the $FFFF,$0,$0 shade of red, while Window three uses the $CCCC,$0,$0 shade. When window four tries to draw it’s shade, Color Quickdraw will probably pick $FFFF,$0,$0 to color with. The selection of which color is actually going to be drawn with is transparent to the programmer; Color Quickdraw calculates the best match, not the program.

Remember that the CLUT contains the information of what colors are actually being displayed on the screen. The video card uses a RAM buffer to model the screen. Each pixel on the screen matches a value in the buffer. The buffer does not have a RGB value for each pixel, instead it has a pixel value. The size (in bits) of this value is fixed at any given moment, but can be changed depending on how much memory is allocated to the buffer (done by the Monitor portion of the Control Panel). So called pixel values in the buffer correspond to an entry in the video card CLUT. Thus a green pixel on the screen might have the pixel value of 7, which would match a color entry in the CLUT which has a pixel value of 7 and a RGB value of $000, $FFFF, $0000. This matching of the video buffer pixel values to the CLUT pixel values tells the card which colors to display on the screen.

Now things suddenly change. The program decided that window one really wants to draw using a shade of Blue $0,$0,$FFFF. The program uses SetEntryColor and ActivatePalette to change Red to Blue. In order for the Window to get this color, the Palette Manager needs to add a Blue entry to the CLUT (assume again there are no Blues in the current CLUT). The exact work of which colors stay and which change is dependent on Tolerances and other variables, but for this example, the Red $FFFF,$0,$0 entry in the CLUT is change to Blue $0,$0,$FFFF. Notice none of the Pixel values in the video buffer have changed. The RGB value of a certain entry in the CLUT has changed, and nothing else.

Back to the screen, while the user watches, everywhere were there was the Red shade $FFFF,$0,$0, there is now a Blue shade $0,$0,$FFFF. Through out the environment, all windows that showed that exact shade of Red, will show Blue. Red lips come out Blue, not exactly what the artist originally wanted. The video card can not change all the former Red pixel values in the video buffer; it has no idea what RGB colors each window wants to display.

This is why when each Palette is connect to a window using SetPalette, there is a cUpdate Boolean that needs to be passed. If cUpdate is true, the window will automatically be updated whenever the color environment changes. Then each window can redraw to the best color using the color environment. The flag should almost always be set to true. Again, this updating is completely transparent to the programmer; there is no code in the main program that causes updates whenever the color environment is changed.

Back at the windows, the top most window is updated. It draws using Blue. The lower three windows draw with the closest shade of Red, $CCCC,$0,$0. Now the second window is selected and becomes the top most window. The program does not change the Palettes, but because the top most window has changed, the color environment is about to. The new top most window has the highest priority when allocating entries in the CLUT, so Red $FFFF,$0,$0 is back on the table. The former top most window still gets it’s Blue (due to the number of entries in the CLUT), so the CLUT is changed. Again all the windows most be updated in order for them to show the best possible colors.

The above model of changing the color environment is important to understand. The reader should have a feel for what happens when the color environment changes before going on.

Palette Animation: Animating Colors

Now think about what the Palette Manager did when it changed the color environment. It changed a few bytes of information in the CLUT. Because of this, no matter how many colors or where there were, those colors on the screen suddenly changed. To do the same thing using Copybits would be many factors slower. Accessing memory on Nubus cards is slower than normal memory. Even if access time was not slow, every pixel with the old color must be changed. If large areas of the screen are that color, redrawing pixels is an extremely slow task. This is assuming that the program knew where all the pixels of a certain color were to begin with. Palette Animation simply consists of changing colors on the screen to create animation effect by manipulating the CLUT, and not the pixel values.

In the above model, while colors do get changed, actual animation would be slow. Every time the environment changed, all the window are updated. Even if the animating window did not have it’s cUpdate flag set, other windows on the screen would need to be updated. It would be faster to use Copybits directly to create animation this way. Remember all Palette colors in the above model were Tolerant or Courteous Colors. All normal Palette use these types of colors, because when Tolerant and Courteous Colors are allocated on the CLUT, they can be used by other windows.

There is another type of Color Entry: Animating Color. When these Colors are added to a CLUT, they are for exclusive use of the window/palette that allocated them. If a window was to be drawn using strictly animation colors, the colors of the entries in it’s palette could be changed without effect the colors of the rest of the windows. Those other windows will never share animation colors with the animated window.

Animating Colors are created the same way as Tolerant or Courteous colors. A Palette containing Animating Colors can be created with NewPalette or GetNewPalette, and attached to a window with SetPalette. Color Entries, which consist of RGB value, Usage value and Tolerance value, can be set with SetEntryColor and SetEntryUsage. Animating Colors have a usage of 4, while the Tolerance is ignored.

To draw with an Animating Color, PmForeColor and PmBackColor must be used. If RGBForeColor or RGBBackColor is used to set the Foreground or Background Color on an Animating Window, that color will NOT be drawn using Animating Colors. Instead it will use the best available color in the color environment.

To change the animating Colors (and thus the screen colors) without changing the color environment, there are two special Palette Manager calls. AnimateEntry and AnimatePalette are designed to do this type of palette animation. Both calls are designed to work strictly with Animating Colors in a Palette. They change RGB values of certain entries of the Palette. The RGB values of entries in the CLUT of the video card will then be changed, causing the actual change of color on the screen. Since no other windows have the animating color, the color environment is not considered to have changed, so no update events are posted.

AnimateEntry is passed the animating window, the entry number of the Animating Color that will be changed, and the new RGB Color it will be changed to. AnimatePalette is passed the animating window, a Color Table (containing numerous RGB values), an Index value, Destination value and Length value. Beginning at the Index entry in the color table, a Length number of entries are copied to the Palette starting at the Destination entry. Thus, this call performs the same function as AnimateEntry, but for a group of animating colors.

Special Effect: Moving Colors

Now that the Palette Manager provides the capability of quickly changing the colors on the screen, how can a programmer use this to create animation effects? Consider what normal animation using normal Quickdraw requires. If a ball was suppose to be animated across the screen at a good speed, first the ball is drawn on on the screen. Then a certain length of time goes by to allow the user to see the ball. Next the ball is quickly erased, and redrawn at the new position. This may be repeated a number of times across the screen. To a viewer, it would look like the ball is being thrown across the screen.

To do the same thing with Palette animation, a Palette is created with 3 entries and attached to a window. (One entry for the background, two for the two positions of the ball). The background will be green, while the ball will be white, so entries zero and two (entries are counted from 0 to N) will be set to RGB Green $0,$FFFF,$0, while entry one will be White $FFFF,$FFFF,$FFFF.

Next the window is drawn (updated). The background is drawn to green, using PmBackColor, not RGBBackColor. The ball in the first position is drawn using entry one. The screen will show the white ball at this point. Next the second position of the ball is drawn using the third entry in the color table. Since that entry is set to green like the background, to the viewer, that ball is invisible.

Fig. 2 Mixing colors

Now the animation starts. AnimateEntry is used to set entry one to green $0,$FFFF,$0 and entry two to white $FFFF,$FFFF,$FFFF. To the viewer, it looks like the ball has moved position. If there were more positions for the ball, there would be more entries (one for each new ball position). In that case, the animation routine would wait a certain amount of time so that the animation is not to fast for the viewer to see. Then ball is moved to the next position in the same manner (ie. entry two is set to green while entry three is set to white), and so on, and so on.

Notice that it would take the same amount of time to animate the Ball using Palette Animation no matter how big the ball is. Using normal animation techniques, speed of animation usually goes down for large objects. Also, the ball’s position can be precalculated, something that may take too much time if done while animating.

Animation Speed is important for all types of animation. If a ball is suppose to move across the screen at a set speed, two concepts effect this movement speed: distance between images of the ball and the speed at which these images can be changed (sometimes measured in images or frames per second). If the speed of animation is to slow, the distance between the ball must be increased. However, large distances between objects causes jerky animation, and is noticeable by viewer. If the speed of animation is very slow, the screen will actually be refreshed while quickdraw is drawing in the video buffer. This causes a ripple effect that is a characteristic of bad animation! It is better to have small movement and a higher animation speed. It might seem that the ball must be moved at least it’s own size between each image or palette animation will not work. Not so...

Special Effects: Overlaps

Again examine what happens during normal animation of a ball across the screen. The ball is drawn in white on green. After a time, the ball is erased (ie. drawn in green) and redrawn at the new position. If the new position overlaps the old position, there will be pixels on the screen that were white at the beginning and are still white at the end of this operation.

Therefore, the pixels in this example can be divided into four groups: those that always stay green, those that always stay white, those that start green, but change to white and those that start white, but change to green. Broken down that way, Palette Animation is simple. First a Palette is created with 4 Animating Color entries, the first and fourth are set to green, while the second and third are set to white. The background is drawn to green using the first animating color. The portion of the ball that is in the first position, but does not overlap the second is then drawn in white using the second color. The portion of the ball that is in the first position, and overlaps the second ball’s position is then drawn in white using the third color. Finally the portion of the ball that is in the second position, but does not overlap the first is then drawn in green using the fourth color. To the user, it would appear that there is only one ball in the first position.

Fig. 3 Ball Animation

To animate, the second color, which was white, is set to green, while the fourth color which was green, is set to white. The first and third colors would stay the same. For the user, it appears as if the ball has moved to the second position.

This is the basis of Palette Animation. If the ball was expanded so that it’s colors were Red, White and Blue, while the background was changed to show Green, Orange and Purple, the picture can still be broken down into groups of pixels that change from one color to another between two frames. First, determine the pixels that never change, then determine the pixels that change from one color to another. This concept can be expanded for more frames and/or more objects. In all cases, group the pixels into sets which change the same colors from one frame to another.

The above techniques can be used for numerous effects. Many arcade games use these techniques along with normal animation. The twinkling of a rocket’s jet or movement of an alien’s fangs are both simple two step Palette Animation. This can be done separately while the rest of the game is being animated using traditional methods. It can be done no matter where the rocket is or how many aliens there are. Due to the low overhead in doing this type of animation, some graphic adventure games prefer simple animation using Palette animation. The twitching of an opponents eyes or the movement of an ocean wave are some common effects of such games. The effects are only limited by the programmer’s imagination.

Special Effects: Rainbows and Fades

Two other types of Special Effects using Palette Animation should be mentioned: Fades and Rainbows. An image drawn with Animating Colors can be easily Faded into some neutral Color. This is an attractive way to exit from an image (ie. similar to the way Movies dissolve from one scene to another). To do this, first a set number of intermediate steps is chosen to work with. Then a loop is cycled for that number of times. In that loop, each entry in the Palette is changed a constant amount so that when the entire loop is done, all the entries will have the same RGB value. For example, to fade to black in 256 cycles, during each cycle each entry’s RGB values are reduced by 256th of the original value. To the viewer, all the colors will slowly fade to Black. An entry which starts with the RGB value of $0,$FF00,$0 would be reduced by $0,$00FF,$0, while an entry with a RGB value of $8000,$6700,$0 would be reduced by a value of $0080,$0067,$0 each cycle. Notice each Red, Green and Blue part of an RGB value is calculated separately. Also an image can fade to any RGB value, not just White, Black or even Gray.

Once an image is faded out, a new image can be faded in with the same technique. Assuming the second image uses the same Palette, it can be drawn while the screen is at the neutral color. Since the colors are still all the same, the new image will be invisible. Then it can be faded to either the old values, or even to any new values. The entries do not care at which point the program stops adjusting them.

The other note-worthy effect is a Rainbow motion. A Palette is created which contains some spectrum of colors. While any arbitrary color set can be selected, in many cases, a Rainbow spectrum is chosen. Once the Palette is create, some shape is drawn so that the full range of entries are used. Different shapes include parallel lines, arcs in a circle, circles within each other or even lines in a web pattern. Then the Palette is animated so that every entry’s RGB value is moved to the next entry (and the last one is moved to the front of the list). The resulting animated motion can be hypnotizing.

PalFun: A Dynamic Colorful Example

PalFun, this months sample program, demonstrates some of the techniques described this month, as well as some of the concepts discussed last issue. The reader can for the most part ignore the main portion of the program (an exceptionally simple program) once he understands that this is a shell designed to create, display and animate Palette windows. In PalFun, every window has a routine associated with it that creates the Window/Palette (MakeSomeWindow), a routine associated that updates the image (UpdateSomeWindow) and some have a routine associated that animates the image (AnimateSomeWindow).

Red Window, Green Window and Blue Window demonstrate various ways to create normal Palettes. Try picking different windows, and notice how this effects the current color environment. Also, notice how each window is automatically updated. Change the bit depth of the Video Card (ie. number of bits per pixel) and see how the Windows change. Notice that the Blue window uses a method of Color Priority (mentioned last month) that allows it to appear at it’s best no matter what Pixel or Window depth.

Current Color Window uses Explicit Colors (explained last month). Explicit colors provide a convenient way for the user to view the current color environment. An Explicit entry is not drawn using it’s RGB value. Instead it displays the CLUT’s color at it’s corresponding position. For example, the fifth entry in Palette will display the fifth entry in the CLUT of the video card. Explicit colors is the only method to view Animating Colors in some window other than the one the Animating Colors were created for. When selecting various windows and/or running through Animation techniques, keep an eye on the Current Color Window to view what’s happening. Converting Current Color into a Desk Accessory creates a useful tool for anyone who tinkers with the Palettes.

Ball Window gives the basic example of moving a non-overlapping ball around a screen. Shapes demonstrates how to calculate the overlaps and pixels of different, completely arbitrary 2-bit images in order to step through the images. It uses unions and intersections of regions in order to find out what pixels change into what colors. For example, given three regions (images), the pixels that will never change colors are the ones that do not intersect the 3 regions, or there intersections between A region, B region and C region. The pixels that would be set (Black) for the first image, then clear (White) for the next 2 images are the pixels that in A Region minus the ones that are in B and C regions. Notice that if the result of any such calculation is the empty region, there is no pixels of that color and that pixel’s Color Entry really does not have to be allocated/used. Fade and Rainbow are the last two windows. They demonstrate the above mentioned Fade and Rainbow techniques.

Last Comments: Palette Techniques

Here are some last comments for this month:

• Always start a Palette with a White and Black color entry. Due to a bug in the Palette Manager, the first entry must be White or Black. Even without this, it is good practice to always have White and Black as the first two colors in a Palette (remember Color Priorities?).

• Animating Windows should still have the cUpdates flag set when SetPalette is called. This will prevent undesired animation when ActivatePalette reserves entries that were being used by the animating window.

• When using the Palette Manager, the user may sometimes see the Background Desktop pattern change colors when the windows are dragged around. This happens most often when the Colors used in the Background Pattern (set by the Control Panel) are something other than Black and White. What has occurred is that the Palette Manager has changed the Color Environment, but the Window Manager (which takes care of the Background) does not known this. This is due to a problem with the Window Manager that will hopefully be fixed.

• While Shape demonstrates how to calculate animating arbitrary images, it is best to design the image with Palette animation in mind. Keep the colors in separate portions of the screen, so they do not intersect too many times and need separate entries. The lower the number of sets of pixels, the more images that can be animated. A good Palette Manager example would be a program that takes arbitrary MacPaint documents and displays them one after another using Palette Animation. The Application could be smart enough to keep track of Pixel differences (ie. unions and intersects of bitmaps) so that it keeps track of the maximum number of entries that need to be used. The Application could keep adding MacPaint pictures until the maximum number of entries of the Video Card is reached, and then quickly flash through the pictures.

• When should a programmer use SetPalette vs using a ‘pltt’ resource? This is the same as asking when a programmer should use NewWindow vs using a ‘WIND’ resource; it depends on when the settings of the Window or Palette are calculated. If they are calculated at run time or constantly changing during the development of the program, use SetPalette. This provides the most flexibility to the programmer. If the Palette is precalculated (ie. always the same for every run of the program), use the ‘pltt’ resource to separate the Color from the Code. This is done for the same reason most programmers separate the Text from the Code: Localization.

• A Special Thank you to Art Cabral for the help he provided in understand the Palette Manager (No more 2 minute long phone calls, Art).

Next issue, manipulating Color Icons and a Color Icon Editor. Even if there are tools to do this by then, the understanding of advance Color Quickdraw data structures is worth the study.

About the Program Code

PalFun is written in MPW Pascal, available on the MacTutor Source Code disk for this issue. The listing here has been translated into LS Pascal (and should work as is in Turbo Pascal also) by the Editor. The resources are also listed here in RMaker format, but the Rez format is on the Source Code Disk. The MPW version is a single program, but for LS Pascal, the program had to be broken into this main program, and two units: one unit for the globals and the other unit for all the Palette specific routines called PalFunStuff. Since LS Pascal is the most generic of all the Pascal compilers, this listing should be most easily entered into all the other compilers, MPW, Turbo and TML by just changing the USES statement.

{PalFun by Steve Sheets 3/88}
{Palette Manager Sample Program designed for MacTutor.}
{It demonstrate various Palette Animation effects. }

PROGRAM PalFun;

 USES
 ROM85, ColorQuickDraw, ColorWindowMgr, PaletteMgr, PickerIntf, PalFunGlobals, 
PalFunStuff;

{********* Main Portion Programs ***********}

 PROCEDURE crash;
 BEGIN
 ExitToShell;
 END;

 PROCEDURE SetUp;
 VAR
 count : integer;
 BEGIN  {Standard Mac Program setup}
 InitGraf(@thePort);
 InitFonts;
 FlushEvents(everyEvent, 0);
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(@crash);
 InitCursor;

 FOR count := appleM TO menuCount DO
 myMenus[count] := GetMenu(count);
 AddResMenu(myMenus[appleM], ‘DRVR’);
 FOR count := appleM TO menuCount DO
 BEGIN
 moveHHi(handle(myMenus[count]));
 HLock(handle(myMenus[count]));
 END;
 FOR count := appleM TO menuCount DO
 InsertMenu(myMenus[count], 0);
 DrawMenuBar;

 WITH screenBits.bounds DO
 SetRect(dragRect, 4, 24, right - 4, bottom - 4);
 doneFlag := FALSE;

 FOR count := 1 TO numWindows DO
 BEGIN
 MyWindow[count] := NIL;
 MyPalette[count] := NIL;
 END;
 END;

{Given a Window ID number, close Window/Palette}
 PROCEDURE CloseIt (N : INTEGER);
 BEGIN
 IF (N > 0) AND (N <= numWindows) THEN
 BEGIN
 IF MyPalette[N] <> NIL THEN
 DisposePalette(MyPalette[N]);
 MyPalette[N] := NIL;
 IF MyWindow[N] <> NIL THEN
 DisposeWindow(MyWindow[N]);
 MyWindow[N] := NIL;
 END;
 END;

{Gets the Front most Window ID number.}
 FUNCTION GetWindowNum (W : WindowPtr) : INTEGER;
 VAR
 N, count : INTEGER;
 BEGIN
 IF W = NIL THEN
 GetWindowNum := 0
 ELSE
 BEGIN
 N := 0;
 FOR count := 1 TO numWindows DO
 IF MyWindow[count] = W THEN
 N := count;
 GetWindowNum := N;
 END;
 END;

{ Standard Handling of the Menu. Selecting the Window Menu, bring that 
window to the front and that’s all (Palette Manager handles changing 
colors and creating update). Animate Menu animate the front window if 
it can. }
 PROCEDURE DoCommand (mResult : LONGINT);
 VAR
 theItem : INTEGER;
 theMenu : INTEGER;
 name : Str255;
 N : INTEGER;
 dummy : Boolean;
 tempPort : GrafPtr;
 BEGIN
 theItem := LoWord(mResult);
 theMenu := HiWord(mResult);
 CASE theMenu OF
 appleM : 
 IF theItem = 1 THEN
 theItem := Alert(AlertID, NIL)
 ELSE
 BEGIN
 GetItem(myMenus[appleM], theItem, name);
 N := OpenDeskAcc(name);
 END;
 fileM : 
 CASE theItem OF
 1 : 
 BEGIN
 GetPort(tempPort);
 SetPort(FrontWindow);
 CASE GetWindowNum(FrontWindow) OF
 ballW : 
 AnimBall;
 shapeW : 
 AnimShape;
 rainbowW : 
 AnimRainbow;
 fadeW : 
 AnimFade;
 OTHERWISE
 END;
 SetPort(tempPort);
 END;
 2 : 
 CloseIt(GetWindowNum(FrontWindow));
 4 : 
 doneFlag := TRUE;
 OTHERWISE
 END;
 editM : 
 dummy := SystemEdit(theItem - 1);
 winM : 
 IF (theItem > 0) AND (theItem <= numWindows) THEN
 BEGIN
 IF MyWindow[theItem] = NIL THEN
 BEGIN
 CASE theItem OF
 redW : 
 MakeRed;
 greenW : 
 MakeGreen;
 blueW : 
 MakeBlue;
 ballW : 
 MakeBall;
 curW : 
 MakeCur;
 shapeW : 
 MakeShape;
 rainbowW : 
 MakeRainbow;
 fadeW : 
 MakeFade;
 OTHERWISE
 END;
 END
 ELSE
 SelectWindow(MyWindow[theItem]);
 END;
 OTHERWISE
 END;
 HiliteMenu(0);
 END;

{Extremely Standard Main Program Loop.}
 PROCEDURE DoMainLoop;
 VAR
 theChar : CHAR;
 myEvent : EventRecord;
 whichWindow : WindowPtr;
 oldPort : GrafPtr;
 dummy : boolean;
 BEGIN
 REPEAT
 SystemTask;
 IF GetNextEvent(everyEvent, myEvent) THEN
 CASE myEvent.what OF
 mouseDown : 
 CASE FindWindow(myEvent.where, whichWindow) OF
 inSysWindow : 
 SystemClick(myEvent, whichWindow);
 inMenuBar : 
 DoCommand(MenuSelect(myEvent.where));
 inGoAway : 
 IF TrackGoAway(whichWindow, myEvent.where) THEN
 CloseIt(GetWindowNum(whichWindow));
 inDrag : 
 IF (FrontWindow <> whichWindow) THEN
 SelectWindow(whichWindow)
 ELSE
 DragWindow(whichWindow, myEvent.where, dragRect);
 inContent : 
 IF (FrontWindow <> whichWindow) THEN
 SelectWindow(whichWindow);
 OTHERWISE
 END; {of mouseDown}
 keyDown, autoKey : 
 BEGIN
 theChar := CHR(BitAnd(myEvent.message, charCodeMask));
 IF BitAnd(myEvent.modifiers, cmdKey) <> 0 THEN
 DoCommand(MenuKey(theChar));
 END;
 updateEvt : 
 BEGIN
 whichWindow := WindowPtr(myEvent.message);
 IF whichWindow <> NIL THEN
 BEGIN
 GetPort(oldPort);
 SetPort(whichWindow);
 BeginUpdate(whichWindow);
 CASE GetWindowNum(whichWindow) OF
 redW : 
 DoRedUpdate;
 greenW : 
 DoGreenUpdate;
 blueW : 
 DoBlueUpdate;
 curW : 
 DoCurUpdate;
 ballW : 
 DoBallUpdate;
 shapeW : 
 DoShapeUpdate;
 rainbowW : 
 DoRainbowUpdate;
 fadeW : 
 DoFadeUpdate;
 OTHERWISE
 END;
 EndUpdate(whichWindow);
 SetPort(oldPort);
 END;
 END;
 OTHERWISE
 END;
 UNTIL doneFlag;
 END;

{Dispose of all the Palettes and closes all the Windows.}
 PROCEDURE CloseDown;
 VAR
 count : integer;
 BEGIN
 FOR count := 1 TO numWindows DO
 CloseIt(count);
 FOR count := appleM TO menuCount DO
 BEGIN
 DeleteMenu(count);
 DisposeMenu(myMenus[count]);
 END;
 DrawMenuBar;
 END;

{ Main Body Program.  Setup, Do it, Close down.}

BEGIN
 IF ColorQDExists THEN
 BEGIN
 SetUp;
 DoMainLoop;
 CloseDown;
 END;
END.


UNIT PalFunStuff;

INTERFACE

 USES
 ROM85, ColorQuickDraw, ColorWindowMgr, PaletteMgr, PickerIntf, PalFunGlobals;

 FUNCTION ColorQDExists : boolean;

 PROCEDURE AnimBall;
 PROCEDURE AnimShape;
 PROCEDURE AnimRainbow;
 PROCEDURE AnimFade;

 PROCEDURE MakeRed;
 PROCEDURE MakeGreen;
 PROCEDURE MakeBlue;
 PROCEDURE MakeBall;
 PROCEDURE MakeCur;
 PROCEDURE MakeShape;
 PROCEDURE MakeRainbow;
 PROCEDURE MakeFade;

 PROCEDURE DoRedUpdate;
 PROCEDURE DoGreenUpdate;
 PROCEDURE DoBlueUpdate;
 PROCEDURE DoCurUpdate;
 PROCEDURE DoBallUpdate;
 PROCEDURE DoShapeUpdate;
 PROCEDURE DoRainbowUpdate;
 PROCEDURE DoFadeUpdate;

IMPLEMENTATION


{******************** General Tools ********************}

{Returns true if the Mac had Color Quickdraw.}
 FUNCTION ColorQDExists; {boolean}
 CONST
 ROM85Loc = $28E;
 TwoHighMask = $C000;
 TYPE
 WordPtr = ^INTEGER;
 VAR
 Wd : WordPtr;
 BEGIN
 Wd := POINTER(ROM85Loc);
 ColorQDExists := (BitAnd(Wd^, TwoHighMask) = 0);
 END;

{Stuffs Red, Green & Blue into RGBColor}
 PROCEDURE SetRGB (VAR RGB : RGBColor;
 R, G, B : INTEGER);
 BEGIN
 RGB.Red := R;
 RGB.Green := G;
 RGB.Blue := B;
 END;

{Copies RGBColor into RGBColor}
 PROCEDURE CopyRGB (RGBsrc : RGBColor;
 VAR RGBdest : RGBColor);
 BEGIN
 RGBdest.Red := RGBsrc.Red;
 RGBdest.Green := RGBsrc.Green;
 RGBdest.Blue := RGBsrc.Blue;
 END;

{Delays a set length time.  usually until}
{ the screen in refreshed (prevents ripples)}
 PROCEDURE DoDelay (N : INTEGER);
 VAR
 L : LONGINT;
 BEGIN
 L := TickCount + N;
 WHILE L > TickCount DO
 ;
 END;

{Using 16 Bit Unsigned Integers: C:=A/B}
 PROCEDURE UnSignedDiv (A, B : INTEGER;
 VAR C : INTEGER);
 VAR
 L : LongInt;
 BEGIN
 IF A < 0 THEN
 L := A + 65536
 ELSE
 L := A;
 C := LoWord(L DIV B);
 END;

{Using 16 Bit Unsigned Integers: A:=A+B}
 PROCEDURE UnSignedAdd (VAR A : INTEGER;
 B : INTEGER);
 VAR
 L : LongInt;
 BEGIN
 IF A < 0 THEN
 L := A + 65536 + B
 ELSE
 L := A + B;
 A := LoWord(L);
 END;

{Using 16 Bit Unsigned Integers: A:=A-B}
 PROCEDURE UnSignedSub (VAR A : INTEGER;
 B : INTEGER);
 VAR
 L : LongInt;
 BEGIN
 IF A < 0 THEN
 L := A + 65536 - B
 ELSE
 L := A - B;
 A := integer(LoWord(L));
 END;

{******************** Color Table Tools ********************}

{Given number of Colors to be placed in it, creates a blank CLUT.  Gives 
it}
{an unique Seed and correct value, but no colors.}
 FUNCTION NewCT (N : integer) : CTabHandle;
 VAR
 MyCT : MyCTabHandle;
 count : integer;
 BEGIN
 MyCT := NIL;
 IF (N > 0) AND (N <= MaxCT) THEN
 BEGIN
 MyCT := POINTER(NewHandle((N * SIZEOF(ColorSpec)) + (2 * SIZEOF(integer)) 
+ SIZEOF(longint)));
 IF MyCT <> NIL THEN
 WITH MyCT^^ DO
 BEGIN
 ctSeed := GetCTSeed;
 ctFlag := 0;
 ctSize := N - 1;
 FOR count := 0 TO N - 1 DO
 WITH ctTable[count] DO
 BEGIN
 value := count;
 SetRGB(rgb, 0, 0, 0);
 END;
 END;
 END;
 NewCT := POINTER(MyCT);
 END;

{Stuffs an RGB value in the Nth Color (numbered 0 to N) of the CLUT.}
 PROCEDURE SetCTEntry (C : CTabHandle;
 N, R, G, B : INTEGER);
 VAR
 MyCT : MyCTabHandle;
 BEGIN
 MyCT := POINTER(C);
 SetRGB(MyCT^^.ctTable[n].rgb, R, G, B);
 END;

{******************** Red ********************}

{Red Window displays encompassing red-shaded circles.}
{This creates a 3-D Globe effect.}

{Create Red Window/Palette with NewPalette & SetEntryColor commands.}
 PROCEDURE MakeRed;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 S : str255;
 count : integer;
 BEGIN
 SetRect(tempRect, 20, 40, 320, 340);
 GetIndString(S, StrID, 1);
 MyWindow[redW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);

 MyPalette[redW] := NewPalette(128, NIL, pmTolerant, 0);
 SetRGB(tempRGB, ColorStart, 0, 0);
 FOR count := 0 TO 127 DO
 BEGIN
 SetEntryColor(MyPalette[redW], count, tempRGB);
 UnSignedSub(tempRGB.red, ColorInc);
 END;

 SetPalette(MyWindow[redW], MyPalette[redW], true);
 END;

{Draw the Red Window using RGBForeColor.}
 PROCEDURE DoRedUpdate;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 count : integer;
 BEGIN
 SetRect(tempRect, 22, 22, 278, 278);
 SetRGB(tempRGB, ColorStart, 0, 0);
 FOR count := 0 TO 127 DO
 BEGIN
 RGBForeColor(tempRGB);
 PaintOval(tempRect);
 InsetRect(tempRect, 1, 1);
 UnSignedSub(tempRGB.red, ColorInc);
 END;
 END;

{******************** Green ********************}

{Green Window displays a Green Globe.}

{Create Green Window/Palette with NewPalette command & CLUT  procedures.}
 PROCEDURE MakeGreen;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 tempCT : CTabHandle;
 Col : INTEGER;
 S : str255;
 count : integer;
 BEGIN
 SetRect(tempRect, 40, 60, 340, 360);
 GetIndString(S, StrID, 2);
 MyWindow[greenW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);

 tempCT := NewCT(128);
 Col := ColorStart;
 FOR count := 0 TO 127 DO
 BEGIN
 SetCTEntry(tempCT, count, 0, Col, 0);
 UnSignedSub(Col, ColorInc);
 END;
 MyPalette[greenW] := NewPalette(128, tempCT, pmTolerant, 0);
 DisposHandle(Handle(tempCT));

 SetPalette(MyWindow[greenW], MyPalette[greenW], true);
 END;

{Draw the Green Window using PmForeColor.}
 PROCEDURE DoGreenUpdate;
 VAR
 tempRect : rect;
 count : integer;
 BEGIN
 SetRect(tempRect, 22, 22, 278, 278);
 FOR count := 0 TO 127 DO
 BEGIN
 PmForeColor(count);
 PaintOval(tempRect);
 InsetRect(tempRect, 1, 1);
 END;
 END;

{******************** Blue ********************}

{Display a Blue Globe (like Green Window), but now the colors }
{are set up for better displaying (ie. Color Priority).}

{Create Green Window/Palette with NewPalette command & CLUT  procedures.}

 PROCEDURE MakeBlue;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 tempCT : CTabHandle;
 Col : INTEGER;
 S : str255;
 h, v : integer;
 BEGIN
 SetRect(tempRect, 60, 80, 360, 380);
 GetIndString(S, StrID, 3);
 MyWindow[blueW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);

 tempCT := NewCT(128);
 Col := ColorStart;
 FOR h := 0 TO 15 DO
 FOR v := 0 TO 7 DO
 BEGIN
 SetCTEntry(tempCT, (v * 16) + h, 0, 0, Col);
 UnSignedSub(Col, ColorInc);
 END;
 MyPalette[blueW] := NewPalette(128, tempCT, pmTolerant, 0);
 DisposHandle(Handle(tempCT));

 SetPalette(MyWindow[blueW], MyPalette[blueW], true);
 END;

{Draw the Blue Window using RGBForeColor.}
 PROCEDURE DoBlueUpdate;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 count : integer;
 BEGIN
 SetRect(tempRect, 22, 22, 278, 278);
 SetRGB(tempRGB, 0, 0, ColorStart);
 FOR count := 0 TO 127 DO
 BEGIN
 RGBForeColor(tempRGB);
 PaintOval(tempRect);
 InsetRect(tempRect, 1, 1);
 UnSignedSub(tempRGB.Blue, ColorInc);
 END;
 END;

{******************** Current Color ********************}

{Displays the Current Color Enviroment}

{Create the current Color Window using Explicit colors }
{(Does not have to set the colors).}
 PROCEDURE MakeCur;
 VAR
 tempRect : rect;
 S : str255;
 BEGIN
 SetRect(tempRect, 100, 80, 420, 400);
 GetIndString(S, StrID, 4);
 MyWindow[curW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);
 MyPalette[curW] := NewPalette(256, NIL, pmExplicit, 0);
 SetPalette(MyWindow[curW], MyPalette[curW], true);
 END;

{Draws the current Graphic Device Colors.}
 PROCEDURE DoCurUpdate;
 VAR
 x, y, n : integer;
 tempRect : rect;
 BEGIN
 n := 0;
 FOR y := 0 TO 15 DO
 FOR x := 0 TO 15 DO
 BEGIN
 PmForeColor(n);
 SetRect(tempRect, x * 20, y * 20, (x + 1) * 20, (y + 1) * 20);
 PaintRect(tempRect);
 n := n + 1;
 END;
 END;

{******************** Ball ********************}

{Simple Palette Animation of a Ball Across the Screen}

{Create the Ball Animation Window using Animated colors.}
 PROCEDURE MakeBall;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 S : str255;
 count : integer;
 BEGIN
 SetRect(tempRect, 100, 120, 400, 420);
 GetIndString(S, StrID, 5);
 MyWindow[ballW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);
 MyPalette[ballW] := NewPalette(19, NIL, pmAnimated, 0);

 SetRGB(tempRGB, $FFFF, $FFFF, $FFFF);
 SetEntryColor(MyPalette[ballW], 0, tempRGB);
 SetRGB(tempRGB, 0, 0, 0);
 SetEntryColor(MyPalette[ballW], 1, tempRGB);
 tempRGB.blue := $FFFF;
 SetEntryColor(MyPalette[ballW], 2, tempRGB);
 tempRGB.blue := 0;
 tempRGB.red := $FFFF;
 FOR count := 3 TO 18 DO
 SetEntryColor(MyPalette[ballW], count, tempRGB);
 SetPalette(MyWindow[ballW], MyPalette[ballW], true);
 END;

{Draw the Balls in the window using PmForeColor.}
 PROCEDURE DoBallUpdate;
 VAR
 R : rect;
 count : integer;
 BEGIN
 SetRect(R, 0, 0, 10000, 10000);
 PmForeColor(18);
 PaintRect(R);

 FOR count := 2 TO 17 DO
 BEGIN
 R.top := 16 * count;
 R.left := R.top;
 R.bottom := R.top + 16;
 R.right := R.bottom;
 PmForeColor(count);
 PaintOval(R);
 END;
 END;

{Animate the Ball through the window using AnimateEntry.}
 PROCEDURE AnimBall;
 VAR
 R, B : RGBcolor;
 time, count, temp : integer;
 BEGIN
 SetRGB(R, $FFFF, 0, 0);
 SetRGB(B, 0, 0, $FFFF);
 FOR time := 1 TO 10 DO
 FOR count := 2 TO 17 DO
 BEGIN
 IF count = 17 THEN
 temp := 2
 ELSE
 temp := count + 1;
 DoDelay(1);
 AnimateEntry(MyWindow[ballW], count, R);
 AnimateEntry(MyWindow[ballW], temp, B);
 END;
 END;

{******************** Shape ********************}

{ Given 3 arbitrary regions (Black/White images), calculates how to draw 
the window so that the images can be shuffled through quickly. }

{Create the Shape Animation Window}
{(‘pltt’ is automatically loaded in).}
 PROCEDURE MakeShape;
 BEGIN
 MyWindow[shapeW] := GetNewCWindow(ShapeID, NIL, POINTER(-1));
 END;

{Draws Shape.  aRgn,bRgn,cRgn are the arbitrary images.}
 PROCEDURE DoShapeUpdate;
 VAR
 aRgn, bRgn, cRgn, TempRgn : RgnHandle;
 count : INTEGER;
 TempRect : Rect;
 PROCEDURE DrawTriag (h, v : INTEGER);
 BEGIN
 MoveTo(h + 25, v);
 Line(-25, 50);
 Line(50, 0);
 Line(-25, -50);
 END;
 BEGIN
 aRgn := NewRgn;
 OpenRgn;
 SetRect(tempRect, 10, 10, 60, 60);
 FrameOval(tempRect);
 SetRect(tempRect, 120, 10, 170, 60);
 FrameRect(tempRect);
 SetRect(tempRect, 120, 80, 170, 130);
 FrameRect(tempRect);
 SetRect(tempRect, 190, 10, 240, 60);
 FrameRect(tempRect);
 SetRect(tempRect, 190, 80, 240, 130);
 FrameRect(tempRect);
 SetRect(tempRect, 10, 80, 110, 81);
 FOR count := 1 TO 25 DO
 BEGIN
 FrameRect(tempRect);
 OffSetRect(tempRect, 0, 2);
 END;
 CloseRgn(aRgn);
 bRgn := NewRgn;
 OpenRgn;
 SetRect(tempRect, 35, 10, 85, 60);
 FrameOval(tempRect);
 SetRect(tempRect, 120, 10, 170, 60);
 FrameOval(tempRect);
 SetRect(tempRect, 120, 80, 170, 130);
 FrameOval(tempRect);
 SetRect(tempRect, 190, 10, 240, 60);
 FrameOval(tempRect);
 SetRect(tempRect, 190, 80, 240, 130);
 FrameOval(tempRect);
 SetRect(tempRect, 10, 80, 11, 130);
 FOR count := 1 TO 25 DO
 BEGIN
 FrameRect(tempRect);
 OffSetRect(tempRect, 4, 0);
 END;
 CloseRgn(bRgn);
 cRgn := NewRgn;
 OpenRgn;
 SetRect(tempRect, 60, 10, 110, 60);
 FrameOval(tempRect);
 DrawTriag(120, 10);
 DrawTriag(120, 80);
 DrawTriag(190, 10);
 DrawTriag(190, 80);
 MoveTo(60, 80);
 Line(50, 25);
 Line(-50, 25);
 Line(-50, -25);
 Line(50, -25);
 CloseRgn(cRgn);
 TempRgn := NewRgn;

{This Region will always be Red (Background)}
 PmForeColor(0);
 SetRect(tempRect, -32000, -32000, 32000, 32000);
 PaintRect(tempRect);

{This region will start Blue, change Red, stay Red}
 PmForeColor(1);
 DiffRgn(aRgn, bRgn, TempRgn);
 DiffRgn(TempRgn, cRgn, TempRgn);
 PaintRgn(TempRgn);

{This region will be Red,Blue,Red}
 PmForeColor(2);
 DiffRgn(bRgn, aRgn, TempRgn);
 DiffRgn(TempRgn, cRgn, TempRgn);
 PaintRgn(TempRgn);

{This region will be Blue,Blue,Red}
 PmForeColor(3);
 SectRgn(aRgn, bRgn, TempRgn);
 PaintRgn(TempRgn);

{This region will be Red,Red,Blue}
 PmForeColor(4);
 DiffRgn(cRgn, aRgn, TempRgn);
 DiffRgn(TempRgn, bRgn, TempRgn);
 PaintRgn(TempRgn);

{This region will be Blue,Red,Blue}
 PmForeColor(5);
 SectRgn(aRgn, cRgn, TempRgn);
 PaintRgn(TempRgn);

{This region will be Red,Blue,Blue}
 PmForeColor(6);
 SectRgn(bRgn, cRgn, TempRgn);
 PaintRgn(TempRgn);

{This Region will always be Blue}
 PmForeColor(7);
 SectRgn(aRgn, bRgn, TempRgn);
 SectRgn(cRgn, TempRgn, TempRgn);
 PaintRgn(TempRgn);

 DisposeRgn(aRgn);
 DisposeRgn(bRgn);
 DisposeRgn(cRgn);
 DisposeRgn(TempRgn);
 END;

{Animate the Shape image using AnimatePalette/CLUT resouces.}
 PROCEDURE AnimShape;
 VAR
 count : INTEGER;
 MyCLUT : ARRAY[1..3] OF CTabHandle;
 BEGIN
 FOR count := 1 TO 3 DO
 MyCLUT[count] := GetCTable(count + 300);

 DoDelay(1);
 AnimatePalette(MyWindow[shapeW], MyCLUT[2], 0, 0, 8);
 DoDelay(60);
 AnimatePalette(MyWindow[shapeW], MyCLUT[3], 0, 0, 8);
 DoDelay(60);
 AnimatePalette(MyWindow[shapeW], MyCLUT[1], 0, 0, 8);
 DoDelay(50);

 FOR count := 1 TO 5 DO
 BEGIN
 DoDelay(10);
 AnimatePalette(MyWindow[shapeW], MyCLUT[2], 0, 0, 8);
 DoDelay(10);
 AnimatePalette(MyWindow[shapeW], MyCLUT[3], 0, 0, 8);
 DoDelay(10);
 AnimatePalette(MyWindow[shapeW], MyCLUT[1], 0, 0, 8);
 END;

 DoDelay(60);

 FOR count := 1 TO 5 DO
 BEGIN
 DoDelay(1);
 AnimatePalette(MyWindow[shapeW], MyCLUT[2], 0, 0, 8);
 DoDelay(1);
 AnimatePalette(MyWindow[shapeW], MyCLUT[3], 0, 0, 8);
 DoDelay(1);
 AnimatePalette(MyWindow[shapeW], MyCLUT[1], 0, 0, 8);
 END;

 FOR count := 1 TO 3 DO
 DisposCTable(MyCLUT[count]);
 END;

{******************** Rainbow ********************}

{Demonstrates the Rainbow Effect (Rotating Circle,}
{Moving Bands and Expanding Circle).}
{}
{{Create the Rainbow Animation Window.}
 PROCEDURE MakeRainbow;
 VAR
 tempRect : rect;
 tempRGB : RGBColor;
 S : str255;
 tempHSV : HSVColor;
 count : integer;
 BEGIN
 SetRect(tempRect, 50, 160, 590, 400);
 GetIndString(S, StrID, 6);
 MyWindow[rainbowW] := NewCWindow(NIL, tempRect, S, true, noGrowDocProc, 
POINTER(-1), true, 0);
 MyPalette[rainbowW] := NewPalette(122, NIL, pmAnimated, 0);

 SetRGB(tempRGB, $FFFF, $FFFF, $FFFF);
 SetEntryColor(MyPalette[rainbowW], 0, tempRGB);
 SetRGB(tempRGB, 0, 0, 0);
 SetEntryColor(MyPalette[rainbowW], 1, tempRGB);
 tempHSV.saturation := $FFFF;
 tempHSV.value := $FFFF;
 FOR count := 1 TO 120 DO
 BEGIN
 tempHSV.hue := ($0FFFF * count) DIV 120;
 HSV2RGB(tempHSV, tempRGB);
 SetEntryColor(MyPalette[rainbowW], count + 1, tempRGB);
 END;
 SetPalette(MyWindow[rainbowW], MyPalette[rainbowW], true);
 END;

{Draws the rays of the Rainbow.}
 PROCEDURE DoRainbowUpdate;
 VAR
 count : INTEGER;
 tempRect, CRect : Rect;
 BEGIN
 SetRect(tempRect, 0, 0, 480, 240);
 PmForeColor(0);
 PaintRect(tempRect);
 SetRect(tempRect, 0, 0, 240, 240);
 SetRect(CRect, 300, 0, 540, 240);
 FOR count := 0 TO 119 DO
 BEGIN
 PmForeColor(count + 2);
 PaintArc(tempRect, count * 3, 3);

 MoveTo(240, count);
 Line(60, 0);
 MoveTo(240, count + 120);
 Line(60, 0);

 PaintOval(CRect);
 InsetRect(CRect, 1, 1);
 END;
 END;

{Rotates all the entries in the CLUT one position.}
 PROCEDURE BumpCTEntry (C : CTabHandle);
 VAR
 tempRGB : RGBcolor;
 MyCT : MyCTabHandle;
 count : INTEGER;
 BEGIN
 MyCT := POINTER(C);
 WITH MyCT^^ DO
 BEGIN
 CopyRGB(ctTable[0].rgb, tempRGB);

 FOR count := 1 TO ctSize DO
 CopyRGB(ctTable[count].rgb, ctTable[count - 1].rgb);

 CopyRGB(tempRGB, ctTable[ctSize].rgb);
 END;
 END;

{Animate the Rainbow using AnimatePalette.  This one}
{creates and manilpulates it’s CLUT directly.}
 PROCEDURE AnimRainbow;
 VAR
 count : INTEGER;
 tempRGB : RGBColor;
 tempCT : CTabHandle;
 BEGIN
 tempCT := NewCT(120);
 FOR count := 1 TO 120 DO
 BEGIN
 GetEntryColor(MyPalette[rainbowW], count + 1, tempRGB);
 SetCTEntry(tempCT, count - 1, tempRGB.red, tempRGB.green, tempRGB.blue);
 END;

 FOR count := 1 TO 360 DO
 BEGIN
 BumpCTEntry(tempCT);
 DoDelay(1);
 AnimatePalette(MyWindow[rainbowW], tempCT, 0, 2, 120);
 END;
 DisposHandle(Handle(tempCT));
 END;

{******************** Fade ********************}

{Demonstrates the Fade effect}

{Create the Fade Animation Window (uses Palette resource).}
 PROCEDURE MakeFade;
 BEGIN
 MyWindow[fadeW] := GetNewCWindow(FadeID, NIL, POINTER(-1));
 END;

{Draws Fade window}
 PROCEDURE DoFadeUpdate;
 VAR
 tempRect : Rect;
 count : INTEGER;
 BEGIN
 PmForeColor(0);
 SetRect(tempRect, -32000, -32000, 32000, 32000);
 PaintRect(tempRect);

 FOR count := 1 TO 4 DO
 BEGIN
 PmForeColor(count);
 SetRect(tempRect, ((count - 1) * 100) + 10, 10, (count * 100) - 10, 
90);
 PaintOval(tempRect);
 END;

 FOR count := 5 TO 8 DO
 BEGIN
 PmForeColor(count);
 SetRect(tempRect, ((count - 5) * 100) + 10, 110, ((count - 4) * 100) 
- 10, 190);
 PaintOval(tempRect);
 END;
 END;

{Animate the Fade.}
 PROCEDURE AnimFade;
 CONST
 FadeStep = 60;
 VAR
 count, E : INTEGER;
 Buf, Inc, Start : ARRAY[0..8] OF RGBColor;
 BEGIN
 SetRGB(Buf[0], -1, -1, -1);
 SetRGB(Buf[1], 0, 0, 0);
 SetRGB(Buf[2], -1, 0, 0);
 SetRGB(Buf[3], 0, -1, 0);
 SetRGB(Buf[4], 0, 0, -1);
 SetRGB(Buf[5], 0, -1, -1);
 SetRGB(Buf[6], -1, 0, -1);
 SetRGB(Buf[7], -1, -1, 0);
 SetRGB(Buf[8], 30000, 30000, 30000);
 FOR E := 0 TO 8 DO
 BEGIN
 CopyRGB(Buf[E], Start[E]);
 UnSignedDiv(Buf[E].Red, FadeStep, Inc[E].Red);
 UnSignedDiv(Buf[E].Green, FadeStep, Inc[E].Green);
 UnSignedDiv(Buf[E].Blue, FadeStep, Inc[E].Blue);
 END;

 FOR count := FadeStep - 1 DOWNTO 1 DO
 BEGIN
 FOR E := 0 TO 8 DO
 BEGIN
 DoDelay(1);
 UnSignedSub(Buf[E].Red, Inc[E].Red);
 UnSignedSub(Buf[E].Green, Inc[E].Green);
 UnSignedSub(Buf[E].Blue, Inc[E].Blue);
 AnimateEntry(MyWindow[fadeW], E, Buf[E]);
 END;
 END;

 DoDelay(1);
 FOR E := 0 TO 8 DO
 BEGIN
 SetRGB(Buf[E], 0, 0, 0);
 AnimateEntry(MyWindow[fadeW], E, Buf[E]);
 END;

 DoDelay(90);

 FOR count := 1 TO FadeStep - 1 DO
 BEGIN
 FOR E := 0 TO 8 DO
 BEGIN
 DoDelay(1);
 UnSignedAdd(Buf[E].Red, Inc[E].Red);
 UnSignedAdd(Buf[E].Green, Inc[E].Green);
 UnSignedAdd(Buf[E].Blue, Inc[E].Blue);
 AnimateEntry(MyWindow[fadeW], E, Buf[E]);
 END;
 END;

 DoDelay(1);
 FOR E := 0 TO 8 DO
 AnimateEntry(MyWindow[fadeW], E, Buf[E]);
 END;

END.


UNIT PalFunGlobals;

INTERFACE

 USES
 ROM85, ColorQuickDraw, ColorWindowMgr, PaletteMgr, PickerIntf;

{ Global Constants }
 CONST

 appleM = 301;   {Menu ID Constants}
 fileM = 302;
 editM = 303;
 winM = 304;
 menuCount = 304;

 numWindows = 9; {Window ID Constants}
 redW = 1;
 greenW = 2;
 blueW = 3;
 curW = 4;
 ballW = 5;
 shapeW = 6;
 rainbowW = 7;
 fadeW = 8;

 StrID = 300;    {Various Resource ID Constants}
 AlertID = 300;
 FadeID = 300;
 ShapeID = 301;

 MaxCT = 2048;  {Max Numbers in CLUT (usually only 256}

 ColorInc = $200;{Amount difference between Color rings}
 ColorStart = $FE00; {Start Color Rings}

{My Version of the CLUT data structure (used for stuffing values).}
 TYPE
 MyCSpecArray = ARRAY[0..MaxCT] OF ColorSpec;

 MyCTabHandle = ^MyCTabPtr;
 MyCTabPtr = ^MyColorTable;
 MyColorTable = RECORD
 ctSeed : LONGINT;
 ctFlag : INTEGER;
 ctSize : INTEGER;
 ctTable : MyCSpecArray;
 END;

{Standard Variables (Menus, Window Pointer, Window Record)}
{And Palette Handles (1 per Window). }
 VAR
 myMenus : ARRAY[appleM..menuCount] OF MenuHandle;
 MyWindow : ARRAY[1..numWindows] OF WindowPtr;
 MyPalette : ARRAY[1..numWindows] OF PaletteHandle;
 dragRect : Rect;
 doneFlag : BOOLEAN;

IMPLEMENTATION

END.
* PalFun.r - Resources for PalFun
* by Steve Sheets for MacTutor
*
PalFun.RSRC
????????

Type PASS = STR 
  ,0    ;;0 by convention
Palette Animation by Steve Sheets \A9 MacTutor 1988

Type BNDL
  ,128
  PASS  0
  ICN# 1
  0 128
  FREF 2
  0 128

Type FREF
  ,128
  APPL  0 ;; local id 0 for icon list

Type SIZE = GNRL
 ,-1
.I
16384 ;; $4000 = bit 14 set
.L
192000  ;; recomended
.L
128000  ;; minimum

Type ALRT
  ,500 (0)
40 170 140 470 
500
4444 

Type DITL
  ,500 (0)
2

Button 
60 120 80 180 
OK

staticText Disabled
20 20 40 280 
^0

Type ALRT
  ,300 (0)
40 170 200 470 
300
4444 

Type DITL
  ,300 (0)
5

Button 
120 120 140 180 
OK

staticText Disabled
20 73 39 227 
PalFun by Steve Sheets

staticText Disabled
40 58 59 242 
Designed for MacTutor 3/88

staticText Disabled
60 15 79 285 
A demonstration of the Palette Manager

staticText Disabled
80 74 99 226 
and Palette Animation.

Type MENU
  ,301 (0)
\14
About PalFun...
(-

Type MENU
  ,302 (0)
File
Animate/A
Close/W
(-
Quit/Q

Type MENU
  ,303 (0)
Edit
Undo
(-
Cut
Copy
Paste
Clear

Type MENU
  ,304 (0)
Window
Red
Green
Blue
Current Color
Ball
Shape
Rainbow
Fade

Type STR#
  ,300 (0)
6
Red
Green
Blue
Current Colors
Ball
Rainbow

Type WIND
  ,308 (0)
Face
120 140 420 520 
Visible GoAway
0 
0 

Type WIND
  ,300 (0)
Fade
100 100 300 500 
Visible GoAway
0 
0 

Type WIND
  ,301 (0)
Shape
140 120 280 370 
Visible GoAway
0 
0 

Type pltt = GNRL
 , 300
.H
0009 0000 0000 0000 0000 0000 0000 0000 
FFFF FFFF FFFF 0004 0000 0000 0000 0000
0000 0000 0000 0004 0000 0000 0000 0000
FFFF 0000 0000 0004 0000 0000 0000 0000
0000 FFFF 0000 0004 0000 0000 0000 0000
0000 0000 FFFF 0004 0000 0000 0000 0000
0000 FFFF FFFF 0004 0000 0000 0000 0000
FFFF 0000 FFFF 0004 0000 0000 0000 0000
FFFF FFFF 0000 0004 0000 0000 0000 0000
7530 7530 7530 0004 0000 0000 0000 0000

Type pltt = GNRL
 , 301
.H
0008 0000 0000 0000 0000 0000 0000 0000
FFFF 0000 0000 0004 0000 0000 0000 0000
0000 0000 FFFF 0004 0000 0000 0000 0000
FFFF 0000 0000 0004 0000 0000 0000 0000
0000 0000 FFFF 0004 0000 0000 0000 0000
FFFF 0000 0000 0004 0000 0000 0000 0000
0000 0000 FFFF 0004 0000 0000 0000 0000
FFFF 0000 0000 0004 0000 0000 0000 0000
0000 0000 FFFF 0004 0000 0000 0000 0000

Type clut = GNRL
 , 301
.H
0000 0000 0000 0007 
0000 FFFF 0000 0000 
0001 0000 0000 FFFF
0002 FFFF 0000 0000 
0003 0000 0000 FFFF 
0004 FFFF 0000 0000
0005 0000 0000 FFFF
0006 FFFF 0000 0000
0007 0000 0000 FFFF

Type clut = GNRL
 , 302
.H
0000 0000 0000 0007
0000 FFFF 0000 0000
0001 FFFF 0000 0000
0002 0000 0000 FFFF
0003 0000 0000 FFFF
0004 FFFF 0000 0000
0005 FFFF 0000 0000
0006 0000 0000 FFFF
0007 0000 0000 FFFF

Type clut = GNRL
 , 303
.H
0000 0000 0000 0007
0000 FFFF 0000 0000
0001 FFFF 0000 0000
0002 FFFF 0000 0000
0003 FFFF 0000 0000
0004 0000 0000 FFFF
0005 0000 0000 FFFF
0006 0000 0000 FFFF
0007 0000 0000 FFFF

Type ICN# = GNRL
  ,128 (4)  ;; The Appl. Icon
.H
FFFFFFFF C0000003 BFFFFFFD B000000D
AFFFFFF5 AC000035 ABFFFFD5 AA000055
AA7E0055 AA810055 AA810855 AA811455
AA812255 AA814155 AA7E3E55 AA000055
AA000055 AA007E55 AA008155 AA108155
AA288155 AA448155 AA828155 AA7C7E55
AA000055 ABFFFFD5 AC000035 AFFFFFF5
B000000D BFFFFFFD C0000003 FFFFFFFF
*
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

BetterTouchTool 3.401 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
Vienna 3.5.6 :e12c952d: - RSS and Atom n...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
WhatsApp 2.2031.5 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Day One 4.16 - Maintain a daily journal.
Day One is an easy, great-looking way to use a journal / diary / text-logging application. Day One is well designed and extremely focused to encourage you to write more through quick Menu Bar entry,... Read more
VMware Fusion 11.5.6 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more
Alfred 4.1 - Quick launcher for apps and...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
Dashlane 6.2032.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
Skype 8.63.0.76 - Voice-over-internet ph...
Skype is a telecommunications app that provides HD video calls, instant messaging, calling to any phone number or landline, and Skype for Business for productive cooperation on the projects. This... Read more
Mellel 5.0.3 - The word processor for sc...
Mellel is the leading word processor for OS X and has been widely considered the industry standard for long form documents since its inception. Mellel focuses on writers and scholars for technical... Read more
A Better Finder Rename 11.20 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more

Latest Forum Discussions

See All

Motorball is a car football game from No...
A few years back Noodlecake Studios announced that they would be dipping in the multiplayer gaming realm with two different games. The first of those, Golf Blitz, released a while back and has proven to be very popular. Now, the second has arrived... | Read more »
SINoALICE's latest update introduce...
SINoALICE's latest update has now arrived, adding several fan-favourite characters from popular RPG series NieR. Young Nier, Kaine, and Young Emil are available in-game as part of a limited-time crossover event set to run until August 20th. [Read... | Read more »
Rocat Jumpurr is an intense roguelite pl...
Rocat Jumpurr is a roguelite platformer from developer Mousetrap Games. You might already be familiar with it if you follow the Big Indie Pitch, where it won first place during this year's Pocket Gamer Connects London competition. Following its... | Read more »
PUBG Mobile's Play As One campaign...
Back in mid-July, we reported that PUGB Mobile had teamed up with Direct Relief to help raise money for the charity's COVID-19 response project. It focused on an in-game running challenge for players, which lead to the PUBG Mobile donating $2... | Read more »
Marvel Contest of Champions' latest...
Marvel Contest of Champions' latest motion comic has arrived, and it shows off new fighters Air-Walker and Dragon Man. Both characters are set to arrive in-game this month. [Read more] | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Global Spy is an intriguing 2D spy sim f...
Developer Yuyosoft Innovations' Global Spy launched last month for iOS and Android, though if you missed it at the time, we're here to tell you why it's well worth a go. This one's all about international espionage, tracking down elusive spies,... | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Hyena Squad is sci-fi turn-based strateg...
Wave Light Games has just revealed its latest release, Hyena Squad, a turn-based RPG set in a space station infested by gross aliens and the living dead. The announcement was first reported on by Touch Arcade. [Read more] | Read more »
Idle Guardians: Never Die is a pixel art...
SuperPlanet has been fairly prolific with game releases so far this year with both Evil Hunter Tycoon and Lucid Adventure releasing earlier this year. Now, they've released another idle RPG called Idle Guardians: Never Die, which you can download... | Read more »

Price Scanner via MacPrices.net

Woot offers numerous 2018-2020 MacBook Pros a...
Amazon-owned Woot has many open-box return MacBook Airs and MacBook Pros available today at prices starting at $879. Shipping is free for Prime members. Here’s what they have as of this post, and... Read more
Apple restocks refurbished 2020 13″ MacBook A...
Apple has restocked Certified Refurbished 2020 13″ MacBook Airs starting at only $849 and up to $200 off the cost of new Airs. Each MacBook features a new outer case, comes with a standard Apple one-... Read more
Apple restocks clearance 2019 13″ 2.4GHz MacB...
Apple has restocked Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros starting at $1359 and up to $560 off original MSRP. Apple’s one-year warranty is included, shipping is free,... Read more
Apple restocks refurbished iPhone XR models s...
Apple has restocked Certified Refurbished, unlocked, iPhone XR models in the refurbished section of their online store starting at $539. Each iPhone comes with Apple’s standard one-year warranty,... Read more
Price drops! $100-$200 off clearance 27″ 5K i...
B&H Photo has dropped prices on clearance, previous-generation 27″ 5K iMacs by up to $200 off Apple’s original MSRP: – 27″ 3.0GHz 6-Core 5K iMac: $1699 $100 off original MSRP – 27″ 3.1GHz 6-Core... Read more
Woot offers Apple Watch and iPhone models fro...
Amazon-owned Woot has refurbished Apple Watch and iPhone models available from $99-$749 through August 6th. According to Woot, the items may show some wear, but they have all been fully tested and... Read more
Apple’s Phil Schiller Steps Down As SVP OF Wo...
NEWS: 08.05.20 – Former Apple senior Vice President of worldwide marketing, Phil Schiller, is stepping down from his long time role at the company in order to focus on spending more time with family... Read more
Expercom offers $320 discount on the 6-core 1...
Apple reseller Expercom has the Silver 16″ 6-core MacBook Pro on sale for a limited time for $2079 shipped. Their price is $320 off Apple’s MSRP for this model, and it’s the cheapest price currently... Read more
Apple announces Education pricing for new 202...
Purchase a new 2020 iMac or iMac Pro at Apple using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email... Read more
Apple reseller Expercom offers $256 discount...
Expercom has Apple’s new 2020 10-core iMac Pro available for order and on sale for $4743 shipped. Their price is $256 off Apple’s MSRP for this new model, and it’s the cheapest price we’ve seen so... Read more

Jobs Board

Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 3 days ago Requisition # 122305 Sign Up Read more
Executive Team Leader GM Sales (Assistant Man...
…(Assistant Manager General Merchandise and Operations) - Apple Valley, CaliforniaApply NowJob ID:R0000082364job family:Store Managementschedule:Full Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 2 days ago Requisition # 122305 Sign Up Read more
Part-time Geek Squad *Apple* Consultation P...
**770829BR** **Job Title:** Part-time Geek Squad Apple Consultation Professional-Store 384(Ithaca) **Job Category:** Store Associates **Store Number or Department:** Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.