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 months 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 months 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 months 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 months 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 its 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 its 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 its 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 its 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 its 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 its 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 its 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 its 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 balls 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 its 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 balls 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 its 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 rockets jet or movement of an aliens 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 programmers 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 entrys 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 entrys 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 its 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 its RGB value. Instead it displays the CLUTs color at its 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 whats 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 pixels 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 thats 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 its 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