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

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.