Animate PICS
Volume Number: 7
Issue Number: 3
Column Tag: Developer Forum
Animating PICS 
By Steven Sheets, Herdon, VA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Animating PICS
In the spring of 1988, at the Apple Developer Conference, a standard was agreed
upon by several animation and graphic developers that would allow each developer to
import and export simple animation sequences. (An animation sequence is a group of
pictures that when shown sequentially create an animated image). Since that time,
most animation developers have either implemented this standard in their programs, or
have promised to have it implemented in the near future. The name of this standard is
PICS.
While obviously animation packages need to know how to manipulate PICS files,
any Macintosh program can use these files, and display the animated images. The file
format is extremely simple and the code needed to animate the images is easy to write
and understand.
The main advantage in using PICS files is that while most Macintosh developers
know how to animate a bit or pixel map across the screen, they are usually lousy
artists. The images they create are often extremely crude, and fail to have a ‘finished’
look. This is e specially true if the programmer has to create the bit or pixel map by
hand and enter the image in as hex data. Instead of these amateurish attempts, a
developer can now create (or have his friend the graphic artist create for him) a
professional looking animation sequence using professional animation packages and
tools. Once the sequence is completed, the programmer can then import the created
PICS data into his program, and animate it himself.
This article will cover how to animate PICS files. First the file format will be
described. Then a sample program will be given that displays PICS files. The program
will include PICSUnit, a unit that reads, disposes and draws PICS data structures.
PICS File Format
The file type for a PICS file is ‘PICS’. The file creator should be set to the
creator type of whatever animation package exported the data. It is recommended that
the following icon be used:
The hex data for this icon is given in the example program (even though the
example does not create PICS files).
The PICS file itself consists of one or more resources. The file’s data fork is not
used by the PICS format. PICS file format is an open ended format in that a developer
(or group of developers) can add or use as much information as he wishes. Only one
resource is required, although that would make a very short animation sequence. If
another application duplicates a PICS file, all resources should be copied. A resource
that is not used by one animation package might be used by another.
The single required resource is a picture (resource type ‘PICT’, id number
128). This picture is the initial setting of the animation sequence. All animation will
be done inside the rectangle defined by this picture’s picFrame rectangle. Normally a
picture’s picFrame is set so that top and left are 0 (zero), while the bottom and right
are the height and width of the picture. But be careful! This is not always the case; the
top left can be any value. Whatever the top left is, the following drawing is done in that
coordinate system.
Once the first frame is defined, each new frame of animation is defined in a new
PICT resource. The resource ID of this new frame is sequentially in order after the
preceding frame. Thus if the PICS file contains 10 frames, there would be 10 PICT
resource in the file, number 128 through 137.
The PICT resource for all the frames after the first one need not be an entire
image. The picFrame of the PICT resource defines exactly how the frame is displayed.
In all cases, the picFrame of the new PICT should never be larger, or outside, than the
picFrame of the first PICT.
First, the picture handle can contain an empty picFrame (usually coordinate
0,0,0,0, but really any coordinates where right is less than or equal to left or bottom
is less than or equal to top). In this case, no new animation is displayed for this frame.
Such a frame is often used to pause the animation.
Second, a frame can contain an image the exact size, and coordinates of the first
PICT. In this case, the entire image is drawn using the same coordinates and rectangle
as the first PICT.
Finally, a frame can have only the changes from the previous frame. In that
case, the picFrame of the PICT resource would be smaller than the size of the entire
image. The picture should only be drawn in the rectange given in the picFrame (again
using the coordinate system of the first PICT). The PICT for such a frame is often called
a delta picture (PICT containing the difference). The advantage of this type of frame is
memory size. If an animation image is large, but the difference from one frame to
another is smaller, a series of delta pictures is much smaller in byte size than the
same number of pictures of the entire image.
In addition to the animation frame, there was one other optional resource. A
resource of type ‘INFO’ and id 128 provides additional information about the animation
sequence. The following is the resource Pascal data structure. Remember that, like the
file format, this structure may be expanded in the future with additional fields. The
resource handle may be longer than this structure:
{1}
TYPE TPICSInfoRec = RECORD
BWColor: INTEGER; {0 = Black & White, 1 = Color}
Depth: INTEGER; {1,2,4,8,16 pixel depth}
Speed: INTEGER; {1..200 frames per sec}
{ else negative seconds per frame}
Version: INTEGER; {0 currently}
Creator: ResType; {original creator signature}
Largest: LongInt; {if non-zero, largest picture size}
END;
TPICSInfoPtr = ^TPICSInfoRec;
TPICSInfoHdl = ^TPICSInfoPtr;
The first field of the structure (BWColor) tells if the animation sequence is in
black & white or color. If the sequence is in color, the next field (depth) defines the
depth (in pixels) of the image (or in other words, the numbers of colors 1, 4, 16,
256, or more). The next field (Depth) defines the speed that the animation images are
displayed on the screen. If the number is 1 to 200, then that number is the number of
frames per second. Remember, due to the flicker rate of most Mac screens (around 60
frames of sec) animation of more than 60 frames per sec is impossible. If the Speed
number is negative, than the absolute value of Speed is the number of seconds between
frames. The Version field defines the version of the file format ( currently 0), while
the Creator field shows the creator of the data (regardless of what the file type creator
was set to). The last field, Largest, gives the size of the largest frame (PICT resource)
in bytes. This is an optional field, and may contain the value zero.
The PICS format does have its deficiencies. The format could have been expanded
to allow better use of memory. For example, the pictures should have been able to be
used in more than one frame. Many times, animation sequences return to the same
image over and over again. The PICS format requires each frame to have it’s own
picture, even if that image is used in more than one spot. Also, the drawing of the delta
frames could have used an X & Y position value. This would have allowed the same
picture to be animated across the screen. The PICS format requires multiple pictures
even if the image is the same but in a different position.
There are a couple more items that could have been added to the format. The
amount of time between two given frames should have been variable. Finally, a color
table could have been provided for color animation sequences, that would give the
optimal colors for the sequence.
Example Program
The example program demonstrates how code can use PICSUnit, a Pascal unit that
manipulates PICS. PICSUnit consists of 3 calls; ReadPICS (which reads a PICS file into
memory and creates a PICS data structure), DisposPICS (which disposes of a PICS data
structure in memory), and DrawPICS (which animates the PICS data structure in the
current grafport at a given X & Y position). Notice that a PICS data structure in
memory is a complex handle (ie. handle that contains other handles in it). Be sure to
use the DisposPICS call, or a program will start to use up memory with unreferenced
handles.
The DrawPICS routine is also passed two flags to indicate how many times to
draw the animation (once or continuous) and also whether or not the animation should
stop if the user presses a key or the mouse. Be careful! If the call is set for continuous
animation without stopping when the user prompts, the program will go into a endless
loop of animation (very pretty, but not a very good user interface).
What Next?
Obviously the example program could be improved. The PICSUnit could be
rewritten for performance, at the cost of more memory. Drawing with a PICT handle
can be a time consuming action; flickering can also occur. If offscreen grafports (and
color grafports) were used, the images could be drawn there. Then when it is time for
a new frame to be animated, Copybits could be used to move the image onto the screen.
For that matter, multiple offscreen grafports could be created for each frame
(assuming enough memory), thus speeding up the animation and eliminating flickers.
The example program that uses the PICSUnit is not the end-all utility it could be.
A Public Domain PICS Editor would be very useful (any one interested?). Such an
Editor would show each frame of animation, and allow the user to step through the
frames any way they wished.
Other PICS utilities could be useful. Perhaps a program that takes a PICS file
that consists of PICTs all the same size (regardless of how much of the image changes),
and calculates exactly the portion of the image that changes from one frame to another.
It would then output a PICS file consisting of these delta PICTs. Such a utility would
greatly reduce the size of a PICS file.
As always, any comment or ideas or suggestions are always appreciated.
Listing: PICSUnit.p
{}
{ PICS Unit - Steve Sheets}
{}
{ This Unit provides the Interface to the PICS data structure}
{ as well as the procedures to create, dispose and draw the}
{ PICS information.}
unit PICSUnit;
interface
const
kPICStype = 'PICS'; {File type of PICS}
kINFOtype = 'INFO';
{Resource type of PICS information resource}
{ Interface for the optional information handle and the PICS handle
which contains 1 or more Pictures and the information handle. Notice
that the PICS handle is a variable length handle based on the number
of frames.}
TPICSInfoRec = record
BWColor: INTEGER; {0 = Black & White, 1 = Color}
Depth: INTEGER; {1,2,4,8,16 pixel depth}
Speed: INTEGER; {1..200 frames per sec, else negative
seconds per frame}
Version: INTEGER; {0 currently}
Creator: ResType; {original creator signature}
Largest: LongInt; {if non-zero, largest picture size}
end;
TPICSInfoPtr = ^TPICSInfoRec;
TPICSInfoHdl = ^TPICSInfoPtr;
TPICSRec = record
NumFrames, DimH, DimV: INTEGER;
PICSInfoHdl: TPICSInfoHdl;
Frame: array[1..1] of PicHandle;
end;
TPICSPtr = ^TPICSRec;
TPICSHdl = ^TPICSPtr;
{ Given a file name and volume reference number, try to read the
PICS file at that location. If successful, return noErr in the
function and the information in thePICS parameter. If there was a
problem, return the error number in the function, and thePICS is set
to NIL.}
function ReadPICS (theFileName: Str255;
theVRefNum: INTEGER;
var thePICS: TPICSHdl): OSErr;
{ Given thePICS data, dispos of all the handles and data
structures.}
procedure DisposePICS (thePICS: TPICSHdl);
{ Given thePICS data, and an V & H position to draw at, draw the
animation. The Loopflag tells the procedure to either loop the
animation continously (TRUE), or only draw once time (FALSE). The
ScanKeyFlag tells the procedure if it should look to see if someone
has pressed a key during animation. If so, the procedure is stopped
at that point. Notice that it is dangerous to have Loopflag set TRUE
and ScanKeyFlag set FALSE (infinite loop time).}
procedure DrawPICS (thePICS: TPICSHdl;
HPos, VPos: INTEGER;
LoopFlag, ScanKeyFlag: BOOLEAN);
implementation
{ Simple utility function, given number of frames, size of the
TPICSRec record in bytes.}
function PICSsize (theNumFrames: INTEGER): INTEGER;
begin
PICSsize := (theNumFrames * 4) + 10;
end;
function ReadPICS (theFileName: Str255;
theVRefNum: INTEGER;