Remove Frame

A Quick Introduction to IFF

Jerry Morrison, Electronic Arts

IFF is the Amiga-standard "Interchange File Format", designed to work across 
many machines.

Why IFF?

Did you ever have this happen to your picture file?

   You can't load it into another paint program.
   You need a converter to adopt to "ZooPaint" release 2.0 or a new 
      hardware feature.
   You must "export" and "import" to use it in a page layout program.
   You can't move it to another brand of computer.

What about interchanging musical scores, digitized audio, and other data?  It 
seems the only thing that does interchange well is plain ASCII text files.

It's inexcusable. And yet this is "normal" in MS-DOS.

What is IFF?

IFF, the "Interchange File Format" standard, encourages multimedia interchange 
between different programs and different computers.  It supports long-lived, 
extensible data.  It's great for composite files like a page layout file that 
includes photos, an animation file that includes music, and a library of 
sound effects.

IFF is a 2-level standard.  The first layer is the "wrapper" or "envelope" 
structure for all IFF files.  Technically, it's the syntax.  The second layer 
defines particular IFF file types such as ILBM (standard raster pictures), 
ANIM (animation), SMUS (simple musical score), and 8SVX (8-bit sampled audio 

IFF is also a design idea: programs should use interchange formats for their 
everyday storage.

This way, users rarely need converters and import/export commands to change 
software releases, application programs, or hardware.

What's the trick?

File compatibility is easy to achieve if programmers let go of one notion--
dumping internal data structures to disk.  A program's internal data structures 
should really be suited to what the program does and how it works.  What's 
"best" changes as the program evolves new functions and methods.  But a disk 
format should be suited to storage and interchange.

Once we design internal formats and disk formats for their own separate 
purposes, the rest is easy.  Reading and writing become behind-the-scenes 
conversions.  But two conversions hidden in each program is much better than 
a pile of conversion programs.

Does this seem strange?  It's what ASCII text programs do!  Text editors use 
line tables, piece tables, gaps, and other structures for fast editing and 
searching.  Text generators and consumers construct and parse files.  That's 
why the ASCII standard works so well.

Also, every file must be self-sufficient.  E.g., a picture file has to include 
its size and number of bits/pixel.

What's an IFF file look like?

IFF is based on data blocks called "chunks". Here's an example color map

                         +------------------+ in an ILBM file, CMAP means
          char typeID[4] | 'CMAP'           | "color map"
  unsigned long dataSize |  48              | 48 data bytes
             char data[] |  0,0,0,255,      | 16 3-byte color values: black, 
                         |  255,255,255...  | white, ....

                         +------------------+ in an ILBM file, CMAP means
                         | 'CMAP'           | "color map"
                         |  48              | 48 data bytes
                         |  0,0,0,255,      | 16 3-byte color values: black, 
                         |  255,255,255...  | white, ....

A chunk is made of a 4-character type identifier, a 32 bit data byte count, 
and the data bytes. It's like a Macintosh "resource" with a 32-bit size.

Fine points:

oEvery 16- and 32-bit number is stored in 68000 byte order--highest 
byte first.

An Intel CPU must reverse the 2- or 4-byte sequence of each number. 
This applies to chunk dataSize fields and to numbers inside chunk 
data.  It does not affect character strings and byte data because you 
can't reverse a 1-byte sequence.  But it does affect the 32-bit math 
used in IFF's MakeID macro.  The standard does allow CPU specific byte 
ordering hidden within a chunk itself, but the practice is discouraged.

oEvery 16- and 32-bit number is stored on an even address.

oEvery odd-length chunk must be followed by a 0 pad byte.  This pad 
byte is not counted in dataSize.

oAn ID is made of 4 ASCII characters in the range " " (space, hex 20) 
through "~" (tilde, hex 7E).  Leading spaces are not permitted.

oIDs are compared using a quick 32-bit equality test.  Case matters.

A chunk typically holds a C structure, Pascal record, or an array.  For 
example, an 'ILBM' picture has a 'BMHD' bitmap header chunk (a structure) and 
a 'BODY' raster body chunk (an array).

To construct an IFF file, just put a file type ID (like 'ILBM') into a wrapper 
chunk called a 'FORM' (Think "FILE").  Inside that wrapper place chunks one 
after another (with pad bytes as needed).  The chunk size always tells you 
how many more bytes you need to skip over to get to the next chunk.

          |                  |
          |  'FORM'          |  FORM is a special chunk ID
          |                  |
          |   24070          |  24070 data bytes
       -- +------------------+ 
       |  |                  |
       |  |  'ILBM'          |  FORM type is ILBM
       |  |                  |
       |  | +--------------+ |
       |  | |  'BMHD'      | |  A BMHD bitmap header chunk 
       |  | |--------------| |  (20 data bytes)
       |  | |   20         | |
       |  | |--------------| |
       |  | |  320, 200,   | |
       |  | |  0 ....      | |
       |  | +--------------+ |
       |  |                  |
       |  | +--------------+ |
       |  | |  'CMAP'      | |  A CMAP color map chunk
24070 <   | |--------------| |  (21 data bytes = 1 pad)
bytes  |  | |   21         | |
       |  | |--------------| |
       |  | |  0, 0, 0,    | |
       |  | |  255 ....    | |
       |  | +--------------+ |
       |  |   0              |  A pad byte
       |  | +--------------+ |
       |  | |  'BODY'      | |
       |  | |--------------| |  A BODY raster body chunk
       |  | |  24000       | |  (24000 data bytes)
       |  | |--------------| |
       |  | |  0, 0, 0.... | |
       |  | +--------------+ |
       -- +------------------+

A FORM always contains one 4-character FORM type ID (a file type, in this 
case 'ILBM') followed by any number of data chunks.  In this example, the FORM 
type is 'ILBM', which stands for "InterLeaved BitMap".  (ILBM is an IFF 
standard for bitplane raster pictures.)  This example has 3 chunks.  Note the 
pad byte after the odd length chunk.

Within FORMs ILBM, 'BMHD' identifies a bitmap header chunk, 'CMAP' a color 
map, and 'BODY' a raster body.  In general, the chunk IDs in a FORM are 
local to the FORM type ID.  The  exceptions are the 4 global chunk IDs 'FORM', 
'LIST', 'CAT ', and 'PROP'.  (A FORM may contain other FORM chunks.  E.g., an 
animation FORM might contain picture FORMs and sound FORMs.)

How to read an IFF file?

Given the C subroutine "GetChunkHeader()":

/* Skip any remaining bytes of the current chunk, skip any pad byte, 
and read the next chunk header. Returns the chunk ID or END_MARK. */

ID GetChunkHeader();

we read the chunks in a FORM ILBM with a loop like this:

  switch (id = GetChunkHeader())
    case 'CMAP': ProcessCMAP(); break;
    case 'BMHD': ProcessBMHD(); break;
    case 'BODY': ProcessBODY(); break;
    /* default: just ignore the chunk */
  until (id == END_MARK);

This loop processes each chunk by dispatching to a routine that reads the 
specific type of chunk data.  We don't assume a particular order of chunks. 
This is a simple parser.  Note that even if you have fully processed a chunk, 
you should respect it's chunk size, even if the size is larger than you 

This sample ignores important details like I/O errors.  There are also higher-
level errors to check, e.g., if we hit END_MARK without reading a BODY, we 
didn't get a picture.

Every IFF file is a 'FORM', 'LIST', or 'CAT ' chunk.  You can recognize an IFF 
file by those first 4 bytes.  ('FORM' is far and away the most common.  We'll 
get to LIST and CAT below.)  If the file contains a FORM, dispatch on the FORM 
type ID to a chunk-reader loop like the one above.

File extensibility

IFF files are extensible and forward/backward compatible:

oChunk contents should be designed for compatibility across 
environments and for longevity.  Every chunk should have a path for 
future expansion; at minimum this will be an unused bit or two.

oThe standards team for a FORM type can extend one of the chunks that 
contains a structure by appending new, optional structure fields.

oAnyone can define new FORM types as well as new chunk types within a 
FORM type.  Storing private chunks within a FORM is OK, but be sure 
to register your activities with Commodore Applications and Technical 

oA chunk can be superseded by a new chunk type, e.g., to store more 
bits per RGB color register. New programs can output the old chunk 
(for backward compatibility) along with the new chunk.

oIf you must change data in an incompatible way, change the chunk ID or 
the FORM type ID.


Advanced Topics: CAT, LIST, and PROP (not all that important)

Sometimes you want to put several "files" into one, such as a picture library.
This is what CAT is for.  It "concatenates" FORM and LIST chunks.

          |                    |
          |   'CAT '           |  concatenation
          |                    |
          |   48160            |  48160 data bytes
          |                    |
          |   'ILBM'           |  This concatenation contains FORMs ILBM
          |                    |
          |  +--------------+  |
          |  |  'FORM'      |  |  A FORM ILBM
          |  |--------------|  |
          |  |   24070      |  |
          |  |--------------|  |
          |  |  'ILBM'      |  |
          |  |--------------|  |
          |  |  ....        |  |
          |  +--------------+  |
          |                    |
          |  +--------------+  |
          |  |  'FORM'      |  |  Another FORM ILBM
          |  |--------------|  |
          |  |   24070      |  |
          |  |--------------|  |
          |  |  'ILBM'      |  |
          |  |--------------|  |
          |  |  ....        |  |
          |  +--------------+  |
          |                    |

This example CAT holds two ILBMs. It can be shown outline-style:

....BMHD      | a complete FORM ILBM picture
....CMAP      |
....BODY      /

Sometimes you want to share the same color map across many pictures. LIST 
and PROP do this:

..PROP ILBM   default properties for FORMs ILBM
....CMAP      an ILBM CMAP chunk (there could be a BMHD chunk here, too)
....BMHD      (there could be a CMAP here to override the default)
....BMHD      (there could be a CMAP here to override the default)

A LIST holds PROPs and FORMs (and occasionally LISTs and CATs).  A PROP ILBM 
contains default data (in the above example, just one CMAP chunk) for all 
FORMs ILBM in the LIST.  Any FORM may override the PROP-defined default with 
its own CMAP.  All PROPs must appear at the beginning of a LIST.  Each FORM 
type standardizes (among other things) which of its chunks are "property 
chunks" (may appear in PROPs) and which are "data chunks" (may not appear 
in PROPs).


"EA IFF 85" Standard for Interchange Format Files

Document Date:January 14, 1985  (Updated Oct, 1988 Commodore-Amiga, Inc.)
From:Jerry Morrison, Electronic Arts
Status:Released to the public domain, and in use

1.  Introduction

Standards are Good for Software Developers

As home computer hardware evolves into better and better media machines, the 
demand increases for higher quality, more detailed data.  Data development 
gets more expensive, requires more expertise and better tools, and has to be 
shared across projects.  Think about several ports of a product on one CD-ROM 
with 500M Bytes of common data!

Development tools need standard interchange file formats.  Imagine scanning 
in images of "player" shapes, transferring them to an image enhancement 
package, moving them to a paint program for touch up, then incorporating them 
into a game.  Or writing a theme song with a Macintosh score editor and 
incorporating it into an Amiga game.  The data must at times be transformed, 
clipped, filled out, and moved across machine kinds.  Media projects will 
depend on data transfer from graphic, music, sound effect, animation, and 
script tools.

Standards are Good for Software Users

Customers should be able to move their own data between independently 
developed software products.  And they should be able to buy data libraries 
usable across many such products.  The types of data objects to exchange are 
open-ended and include plain and formatted text, raster and structured 
graphics, fonts, music, sound effects, musical instrument descriptions, 
and animation.

The problem with expedient file formats--typically memory dumps is that 
they're too provincial.  By designing data for one particular use (such as a 
screen snapshot), they preclude future expansion (would you like a full page 
picture?  a multi-page document?).  In neglecting the possibility that other 
programs might read their data, they fail to save contextual information
(how many bit planes?  what resolution?).  Ignoring that other programs might 
create such files, they're intolerant of extra data (a different picture 
editor may want to save a texture palette with the image), missing data (such 
as no color map), or minor variations (perhaps a smaller image).  In practice, 
a filed representation should rarely mirror an in-memory representation.  The
former should be designed for longevity; the latter to optimize the 
manipulations of a particular program.  The same filed data will be read into 
different memory formats by different programs.

The IFF philosophy: "A little behind-the-scenes conversion when programs read 
and write files is far better than NxM explicit conversion utilities for 
highly specialized formats".

So we need some standardization for data interchange among development tools 
and products.  The more developers that adopt a standard, the better for all 
of us and our customers.

Here is "EA IFF 1985"

Here is our offering: Electronic Arts' IFF standard for Interchange File 
Format.  The full name is "EA IFF 1985".  Alternatives and justifications 
are included for certain choices.  Public domain subroutine packages and 
utility programs are available to make it easy to write and use IFF-compatible 

Part 1 introduces the standard.  Part 2 presents its requirements and 
background.  Parts 3, 4, and 5 define the primitive data types, FORMs, and 
LISTs, respectively, and how to define new high level types.  Part 6 
specifies the top level file structure.  Section 7 lists names of the group
responsible for this standard.  Appendix A is included for quick reference 
and Appendix B.


American National Standard Additional Control Codes for Use with ASCII, ANSI 
standard 3.64-1979 for an 8-bit character set.  See also ISO standard 2022 
and ISO/DIS standard 6429.2.

The C Programming Language, Brian W. Kernighan and Dennis M. Ritchie, Bell 
Laboratories.  Prentice-Hall, Englewood Cliffs, NJ, 1978.

C, A Reference Manual, Samuel P. Harbison and Guy L. Steele Jr., Tartan 
Laboratories.  Prentice-Hall, Englewood Cliffs, NJ, 1984.

Compiler Construction, An Advanced Course, edited by F. L. Bauer and J. Eickel
(Springer-Verlag, 1976).  This book is one of many sources for information on 
recursive descent parsing.

DIF Technical Specification (c) 1981 by Software Arts, Inc.  DIF(tm) is the 
format for spreadsheet data interchange developed by Software Arts, Inc.  
DIF(tm) is a trademark of Software Arts, Inc.

"FTXT" IFF Formatted Text, from Electronic Arts.  IFF supplement document for 
a text format.

"ILBM" IFF Interleaved Bitmap, from Electronic Arts.  IFF supplement document 
for a raster image format.

M68000 16/32-Bit Microprocessor Programmer's Reference Manual (c) 1984, 1982, 
1980, 1979 by Motorola, Inc.

PostScript Language Manual (c) 1984 Adobe Systems Incorporated.
PostScript(tm) is a trademark of Adobe Systems, Inc.
Times and Helvetica. are registered trademarks of Allied Corporation.

Inside Macintosh (c) 1982, 1983, 1984, 1985 Apple Computer, Inc., a programmer's
reference manual.
Apple. is a trademark of Apple Computer, Inc.
MacPaint(tm) is a trademark of Apple Computer, Inc.
Macintosh(tm) is a trademark licensed to Apple Computer, Inc.

InterScript: A Proposal for a Standard for the Interchange of Editable 
Documents (c) 1984 Xerox Corporation.  Introduction to InterScript (c) 1985 
Xerox Corporation.

Amiga. is a registered trademark of Commodore-Amiga, Inc.

Electronics Arts(tm) is a trademark of Electronic Arts.

2.  Background for Designers

Part 2 is about the background, requirements, and goals for the standard.  
It's geared for people who want to design new types of IFF objects.  People 
just interested in using the standard may wish to quickly scan this section.

What Do We Need?

A standard should be long on prescription and short on overhead.  It should 
give lots of rules for designing programs and data files for synergy.  But 
neither the programs nor the files should cost too much more than the 
expedient variety.  Although we are looking to a future with CD-ROMs and
perpendicular recording, the standard must work well on floppy disks.

For program portability, simplicity, and efficiency, formats should be 
designed with more than one implementation style in mind.  It ought to be 
possible to read one of many objects in a file without scanning all the 
preceding data.   (In practice, pure stream I/O is adequate although random 
access makes it easier to write files.)  Some programs need to read and play 
out their data in real time, so we need good compromises between generality 
and efficiency.

As much as we need standards, they can't hold up product schedules.  So we 
also need a kind of decentralized extensibility where any software developer 
can define and refine new object types without some "standards authority" in 
the loop.  Developers must be able to extend existing formats in a forward- 
and backward-compatible way.  A central repository for design information and 
example programs can help us take full advantage of the standard.

For convenience, data formats should heed the restrictions of various 
processors and environments.  For example, word-alignment greatly helps 68000 
access at insignificant cost to 8088 programs.

Other goals include the ability to share common elements over a list of 
objects and the ability to construct composite objects. 

And finally, "Simple things should be simple and complex things should be 
possible" - Alan Kay.

Think Ahead

Let's think ahead and build programs that read and write files for each other 
and for programs yet to be designed.  Build data formats to last for future 
computers so long as the overhead is acceptable.  This extends the usefulness 
and life of today's programs and data.

To maximize interconnectivity, the standard file structure and the specific 
object formats must all be general and extensible.  Think ahead when designing 
an object.  File formats should serve many purposes and allow many programs to 
store and read back all the information they need; even squeeze in custom 
data.  Then a programmer can store the available data and is encouraged to
include fixed contextual details.  Recipient programs can read the needed 
parts, skip unrecognized stuff, default missing data, and use the stored 
context to help transform the data as needed.



IFF addresses these needs by defining a standard file structure, some initial 
data object types, ways to define new types, and rules for accessing these 
files.  We can accomplish a great deal by writing programs according to this 
standard, but do not expect direct compatibility with existing software.  
We'll need conversion programs to bridge the gap from the old world.

IFF is geared for computers that readily process information in 8-bit bytes.  
It assumes a "physical layer" of data storage and transmission that reliably 
maintains "files" as sequences of 8-bit bytes.  The standard treats a "file" 
as a container of data bytes and is independent of how to find a file and 
whether it has a byte count.

This standard does not by itself implement a clipboard for cutting and pasting 
data between programs.  A clipboard needs software to mediate access, and 
provide a notification mechanism so updates and requests for data can be 

Data Abstraction

The basic problem is how to represent information  in a way that's program-
independent, compiler- independent, machine-independent, and device-independent.

The computer science approach is "data abstraction", also known as "objects", 
"actors", and "abstract data types".  A data abstraction has a "concrete 
representation" (its storage format), an "abstract representation" (its 
capabilities and uses), and access procedures that isolate all the calling 
software from the concrete representation.  Only the access procedures touch 
the data storage.  Hiding mutable details behind an interface is called 
"information hiding".  What is hidden are the non-portable details of 
implementing the object, namely the selected storage representation and 
algorithms for manipulating it.

The power of this approach is modularity.  By adjusting the access procedures 
we can extend and restructure the data without impacting the interface or its 
callers.  Conversely, we can extend and restructure the interface and callers 
without making existing data obsolete.  It's great for interchange!

But we seem to need the opposite: fixed file formats for all programs to 
access.  Actually, we could file data abstractions ("filed objects") by 
storing the data and access procedures together.  We'd have to encode the 
access procedures in a standard machine-independent programming language a la 
PostScript.  Even with this, the interface can't evolve freely since we can't 
update all copies of the access procedures.  So we'll have to design our 
abstract representations for limited evolution and occasional revolution 

In any case, today's microcomputers can't practically store true data 
abstractions.  They can do the next best thing: store arbitrary types of data 
in "data chunks", each with a type identifier and a length count.  The type 
identifier is a reference by name to the access procedures (any local 
implementation).  The length count enables storage-level object operations 
like "copy" and "skip to next" independent of object type or contents.

Chunk writing is straightforward.  Chunk reading requires a trivial parser to 
scan each chunk and dispatch to the proper access/conversion procedure.  
Reading chunks nested inside other chunks may require recursion, but no look 
ahead or backup.

That's the main idea of IFF.  There are, of course, a few other details....

Previous Work

Where our needs are similar, we borrow from existing standards.

Our basic need to move data between independently developed programs is 
similar to that addressed by the Apple Macintosh desk scrap or "clipboard" 
[Inside Macintosh chapter "Scrap Manager"].  The Scrap Manager works closely 
with the Resource Manager, a handy filer and swapper for data objects (text 
strings, dialog window templates, pictures, fonts?) including types yet to be 
designed [Inside Macintosh chapter "Resource Manager"].  The Resource Manager
is akin to Smalltalk's object swapper.

We will probably write a Macintosh desk accessory that converts IFF files to 
and from the Macintosh clipboard for quick and easy interchange with programs 
like MacPaint and Resource Mover.

Macintosh uses a simple and elegant scheme of four-character "identifiers" to 
identify resource types, clipboard format types, file types, and file creator 
programs.  Alternatives are unique ID numbers assigned by a central authority 
or by hierarchical authorities, unique ID numbers generated by algorithm, 
other fixed length character strings, and variable length strings.  Character 
string identifiers double as readable signposts in data files and programs.  
The choice of 4 characters is a good tradeoff between storage space, 
fetch/compare/store time, and name space size.  We'll honor Apple's designers 
by adopting this scheme.

"PICT" is a good example of a standard structured graphics format (including 
raster images) and its many uses [Inside Macintosh chapter "QuickDraw"].  
Macintosh provides QuickDraw routines in ROM to create, manipulate, and 
display PICTs.  Any application can create a PICT by simply asking QuickDraw 
to record a sequence of drawing commands.  Since it's just as easy to ask 
QuickDraw to render a PICT to a screen or a printer, it's very effective to 
pass them betweenprograms, say from an illustrator to a word processor.  An 
important feature is the ability to store "comments" in a PICT which QuickDraw 
will ignore.  (Actually, it passes them to your optional custom "comment 

PostScript, Adobe System's print file standard, is a more general way to 
represent any print image (which is a specification for putting marks on 
paper) [PostScript Language Manual].  In fact, PostScript is a full-fledged 
programming language.  To interpret a PostScript program is to render a 
document on a raster output device.  The language is defined in layers: a 
lexical layer of identifiers, constants, and operators; a layer of reverse 
polish semantics including scope rules and a way to define new subroutines; 
and a printing-specific layer of built-in identifiers and operators for 
rendering graphic images.  It is clearly a powerful (Turing equivalent) image
definition language.  PICT and a subset of PostScript are candidates for 
structured graphics standards.

A PostScript document can be printed on any raster output device (including 
a display) but cannot generally be edited.  That's because the original 
flexibility and constraints have been discarded.  Besides, a PostScript 
program may use arbitrary computation to supply parameters like placement and 
size to each operator.  A QuickDraw PICT, in comparison, is a more restricted
format of graphic primitives parameterized by constants.  So a PICT can be 
edited at the level of the primitives, e.g., move or thicken a line.  It cannot 
be edited at the higher level of, say, the bar chart data which generated the 

PostScript has another limitation: not all kinds of data amount to marks on 
paper.  A musical instrument description is one example.  PostScript is just 
not geared for such uses.

"DIF" is another example of data being stored in a general format usable by 
future programs [DIF Technical Specification].  DIF is a format for 
spreadsheet data interchange.  DIF and PostScript are both expressed in plain 
ASCII text files.  This is very handy for printing, debugging, experimenting, 
and transmitting across modems.  It can have substantial cost in compaction 
and read/write work, depending on use.  We won't store IFF files this way but 
we could define an ASCII alternate representation with a converter program.

InterScript is the Xerox standard for interchange of editable documents 
[Introduction to InterScript].  It approaches a harder problem: How to 
represent editable word processor documents that may contain formatted text, 
pictures, cross-references like figure numbers, and even highly specialized 
objects like mathematical equations?  InterScript aims to define one standard 
representation for each kind of information.  Each InterScript-compatible 
editor is supposed to preserve the objects it doesn't understand and even 
maintain nested cross-references.  So a simple word processor would let you 
edit the text of a fancy document without discarding the equations or 
disrupting the equation numbers.

Our task is similarly to store high level information and preserve as much 
content as practical while moving it between programs.  But we need to span a 
larger universe of data types and cannot expect to centrally define them all.  
Fortunately, we don't need to make programs preserve information that they 
don't understand.  And for better or worse, we don't have to tackle general-
purpose cross-references yet.

3.  Primitive Data Types

Atomic components such as integers and characters that are interpretable 
directly by the CPU are specified in one format for all processors.  We chose 
a format that's the same as used by the Motorola MC68000 processor [M68000 
16/32-Bit Microprocessor Programmer's Reference Manual].  The high byte and 
high word of a number are stored first.

N.B.: Part 3 dictates the format for "primitive" data types where--and only 
where--used in the overall file structure.  The number of such occurrences of 
dictated formats will be small enough that the costs of conversion, storage, 
and management of processor-specific files would far exceed the costs of 
conversion during I/O by "foreign" programs.  A particular data chunk may be
specified with a different format for its internal primitive types or with 
processor or environment specific variants if necessary to optimize local 
usage.  Since that hurts data interchange, it's not recommended.  (Cf. 
Designing New Data Sections, in Part 4.)


All data objects larger than a byte are aligned on even byte addresses 
relative to the start of the file.  This may require padding.  Pad bytes are 
to be written as zeros, but don't count on that when reading.

This means that every odd-length "chunk" must be padded so that the next one 
will fall on an even boundary.  Also, designers of structures to be stored in 
chunks should include pad fields where needed to align every field larger than 
a byte.  For best efficiency, long word data should be arranged on long word 
(4 byte) boundaries.  Zeros should be stored in all the pad bytes.

Justification: Even-alignment causes a little extra work for files that are 
used only on certain processors but allows 68000 programs to construct and 
scan the data in memory and do block I/O.  Any 16-bit or greater CPU will have 
faster access to aligned data.  You just add an occasional pad field to data 
structures that you're going to block read/write or else stream read/write an
extra byte.  And the same source code works on all processors.  Unspecified 
alignment, on the other hand, would force 68000 programs to (dis)assemble 
word and long word data one byte at a time.  Pretty cumbersome in a high level 
language.  And if you don't conditionally compile that step out for other 
processors, you won't gain anything.


Numeric types supported are two's complement binary integers in the format 
used by the MC68000 processor--high byte first, high word first--the reverse 
of 8088 and 6502 format.

UBYTE 8 bits unsigned
WORD16 bits signed
UWORD16 bits unsigned
LONG32 bits signed

The actual type definitions depend on the CPU and the compiler.  In this 
document, we'll express data type definitions in the C programming language.  
[See C, A Reference Manual.]  In 68000 Lattice C:

typedef unsigned charUBYTE;/*  8 bits unsigned*/
typedef shortWORD;/* 16 bits signed*/
typedef unsigned shortUWORD;/* 16 bits unsigned*/
typedef longLONG;/* 32 bits signed*/


The following character set is assumed wherever characters are used, e.g., in 
text strings, IDs, and TEXT chunks (see below).  Characters are encoded in 
8-bit ASCII.  Characters in the range NUL (hex 0) through DEL (hex 7F) are 
well defined by the 7-bit ASCII standard.  IFF uses the graphic group " " 
(SP, hex 20) through "~" (hex 7E).

Most of the control character group hex 01 through hex 1F have no standard 
meaning in IFF.  The control character LF (hex 0A) is defined as a "newline" 
character.  It denotes an intentional line break, that is, a paragraph or line 
terminator.  (There is no way to store an automatic line break.  That is 
strictly a function of the margins in the environment the text is placed.) 
The controlcharacter ESC (hex 1B) is a reserved escape character under the 
rules of ANSI standard 3.64-1979 American National Standard Additional 
Control Codes for Use with ASCII, ISO standard 2022, and ISO/DIS standard 

Characters in the range hex 7F through hex FF are not globally defined in IFF. 
They are best left reserved for future standardization.  (Note that the FORM 
type FTXT (formatted text) defines the meaning of these characters within FTXT 
forms.)  In particular, character values hex 7F through hex 9F are control 
codes while characters hex A0 through hex FF are extended graphic characters 
like E, as per the ISO and ANSI standards cited above.  [See the supplementary
document "FTXT" IFF Formatted Text.]


A "creation date" is defined as the date and time a stream of data bytes was 
created.  (Some systems call this a "last modified date".)  Editing some data 
changes its creation date.  Moving the data between volumes or machines does 

The IFF standard date format will be one of those used in MS-DOS, Macintosh, 
or AmigaDOS (probably a 32-bit unsigned number of seconds since a reference 
point).  Issue: Investigate these three.

Type IDs

A "type ID", "property name", "FORM type", or any other IFF identifier is a 
32-bit value: the concatenation of four ASCII characters in the range " " (SP, 
hex 20) through "~" (hex 7E).  Spaces (hex 20) should not precede printing 
characters; trailing spaces are OK.  Control characters are forbidden.

typedef CHAR ID[4];

IDs are compared using a simple 32-bit case-dependent equality test.  FORM 
type IDs are restricted.  Since they may be stored in filename extensions 
lower case letters and punctuation marks are forbidden.  Trailing spaces are OK.

Carefully choose those four characters when you pick a new ID.  Make them 
mnemonic so programmers can look at an interchange format file and figure 
out what kind of data it contains.  The name space makes it possible for 
developers scattered around the globe to generate ID values with minimal 
collisions so long as they choose specific names like "MUS4" instead of 
general ones like "TYPE" and "FILE".

Commodore Applications and Technical Support has undertaken the task of 
maintaining the registry of FORM type IDs and format descriptions.  See the 
IFF registry document for more information.

Sometimes it's necessary to make data format changes that aren't backward 
compatible.  As much as we work for compatibility, unintended interactions 
can develop.  Since IDs are used to denote data formats in IFF, new IDs are 
chosen to denote revised formats.  Since programs won't read chunks whose IDs 
they don't recognize (see Chunks, below), the new IDs keep old programs from
stumbling over new data.  The conventional way to chose a "revision" ID is to 
increment the last character if it's a digit or else change the last character 
to a digit.  E.g., first and second revisions of the ID "XY" would be "XY1" 
and "XY2".  Revisions of "CMAP" would be "CMA1" and "CMA2".


Chunks are the building blocks in the IFF structure.  The form expressed as 
a C typedef is:

    typedef struct {
IDckID;/* 4 character ID */
LONGckSize;/* sizeof(ckData) */
UBYTEckData[/* ckSize */];
} Chunk;


We can diagram an example chunk --a "CMAP" chunk containing 12 data bytes --
like this:

                        +------------------+   -
        ckID:           | 'CMAP'           |   |
                        |------------------+   |
        ckSize:         |  12              |   |
                    -   |------------------+   |
        ckData:     |   |  0, 0, 0, 32     |  20 bytes
                   12   |------------------+   |
                  bytes |  0, 0, 64, 0     |   |
                    |   |------------------+   |
                    |   |  0, 0, 64, 0     |   |
                    -   +------------------+   -

That's 4 bytes of ckID, 4 bytes of ckSize and 12 data bytes.  The total space 
used is 20 bytes.

The ckID identifies the format and purpose of the chunk.  As a rule, a 
program must recognize ckID to interpret ckData.  It should skip over all 
unrecognized chunks.  The ckID also serves as a format version number as long 
as we pick new IDs to identify new formats of ckData (see above).

The following ckIDs are universally reserved to identify chunks with 
particular IFF meanings: "LIST", "FORM", "PROP", "CAT ", and "    ".  The 
special ID "    " (4 spaces) is a ckID for "filler" chunks, that is, chunks 
that fill space but have no meaningful contents.  The IDs "LIS1" through 
"LIS9", "FOR1" through "FOR9", and "CAT1" through "CAT9" are reserved for 
future "version number" variations.  All IFF-compatible software must account 
for these chunk IDs.

The ckSize is a logical block size--how many data bytes are in ckData.  If 
ckData is an odd number of bytes long, a 0 pad byte follows which is not 
included in ckSize.  (Cf. Alignment.)  A chunk's total physical size is ckSize 
rounded up to an even number plus the size of the header.  So the smallest 
chunk is 8 bytes long with ckSize = 0.  For the sake of following chunks, 
programs must respect every chunk's ckSize as a virtual end-of-file for 
reading its ckData even if that data is malformed, e.g., if nested contents 
are truncated.

We can describe the syntax of a chunk as a regular expression with "#" 
representing the ckSize, the length of the following {braced} bytes.  The 
"[0]" represents a sometimes needed pad byte.  (The regular expressions in 
this document are collected in Appendix A along with an explanation of 

Chunk::= ID #{ UBYTE* } [0]

One chunk output technique is to stream write a chunk header, stream write 
the chunk contents, then random access back to the header to fill in the size. 
Another technique is to make a preliminary pass over the data to compute the 
size, then write it out all at once.

Strings, String Chunks, and String Properties

In a string of ASCII text, linefeed (0x0A) denotes a forced line break 
(paragraph or line terminator).  Other control characters are not used.  (Cf. 
Characters.)  For maximum compatibility with line editors, two linefeed 
characters are often used to indicate a paragraph boundary.

The ckID for a chunk that contains a string of plain, unformatted text is 
"TEXT".  As a practical matter, a text string should probably not be longer 
than 32767 bytes.  The standard allows up to 2^31 - 1 bytes.  The ckID "TEXT" 
is globally reserved for this use.

When used as a data property (see below), a text string chunk may be 0 to 255 
characters long.  Such a string is readily converted to a C string or a Pascal 
STRING[255].  The ckID of a property must have a unique property name, not  

When used as a part of a chunk or data property, restricted C string format is 
normally used.  That means 0 to 255 characters followed by a NULL byte (ASCII 
value 0).

Data Properties (advanced topic)

Data properties specify attributes for following (non-property) chunks.  A 
data property essentially says "identifier = value", for example "XY = (10, 
200)", telling something about following chunks.  Properties may only appear 
inside data sections ("FORM" chunks, cf. Data Sections) and property sections 
("PROP" chunks, cf. Group PROP).

The form of a data property is a type of Chunk.  The ckID is a property name 
as well as a property type.  The ckSize should be small since data properties 
are intended to be accumulated in RAM when reading a file.  (256 bytes is a 
reasonable upper bound.)  Syntactically:

Property::= Chunk

When designing a data object, use properties to describe context information 
like the size of an image, even if they don't vary in your program.  Other 
programs will need this information.

Think of property settings as assignments to variables in a programming 
language.  Multiple assignments are redundant and local assignments 
temporarily override global assignments.  The order of assignments doesn't 
matter as long as they precede the affected chunks.  (Cf. LISTs, CATs, and 
Shared Properties.)

Each object type (FORM type) is a local name space for property IDs.  Think of 
a "CMAP" property in a "FORM ILBM" as the qualified ID "ILBM.CMAP".  A "CMAP" 
inside some other type of FORM may not have the same meaning.  Property IDs 
specified when an object type is designed (and therefore known to all clients) 
are called "standard" while specialized ones added later are "nonstandard".


Issue: A standard mechanism for "links" or "cross references" is very 
desirable for things like combining images and sounds into animations.  
Perhaps we'll define "link" chunks within FORMs that refer to other FORMs or 
to specific chunks within the same and other FORMs.  This needs further work.  
EA IFF 1985 has no standard link mechanism.

For now, it may suffice to read a list of, say, musical instruments, and then 
just refer to them within a musical score by  sequence  number.


File References

Issue: We may need a standard form for references to other files.  A "file ref"
could name a directory and a file in the same type of operating system as the 
reference's originator.  Following the reference would expect the file to be 
on some mounted volume, or perhaps the same directory as the file that made 
the reference.  In a network environment, a file reference could name a server, 

Issue: How can we express operating-system independent file references?

Issue: What about a means to reference a portion of another file?  Would this 
be a "file ref" plus a reference to a "link" within the target file?

4.  Data Sections

The first thing we need of a file is to check: Does it contain IFF data and, 
if so, does it contain the kind of data we're looking for?  So we come to the 
notion of a "data section".

A "data section" or IFF "FORM" is one self-contained "data object" that might 
be stored in a file by itself.  It is one high level data object such as a 
picture or a sound effect, and generally contains a grouping of chunks.  The 
IFF structure "FORM" makes it self-identifying.  It could be a composite
object like a musical score with nested musical instrument descriptions.

Group FORM

A data section is a chunk with ckID "FORM" and this arrangement:

FORM   ::= "FORM" #{ FormType (LocalChunk | FORM | LIST | CAT)* }
FormType   ::= ID
LocalChunk ::= Property | Chunk

The ID "FORM" is a syntactic keyword like "struct" in C.  Think of a "struct 
ILBM" containing a field "CMAP".  If you see "FORM" you will know to expect a 
FORM type ID (the structure name, "ILBM" in this example) and a particular 
contents arrangement or "syntax" (local chunks, FORMs, LISTs, and CATs).  A 
"FORM ILBM", in particular, might contain a local chunk "CMAP", an "ILBM.CMAP" 
(to use a qualified name).

So the chunk ID "FORM" indicates a data section.  It implies that the chunk 
contains an ID and some number of nested chunks.  In reading a FORM, like any 
other chunk, programs must respect its ckSize as a virtual end-of-file for 
reading its contents, even if they're truncated.

The FORM type is a restricted ID that may not contain lower case letters or 
punctuation characters.  (Cf. Type IDs.  Cf. Single Purpose Files.)

The type-specific information in a FORM is composed of its "local chunks": 
data properties and other chunks.  Each FORM type is a local name space for 
local chunk IDs.  So "CMAP" local chunks in other FORM types may be unrelated 
to "ILBM.CMAP".  More than that, each FORM type defines semantic scope.  If 
you know what a FORM ILBM is, you will know what an ILBM.CMAP is.

Local chunks defined when the FORM type is designed (and therefore known to 
all clients of this type) are called "standard" while specialized ones added 
later are "nonstandard".

Among the local chunks, property chunks give settings for various details 
like text font while the other chunks supply the essential information.  This 
distinction is not clear cut.  A property setting can be cancelled by a later 
setting of the same property.  E.g., in the sequence:

prop1 = x  (Data A)  prop1 = z  prop1 = y (Data B)

prop1 is = x for Data A, and y for Data B.  The setting prop1 = z has no effect.

For clarity, the universally reserved chunk IDs "LIST", "FORM", "PROP", 
"CAT ", "    ", "LIS1" through "LIS9", "FOR1" through "FOR9", and "CAT1" 
through "CAT9" may not be FORM type IDs.

Part 5, below, talks about grouping FORMs into LISTs and CATs.  They let you 
group a bunch of FORMs but don't impose any particular meaning or constraints 
on the grouping.  Read on.

Composite FORMs

A FORM chunk inside a FORM is a full-fledged data section.  This means you can 
build a composite object such as a multi-frame animation sequence by nesting 
available picture FORMs and sound effect FORMs.  You can insert additional 
chunks with information like frame rate and frame count.

Using composite FORMs, you leverage on existing programs that create and edit 
the component FORMs.  Those editors may even look into your composite object 
to copy out its type of component.  Such editors are not allowed to replace 
their component objects within your composite object.  That's because the IFF 
standard lets you specify consistency requirements for the composite FORM such 
as maintaining a count or a directory of the components.  Only programs that 
are written to uphold the rules of your FORM type may create or modify such 

Therefore, in designing a program that creates composite objects, you are 
strongly requested to provide a facility for your users to import and export 
the nested FORMs.  Import and export could move the data through a clipboard 
or a file.

Here are several existing FORM types and rules for defining new ones:


An FTXT data section contains text with character formatting information 
like fonts and faces.  It has no paragraph or document formatting information 
like margins and page headers.  FORM FTXT is well matched to the text 
representation in Amiga's Intuition environment.  See the supplemental 
document "FTXT" IFF Formatted Text.


"ILBM" is an InterLeaved BitMap image with color map; a machine-independent 
format for raster images.  FORM ILBM is the standard image file format for 
the Commodore-Amiga computer and is useful in other environments, too.  See 
the supplemental document "ILBM" IFF Interleaved Bitmap.


The data chunk inside a "PICS" data section has ID "PICT" and holds a 
QuickDraw picture.  Issue: Allow more than one PICT in a PICS?  See Inside 
Macintosh chapter "QuickDraw" for details on PICTs and how to create and 
display them on the Macintosh computer.

The only standard property for PICS is "XY", an optional property that 
indicates the position of the PICT relative to "the big picture".  The 
contents of an XY is a QuickDraw Point.

Note: PICT may be limited to Macintosh use, in which case there'll be another 
format for structured graphics in other environments.

Other Macintosh Resource Types

Some other Macintosh resource types could be adopted for use within IFF files; 
perhaps MWRT, ICN, ICN#, and STR#.

Issue: Consider the candidates and reserve some more IDs.

Designing New Data Sections

Supplemental documents will define additional object types.  A supplement 
needs to specify the object's purpose, its FORM type ID, the IDs and formats 
of standard local chunks, and rules for generating and interpreting the data.  
It's a good idea to supply typedefs and an example source program that 
accesses the new object.  See "ILBM" IFF Interleaved Bitmap for such an example.

Anyone can pick a new FORM type ID but should reserve it with Commodore
Applications and Technical Support (CATS) at their earliest convenience.  
While decentralized  format definitions and extensions are possible in IFF, 
our preference is to get design consensus by committee, implement a program 
to read and write it, perhaps tune the format before it becomes locked in 
stone, and then publish the format with example code.  Some organization 
should remain in charge of answering questions and coordinating extensions 
to the format.

If it becomes necessary to incompatibly revise the design of some data 
section, its FORM type ID will serve as a version number (Cf. Type IDs).  E.g., 
a revised "VDEO" data section could be called "VDE1".  But try to get by with 
compatible revisions within the existing FORM type.

In a new FORM type, the rules for primitive data types and word-alignment (Cf. 
Primitive Data Types) may be overridden for the contents of its local chunks--
but not for the chunk structure itself--if your documentation spells out the 
deviations.  If machine-specific type variants are needed, e.g., to store vast 
numbers of integers in reverse bit order, then outline the conversion 
algorithm and indicate the variant inside each file, perhaps via different 
FORM types.  Needless to say, variations should be minimized.

In designing a FORM type, encapsulate all the data that other programs will 
need to interpret your files.  E.g., a raster graphics image should specify the 
image size even if your program always uses 320 x 200 pixels x 3 bitplanes.  
Receiving programs are then empowered to append or clip the image rectangle, 
to add or drop bitplanes, etc.  This enables a lot more compatibility.

Separate the central data (like musical notes) from more specialized 
information (like note beams) so simpler programs can extract the central 
parts during read-in.  Leave room for expansion so other programs can squeeze 
in new kinds of information (like lyrics).  And remember to keep the property 
chunks manageably short--let's say <= 256 bytes.

When designing a data object, try to strike a good tradeoff between a super-
general format and a highly-specialized one.  Fit the details to at least one 
particular need, for example a raster image might as well store pixels in the 
current machine's scan order.  But add the kind of generality that makes the 
format usable with foreseeable hardware and software.  E.g., use a whole byte 
for each red, green, and blue color value even if this year's computer has 
only 4-bit video DACs.  Think ahead and help other programs so long as the 
overhead is acceptable.  E.g., run compress a raster by scan line rather than 
as a unit so future programs can swap images by scan line to and from 
secondary storage.

Try to design a general purpose "least common multiple" format that 
encompasses the needs of many programs without getting too complicated.  Be 
sure to leave provisions for future expansion.  Let's coalesce our uses around 
a few such formats widely separated in the vast design space.  Two factors 
make this flexibility and simplicity practical.  First, file storage space is 
getting very plentiful, so compaction is not always a priority.  Second, 
nearly any locally-performed data conversion work during file reading and 
writing will be cheap compared to the I/O time.

It must be OK to copy a LIST or FORM or CAT intact, e.g., to incorporate it 
into a composite FORM.  So any kind of internal references within a FORM must 
be relative references.  They could be relative to the start of the containing 
FORM, relative from the referencing chunk, or a sequence number into a 

With composite FORMs, you leverage on existing programs that create and edit 
the components.  If you write a program that creates composite objects, please 
provide a facility for users to import and export the nested FORMs.

Finally, don't forget to specify all implied rules in detail.

5.  LISTs, CATs, and Shared Properties (Advanced topics)

Data often needs to be grouped together, for example, consider a list of icons. 
Sometimes a trick like arranging little images into a big raster works, but 
generally they'll need to be structured as a first class group.  The objects 
"LIST" and "CAT " are IFF-universal mechanisms for this purpose.  Note: LIST 
and CAT are advanced topics the first time reader will want to skip.

Property settings sometimes need to be shared over a list of similar objects.  
E.g., a list of icons may share one color map.  LIST provides a means called 
"PROP" to do this.  One purpose of a LIST is to define the scope of a PROP.  
A "CAT ", on the other hand, is simply a concatenation of objects.

Simpler programs may skip LISTs and PROPs altogether and just handle FORMs and 
CATs.  All "fully-conforming" IFF programs also know about "CAT ", "LIST", and 
"PROP".  Any program that reads a FORM inside a LIST must process shared PROPs 
to correctly interpret that FORM.

Group CAT

A CAT is just an untyped group of data objects.

Structurally, a CAT is a chunk with chunk ID "CAT " containing a "contents 
type" ID followed by the nested objects.  The ckSize of each contained chunk 
is essentially a relative pointer to the next one.

CAT::= "CAT " #{ ContentsType (FORM | LIST | CAT)* }
ContentsType    ::= ID-- a hint or an "abstract data type" ID

In reading a CAT, like any other chunk, programs must respect its ckSize as a 
virtual end-of-file for reading the nested objects even if they're malformed 
or truncated.

The "contents type" following the CAT's ckSize indicates what kind of FORMs 
are inside.  So a CAT of ILBMs would store "ILBM" there.  It's just a hint.  
It may be used to store an "abstract data type".  A CAT could just have blank 
contents ID ("    ") if it contains more than one kind of FORM.

CAT defines only the format of the group.  The group's meaning is open to 
interpretation.  This is like a list in LISP: the structure of cells is 
predefined but the meaning of the contents as, say, an association list 
depends on use.  If you need a group with an enforced meaning (an "abstract 
datatype" or Smalltalk "subclass"), some consistency constraints, or 
additional data chunks, use a composite FORM instead (Cf. Composite FORMs).

Since a CAT just means a concatenation of objects, CATs are rarely nested.  
Programs should really merge CATs rather than nest them.

Group LIST

A LIST defines a group very much like CAT but it also gives a scope for PROPs 
(see below).  And unlike CATs, LISTs should not be merged without 
understanding their contents.

Structurally, a LIST is a chunk with ckID "LIST" containing a "contents type" 
ID, optional shared properties, and the nested contents (FORMs, LISTs, and 
CATs), in that order.  The ckSize of each contained chunk is a relative 
pointer to the next one.  A LIST is not an arbitrary linked list--the cells 
are simply concatenated.

LIST     ::= "LIST" #{ ContentsType PROP* (FORM | LIST | CAT)* }
ContentsType ::= ID

Group PROP

PROP chunks may appear in LISTs (not in FORMs or CATs).  They supply shared 
properties for the FORMs in that LIST.  This ability to elevate some property 
settings to shared status for a list of forms is useful for both indirection 
and compaction.  E.g., a list of images with the same size and colors can share 
one "size" property and one "color map" property.  Individual FORMs can
override the shared settings.

The contents of a PROP is like a FORM with no data chunks:

PROP::= "PROP" #{ FormType Property* }

It means, "Here are the shared properties for FORM type <FormType>".

A LIST may have at most one PROP of a FORM type, and all the PROPs must appear
before any of the FORMs or nested LISTs and CATs.  You can have subsequences 
of FORMs sharing properties by making each subsequence a LIST.

Scoping: Think of property settings as variable bindings in nested blocks of a 
programming language.  In C this would look like:

#define Roman0
#define Helvetica1

void main()
int font=Roman;/* The global default */
printf("The font number is %d\n",font);
int font=Helvetica;/* local setting */
printf("The font number is %d\n",font);
printf("The font number is %d\n",font);

 * Sample output:The font number is 0
 *The font number is 1
 *The font number is 0

An IFF file could contain:

FONT {TimesRoman}/* shared setting*/

FONT {Helvetica}/* local setting*/
CHRS {Hello }/* uses font Helvetica*/

CHRS {there.}/* uses font TimesRoman*/

The shared property assignments selectively override the reader's global 
defaults, but only for FORMs within the group.  A FORM's own property 
assignments selectively override the global and group-supplied values.  So 
when reading an IFF file, keep property settings on a stack.  They are 
designed to be small enough to hold in main memory.

Shared properties are semantically equivalent to copying those properties into 
each of the nested FORMs right after their FORM type IDs.


Properties for LIST

Optional "properties for LIST" store the origin of the list's contents in a 
PROP chunk for the pseudo FORM type "LIST".  They are the properties 
originating program "OPGM", processor family "OCPU", computer type "OCMP", 
computer serial number or network address "OSN ", and user name "UNAM".  In 
our imperfect world, these could be called upon to distinguish between 
unintended variations of a data format or to work around bugs in particular 
originating/receiving program pairs.  Issue: Specify the format of these 

A creation date could also be stored in a property, but let's ask that file 
creating, editing, and transporting programs maintain the correct date in the 
local file system.  Programs that move files between machine types are 
expected to copy across the creation dates.

6.  Standard File Structure

File Structure Overview

An IFF file is just a single chunk of type FORM, LIST, or CAT.  Therefore an 
IFF file can be recognized by its first 4 bytes: "FORM", "LIST", or "CAT ".  
Any file contents after the chunk's end are to be ignored.  (Some file 
transfer programs add garbage to the end of transferred files.  This 
specification protects against such common damage).

The simplest IFF file would be one that does no more than encapsulate some 
binary data (perhaps even an old-fashioned single-purpose binary file).  Here 
is a binary dump of such a minimal IFF example:

0000: 464F524D 0000001A 534E4150 43524143    FORM....SNAPCRAC
0010: 0000000D 68656C6C 6F2C776F 726C6421    ....hello,world!
0020: 0A00                                   ..                    

The first 4 bytes indicate this is a "FORM"; the most common IFF top level 
structure.  The following 4 bytes indicate that the contents totals 26 bytes.  
The form type is listed as "SNAP".

Our form "SNAP" contains only one chunk at the moment; a chunk of type "CRAC". 
>From the size ($0000000D) the amount of data must be 13 bytes.  In this case, 
the data happens to correspond to the ASCII string "hello, world!<lf>".   
Since the number 13 is odd, a zero pad byte is added to the file.  At any time 
new chunks could be added to form SNAP without affecting any other aspect of
the file (other than the form size).  It's that simple.

Since an IFF file can be a group of objects, programs that read/write single 
objects can communicate to an extent with programs that read/write groups.  
You're encouraged to write programs that handle all the objects in a LIST or 
CAT.  A graphics editor, for example, could process a list of pictures as a 
multiple page document, one page at a time.

Programs should enforce IFF's syntactic rules when reading and writing files.  
Users should be told when a file is corrupt.  This ensures robust data 
transfer.  For minor damage, you may wish to give the user the option of using 
the suspect data, or cancelling.  Presumably a user could read in a damaged 
file, then save whatever was salvaged to a valid file.  The public domain IFF 
reader/writer subroutine package does some syntatic checks for you.  A utility
program"IFFCheck" is available that scans an IFF file and checks it for 
conformance to IFF's syntactic rules.  IFFCheck also prints an outline of the 
chunks in the file, showing the ckID and ckSize of each.  This is quite handy 
when building IFF programs.  Example programs are also available to show 
details of reading and writing IFF files.

A merge program "IFFJoin" will be available that logically appends IFF files 
into a single CAT group.  It "unwraps" each input file that is a CAT so that 
the combined file isn't nested CATs.

If we need to revise the IFF standard, the three anchoring IDs will be used as 
"version numbers".  That's why IDs "FOR1" through "FOR9", "LIS1" through 
"LIS9", and "CAT1" through "CAT9" are reserved.

IFF formats are designed for reasonable performance with floppy disks.  We 
achieve considerable simplicity in the formats and programs by relying on the 
host file system rather than defining universal grouping structures like 
directories for LIST contents.  On huge storage systems, IFF files could be 
leaf nodes in a file structure like a B-tree.  Let's hope the host file system
implements that for us!

There are two kinds of IFF files: single purpose files and scrap files.  They 
differ in the interpretation of multiple data objects and in the file's 
external type.

Single Purpose Files

A single purpose IFF file is for normal "document" and "archive" storage.  
This is in contrast with "scrap files" (see below) and temporary backing 
storage (non-interchange files).

The external file type (or filename extension, depending on the host file 
system) indicates the file's contents.  It's generally the FORM type of the 
data contained, hence the restrictions on FORM type IDs.

Programmers and users may pick an "intended use" type as the filename 
extension to make it easy to filter for the relevant files in a filename 
requester.  This is actually a "subclass" or "subtype" that conveniently 
separates files of the same FORM type that have different uses.  Programs 
cannot demand conformity to its expected subtypes without overly restricting 
data interchange since they cannot know about the subtypes to be used by 
future programs that users will want to exchange data with.

Issue: How to generate 3-letter MS-DOS extensions from 4-letter FORM type IDs?

Most single purpose files will be a single FORM (perhaps a composite FORM like
a musical score containing nested FORMs like musical instrument descriptions). 
If it's a LIST or a CAT, programs should skip over unrecognized objects to 
read the recognized ones or the first recognized one.  Then a program that 
can read a single purpose file can read something out of a "scrap file", too.

Scrap Files (not currently used)

A "scrap file" is for maximum interconnectivity in getting data between 
programs; the core of a clipboard function.  Scrap files may have type "IFF " 
or filename extension ".IFF".

A scrap file is typically a CAT containing alternate representations of the 
same basic information.  Include as many alternatives as you can readily 
generate.  This redundancy improves interconnectivity in situations where we 
can't make all programs read and write super-general formats.  [Inside 
Macintosh chapter "Scrap Manager".]  E.g., a graphically-annotated musical score
might be supplemented by a stripped down 4-voice melody and by a text (the 

The originating program should write the alternate representations in order of 
"preference": most preferred (most comprehensive) type to least preferred 
(least comprehensive) type.  A receiving program should either use the first 
appearing type that it understands or search for its own "preferred" type.

A scrap file should have at most one alternative of any type.  (A LIST of same 
type objects is OK as one of the alternatives.)  But don't count on this when 
reading; ignore extra sections of a type.  Then a program that reads scrap 
files can read something out of single purpose files.

Rules for Reader Programs

Here are some notes on building programs that read IFF files.  For LIST and 
PROP work, you should also read up on recursive descent parsers.  [See, for 
example, Compiler Construction, An Advanced Course.]

oThe standard is very flexible so many programs can exchange data.  
This implies a program has to scan the file and react to what's 
actually there in whatever order it appears.  An IFF reader program 
is a parser.

oFor interchange to really work, programs must be willing to do some 
conversion during read-in.  If the data isn't exactly what you expect, 
say, the raster is smaller than those created by your program, then 
adjust it.  Similarly, your program could crop a large picture, add 
or drop bitplanes, or create/discard a mask plane.  The program 
should give up gracefully on data that it can't convert.

oIf it doesn't start with "FORM", "LIST", or "CAT ", it's not an IFF-85 

oFor any chunk you encounter, you must recognize its type ID to 
understand its contents.

oFor any FORM chunk you encounter, you must recognize its FORM type ID 
to understand the contained "local chunks".  Even if you don't 
recognize the FORM type, you can still scan it for nested FORMs, 
LISTs, and CATs of interest.

oDon't forget to skip the implied pad byte after every odd-length chunk, 
this is not  included in the chunk count!

oChunk types LIST, FORM, PROP, and CAT are generic groups.  They always 
contain a subtype ID followed by chunks.

oReaders ought to handle a CAT of FORMs in a file.  You may treat the 
FORMs like document pages to sequence through, or just use the first 

oMany  IFF readers completely skip LISTs.  "Fully IFF-conforming" 
readers are those that handle LISTs, even if just to read the first 
FORM from a file.  If you do look into a LIST, you must process
shared properties (in PROP chunks) properly.  The idea is to get the 
correct data or none at all.

oThe nicest readers are willing to look into unrecognized FORMs for 
nested FORM types that they do recognize.  For example, a musical 
score may contain nested instrument descriptions and animation or 
desktop publishing files may contain still pictures.   This extra 
step is highly recommended.

Note to programmers: Processing PROP chunks is not simple! You'll need some 
background in interpreters with stack frames.  If this is foreign to you, 
build programs that read/write only one FORM per file.  For the more intrepid
programmers, the next paragraph summarizes how to process LISTs and PROPs.  

Allocate a stack frame for every LIST and FORM you encounter and initialize it 
by copying the stack frame of the parent LIST or FORM.  At the top level, 
you'll need a stack frame initialized to your program's global defaults.  
While reading each LIST or FORM, store all encountered properties into the 
current stack frame.  In the example ShowILBM, each stack frame has a place
for a bitmap header property ILBM.BMHD and a color map property ILBM.CMAP.  
When you finally get to the ILBM's BODY chunk, use the property settings 
accumulated in the current stack frame.

An alternate implementation would just remember PROPs encountered, forgetting 
each on reaching the end of its scope (the end of the containing LIST).  When 
a FORM XXXX is encountered, scan the chunks in all remembered PROPs XXXX, in 
order, as if they appeared before the chunks actually in the FORM XXXX.  This 
gets trickier if you read FORMs inside of FORMs.

Rules for Writer Programs

Here are some notes on building programs that write IFF files, which is much 
easier than reading them.  

oAn IFF file is a single FORM, LIST, or CAT chunk.

oAny IFF-85 file must start with the 4 characters "FORM", "LIST", or 
"CAT ", followed by a LONG ckSize.  There should be no data after 
the chunk end.

oChunk types LIST, FORM, PROP, and CAT are generic.  They always 
contain a subtype ID followed by chunks.  These three IDs are 
universally reserved, as are "LIS1" through "LIS9", "FOR1" through 
"FOR9", "CAT1" through "CAT9", and "    ".

oDon't forget to write a 0 pad byte after each odd-length chunk.

oDo not try to edit a file that you don't know how to create.  Programs 
may look into a file and copy out nested FORMs of types that they 
recognize, but they should not edit and replace the nested FORMs and 
not add or remove them.  Breaking these rules could make the 
containing structure inconsistent.  You may write a new file 
containing items you copied, or copied and modified, but don't copy 
structural parts you don't understand.

oYou must adhere to the syntax descriptions in Appendix A.  E.g., PROPs 
may only appear inside LISTs.

There are at least four common techniques for writing an IFF group:

(1)  build the data in a file mapped into virtual memory.
(2)  build the data in memory blocks and use block I/O.
(3)  stream write the data piecemeal and (don't forget!) random 
access back to set the group (or FORM) length count.
(4)  make a preliminary pass to compute the length count then stream 
write the data.

Issue: The standard disallows "blind" chunk copying for consistency reasons.  
Perhaps we can define a ckID convention for chunks that are OK to replicate 
without knowledge of the contents.  Any such chunks would need to be 
internally consistent, and not be bothered by changed external references.

Issue: Stream-writing an IFF FORM can be inconvenient.  With random access 
files one can write all the chunks then go back to fix up the FORM size.  With 
stream access, the FORM size must be calculated before the file is written.  
When compression is involved, this can be slow or inconvenient.  Perhaps we 
can define an "END " chunk.  The stream writer would use -1 ($FFFFFFFF) as 
the FORM size.  The reader would follow each chunk, when the reader reaches 
an "END ", it would terminate the last -1 sized chunk.  Certain new IFF FORMs 
could require that readers understand "END ".


Appendix A.  Reference

Type Definitions

The following C typedefs describe standard IFF structures.  Declarations to 
use in practice will vary with the CPU and compiler.  For example, 68000 
Lattice C produces efficient comparison code if we define ID as a "LONG".  A 
macro "MakeID" builds these IDs at compile time.

/* Standard IFF types, expressed in 68000 Lattice C.*/

typedef unsigned char UBYTE;/*  8 bits unsigned*/
typedef short WORD;/* 16 bits signed*/
typedef unsigned short UWORD;/* 16 bits unsigned*/
typedef long LONG;/* 32 bits signed*/

typedef char ID[4];/* 4 chars in ' ' through '~' */

typedef struct {
  LONGckSize;/* sizeof(ckData)*/
  UBYTEckData[/* ckSize */];
  } Chunk;

/* ID typedef and builder for 68000 Lattice C. */
typedef LONG ID; /* 4 chars in ' ' through '~'*/

#define MakeID(a,b,c,d) ( (a)<<24 | (b)<<16 | (c)<<8 | (d) )

/* Globally reserved IDs. */
#define ID_FORM   MakeID('F','O','R','M')
#define ID_LIST   MakeID('L','I','S','T')
#define ID_PROP   MakeID('P','R','O','P')
#define ID_CAT    MakeID('C','A','T',' ')
#define ID_FILLER MakeID(' ',' ',' ',' ')

Syntax Definitions

Here's a collection of the syntax definitions in this document.

Chunk     ::= ID #{ UBYTE* } [0]

Property     ::= Chunk

FORM     ::= "FORM" #{ FormType (LocalChunk | FORM | LIST | CAT)* }
FormType     ::= ID
LocalChunk   ::= Property | Chunk

CAT     ::= "CAT " #{ ContentsType (FORM | LIST | CAT)* }
ContentsType ::= ID-- a hint or an "abstract data type" ID

LIST     ::= "LIST" #{ ContentsType PROP* (FORM | LIST | CAT)* }
PROP     ::= "PROP" #{ FormType Property* }


In this extended regular expression notation, the token "#" represents a count 
of the following {braced} data bytes.  Literal items are shown in "quotes", 
[square bracketed items] are optional, and "*" means 0 or more instances.  A 
sometimes-needed pad byte is shown as "[0]".

Example Diagrams

Here's a box diagram for an example IFF file, a raster image FORM ILBM.  This 
FORM contains a bitmap header property chunk BMHD, a color map property chunk 
CMAP, and a raster data chunk BODY.  This particular raster is 320 x 200 
pixels x 3 bit planes uncompressed.  The "0" after the CMAP chunk represents 
a zero pad byte; included since the CMAP chunk has an odd length.  The text 
to the right of the diagram shows the outline that would be printed by the 
IFFCheck utility program for this particular file.

          |                            |
          |  'FORM'            24070   |  FORM 24070 ILBM
          |                            |
       -  +----------------------------+
       |  |                            |
       |  |    'ILBM'                  |  
       |  |                            |
       |  |  +----------------------+  |
       |  |  |  'BMHD'         20   |  |  .BMHD 20
       |  |  |----------------------|  |  
       |  |  |  320, 200, 0, 0, 3,  |  |
       |  |  |  0, 0, 0, ....       |  |
       |  |  +----------------------+  |
       |  |                            |
24070 <   |  +----------------------+  |
 bytes |  |  |  'CMAP'         21   |  |  .CMAP 21
       |  |  |----------------------|  |  
       |  |  |  0, 0, 0; 32, 0, 0;  |  |
       |  |  |  64, 0, 0 ...        |  |
       |  |  +----------------------+  |
       |  |     0                      |  
       |  |  +----------------------+  |
       |  |  |  'BODY'      24000   |  |
       |  |  |----------------------|  |  .BODY 24000
       |  |  |  0, 0, 0....         |  |
       |  |  +----------------------+  |
       |  |                            |
       -- +----------------------------+


This second diagram shows a LIST of two FORMs ILBM sharing a common BMHD 
property and a common CMAP property.  Again, the text on the right is an 
outline a la IFFCheck.

        | 'LIST'              48114    |
        |                              |
        |   'ILBM'                     |
        |                              |
        |  +------------------------+  |
        |  |  'PROP'        62      |  | .PROP 62 ILBM
        |  +------------------------+  |
        |  |                        |  |
        |  |  'ILBM'                |  |
        |  | +--------------------+ |  |
        |  | |  'BMHD'       20   | |  | ..BMHD 20
        |  | |--------------------| |  |
        |  | |  320, 200, 0, 0, 3,| |  |
        |  | |  0, 0, 0, ....     | |  |
        |  | +--------------------+ |  |
        |  |                        |  |
        |  | +--------------------+ |  |
        |  | |  'CMAP'       21   | |  | ..CMAP 21
        |  | |--------------------| |  |
        |  | |  0, 0, 0; 32, 0, 0;| |  |
        |  | |  64, 0, 0 ....     | |  |
        |  | +--------------------+ |  |
        |  |   0                    |  |
        |  +------------------------+  |
        |                              |
        |                              |
        |  +------------------------+  |
        |  |  'FORM'        24012   |  | .FORM 24012 ILBM
        |  +------------------------+  |
        |  |                        |  |
        |  |  'ILBM'                |  |
        |  | +--------------------+ |  |
        |  | |  'BODY'    24000   | |  | ..BODY 24000
        |  | |--------------------| |  |
        |  | |  0, 0, 0....       | |  |
        |  | +--------------------+ |  |
        |  +------------------------+  |
        |                              |
        |                              |
        |  +------------------------+  |
        |  |  'FORM'        24012   |  | .FORM 24012 ILBM
        |  +------------------------+  |
        |  |                        |  |
        |  |  'ILBM'                |  |
        |  | +--------------------+ |  |
        |  | |  'BODY'    24000   | |  | ..BODY 24000
        |  | |--------------------| |  |
        |  | |  0, 0, 0....       | |  |
        |  | +--------------------+ |  |
        |  +------------------------+  |
        |                              |
        |                              |

"ILBM" IFF Interleaved Bitmap

Date:January 17, 1986 (CRNG data updated Oct, 1988 by Jerry Morrison)
(Appendix E added and CAMG updated Oct, 1988 by Commodore-Amiga, Inc.)

From:Jerry Morrison, Electronic Arts

Status:Released and in use

1. Introduction

"EA IFF 85" is Electronic Arts' standard for interchange format files.  "ILBM" 
is a format for a 2 dimensional raster graphics image, specifically an 
InterLeaved bitplane BitMap image with color map.  An ILBM is an IFF "data 
section" or "FORM type", which can be an IFF file or a part of one.  ILBM 
allows simple, highly portable raster graphic storage.

An ILBM is an archival representation designed for three uses.  First, a stand-
alone image that specifies exactly how to display itself (resolution, size, 
color map, etc.).  Second, an image intended to be merged into a bigger 
picture which has its own depth, color map, and so on.  And third, an empty 
image with a color map selection or "palette" for a paint program.  ILBM is 
also intended as a building block for composite IFF FORMs like "animation 
sequences" and "structured graphics".  Some uses of ILBM will be to preserve as 
much information as possible across disparate environments.  Other uses will be 
to store data for a single program or highly cooperative programs while 
maintaining subtle details.  So we're trying to accomplish a lot with this one 

This memo is the IFF supplement for FORM ILBM.  Section 2 defines the purpose 
and format of property chunks bitmap header "BMHD", color map "CMAP", hotspot 
"GRAB", destination merge data "DEST", sprite information "SPRT", and 
Commodore Amiga viewport mode "CAMG".  Section 3 defines the standard data 
chunk "BODY".  These are the "standard" chunks.  Section 4 defines the non-
standard data chunks.  Additional specialized chunks like texture pattern can 
be added later.  The ILBM syntax is summarized in Appendix A as a regular 
expression and in Appendix B as a box diagram.  Appendix C explains the 
optional run encoding scheme.  Appendix D names the committee responsible for 
this FORM ILBM standard.

Details of the raster layout are given in part 3, "Standard Data Chunk".  Some 
elements are based on the Commodore Amiga hardware but generalized for use on 
other computers.  An alternative to ILBM would be appropriate for computers 
with true color data in each pixel, though the wealth of available ILBM images 
makes import and export important.


"EA IFF 85" Standard for Interchange Format Files describes the underlying 
conventions for all IFF files.
Amiga. is a registered trademark of Commodore-Amiga, Inc.
Electronic Arts(tm) is a trademark of Electronic Arts.
Macintosh(tm) is a trademark licensed to Apple Computer, Inc.
MacPaint(tm) is a trademark of Apple Computer, Inc.


2. Standard Properties

ILBM has several defined property chunks that act on the main data chunks.  
The required property "BMHD" and any optional properties must appear before 
any "BODY" chunk.  (Since an ILBM has only one BODY chunk, any following 
properties would be superfluous.)  Any of these properties may be shared over 
a LIST of several IBLMs by putting them in a PROP ILBM (See the EA IFF 85 


The required property "BMHD" holds a BitMapHeader as defined in the following 
documentation.  It describes the dimensions of the image, the encoding used, 
and other data necessary to understand the BODY chunk to follow.

    typedef UBYTE Masking;     /* Choice of masking technique. */

    #define mskNone                0
    #define mskHasMask             1
    #define mskHasTransparentColor 2
    #define mskLasso               3

    typedef UBYTE Compression;    /* Choice of compression algorithm 
       applied to the rows of all source and mask planes.  "cmpByteRun1"
       is the byte run encoding described in Appendix C.  Do not compress 
       across rows! */
    #define cmpNone        0
    #define cmpByteRun1    1

    typedef struct {
      UWORD       w, h;             /* raster width & height in pixels      */
      WORD        x, y;             /* pixel position for this image        */
      UBYTE       nPlanes;          /* # source bitplanes                   */
      Masking     masking;
      Compression compression;
      UBYTE       Flags;            /* CMAP flags (formerly pad1, unused)   */
      UWORD       transparentColor; /* transparent "color number" (sort of) */
      UBYTE       xAspect, yAspect; /* pixel aspect, a ratio width : height */
      WORD        pageWidth, pageHeight; /* source "page" size in pixels    */
    } BitMapHeader;

Fields are filed in the order shown.  The UBYTE fields are byte-packed (the C 
compiler must not add pad bytes to the structure).

The fields w and h indicate the size of the image rectangle in pixels.  Each 
row of the image is stored in an integral number of 16 bit words.  The number 
of words per row is words=((w+15)/16) or Ceiling(w/16).  The fields x and y 
indicate the desired position of this image within the destination picture. 
Some reader programs may ignore x and y.  A safe default for writing an ILBM 
is (x, y) = (0, 0).

The number of source bitplanes in the BODY chunk is stored in nPlanes.  An ILBM 
with a CMAP but no BODY and nPlanes = 0 is the recommended way to store a 
color map.

Note: Color numbers are color map index values formed by pixels in the 
destination bitmap, which may be deeper than nPlanes if a DEST chunk calls 
for merging the image into a deeper image.

The field masking indicates what kind of masking is to be used for this image. 
The value mskNone designates an opaque rectangular image.  The value mskHasMask 
means that a mask plane is interleaved with the bitplanes in the BODY chunk 
(see below).  The value mskHasTransparentColor indicates that pixels in the 
source planes matching transparentColor are to be considered "transparent". 
(Actually, transparentColor isn't a "color number" since it's matched with 
numbers formed by the source bitmap rather than the possibly deeper destination
bitmap.  Note that having a transparent color implies ignoring one of the color 
registers.  The value mskLasso indicates the reader may construct a mask by 
lassoing the image as in MacPaint(tm).  To do this, put a 1 pixel border of 
transparentColor around the image rectangle.  Then do a seed fill from this 
border.  Filled pixels are to be transparent.

Issue: Include in an appendix an algorithm for converting a transparent color 
to a mask plane, and maybe a lasso algorithm.

A code indicating the kind of data compression used is stored in compression. 
Beware that using data compression makes your data unreadable by programs that 
don't implement the matching decompression algorithm.  So we'll employ as few 
compression encodings as possible.  The run encoding byteRun1 is documented in 
Appendix C.

The field pad1 is a pad byte reserved for future use.  It must be set to 0 for 

The transparentColor specifies which bit pattern means "transparent".  This 
only applies if masking is mskHasTransparentColor or mskLasso.  Otherwise, 
transparentColor should be 0 (see above).

The pixel aspect ratio is stored as a ratio in the two fields xAspect and 
yAspect.  This may be used by programs to compensate for different aspects 
or to help interpret the fields w, h, x, y, pageWidth, and pageHeight, which 
are in units of pixels.  The fraction xAspect/yAspect represents a pixel's 
width/height.  It's recommended that your programs store proper fractions in 
the BitMapHeader, but aspect ratios can always be correctly compared with the 

xAspect * yDesiredAspect = yAspect * xDesiredAspect

Typical values for aspect ratio are width : height = 10 : 11  for an Amiga 320 
x 200 display and 1 : 1 for a Macintosh(tm) display.

The size in pixels of the source "page" (any raster device) is stored in 
pageWidth and pageHeight, e.g., (320, 200) for a low resolution Amiga display. 
This information might be used to scale an image or to automatically set the 
display format to suit the image.  Note that the image can be larger than the 


The optional (but encouraged) property "CMAP" stores color map data as 
triplets of red, green, and blue intensity values.  The n color map entries 
("color registers") are stored in the order 0 through n-1, totaling 3n bytes. 
Thus n is the ckSize/3.  Normally, n would equal 2^(nPlanes).

A CMAP chunk contains a ColorMap array as defined below.  Note that these 
typedefs assume a C compiler that implements packed arrays of 3-byte elements.

    typedef struct {
        UBYTE red, green, blue;           /* color intensities 0..255 */
        } ColorRegister;                  /* size = 3 bytes           */

    typedef ColorRegister ColorMap[n];    /* size = 3n bytes          */

The color components red, green, and blue represent fractional intensity 
values in the range 0 through 255 256ths.  White is (255, 255, 255) and black 
is (0, 0, 0).  If your machine has less color resolution, use the high order 
bits.  Shift each field right on reading (or left on writing) and assign it 
to (from) a field in a local packed format like Color4, below.  This achieves 
automatic conversion of images across environments with different color 
resolutions.  On reading an ILBM, use defaults if the color map is absent or 
has fewer color registers than you need.  Ignore any extra color registers.  
(See Appendix E for a better way to write colors)

The example type Color4 represents the format of a color register in the 
memory of an Amiga with the original graphics chip set, i.e., 4 bit video 
DACs.  (The ":4" tells smarter C compilers to pack the field into 4 bits.)

    typedef struct {
        unsigned pad1 :4, red :4, green :4, blue :4;
        } Color4;                      /*   Amiga RAM format.  Not filed.  */

With the latest generation of Amigas, there is a new complication.  Amigas 
with the AA chip set support a full 8 bits of color resolution.  In order 
to distinguish older CMAPs with 4-bit values from newer CMAP chunks with full 
8-bit values, use the high order bit of BMHD.Flags (1 << 7).  If this bit is 
set, it means this CMAP is a full 8-bit value.  If this bit is clear, it is 
probably an older 4-bit CMAP with values either left justified or scaled.

Remember that every chunk must be padded to an even length, so a color map 
with an odd number of entries would be followed by a 0 byte, not included 
in the ckSize.


The optional property "GRAB" locates a "handle" or "hotspot" of the image 
relative to its upper left corner, e.g., when used as a mouse cursor or a 
"paint brush".  A GRAB chunk contains a Point2D.

    typedef struct {
        WORD x, y;         /* relative coordinates (pixels) */
        } Point2D;


The optional property "DEST" is a way to say how to scatter zero or more 
source bitplanes into a deeper destination image.  Some readers may ignore DEST.

The contents of a DEST chunk is DestMerge structure:

   typedef struct {
       UBYTE depth;      /* # bitplanes in the original source               */
       UBYTE pad1;       /* unused; for consistency put 0 here               */
       UWORD planePick;  /* how to scatter source bitplanes into destination */
       UWORD planeOnOff; /* default bitplane data for planePick              */
       UWORD planeMask;  /* selects which bitplanes to store into            */
       } DestMerge;

The low order depth number of bits in planePick, planeOnOff, and planeMask 
correspond one-to-one with destination bitplanes.  Bit 0 with bitplane 0, etc. 
(Any higher order bits should be ignored.)  "1" bits in planePick mean "put 
the next source bitplane into this bitplane", so the number of "1" bits should 
equal nPlanes.  "0" bits mean "put the corresponding bit from planeOnOff into 
this bitplane".  Bits in planeMask gate writing to the destination bitplane: 
"1" bits mean "write to this bitplane" while "0" bits mean "leave this 
bitplane alone".  The normal case (with no DEST property) is equivalent to 
planePick = planeMask = 2^(nPlanes) - 1.

Remember that color numbers are formed by pixels in the destination bitmap 
(depth planes deep) not in the source bitmap (nPlanes planes deep).


The presence of an "SPRT" chunk indicates that this image is intended as a 
sprite.  It's up to the reader program to actually make it a sprite, if even 
possible, and to use or overrule the sprite precedence data inside the SPRT 

    typedef UWORD SpritePrecedence; /* relative precedence, 0 is the highest */

Precedence 0 is the highest, denoting a sprite that is foremost.

Creating a sprite may imply other setup.  E.g., a 2 plane Amiga sprite would 
have transparentColor = 0.  Color registers 1, 2, and 3 in the CMAP would be 
stored into the correct hardware color registers for the hardware sprite 
number used, while CMAP color register 0 would be ignored.


A "CAMG" chunk is specifically for Commodore Amiga ILBM's.  All Amiga-based 
reader and writer software should deal with CAMG.  The Amiga supports many 
different video display modes including interlace, Extra Halfbrite, hold and 
modify (HAM), plus a variety of new modes under the 2.0 operating system.
A CAMG chunk contains a single long word (length=4) which specifies the Amiga 
display mode of the picture.

Prior to 2.0, it was possible to express all available Amiga ViewModes in 16 
bits of flags (Viewport->Modes or NewScreen->ViewModes).  Old-style writers and 
readers place a 16-bit Amiga ViewModes value in the low word of the CAMG, and 
zeros in the high word.  The following Viewmode flags should always be 
removed from old-style 16-bit ViewModes values when writing or reading them:

          GENLOCK_AUDIO | GENLOCK_VIDEO (=0x7102, mask=0x8EFD)

New ILBM readers and writers, should treat the full CAMG longword as a 32-bit 
ModeID to support new and future display modes.

New ILBM writers, when running under the 2.0 Amiga operating system, should 
directly store the full 32-bit return value of the graphics function 
GetVPModeID(vp) in the CAMG longword.  When running under 1.3, store a 16-bit 
Viewmodes value masked as described above.

ILBM readers should only mask bits out of a CAMG if the CAMG has a zero upper 
word (see exception below).  New ILBM readers, when running under 2.0, should 
then treat the 32-bit CAMG value as a ModeID, and should use the graphics 
ModeNotAvailable() function to determine if the mode is available.  If the 
mode is not available, fall back to another suitable display mode.  When 
running under 1.3, the low word of the CAMG may generally be used to open a 
compatible display.

Note that one popular graphics package stores junk in the upper word of the 
CAMG of brushes, and incorrect values (generally zero) in the low word.  You 
can screen for such junk values by testing for non-zero in the upper word of 
a ModeID in conjunction with the 0x00001000 bit NOT set in the low word.

The following code fragment demonstrates ILBM reader filtering of
inappropriate bits in 16-bit CAMG values.

#include <graphics/view.h>
#include <graphics/displayinfo.h>

/* Knock bad bits out of old-style CAMG modes before checking availability.
 * (some ILBM CAMG's have these bits set in old 1.3 modes, and should not)
 * If not an extended monitor ID, or if marked as extended but missing
 * upper 16 bits, screen out inappropriate bits now.
if((!(modeid & MONITOR_ID_MASK)) ||
    ((modeid & EXTENDED_MODE)&&(!(modeid & 0xFFFF0000))))
   modeid &= 

/* Check for bogus CAMG like some brushes have, with junk in
 * upper word and extended bit NOT set not set in lower word.
if((modeid & 0xFFFF0000)&&(!(modeid & EXTENDED_MODE)))
    /* Bad CAMG, so ignore CAMG and determine a mode based on 
     * based on pagesize or aspect
     modeid = NULL;
     if(wide >= 640) modeid |= HIRES;
     if(high >= 400) modeid |= LACE;

/* Now, ModeNotAvailable() may be used to determine if the mode is available.
 * If the mode is not available, you may prompt the user for a mode
 * choice, or search the 2.0 display database for an appropriate
 * replacement mode, or you may be able to get a relatively compatible
 * old display mode by masking out all bits except

   if((modeid & 0xFFFF0000)&&(!(modeid & 0x00001000)))
/* Then probably invalid ModeID */

3. Standard "BODY" Data Chunk

Raster Layout

Raster scan proceeds left-to-right (increasing X) across scan lines, then 
top-to-bottom (increasing Y) down columns of scan lines.  The coordinate 
system is in units of pixels, where (0,0) is the upper left corner.

The raster is typically organized as bitplanes in memory.  The corresponding 
bits from each plane, taken together, make up an index into the color map 
which gives a color value for that pixel.  The first bitplane, plane 0, is 
the low order bit of these color indexes.

A scan line is made of one "row" from each bitplane.  A row is one plane's bits 
for one scan line, but padded out to a word (2 byte) boundary (not necessarily 
the first word boundary).  Within each row, successive bytes are displayed in 
order and the most significant bit of each byte is displayed first.

A "mask" is an optional "plane" of data the same size (w, h) as a bitplane.  
It tells how to "cut out" part of the image when painting it onto another 
image.  "One" bits in the mask mean "copy the corresponding pixel to the 
destination".  "Zero" mask bits mean "leave this destination pixel alone".  In 
other words, "zero" bits designate transparent pixels.

The rows of the different bitplanes and mask are interleaved in the file (see 
below).  This localizes all the information pertinent to each scan line.  It 
makes it much easier to transform the data while reading it to adjust the 
image size or depth.  It also makes it possible to scroll a big image by 
swapping rows directly from the file without the need for random-access to 
all the bitplanes.


The source raster  is stored in a "BODY" chunk.  This one chunk holds all 
bitplanes and the optional mask, interleaved by row.

The BitMapHeader, in a BMHD property chunk, specifies the raster's dimensions 
w, h, and nPlanes.  It also holds the masking field which indicates if there 
is a mask plane and the compression field which indicates the compression 
algorithm used.  This information is needed to interpret the BODY chunk, so 
the BMHD chunk must appear first.  While reading an ILBM's BODY, a program 
may convert the image to another size by filling (with transparentColor) or 

The BODY's content is a concatenation of scan lines.  Each scan line is a 
concatenation of one row of data from each plane in order 0 through nPlanes-1 
followed by one row from the mask (if masking = hasMask ).  If the BitMapHeader 
field compression is cmpNone, all h rows are exactly (w+15)/16 words wide. 
Otherwise, every row is compressed according to the specified algorithm and 
the stored widths depend on the data compression.

Reader programs that require fewer bitplanes than appear in a particular ILBM 
file can combine planes or drop the high-order (later) planes.  Similarly, 
they may add bitplanes and/or discard the mask plane.

Do not compress across rows, and don't forget to compress the mask just like 
the bitplanes.  Remember to pad any BODY chunk that contains an odd number of 
bytes and skip the pad when reading.

4. Nonstandard Data Chunks

The following data chunks were defined after various programs began using FORM 
ILBM so they are "nonstandard" chunks.  See the registry document for the 
latest information on additional non-standard chunks.


A "CRNG" chunk contains "color register range" information.  It's used by 
Electronic Arts' Deluxe Paint program to identify a contiguous range of 
color registers for a "shade range" and color cycling.  There can be zero or 
more CRNG chunks in an ILBM, but all should appear before the BODY chunk. 
Deluxe Paint normally writes 4 CRNG chunks in an ILBM when the user asks it 
to "Save Picture".

    typedef struct {
        WORD  pad1;      /* reserved for future use; store 0 here    */
        WORD  rate;      /* color cycle rate                         */
        WORD  flags;     /* see below                                */
        UBYTE low, high; /* lower and upper color registers selected */
        } CRange;

The bits of the flags word are interpreted as follows: if the low bit is set 
then the cycle is "active", and if this bit is clear it is not active.  
Normally, color cycling is done so that colors move to the next higher 
position in the cycle, with the color in the high slot moving around to the
low slot.  If the second bit of the flags word is set, the cycle moves in the 
opposite direction.  As usual, the other bits of the flags word are reserved 
for future expansion.  Here are the masks to test these bits:

#define RNG_ACTIVE  1
#define RNG_REVERSE 2

The fields low and high indicate the range of color registers (color numbers) 
selected by this CRange.

The field active indicates whether color cycling is on or off.  Zero means off.

The field rate determines the speed at which the colors will step when color 
cycling is on.  The units are such that a rate of 60 steps per second is 
represented as 2^14 = 16384.  Slower rates can be obtained by linear scaling: 
for 30 steps/second, rate = 8192; for 1 step/second, rate = 16384/60 * 273.


Commodore's Graphicraft program uses a similar chunk "CCRT" (for Color 
Cycling Range and Timing).  This chunk contains a CycleInfo structure.

    typedef struct {
        WORD  direction;    /* 0 = don't cycle.  1 = cycle forwards      */
                            /* (1, 2, 3). -1 = cycle backwards (3, 2, 1) */
        UBYTE start, end;   /* lower and upper color registers selected  */
        LONG  seconds;      /* # seconds between changing colors plus... */
        LONG  microseconds; /* # microseconds between changing colors    */
        WORD  pad;          /* reserved for future use; store 0 here     */
        } CycleInfo;

This is very similar to a CRNG chunk.  A program would probably only use one 
of these two methods of expressing color cycle data, new programs should use 
CRNG.  You could write out both if you want to communicate this information 
to both Deluxe Paint and Graphicraft.

Appendix A. ILBM Regular Expression

Here's a regular expression summary of the FORM ILBM syntax.  This could be an 
IFF file or a part of one.

    CRNG* CCRT* [BODY]    }

BMHD ::= "BMHD" #{    BitMapHeader    }
CMAP ::= "CMAP" #{    (red green blue)*    } [0]
GRAB ::= "GRAB" #{    Point2D    }
DEST ::= "DEST" #{    DestMerge    }
SPRT ::= "SPRT" #{    SpritePrecedence    }
CAMG ::= "CAMG" #{    LONG    }

CRNG ::= "CRNG" #{    CRange    }
CCRT ::= "CCRT" #{    CycleInfo    }
BODY ::= "BODY" #{    UBYTE*    } [0]

The token "#" represents a ckSize LONG count of the following {braced} data 
bytes.  E.g., a BMHD's "#" should equal sizeof(BitMapHeader).  Literal strings 
are shown in "quotes", [square bracket items] are optional, and "*" means 0 
or more repetitions.  A sometimes-needed pad byte is shown as "[0]".

The property chunks BMHD, CMAP, GRAB, DEST, SPRT, CAMG and any CRNG and CCRT 
data chunks may actually be in any order but all must appear before the BODY 
chunk since ILBM readers usually stop as soon as they read the BODY.  If any 
of the 6 property chunks are missing, default values are inherited from any 
shared properties (if the ILBM appears inside an IFF LIST with PROPs) or from 
the reader program's defaults.  If any property appears more than once, the 
last occurrence before the BODY is the one that counts since that's the one 
that modifies the BODY.

Appendix B. ILBM Box Diagram

Here's a box diagram for a simple example: an uncompressed image 320 x 200 
pixels x 3 bitplanes.  The text to the right of the diagram shows the outline 
that would be printed by the Sift utility program for this particular file.

          |                    |
          |  'FORM'     24070  |  FORM 24070 ILBM
          |                    |
       -- +--------------------+ 
       |  |                    |
       |  |  'ILBM'            |
       |  |                    |
       |  | +----------------+ |
       |  | |  'BMHD'    20  | |  .BMHD 20
       |  | |----------------| |  
       |  | |  320, 200,     | |
       |  | |  0 ....        | |
       |  | +----------------+ |
       |  |                    |
       |  | +----------------+ |
24070 <   | |  'CMAP'    21  | |  .CMAP 21
 bytes |  | |----------------| |  
       |  | |  0, 0, 0,      | |
       |  | |  255 ....      | |
       |  | +----------------+ |
       |  |   0                |  
       |  | +----------------+ |
       |  | |  'BODY'  24000 | |  .BODY 24000
       |  | |----------------| |  
       |  | |  0, 0, 0....   | |
       |  | +----------------+ |
       |  |                    |
       -- +--------------------+

The "0" after the CMAP chunk is a pad byte.


Appendix C. IFF Hints

Hints on ILBM files from Jerry Morrison, Oct 1988.  How to avoid some 
pitfalls when reading ILBM files:

oDon't ignore the BitMapHeader.masking field.  A bitmap with a mask 
(such as a partially-transparent DPaint brush or a DPaint picture with 
a stencil) will read as garbage if you don't de-interleave the mask.

oDon't assume all images are compressed.  Narrow images aren't usually 
run-compressed since that would actually make them longer.

oDon't assume a particular image size.  You may encounter overscan 
pictures and PAL pictures.

There's a better way to read a BODY than the example IFF code.  The GetBODY 
routine should call a GetScanline routine once per scan line, which calls a 
GetRow routine for each bitplane in the file.  This in turn calls a 
GetUnpackedBytes routine, which calls a GetBytes routine as needed and unpacks 
the result.  (If the picture is uncompressed, GetRow calls GetBytes directly.) 
Since theunpacker knows how many packed bytes to read, this avoids juggling 
buffers for a memory-to-memory UnPackBytes routine.

Caution: If you make many AmigaDOS calls to read or write a few bytes at a 
time, performance will be mud! AmigaDOS has a high overhead per call, even 
with RAM disk.  So use buffered read/write routines.

Different hardware display devices have different color resolutions:

    Device        R:G:B bits    maxColor
    -------       ----------    --------
    Mac SE            1              1
    IBM EGA       2:2:2              3
    Atari ST      3:3:3              7
    Amiga         4:4:4             15 
    CD-I          5:5:5             31
    IBM VGA       6:6:6             63
    Mac II        8:8:8            255

An ILBM CMAP defines 8 bits of Red, Green and Blue (i.e., 8:8:8 bits of R:G:B). 
When displaying on hardware which has less color resolution, just take the 
high order bits.  For example, to convert ILBM's 8-bit Red to the Amiga's 
4-bit Red, right shift the data by 4 bits  (R4 := R8 >> 4).

To convert hardware colors to ILBM colors, the ILBM specification says just 
set the high bits (R8 := R4 << 4).  But you can transmit higher contrast to 
foreign display devices by scaling the data [0..maxColor] to the full range 
[0..255].  In other words, R8 := (Rn x 255) w maxColor.  (Example #1:  EGA 
color 1:2:3 scales to 85:170:255.  Example #2:  Amiga 15:7:0 scales to
255:119:0).  This makes a big difference where maxColor is less than 15.  In 
the extreme case, Mac SE white (1) should be converted to ILBM white (255), 
not to ILBM gray (128).


CGA and EGA subtleties

IBM EGA colors in 350 scan line mode are 2:2:2 bits of R:G:B, stored in memory 
as xxR'G'B'RBG.  That's 3 low-order bits followed by 3 high-order bits.

IBM CGA colors are 4 bits stored in a byte as xxxxIRGB.  (EGA colors in 200 
scan line modes are the same as CGA colors, but stored in memory as xxxIxRGB.) 
That's 3 high-order bits (one for each of R, G, and B) plus one low-order "
Intensity" bit for all 3 components R, G, and B.  Exception: IBM monitors show 
IRGB = 0110 as brown, which is really the EGA color R:G:B = 2:1:0, not dark 
yellow 2:2:0.

24-bit ILBMS

When storing deep images as ILBMs (such as images with 8 bits each of R,G, 
and B), the bits for each pixel represent an absolute RGB value for that 
pixel rather than an index into a limited color map.  The order for saving 
the bits is critical since a deep ILBM would not contain the usual CMAP of 
RGB values (such a CMAP would be too large and redundant).

To interpret these "deep" ILBMs, it is necessary to have a standard order in 
which the bits of the R, G, and B values will be stored.  A number of 
different orderings have already been used in deep ILBMs, so it was necessary 
to us chose one of these orderings as a standard.

The following bit ordering has been chosen as the default bit ordering for 
deep ILBMs.

Default standard deep ILBM bit ordering:
saved first -------------------------------------------------> saved last
R0 R1 R2 R3 R4 R5 R6 R7  G0 G1 G2 G3 G4 G5 G6 G7  B0 B1 B2 B3 B4 B5 B6 B7

One other existing deep bit ordering that you may encounter is the 21-bit 
NewTek format.

NewTek deep ILBM bit ordering:
saved first ------------------------------------------------------> saved last
R7 G7 B7  R6 G6 B6  R5 G5 B5  R4 G4 B4  R3 G3 B3  R2 G2 B2  R1 G1 B1  R0 G0 B0

Note that you may encounter CLUT chunks in deep ILBM's.  See the Third Party 
Specs appendix for more information on CLUT chunks.


Appendix D. ByteRun1 Run Encoding

The run encoding scheme byteRun1 is best described by pseudo code for the 
decoder Unpacker (called UnPackBits in the Macintosh(tm) toolbox):

    LOOP until produced the desired number of bytes
        Read the next source byte into n
        SELECT n FROM
            [0..127]   => copy the next n+1 bytes literally
            [-1..-127] => replicate the next byte -n+1 times
            -128       => no operation

In the inverse routine Packer, it's best to encode a 2 byte repeat run as a 
replicate run except when preceded and followed by a literal run, in which 
case it's best to merge the three into one literal run.  Always encode 3 byte 
repeats as replicate runs.

Remember that each row of each scan line of a raster is separately packed.


           Intro to IFF Amiga ILBM Files and Amiga Viewmodes

The IFF (Interchange File Format) for graphic images on the Amiga is called 
FORM ILBM (InterLeaved BitMap).  It follows a standard parsable IFF format.

Sample hex dump of beginning of an ILBM:

Important note!  You can NOT ever depend on any particular ILBM chunk being 
at any particular offset into the file!  IFF files are composed, in their 
simplest form, of chunks within a FORM.  Each chunk starts starts with a 
4-letter chunkID, followed by a 32-bit length of the rest of the chunk.  You 
PARSE IFF files, skipping past unneeded or unknown chunks by seeking their 
length (+1 if odd length) to the next 4-letter chunkID.

0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD
0010: 00000014 01400190 00000000 06000100    .....@..........
0020: 00000A0B 01400190 43414D47 00000004    [email protected]....
0030: 00000804 434D4150 00000030 001100EE    ....CMAP...0....
0040: EEEE0000 22000055 33333355 55550033    .... ..P000PPP.0
0050: 99885544 77777711 66EE2266 EE6688DD    ..P@ppp.`. `.`..
0060: AAAAAAAA 99EECCCC CCDDAAEE 424F4459    ............BODY
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...  etc.


      'F O R M' length  'I L B M''B M H D'<-start of BitMapHeader chunk
0000: 464F524D 00016418 494C424D 424D4844    FORM..d.ILBMBMHD

       length  WideHigh XorgYorg PlMkCoPd <- Planes Mask Compression Pad
0010: 00000014 01400190 00000000 06000100    .....@..........

      TranAspt PagwPagh 'C A M G' length  <- start of C-AMiGa View modes chunk
0020: 00000A0B 01400190 43414D47 00000004    [email protected]....

      Viewmode 'C M A P' length  R g b R  <- Viewmode 800=HAM | 4=LACE 
0030: 00000804 434D4150 00000030 001100EE    ....CMAP...0....

      g b R g  b R g b  R g b R  g b R g  <- Rgb's are for reg0 thru regN
0040: EEEE0000 22000055 33333355 55550033    .... ..P000PPP.0

      b R g b  R g b R  g b R g  b R g b
0050: 99885544 77777711 66EE2266 EE6688DD    ..P@ppp.`. `.`..

      R g b R  g b R g  b R g b 'B O D Y' 
0060: AAAAAAAA 99EECCCC CCDDAAEE 424F4459    ............BODY

       length   start of body data        <- Compacted (Compression=1 above)
0070: 000163AC F8000F80 148A5544 2ABDEFFF    ..c.......UD*...
0080: FFBFF800 0F7FF7FC FF04F85A 77AD5DFE    ...........Zw.].  etc.

Notes on CAMG Viewmodes:  HIRES=0x8000  LACE=0x4  HAM=0x800  HALFBRITE=0x80


Interpreting ILBMs
ILBM is a fairly simple IFF FORM.  All you really need to deal with to 
extract the image are the following chunks:

(Note - Also watch for AUTH Author chunks and (c) Copyright chunks
 and preserve any copyright information if you rewrite the ILBM)

   BMHD - info about the size, depth, compaction method
          (See interpreted hex dump above)

   CAMG - optional Amiga viewmodes chunk
          Most HAM and HALFBRITE ILBMs should have this chunk.  If no
          CAMG chunk is present, and image is 6 planes deep, assume
          HAM and you'll probably be right.  Some Amiga viewmodes
          flags are HIRES=0x8000, LACE=0x4, HAM=0x800, HALFBRITE=0x80.
  Note that new Amiga 2.0 ILBMs may have more complex 32-bit
  numbers (modeid) stored in the CAMG.  However, the bits
  described above should get you a compatible old viewmode.
   CMAP - RGB values for color registers 0 to n
          (each component left justified in a byte)
  If a deep ILBM (like 12 or 24 planes), there should be no CMAP
  and instead the BODY planes are interpreted as the bits of RGB
  in the order R0...Rn G0...Gn B0...Bn

   BODY - The pixel data, stored in an interleaved fashion as follows:
          (each line individually compacted if BMHD Compression = 1)
             plane 0 scan line 0
             plane 1 scan line 0
             plane 2 scan line 0
             plane n scan line 0
             plane 0 scan line 1
             plane 1 scan line 1

Body Compression

The BODY contains pixel data for the image.  Width, Height, and depth 
(Planes) is specified in the BMHD.

If the BMHD Compression byte is 0, then the scan line data is not compressed.
If Compression=1, then each scan line is individually compressed as follows:

   More than 2 bytes the same stored as BYTE code value n from  -1 to -127 
     followed by byte to be repeated (-n) + 1 times.
   Varied bytes stored as BYTE code n from 0 to 127 followed by n+1 bytes 
     of data.
   The byte code -128 is a NOP.


Interpreting the Scan Line Data:

If the ILBM is not HAM or HALFBRITE, then after parsing and uncompacting if 
necessary, you will have N planes of pixel data.  Color register used for 
each pixel is specified by looking at each pixel thru the planes.  I.e., 
if you have 5 planes, and the bit for a particular pixel is set in planes 
0 and 3:

       PLANE     4 3 2 1 0
       PIXEL     0 1 0 0 1

then that pixel uses color register binary 01001 = 9

The RGB value for each color register is stored in the CMAP chunk of the 
ILBM, starting with register 0, with each register's RGB value stored as
one byte of R, one byte G, and one byte of B, with each component scaled
to 8-bits.  (ie. 4-bit Amiga R, G, and B components are each stored in the
high nibble of a byte.  The low nibble may also contain valid data if the 
color was stored with 8-bit-per-gun color resolution).

BUT - if the picture is HAM or HALFBRITE, it is interpreted differently.
                        ===    =========

Hopefully, if the picture is HAM or HALFBRITE, the package that saved it 
properly saved a CAMG chunk (look at a hex dump of your file with ACSII 
interpretation - you will see the chunks - they all start with a 4-ASCII-
character chunk ID).  If the picture is 6 planes deep and has no CAMG chunk, 
it is probably HAM.   If you see a CAMG chunk, the "CAMG" is followed by the 
32-bit chunk length, and then the 32-bit Amiga Viewmode flags.  

HAM pics with a 16-bit CAMG will have the 0x800 bit set in CAMG ViewModes.
HALBRITE pics will have the 0x80 bit set.

To transport a HAM or HALFBRITE picture to another machine, you must 
understand how HAM and HALFBRITE work on the Amiga.

How Amiga HAM mode works:

Amiga HAM (Hold and Modify) mode lets the Amiga display all 4096 RGB values.  
In HAM mode, the bits in the two last planes describe an R G or B 
modification to the color of the previous pixel on the line to create the 
color of the current pixel.  So a 6-plane HAM picture has 4 planes for 
specifying absolute color pixels giving up to 16 absolute colors which would 
be specified in the ILBM CMAP chunk.  The bits in the last two planes are 
color modification bits which cause the Amiga, in HAM mode, to take the RGB 
value of the previous pixel (Hold and), substitute the 4 bits in planes 0-3 
for the previous color's R G or B component (Modify) and display the result 
for the current pixel.  If the first pixel of a scan line is a modification 
pixel, it modifies the RGB value of the border color (register 0).  The color 
modification bits in the last two planes (planes 4 and 5) are interpreted as 

   00 - no modification.  Use planes 0-3 as normal color register index
   10 - hold previous, replacing Blue component with bits from planes 0-3 
   01 - hold previous, replacing Red component with bits from planes 0-3
   11 - hold previous. replacing Green component with bits from planes 0-3

How Amiga HALFBRITE mode works:

This one is simpler.  In HALFBRITE mode, the Amiga interprets the bit in the 
last plane as HALFBRITE modification.  The bits in the other planes are 
treated as normal color register numbers (RGB values for each color register 
is specified in the CMAP chunk).  If the bit in the last plane is set (1), 
then that pixel is displayed at half brightness.  This can provide up to 64 
absolute colors.

Other Notes:

Amiga ILBMs images must be a even number of bytes wide.  Smaller images (such 
as brushes) are padded to an even byte width.

ILBMs created with Electronic Arts IBM and Amiga "DPaintII" packages are 
compatible (though you may have to use a '.lbm' filename extension on an 
IBM).  The ILBM graphic files may be transferred between the machines (or 
between the Amiga and IBM sides your Amiga if you have a CBM Bridgeboard 
card installed) and loaded into either package.

Color Lookup Table chunk

TITLE: CLUT IFF chunk proposal

"CLUT" IFF 8-Bit Color Look Up Table

Date:July 2, 1989
From:Justin V. McCormick
Status:Public Proposal
Supporting Software:  FG 2.0 by Justin V. McCormick for PP&S


  This memo describes the IFF supplement for the new chunk "CLUT".


  A CLUT (Color Look Up Table) is a special purpose data module
containing table with 256 8-bit entries.  Entries in this table
can be used directly as a translation for one 8-bit value to


  To store 8-bit data look up tables in a simple format for
later retrieval.  These tables are used to translate or bias
8-bit intensity, contrast, saturation, hue, color registers, or
other similar data in a reproducable manner.


/* Here is the IFF chunk ID macro for a CLUT chunk */
#define ID_CLUT MakeID('C','L','U','T')

 * Defines for different flavors of 8-bit CLUTs.
#define CLUT_MONO0L/* A Monochrome, contrast or intensity LUT */
#define CLUT_RED1L/* A LUT for reds */
#define CLUT_GREEN2L/* A LUT for greens */
#define CLUT_BLUE3L/* A LUT for blues */
#define CLUT_HUE4L/* A LUT for hues */
#define CLUT_SAT5L/* A LUT for saturations */
#define CLUT_UNUSED66L/* How about a Signed Data flag */
#define CLUT_UNUSED77L/* Or an Assumed Negative flag*/

/* All types > 7 are reserved until formally claimed */
#define CLUT_RESERVED_BITS 0xfffffff8L

/* The struct for Color Look-Up-Tables of all types */
typedef struct
  ULONG type;/* See above type defines */
  UBYTE lut[256];/* The 256 byte look up table */
} ColorLUT;

CLUT Example:

  Normally, the CLUT chunk will appear after the BMHD of an FORM
ILBM before the BODY chunk, in the same "section" as CMAPs are
normally found.  However, a FORM may contain only CLUTs with no
other supporting information.

  As a general guideline, it is desirable to group all CLUTs
together in a form without other chunk types between them.
If you were using CLUTs to store RGB intensity corrections, you
would write three CLUTs in a row, R, G, then B.

  Here is a box diagram for a 320x200x8 image stored as an IFF ILBM
with a single CLUT chunk for intensity mapping:

      |'FORM'64284    |     FORM 64284 ILBM
      |'ILBM'    |     
      | +-------------------------------+ |   
      | | 'BMHD'20  | |     .BMHD 20
      | | 320, 200, 0, 0, 8, 0, 0, ...  | |   
      | | ------------------------------+ |   
      | | 'CLUT'264          | |     .CLUT 264
      | | 0, 0, 0; 32, 0, 0; 64,0,0; .. | |   
      | +-------------------------------+ |   
      | +-------------------------------+ |   
      | |'BODY'64000     | |     .BODY 64000
      | |0, 0, 0, ...  | |   
      | +-------------------------------+ |   

Design Notes:

  I have deliberately kept this chunk simple (KISS) to
facilitate implementation.  In particular, no provision is made
for expansion to 16-bit or 32-bit tables.  My reasoning is that
a 16-bit table can have 64K entries, and thus would benefit from
data compression.  My suggestion would be to propose another
chunk or FORM type better suited for large tables rather than
small ones like CLUT.


Newtek Dynamic Ham color chunks

Newtek for Digiview IV (dynamic Ham)

ILBM.DYCP - dynamic color palette
3 longwords (file setup stuff)

ILBM.CTBL - array of words, one for each color (0rgb)
Dots per inch chunk


ILBM DPI chunk (1-16-90)

ILBM.DPI   Dots Per Inch   to allow output of an image at the
same resolution it was scanned at

typedef struct {
UWORD dpi_x;
UWORD dpi_y;
} DPIHeader ;

For example, an image scanned at horizontal resolution of
240dpi and vertical resolution of 300dpi would be saved as:

44504920 00000004 00F0 012C
D P I    size     dpi_x dpi_y

DPaint perspective chunk (EA)


Form/Chunk ID:   Chunk DPPV  (DPaint II ILBM perspective chunk)
Date Submitted:  12/86
Submitted by:    Dan Silva 

Chunk Description:

   The DPPV chunk describes the perspective state in a DPaintII ILBM.

Chunk Spec:

/* The chunk identifier DPPV */
#define ID_DPPV    MakeID('D','P','P','V')

typedef LONG LongFrac;
typedef struct ( LongFrac x,y,z; )  LFPoint;
typedef LongFrac  APoint[3];

typedef union {
   LFPoint l;
   APoint  a;
   } UPoint;

/* values taken by variable rotType */
#define ROT_EULER  0
#define ROT_INCR   1

/* Disk record describing Perspective state */

typedef struct {
   WORD     rotType;           /* rotation type */
   WORD     iA, iB, iC;        /* rotation angles (in degrees) */
   LongFrac Depth;             /* perspective depth */
   WORD     uCenter, vCenter;  /* coords of center perspective,
                                * relative to backing bitmap,
                                * in Virtual coords
   WORD     fixCoord;          /* which coordinate is fixed */
   WORD     angleStep;         /* large angle stepping amount */
   UPoint   grid;              /* gridding spacing in X,Y,Z */
   UPoint   gridReset;         /* where the grid goes on Reset */
   UPoint   gridBrCenter;      /* Brush center when grid was last on,
                                * as reference point
   UPoint   permBrCenter;      /* Brush center the last time the mouse
                                * button was clicked, a rotation performed,
                                * or motion along "fixed" axis
   LongFrac rot[3][3];         /* rotation matrix */
   } PerspState;

DPaint II   by Dan Silva for Electronic Arts


DPaint IV enhanced color cycle chunk (EA)


Submitted by Lee Taran


Enhanced Color Cycling Capabilities
   * DPaintIV supports a new color cycling model which does NOT
     require that color cycles contain a contiguous range of color

     For example:
       If your range looks like:  [1][3][8][2]
       then at each cycle tick
          temp = [2], 
          [2] = [8],
          [8] = [3],
          [3] = [1], 
          [1] = temp

   * You can now cycle a single register thru a series of rgb values.
     For example:
        If your range looks like: [1] [orange] [blue] [purple]
        then at each cycle tick color register 1 will take on the
        next color in the cycle.
        ie:  t=0:  [1] = curpal[1]
             t=1:  [1] = purple
             t=2:  [1] = blue
             t=3:  [1] = orange
             t=4:  goto t=0

   * You can combine rgb cycling with traditional color cycling.
     For example:
         Your range can look like:
             [1] [orange] [blue] [2] [green] [yellow]

         t=0: [1] = curpal[1], [2] = curpal[2]
         t=1: [1] = yellow,    [2] = blue
         t=2: [1] = green,     [2] = orange
         t=3: [1] = curpal[2], [2] = curpal[1]
         t=4: [1] = blue,      [2] = yellow
         t=5: [1] = orange,    [2] = green
         t=6: goto t=0

   * DPaint will save out an old style range CRNG if the range fits
     the CRNG model otherwise it will save out a DRNG chunk. 
   * no thought has been given (yet) to interlocking cycles

/* --------------------------------------------------------------------- 

 IFF Information:  DPaintIV DRNG chunk

          DRNG ::= "DRNG" # { DRange DColor* DIndex* }
 a <cell> is where the color or register appears within the range     

 The RNG_ACTIVE flags is set when the range is cyclable. A range 
 should only have the RNG_ACTIVE if it:
      1> contains at least one color register
      2> has a defined rate 
      3> has more than one color and/or color register
 If the above conditions are met then RNG_ACTIVE is a user/program 
 preference.  If the bit is NOT set the program should NOT cycle the

 The RNG_DP_RESERVED flags should always be 0!!!
 --------------------------------------------------------------------- */
typedef struct {
   UBYTE min;           /* min cell value */
   UBYTE max;           /* max cell value */
   SHORT rate;          /* color cycling rate, 16384 = 60 steps/second */
   SHORT flags;         /* 1=RNG_ACTIVE,4=RNG_DP_RESERVED */
   UBYTE ntrue;         /* number of DColor structs to follow */
   UBYTE nregs;         /* number of DIndex structs to follow */
   } DRange;           

typedef struct { UBYTE cell; UBYTE r,g,b; } DColor; /* true color cell */
typedef struct { UBYTE cell; UBYTE index; } DIndex; /* color register cell */


Encapsulated Postscript chunk


Pixelations   Kevin Saltzman   617-277-5414

Chunk to hold encapsulated postscript

Used by PixelScript in their clip art.  Holds a postscript
representation of the ILBM's graphic image.

EPSF length
   ; Bounding box
   WORD lowerleftx;
   WORD lowerlefty;
   WORD upperrightx;
   WORD upperrighty;
   CHAR []    ; ascii postscript file


RGB image forms, Turbo Silver (Impulse)

                    FORM RGBN and FORM RGB8

RGBN and RGB8 files are used in Impulse's Turbo Silver and Imagine.
They are almost identical to FORM ILBM's except for the BODY chunk
and slight differences in the BMHD chunk.


The BMHD chunk specfies the number of bitplanes as 13 for type RGBN
and 25 for type RGB8, and the compression type as 4.

The FORM RGBN uses 12 bit RGB values, and the FORM RGB8 uses
24 bit RGB values.

The BODY chunk contains RGB values, a "genlock" bit, and repeat
counts.  In Silver, when "genlock" bit is set, a "zero color" is
written into the bitplanes for genlock video to show through.
In Diamond and Light24 (Impulse 12 & 24 bit paint programs),
the genlock bit is ignored if the file is loaded as a picture
(and the RGB color is used instead), and if the file is loaded
as a brush the genlock bit marks pixels that are not part of
the brush.

For both RGBN and RGB8 body chunks, each RGB value always has a
repeat count.  The values are written in different formats depending
on the magnitude of the repeat count.

For the RGBN BODY chunk:

For each RGB value, a WORD (16-bits) is written: with the
12 RGB bits in the MSB (most significant bit) positions;
the "genlock" bit next; and then a 3 bit repeat count.  
If the repeat count is greater than 7, the 3-bit count is
zero, and a BYTE repeat count follows.  If the repeat count
is greater than 255, the BYTE count is zero, and a WORD
repeat count follows.  Repeat counts greater than 65536 are
not supported.

For the RGB8 body chunk:

For each RGB value, a LONG-word (32 bits) is written:
with the 24 RGB bits in the MSB positions; the "genlock"
bit next, and then a 7 bit repeat count.

In a previous version of this document, there appeared the
following line:

"If the repeat count is greater than 127, the same rules apply
as in the RGBN BODY."

But Impulse has never written more than a 7 bit repeat count,
and when Imagine and Light24 were written, they didn't support
reading anything but 7 bit counts.
Sample BODY code:

if(!count) {
if (Rgb8) {
fread (&w, 4, 1, RGBFile);
lock = w & 0x00000080;
rgb = w >> 8;
count = w & 0x0000007f;
} else {
w = (UWORD) getw (RGBFile);
lock = w & 8;
rgb = w >> 4;
count = w & 7;
if (!count)
if (!(count = (UBYTE) getc (RGBFile)))
count = (UWORD) getw (RGBFile);

The pixels are scanned from left to right across horizontal
lines, processing from top to bottom.  The (12 or 24 bit) RGB
values are stored with the red bits as the MSB's, the green
bits next, and the blue bits as the LSB's.

Special note:  As of this writing (Sep 88), Silver does NOT
support anything but black for color zero.


Cel animation form

                              A N I M
                  An IFF Format For CEL Animations

                    Revision date:  4 May 1988

                     prepared by:
                          SPARTA Inc.
                          23041 de la Carlota
                          Laguna Hills, Calif 92653
                          (714) 768-8161
                          contact: Gary Bonham

                     also by:
                          Aegis Development Co.
                          2115 Pico Blvd.
                          Santa Monica, Calif 90405
                          213) 392-9972

1.0 Introduction
   The ANIM IFF format was developed at Sparta originally for the
   production of animated video sequences on the Amiga computer.  The
   intent was to be able to store, and play back, sequences of frames
   and to minimize both the storage space on disk (through compression)
   and playback time (through efficient de-compression algorithms).
   It was desired to maintain maximum compatibility with existing
   IFF formats and to be able to display the initial frame as a normal
   still IFF picture.
   Several compression schemes have been introduced in the ANIM format.
   Most of these are strictly of historical interest as the only one
   currently being placed in new code is the vertical run length encoded
   byte encoding developed by Jim Kent.
   1.1 ANIM Format Overview
      The general philosophy of ANIMs is to present the initial frame
      as a normal, run-length-encoded, IFF picture.  Subsequent
      frames are then described by listing only their differences
      from a previous frame.  Normally, the "previous" frame is two
      frames back as that is the frame remaining in the hidden 
      screen buffer when double-buffering is used.  To better
      understand this, suppose one has two screens, called A and B,
      and the ability to instantly switch the display from one to
      the other.  The normal playback mode is to load the initial
      frame into A and duplicate it into B.  Then frame A is displayed
      on the screen.  Then the differences for frame 2 are used to
      alter screen B and it is displayed.  Then the differences for
      frame 3 are used to alter screen A and it is displayed, and so
      on.  Note that frame 2 is stored as differences from frame 1,
      but all other frames are stored as differences from two frames
      ANIM is an IFF FORM and its basic format is as follows (this
      assumes the reader has a basic understanding of IFF format
                      FORM ANIM
                      . FORM ILBM         first frame
                      . . BMHD                normal type IFF data
                      . . ANHD                optional animation header
                                              chunk for timing of 1st frame.
                      . . CMAP
                      . . BODY
                      . FORM ILBM         frame 2
                      . . ANHD                animation header chunk
                      . . DLTA                delta mode data
                      . FORM ILBM         frame 3
                      . . ANHD
                      . . DLTA
      The initial FORM ILBM can contain all the normal ILBM chunks,
      such as CRNG, etc.  The BODY will normally be a standard
      run-length-encoded data chunk (but may be any other legal
      compression mode as indicated by the BMHD).  If desired, an ANHD
      chunk can appear here to provide timing data for the first
      frame.  If it is here, the operation field should be =0.
      The subsequent FORMs ILBM contain an ANHD, instead of a BMHD,
      which duplicates some of BMHD and has additional parameters
      pertaining to the animation frame.  The DLTA chunk contains
      the data for the delta compression modes.  If the older XOR 
      compression mode is used, then a BODY chunk will be here.  In 
      addition, other chunks may be placed in each of these as deemed 
      necessary (and as code is placed in player programs to utilize 
      them).  A good example would be CMAP chunks to alter the color 
      palette.  A basic assumption in ANIMs is that the size of the 
      bitmap, and the display mode (e.g. HAM) will not change through 
      the animation.  Take care when playing an ANIM that if a CMAP 
      occurs with a frame, then the change must be applied to both buffers.
      Note that the DLTA chunks are not interleaved bitmap representations,
      thus the use of the ILBM form is inappropriate for these frames.  
      However, this inconsistency was not noted until there were a number
      of commercial products either released or close to release which
      generated/played this format.  Therefore, this is probably an
      inconsistency which will have to stay with us.

   1.2 Recording ANIMs

      To record an ANIM will require three bitmaps - one for creation of 
      the next frame, and two more for a "history" of the previous two 
      frames for performing the compression calculations (e.g. the delta 
      mode calculations).
      There are five frame-to-frame compression methods currently defined.  
      The first three are mainly for historical interest.  The product Aegis 
      VideoScape 3D utilizes the third method in version 1.0, but switched 
      to method 5 on 2.0.  This is the only instance known of a commercial 
      product generating ANIMs of any of the first three methods.  The 
      fourth method is a general short or long word compression scheme which 
      has several options including whether the compression is horizontal
      or vertical, and whether or not it is XOR format.  This offers a 
      choice to the user for the optimization of file size and/or playback 
      speed.  The fifth method is the byte vertical run length encoding as 
      designed by Jim Kent.  Do not confuse this with Jim's RIFF file format 
      which is different than ANIM.  Here we utilized his compression/
      decompression routines within the ANIM file structure.
      The following paragraphs give a general outline of each of the
      methods of compression currently included in this spec.

      1.2.1 XOR mode
         This mode is the original and is included here for historical
         interest.  In general, the delta modes are far superior.
         The creation of XOR mode is quite simple.  One simply
         performs an exclusive-or (XOR) between all corresponding
         bytes of the new frame and two frames back.  This results
         in a new bitmap with 0 bits wherever the two frames were
         identical, and 1 bits where they are different.  Then this
         new bitmap is saved using run-length-encoding.  A major
         obstacle of this mode is in the time consumed in performing
         the XOR upon reconstructing the image.
      1.2.2 Long Delta mode
         This mode stores the actual new frame long-words which are
         different, along with the offset in the bitmap.  The
         exact format is shown and discussed in section 2 below.
         Each plane is handled separately, with no data being saved
         if no changes take place in a given plane.  Strings of
         2 or more long-words in a row which change can be run
         together so offsets do not have to be saved for each one.
         Constructing this data chunk usually consists of having a buffer 
         to hold the data, and calculating the data as one compares the 
         new frame, long-word by long-word, with two frames back.
      1.2.3 Short Delta mode
         This mode is identical to the Long Delta mode except that
         short-words are saved instead of long-words.  In most
         instances, this mode results in a smaller DLTA chunk.
         The Long Delta mode is mainly of interest in improving
         the playback speed when used on a 32-bit 68020 Turbo Amiga.
      1.2.4 General Delta mode

         The above two delta compression modes were hastily put together.
         This mode was an attempt to provide a well-thought-out delta
         compression scheme.  Options provide for both short and long
         word compression, either vertical or horizontal compression,
         XOR mode (which permits reverse playback), etc.  About the time
         this was being finalized, the fifth mode, below, was developed
         by Jim Kent.  In practice the short-vertical-run-length-encoded
         deltas in this mode play back faster than the fifth mode (which
         is in essence a byte-vertical-run-length-encoded delta mode) but
         does not compress as well - especially for very noisy data such
         as digitized images.  In most cases, playback speed not being
         terrifically slower, the better compression (sometimes 2x) is
         preferable due to limited storage media in most machines.

         Details on this method are contained in section 2.2.2 below.

      1.2.5 Byte Vertical Compression

         This method does not offer the many options that method 4 offers,
         but is very successful at producing decent compression even for
         very noisy data such as digitized images.  The method was devised
         by Jim Kent and is utilized in his RIFF file format which is 
         different than the ANIM format.  The description of this method
         in this document is taken from Jim's writings.  Further, he has
         released both compression and decompression code to public domain.
         Details on this method are contained in section 2.2.3 below.

   1.3 Playing ANIMs
      Playback of ANIMs will usually require two buffers, as mentioned
      above, and double-buffering between them.  The frame data from
      the ANIM file is used to modify the hidden frame to the next
      frame to be shown.  When using the XOR mode, the usual run-
      length-decoding routine can be easily modified to do the 
      exclusive-or operation required.  Note that runs of zero bytes,
      which will be very common, can be ignored, as an exclusive or
      of any byte value to a byte of zero will not alter the original
      byte value.
      The general procedure, for all compression techniques, is to first
      decode the initial ILBM picture into the hidden buffer and double-
      buffer it into view.  Then this picture is copied to the other (now
      hidden) buffer.  At this point each frame is displayed with the
      same procedure.  The next frame is formed in the hidden buffer by
      applying the DLTA data (or the XOR data from the BODY chunk in the
      case of the first XOR method) and the new frame is double-buffered
      into view.  This process continues to the end of the file.

      A master colormap should be kept for the entire ANIM which would
      be initially set from the CMAP chunk in the initial ILBM.  This
      colormap should be used for each frame.  If a CMAP chunk appears
      in one of the frames, then this master colormap is updated and the
      new colormap applies to all frames until the occurrance of another
      CMAP chunk.

      Looping ANIMs may be constructed by simply making the last two frames
      identical to the first two.  Since the first two frames are special
      cases (the first being a normal ILBM and the second being a delta from
      the first) one can continually loop the anim by repeating from frame
      three.  In this case the delta for creating frame three will modify
      the next to the last frame which is in the hidden buffer (which is
      identical to the first frame), and the delta for creating frame four
      will modify the last frame which is identical to the second frame.

      Multi-File ANIMs are also supported so long as the first two frames
      of a subsequent file are identical to the last two frames of the
      preceeding file.  Upon reading subsequent files, the ILBMs for the
      first two frames are simply ignored, and the remaining frames are
      simply appended to the preceeding frames.  This permits splitting
      ANIMs across multiple floppies and also permits playing each section
      independently and/or editing it independent of the rest of the ANIM.
      Timing of ANIM playback is easily achieved using the vertical blank
      interrupt of the Amiga.  There is an example of setting up such
      a timer in the ROM Kernel Manual.  Be sure to remember the timer
      value when a frame is flipped up, so the next frame can be flipped
      up relative to that time.  This will make the playback independent
      of how long it takes to decompress a frame (so long as there is enough
      time between frames to accomplish this decompression).
2.0 Chunk Formats
   2.1 ANHD Chunk
      The ANHD chunk consists of the following data structure:
           UBYTE operation  The compression method:
                            =0 set directly (normal ILBM BODY),
                            =1 XOR ILBM mode,
                            =2 Long Delta mode,
                            =3 Short Delta mode,
                            =4 Generalized short/long Delta mode,
                            =5 Byte Vertical Delta mode
                            =6 Stereo op 5 (third party)
                            =74 (ascii 'J') reserved for Eric Graham's
                               compression technique (details to be
                               released later).

           UBYTE mask      (XOR mode only - plane mask where each
                            bit is set =1 if there is data and =0
                            if not.)
           UWORD w,h       (XOR mode only - width and height of the
                            area represented by the BODY to eliminate
                            unnecessary un-changed data)
           WORD  x,y       (XOR mode only - position of rectangular
                            area representd by the BODY)
           ULONG abstime   (currently unused - timing for a frame
                            relative to the time the first frame
                            was displayed - in jiffies (1/60 sec))
           ULONG reltime   (timing for frame relative to time
                            previous frame was displayed - in
                            jiffies (1/60 sec))
           UBYTE interleave (unused so far - indicates how may frames
                             back this data is to modify.  =0 defaults
                             to indicate two frames back (for double
                             buffering). =n indicates n frames back.
                             The main intent here is to allow values
                             of =1 for special applications where
                             frame data would modify the immediately
                             previous frame)
           UBYTE pad0        Pad byte, not used at present.
           ULONG bits        32 option bits used by options=4 and 5.
                             At present only 6 are identified, but the
                             rest are set =0 so they can be used to
                             implement future ideas.  These are defined
                             for option 4 only at this point.  It is
                             recommended that all bits be set =0 for
                             option 5 and that any bit settings used in
                             the future (such as for XOR mode) be compatible 
                             with the option 4 bit settings.   Player code 
                             should check undefined bits in options 4 and 5
                             to assure they are zero.

                             The six bits for current use are:

                             bit #              set =0               set =1
                             0              short data           long data
                             1                 set                  XOR
                             2             separate info        one info list
                                           for each plane       for all planes
                             3               not RLC        RLC (run length coded)
                             4              horizontal           vertical
                             5           short info offsets   long info offsets

           UBYTE pad[16]     This is a pad for future use for future
                             compression modes.
   2.2 DLTA Chunk
      This chunk is the basic data chunk used to hold delta compression
      data.  The format of the data will be dependent upon the exact
      compression format selected.  At present there are two basic
      formats for the overall structure of this chunk.

      2.2.1 Format for methods 2 & 3

         This chunk is a basic data chunk used to hold the delta
         compression data.  The minimum size of this chunk is 32 bytes
         as the first 8 long-words are byte pointers into the chunk for
         the data for each of up to 8 bitplanes.  The pointer for the
         plane data starting immediately following these 8 pointers will
         have a value of 32 as the data starts in the 33-rd byte of the
         chunk (index value of 32 due to zero-base indexing).
         The data for a given plane consists of groups of data words.  In
         Long Delta mode, these groups consist of both short and long
         words - short words for offsets and numbers, and long words for
         the actual data.  In Short Delta mode, the groups are identical
         except data words are also shorts so all data is short words.
         Each group consists of a starting word which is an offset.  If
         the offset is positive then it indicates the increment in long
         or short words (whichever is appropriate) through the bitplane.
         In other words, if you were reconstructing the plane, you would
         start a pointer (to shorts or longs depending on the mode) to
         point to the first word of the bitplane.  Then the offset would
         be added to it and the following data word would be placed at
         that position.  Then the next offset would be added to the
         pointer and the following data word would be placed at that
         position.  And so on...  The data terminates with an offset
         equal to 0xFFFF.
         A second interpretation is given if the offset is negative.  In
         that case, the absolute value is the offset+2.  Then the 
         following short-word indicates the number of data words that
         follow.  Following that is the indicated number of contiguous
         data words (longs or shorts depending on mode) which are to
         be placed in contiguous locations of the bitplane.
         If there are no changed words in a given plane, then the pointer
         in the first 32 bytes of the chunk is =0.
      2.2.2 Format for method 4
         The DLTA chunk is modified slightly to have 16 long pointers at
         the start.  The first 8 are as before - pointers to the start of
         the data for each of the bitplanes (up to a theoretical max of 8
         planes).  The next 8 are pointers to the start of the offset/numbers
         data list.  If there is only one list of offset/numbers for all
         planes, then the pointer to that list is repeated in all positions
         so the playback code need not even be aware of it.  In fact, one
         could get fancy and have some bitplanes share lists while others
         have different lists, or no lists (the problems in these schemes
         lie in the generation, not in the playback).
         The best way to show the use of this format is in a sample playback

            struct BitMap *bm;
            WORD *deltaword;
               int i;
               LONG *deltadata;
               WORD *ptr,*planeptr;
               register int s,size,nw;
               register WORD *data,*dest;

               deltadata = (LONG *)deltaword;
               nw = bm->BytesPerRow >>1;

               for (i=0;i<bm->Depth;i++) {
                  planeptr = (WORD *)(bm->Planes[i]);
                  data = deltaword + deltadata[i];
                  ptr  = deltaword + deltadata[i+8];
                  while (*ptr != 0xFFFF) {
                     dest = planeptr + *ptr++;
                     size = *ptr++;
                     if (size < 0) {
                        for (s=size;s<0;s++) {
                           *dest = *data;
                           dest += nw;
                     else {
                        for (s=0;s<size;s++) {
                           *dest = *data++;
                           dest += nw;

         The above routine is for short word vertical compression with
         run length compression.  The most efficient way to support 
         the various options is to replicate this routine and make 
         alterations for, say, long word or XOR.  The variable nw
         indicates the number of words to skip to go down the vertical
         column.  This one routine could easily handle horizontal
         compression by simply setting nw=1.  For ultimate playback
         speed, the core, at least, of this routine should be coded in
         assembly language.

      2.2.2 Format for method 5

         In this method the same 16 pointers are used as in option 4.
         The first 8 are pointers to the data for up to 8 planes.
         The second set of 8 are not used but were retained for several
         reasons.  First to be somewhat compatible with code for option
         4 (although this has not proven to be of any benefit) and 
         second, to allow extending the format for more bitplanes (code
         has been written for up to 12 planes).  
         Compression/decompression is performed on a plane-by-plane basis.
         For each plane, compression can be handled by the skip.c code
         (provided Public Domain by Jim Kent) and decompression can be
         handled by unvscomp.asm (also provided Public Domain by Jim Kent).
         Compression/decompression is performed on a plane-by-plane basis.
         The following description of the method is taken directly from
         Jim Kent's code with minor re-wording.  Please refer to Jim's
         code (skip.c and unvscomp.asm) for more details:

            Each column of the bitplane is compressed separately.
            A 320x200 bitplane would have 40 columns of 200 bytes each.
            Each column starts with an op-count followed by a number
            of ops.  If the op-count is zero, that's ok, it just means
            there's no change in this column from the last frame.
            The ops are of three classes, and followed by a varying
            amount of data depending on which class:
              1. Skip ops - this is a byte with the hi bit clear that
                 says how many rows to move the "dest" pointer forward,
                 ie to skip. It is non-zero.
              2. Uniq ops - this is a byte with the hi bit set.  The hi
                 bit is masked down and the remainder is a count of the
                 number of bytes of data to copy literally.  It's of
                 course followed by the data to copy.
              3. Same ops - this is a 0 byte followed by a count byte,
                 followed by a byte value to repeat count times.
            Do bear in mind that the data is compressed vertically rather
            than horizontally, so to get to the next byte in the destination
            we add the number of bytes per row instead of one!

ANIM brush format

                    Dpaint Anim Brush IFF Format

             From a description by the author of DPaint,
                      Dan Silva, Electronic Arts

The "Anim Brushes" of DPaint III are saved on disk in the IFF "ANIM" format.  
Basically, an ANIM Form consists of an initial ILBM which is the first frame 
of the animation, and any number of subsequent "ILBM"S (which aren't really 
ILBM's) each of which contains an ANHD animation header chunk and a DLTA chunk  
comprised of the encoded difference between a frame and a previous one.

To use ANIM terminology (for a description of the ANIM format, see the IFF 
Anim Spec, by Gary Bonham). Anim Brushes use a "type 5" encoding, which is 
a vertical, byte-oriented delta encoding (based on Jim Kent's RIFF).  The 
deltas have an interleave of 1, meaning deltas are computed between adjacent 
frames, rather than between frames 2 apart, which is the usual ANIM custom 
for the purpose of fast hardware page-flipping.  Also, the deltas use 
Exclusive Or to allow reversable play.

However, to my knowledge, all the existing Anim players in the Amiga world 
will only play type 5 "Anim"s which have an interleave of 0 (i.e. 2) and 
which use a Store operation rather than Exclusive Or, so no existing programs 
will read Anim Brushes anyway.  The job of modifying existing Anim readers 
to read Anim Brushes should be simplified, however.

Here is an outline of the structure of the IFF Form output by DPaint III as 
an "Anim Brush".  The IFF Reader should of course be flexible enough to 
tolerate variation in what chunks actually appear in the initial ILBM.

                  FORM ANIM
                      . FORM ILBM         first frame
                      . . BMHD        
                      . . CMAP
                      . . DPPS
                      . . GRAB
                      . . CRNG
                      . . CRNG
                      . . CRNG
                      . . CRNG
                      . . CRNG
                      . . CRNG
                      . . DPAN     my own little chunk.
                      . . CAMG
                      . . BODY

                      . FORM ILBM         frame 2
                      . . ANHD                animation header chunk
                      . . DLTA                delta mode data

                      . FORM ILBM         frame 3
                      . . ANHD                animation header chunk
                      . . DLTA                delta mode data

                      . FORM ILBM         frame 4
                      . . ANHD                animation header chunk
                      . . DLTA                delta mode data
                   . FORM ILBM         frame N
                      . . ANHD                animation header chunk
                      . . DLTA                delta mode data

--- Here is the format of the DPAN chunk:

typedef struct {
 UWORD version;   /* current version=4 */
 UWORD nframes;   /* number of frames in the animation.*/
 ULONG flags;   /* Not used */
 } DPAnimChunk;

The version number was necessary during development. At present all I look 
at is "nframes".

--- Here is the ANHD chunk format:

typedef struct {
 UBYTE operation;  /* =0  set directly
       =1  XOR ILBM mode,
       =2 Long Delta mode,
       =3 Short Delta mode
       =4 Generalize short/long Delta mode,
       =5 Byte Vertical Delta (riff)
       =74 (Eric Grahams compression mode)
 UBYTE mask;      /* XOR ILBM only: plane mask where data is*/
 UWORD w,h;  
 WORD x,y;
 ULONG abstime;
 ULONG reltime;
 UBYTE interleave; /* 0 defaults to 2 */
 UBYTE pad0;   /* not used */
 ULONG bits;   /* meaning of bits:
    bit# =0                 =1
    0    short data         long data
    1    store              XOR
    2    separate info      one info for
         for each plane     for all planes
    3    not RLC            RLC (run length encoded)
    4    horizontal         vertical
    5    short info offsets long info offsets
 UBYTE pad[16];
 } AnimHdr;

for Anim Brushes, I set:

 animHdr.operation = 5;  /* RIFF encoding */
 animHdr.interleave = 1;
 animHdr.w = curAnimBr.bmob.pict.box.w; 
 animHdr.h = curAnimBr.bmob.pict.box.h; 
 animHdr.reltime = 1;
 animHdr.abstime = 0;
 animHdr.bits = 4; /* indicating XOR */

-- everything else is set to 0.

NOTE: the "bits" field was actually intended ( by the original creator of 
the ANIM format, Gary Bonham of SPARTA, Inc.) for use with only with 
compression method 4. I am using bit 2 of the bits field to indicate the 
Exclusive OR operation in the context of method 5, which seems like a 
reasonable generalization. 

For an Anim Brush with 10 frames, there will be an initial frame followed 
by 10 Delta's (i.e ILBMS containing ANHD and DLTA chunks).  Applying the 
first Delta to the initial frame generates the second frame, applying the 
second Delta to the second frame generates the third frame, etc.  Applying 
the last Delta thus brings back the first frame.  

The DLTA chunk begins with 16 LONG plane offets, of which DPaint only uses 
the first 6 (at most).  These plane offsets are either the offset (in bytes) 
from the beginning of the DLTA chunk to the data for the corresponding plane, 
or Zero, if there was no change in that plane.  Thus the first plane offset 
is either 0 or 64.

(The following description of the method is based on Gary Bonham's rewording 
of Jim Kent's RIFF documentation.)

  Compression/decompression is performed on a plane-by-plane 

  Each byte-column of the bitplane is compressed separately.  A 
  320x200 bitplane would have 40 columns of 200 bytes each.  In 
  general, the bitplanes are always an even number of bytes wide, 
  so for instance a 17x20 bitplane would have 4 columns of 20 
  bytes each.

  Each column starts with an op-count followed by a number of 
  ops.  If the op-count is zero, that's ok, it just means there's 
  no change in this column from the last frame.  The ops are of 
  three kinds, and followed by a varying amount of data depending 
  on which kind:

     1. SKIP - this is a byte with the hi bit clear that   says 
        how many rows to move the "dest" pointer forward, ie to 
        skip. It is non-zero.

     2. DUMP - this is a byte with the hi bit set.  The hi bit is 
        masked off and the remainder is a count of the number of 
        bytes of data to XOR directly.  It is followed by the 
        bytes to copy.

     3. RUN - this is a 0 byte followed by a count byte, followed 
        by a byte value to repeat "count" times, XOR'ing it into 
        the destination.

  Bear in mind that the data is compressed vertically rather than 
  horizontally, so to get to the next byte in the destination  you 
  add the number of bytes per row instead of one.

The Format of DLTA chunks is as described in section 2.2.2 of the Anim Spec. 
The encoding for type 5 is described in section 2.2.3 of the Anim Spec.  


2-D Object standard format


Description by Ross Cunniff and John Orr

A standard IFF FORM to describe 2D drawings has been sorely needed for
a long time.  Several commercial drawing packages have been available
for some time but none has established its file format as the Amiga
standard.  The absence of a 2D drawing standard hinders the
development of applications that use 2D drawings as it forces each
application to understand several private standards instead of a
single one.  Without a standard, data exchange for both the developer
and the user is difficult, if not impossible.

The DR2D FORM fills this void.  This FORM was developed by Taliesin,
Inc. for use as the native file format for their two-dimensional
structured drawing package, ProVector.  Saxon Industries and Soft
Logik Publishing Corporation are planning to support this new FORM in
the near future.

Many of the values stored in the DR2D FORM are stored as IEEE single
precision floating point numbers.  These numbers consist of 32 bits,
arranged as follows:

| s e e e e e e e | e m m m m m m m | m m m m m m m m | m m m m m m m m |
  31            24  23            16  15            8   7             0


sis the sign of the number where 1 is negative and 0 is
eis the 8 bit exponent in excess 127 form.  This number
  is the power of two to which the mantissa is raised
  (Excess 127 form means that 127 is added to the
  exponent before packing it into the IEEE number.) 
mis the 23 bit mantissa.  It ranges from 1.0000000 to
  1.999999..., where the leading base-ten one is

An IEEE single precision with the value of 0.0000000 has all its bits

The DR2D Chunks

FORM (0x464F524D)  /* All drawings are a FORM */

struct FORMstruct {
    ULONGID;    /* DR2D */

DR2D (0x44523244)  /* ID of 2D drawing */

The DR2D chunks are broken up into three groups: the global drawing
attribute chunks, the object attribute chunks, and the object chunks.
The global drawing attribute chunks describe elements of a 2D drawing
that are common to many objects in the drawing.  Document preferences,
palette information, and custom fill patterns are typical
document-wide settings defined in global drawing attribute chunks.
The object attribute chunks are used to set certain properties of the
object chunk(s) that follows the object attribute chunk.  The current
fill pattern, dash pattern, and line color are all set using an object
attribute chunk.  Object chunks describe the actual DR2D drawing.
Polygons, text, and bitmaps are found in these chunks.

The Global Drawing Attribute Chunks

The following chunks describe global attributes of a DR2D document.

DRHD (0x44524844) /* Drawing header */

The DRHD chunk contains the upper left and lower right extremes of the
document in (X, Y) coordinates.  This chunk is required and should
only appear once in a document in the outermost layer of the DR2D file
(DR2Ds can be nested).

struct DRHDstruct {
    ULONGSize;    /* Always 16 */
    IEEEXLeft, YTop,
XRight, YBot;

The point (XLeft,YTop) is the upper left corner of the project and the
point (XRight,YBot) is its lower right corner.  These coordinates not
only supply the size and position of the document in a coordinate
system, they also supply the project's orientation.  If XLeft <
XRight, the X-axis increases toward the right.  If YTop < YBot, the
Y-axis increases toward the bottom.  Other combinations are possible;
for example in Cartesian coordinates, XLeft would be less than XRight
but YTop would be greater than YBot.

PPRF (0x50505249)   /* Page preferences */

The PPRF chunk contains preference settings for ProVector.  Although
this chunk is not required, its use is encouraged because it contains
some important environment information.

struct PPRFstruct {

DR2D stores preferences as a concatenation of several null-terminated
strings, in the Prefs[] array.  The strings can appear in any order.
The currently supported strings are:

<unit-type> is either Inch, Cm, or Pica
<boolean>   is either True or False
<page-type> is either Standard, Legal, B4, B5, A3,
  A4, A5, or Custom 
<number>    is a floating-point number

The DR2D FORM does not require this chunk to explicitly state all the
possible preferences.  In the absence of any particular preference
string, a DR2D reader should fall back on the default value.  The
defaults are:


CMAP (0x434D4150)   /* Color map (Same as ILBM CMAP) */

This chunk is identical to the ILBM CMAP chunk as described in the IFF
ILBM documentation.

struct CMAPstruct {

ColorMap is an array of 24-bit RGB color values.  The 24-bit value is
spread across three bytes, the first of which contains the red
intensity, the next contains the green intensity, and the third
contains the blue intensity.  Because DR2D stores its colors with
24-bit accuracy, DR2D readers must not make the mistake that some ILBM
readers do in assuming the CMAP chunk colors correspond directly to
Amiga color registers.

FONS (0x464F4E53)   /* Font chunk (Same as FTXT FONS chunk) */

The FONS chunk contains information about a font used in the DR2D
FORM.  ProVector does not include support for Amiga fonts.  Instead,
ProVector uses fonts defined in the OFNT FORM which is documented
later in this article.

struct FONSstruct {
    UBYTEFontID;/* ID the font is referenced by */
    UBYTEPad1;          /* Always 0 */
    UBYTEProportional;/* Is it proportional? */
    UBYTESerif;/* does it have serifs? */
    CHARName[Size-4];/* The name of the font */

The UBYTE FontID field is the number DR2D assigns to this font.
References to this font by other DR2D chunks are made using this
The Proportional and Serif fields indicate properties of this font.
Specifically, Proportional indicates if this font is proportional, and
Serif indicates if this font has serifs.  These two options were
created to allow for font substitution in case the specified font is
not available.  They are set according to these values:

0The DR2D writer didn't know if this font is
  proportional/has serifs. 
1No, this font is not proportional/does not have
2Yes, this font is proportional/does have serifs.

The last field, Name[], is a NULL terminated string containing the
name of the font. 

DASH (0x44415348)   /* Line dash pattern for edges */

This chunk describes the on-off dash pattern associated with a line.

struct DASHstruct {
    USHORTDashID;/* ID of the dash pattern */
    USHORTNumDashes; /* Should always be even */
    IEEEDashes[NumDashes];  /* On-off pattern */

DashID is the number assigned to this specific dash pattern.
References to this dash pattern by other DR2D chunks are made using
this number.

The Dashes[] array contains the actual dash pattern.  The first number
in the array (element 0) is the length of the ``on'' portion of the
pattern.  The second number (element 1) specifies the ``off'' portion
of the pattern.  If there are more entries in the Dashes array, the
pattern will continue.  Even-index elements specify the length of an
``on'' span, while odd-index elements specify the length of an ``off''
span.  There must be an even number of entries.  These lengths are not
in the same units as specified in the PPRF chunk, but are multiples of
the line width, so a line of width 2.5 and a dash pattern of 1.0, 2.0
would have an ``on'' span of length 1.0 x 2.5 = 2.5 followed by an
``off'' span of length 2.0 x 2.5 = 5.  The following figure shows
several dash pattern examples.  Notice that for lines longer than the
dash pattern, the pattern repeats.

[figure 1 - dash patterns]

By convention, DashID 0 is reserved to mean `No line pattern at all',
i.e. the edges are invisible.  This DASH pattern should not be defined
by a DR2D DASH chunk.  Again by convention, a NumDashes of 0 means
that the line is solid.

AROW (0x41524F57)   /* An arrow-head pattern */

The AROW chunk describes an arrowhead pattern.  DR2D open polygons
(OPLY) can have arrowheads attached to their endpoints.  See the
description of the OPLY chunk later in this article for more
information on the OPLY chunk.

#define ARROW_FIRST  0x01 /* Draw an arrow on the OPLY's first point */
#define ARROW_LAST   0x02 /* Draw an arrow on the OPLY's last point */

struct AROWstruct {
    UBYTEFlags;    /* Flags, from ARROW_*, above */
    UBYTEPad0;/* Should always 0 */
    USHORTArrowID;/* Name of the arrow head */

The Flags field specifies which end(s) of an OPLY to place an
arrowhead based on the #defines above.  ArrowID is the number by which
an OPLY will reference this arrowhead pattern.

The coordinates in the array ArrowPoints[] define the arrowhead's
shape.  These points form a closed polygon.  See the section on the
OPLY/CPLY object chunks for a descriptionof how DR2D defines shapes.
The arrowhead is drawn in the same coordinate system relative to the
endpoint of the OPLY the arrowhead is attached to.  The arrowhead's
origin (0,0) coincides with the OPLY's endpoint.  DR2D assumes that
the arrowhead represented in the AROW chunk is pointing to the right
so the proper rotation can be applied to the arrowhead.  The arrow is
filled according to the current fill pattern set in the ATTR object
attribute chunk.

FILL (0x46494C4C)   /* Object-oriented fill pattern */

The FILL chunk defines a fill pattern.  This chunk is only valid
inside nested DR2D FORMs.  The GRUP object chunk section of this
article contans an example of the FILL chunk.

struct FILLstruct {
    USHORTFillID;  /* ID of the fill */

FillID is the number by which the ATTR object attribute chunk
references fill patterns.  The FILL chunk must be the first chunk
inside a nested DR2D FORM.  A FILL is followed by one DR2D object plus
any of the object attribute chunks (ATTR, BBOX) associated with the

[Figure 2 - fill patterns]

DR2D makes a ``tile'' out of the fill pattern, giving it a virtual
bounding box based on the extreme X and Y values of the FILL's object
(Fig. A).  The bounding box shown in Fig. A surrounding the pattern
(the two ellipses) is invisible to the user.  In concept, this
rectangle is pasted on the page left to right, top to bottom like
floor tiles (Fig. B).  Again, the bounding boxes are not visible.  The
only portion of this tiled pattern that is visible is the part that
overlaps the object (Fig. C) being filled.  The object's path is
called a clipping path, as it ``clips'' its shape from the tiled
pattern (Fig. D).  Note that the fill is only masked on top of
underlying objects, so any ``holes'' in the pattern will act as a
window, leaving visible underlying objects.

LAYR (0x4C415952)   /* Define a layer */

A DR2D project is broken up into one or more layers.  Each DR2D object
is in one of these layers.  Layers provide several useful features.
Any particular layer can be ``turned off'', so that the objects in the
layer are not displayed.  This eliminates the unnecessary display of
objects not currently needed on the screen.  Also, the user can lock a
layer to protect the layer's objects from accidental changes.

struct LAYRstruct {
    ULONG    ID;
    ULONG    Size;
    USHORT   LayerID;   /* ID of the layer */
    char     LayerName[16];   /* Null terminated and padded */
    UBYTE    Flags;   /* Flags, from LF_*, below */
    UBYTE    Pad0;   /* Always 0 */

LayerID is the number assigned to this layer.  As the field's name
indicates, LayerName[] is the NULL terminated name of the layer.
Flags is a bit field who's bits are set according to the #defines

#define LF_ACTIVE     0x01  /* Active for editing */
#define LF_DISPLAYED  0x02  /* Displayed on the screen */

If the LF_ACTIVE bit is set, this layer is unlocked.  A set
LF_DISPLAYED bit indicates that this layer is currently visible on the
screen.  A cleared LF_DISPLAYED bit implies that LF_ACTIVE is not set.
The reason for this is to keep the user from accidentally editing
layers that are invisible.

The Object Attribute Chunks

ATTR (0x41545452)   /* Object attributes */

The ATTR chunk sets various attributes for the objects that follow it.
The attributes stay in effect until the next ATTR changes the
attributes, or the enclosing FORM ends, whichever comes first.

/* Various fill types */
#define FT_NONE0    /* No fill*/
#define FT_COLOR    1    /* Fill with color from palette */
#define FT_OBJECTS2    /* Fill with tiled objects*/

struct ATTRstruct {
    UBYTEFillType;    /* One of FT_*, above*/
    UBYTEJoinType;    /* One of JT_*, below*/
    UBYTEDashPattern; /* ID of edge dash pattern */
    UBYTEArrowHead;   /* ID of arrowhead to use  */
    USHORTFillValue;   /* Color or object with which to fill */
    USHORTEdgeValue;   /* Edge color index*/
    USHORTWhichLayer;  /* ID of layer it's in*/
    IEEEEdgeThick;   /* Line width*/

FillType specifies what kind of fill to use on this ATTR chunk's
objects.  A value of FT_NONE means that this ATTR chunk's objects are
not filled.  FT_COLOR indicates that the objects should be filled in
with a color.  That color's ID (from the CMAP chunk) is stored in the
FillValue field.  If FillType is equal to FT_OBJECTS, FillValue
contains the ID of a fill pattern defined in a FILL chunk.

JoinType determines which style of line join to use when connecting
the edges of line segments.  The field contains one of these four

/* Join types */
#define JT_NONE   0    /* Don't do line joins */
#define JT_MITER  1    /* Mitered join */
#define JT_BEVEL  2    /* Beveled join */
#define JT_ROUND  3    /* Round join */

DashPattern and ArrowHead contain the ID of the dash pattern and arrow
head for this ATTR's objects.  A DashPattern of zero means that there
is no dash pattern so lines will be invisible.  If ArrowHead is 0,
OPLYs have no arrow head.  EdgeValue is the color of the line
segments.  WhichLayer contains the ID of the layer this ATTR's objects
are in.  EdgeThick is the width of this ATTR's line segments.

BBOX (0x42424F48)   /* Bounding box of next object in FORM */

The BBOX chunk supplies the dimensions and position of a bounding box
surrounding the DR2D object that follows this chunk in the FORM.  A
BBOX chunk can apply to a FILL or AROW as well as a DR2D object.  The
BBOX chunk appears just before its DR2D object, FILL, or AROW chunk.

struct BBOXstruct {
    IEEEXMin, YMin,  /* Bounding box of obj. */
XMax, YMax;  /* including line width*/

In a Cartesian coordinate system, the point (XMin, YMin) is the
coordinate of the lower left hand corner of the bounding box and
(XMax, YMax) is the upper right.  These coordinates take into
consideration the width of the lines making up the bounding box.

XTRN (0x5854524E)   /* Externally controlled object */

The XTRN chunk was created primarily to allow ProVector to link DR2D
objects to ARexx functions. 

struct XTRNstruct {
    ULONG   ID;
    ULONG   Size;
    short   ApplCallBacks;             /* From #defines, below */
    short   ApplNameLength;
    char    ApplName[ApplNameLength];  /* Name of ARexx func to call */

ApplName[] contains the name of the ARexx script ProVector calls when
the user manipulates the object in some way.  The ApplCallBacks field
specifies the particular action that triggers calling the ARexx script
according to the #defines listed below.

/* Flags for ARexx script callbacks */
#define    X_CLONE     0x0001    /* The object has been cloned */
#define    X_MOVE      0x0002    /* The object has been moved */
#define    X_ROTATE    0x0004    /* The object has been rotated */
#define    X_RESIZE    0x0008    /* The object has been resized */
#define    X_CHANGE    0x0010    /* An attribute (see ATTR) of the 
                                    object has changed */
#define    X_DELETE    0x0020    /* The object has been deleted */
#define    X_CUT       0x0040    /* The object has been deleted, but
                                    stored in the clipboard */
#define    X_COPY      0x0080    /* The object has been copied to the
                                    clipboard */
#define    X_UNGROUP   0x0100    /* The object has been ungrouped */

For example, given the XTRN object:

FORM xxxx DR2D {
XTRN xxxx { X_RESIZE | X_MOVE, 10, "Dimension" }
ATTR xxxx { 0, 0, 1, 0, 0, 0, 0.0 }
FORM xxxx DR2D {
GRUP xxxx { 2 }
STXT xxxx { 0, 0.5, 1.0, 6.0, 5.0, 0.0, 4, "3.0" }
OPLY xxxx { 2, { 5.5, 5.5, 8.5, 5.5 } }

ProVector would call the ARexx script named Dimension if the user
resized or moved this object.  What exactly ProVector sends depends
upon what the user does to the object.  The following list shows what
string(s) ProVector sends according to which flag(s) are set.  The
parameters are described below.

X_CLONE     ``appl CLONE objID dx dy''
X_MOVE      ``appl MOVE objID dx dy''
X_ROTATE    ``appl ROTATE objID cx cy angle''
X_RESIZE    ``appl RESIZE objID cx cy sx sy''
X_CHANGE    ``appl CHANGE objID et ev ft fv ew jt fn''
X_DELETE    ``appl DELETE objID''
X_CUT       ``appl CUT objID''
X_COPY      ``appl COPY objID''
X_UNGROUP   ``appl UNGROUP objID''

appl is the name of the ARexx script
CLONE, MOVE, ROTATE, RESIZE, etc. are literal strings
objID is the object ID that ProVector assigns to this object
(dx, dy) is the position offset of the CLONE or MOVE
(cx, cy) is the point around which the object is rotated or resized
angle is the angle (in degrees) the object is rotated
sx and sy are the scaling factors in the horizontal and
  vertical directions, respectively. 
et is the edge type (the dash pattern index)
ev is the edge value (the edge color index)
ft is the fill type
fv is the fill index
ew is the edge weight
jt is the join type
fn is the font name

The X_CHANGE message reflects changes to the attributes found in the
ATTR chunk. 
If the user resized the XTRN object shown above by factor of 2,
ProVector would call the ARexx script Dimension like this: 

Dimension RESIZE 1985427 7.0 4.75 2.0 2.0

The Object Chunks

The following chunks define the objects available in the DR2D FORM.

VBM  (0x56424D20)   /* Virtual BitMap */

The VBM chunk contains the position, dimensions, and file name of an
ILBM image. 

struct VBMstruct {
    IEEEXPos, YPos,   /* Virtual coords */
XSize, YSize, /* Virtual size */
Rotation;       /* in degrees */
    USHORTPathLen;        /* Length of dir path */
    charPath[PathLen];  /* Null-terminated path of file */

The coordinate (XPos, YPos) is the position of the upper left hand
corner of the bitmap and the XSize and YSize fields supply the x and y
dimensions to which the image should be scaled.  Rotation tells how
many degrees to rotate the ILBM around its upper left hand corner.
ProVector does not currently support rotation of bitmaps and will
ignore this value.  Path contains the name of the ILBM file and may
also contain a partial or full path to the file.  DR2D readers should
not assume the path is correct.  The full path to an ILBM on one
system may not match the path to the same ILBM on another system.  If
a DR2D reader cannot locate an ILBM file based on the full path name
or the file name itself (looking in the current directory), it should
ask the user where to find the image.

CPLY (0x43504C59)   /* Closed polygon */
OPLY (0x4F504C59)   /* Open polygon */

Polygons are the basic components of almost all 2D objects in the DR2D
FORM.  Lines, squares, circles, and arcs are all examples of DR2D
polygons.  There are two types of DR2D polygons, the open polygon
(OPLY) and the closed polygon (CPLY).  The difference between a closed
and open polygon is that the computer adds a line segment connecting
the endpoints of a closed polygon so that it is a continuous path.  An
open polygon's endpoints do not have to meet, like the endpoints of a
line segment.

struct POLYstruct {
The NumPoints field contains the number of points in the polygon and
the PolyPoints array contains the (X, Y) coordinates of the points of
the non-curved parts of polygons.  The even index elements are X
coordinates and the odd index elements are Y coordinates.

[Figure 3 - Bezier curves]

DR2D uses Bezier cubic sections, or cubic splines, to describe curves
in polygons.  A set of four coordinates (P1 through P4) defines the
shape of a cubic spline.  The first coordinate (P1) is the point where
the curve begins.  The line from the first to the second coordinate
(P1 to P2) is tangent to the curve at the first point.  The line from
P3 to P4 is tangent to the cubic section, where it ends at P4.

The coordinates describing the cubic section are stored in the
PolyPoints[] array with the coordinates of the normal points.  DR2D
inserts an indicator point before a set of cubic section points to
differentiate a normal point from the points that describe a curve.
An indicator point has an X value of 0xFFFFFFFF.  The indicator
point's Y value is a bit field.  If this bit field's low-order bit is
set, the points that follow the indicator point make up a cubic

The second lowest order bit in the indicator point's bit field is the
MOVETO flag.  If this bit is set, the point (or set of cubic section
points) starts a new polygon, or subpolygon.  This subpolygon will
appear to be completely separate from other polygons but there is an
important connection between a polygon and its subpolygon.
Subpolygons make it possible to create holes in polygons.  An example
of a polygon with a hole is the letter ``O''.  The ``O'' is a filled
circular polygon with a smaller circular polygon within it.  The
reason the inner polygon isn't covered up when the outer polygon is
filled is that DR2D fills are done using the even-odd rule.

The even-odd rule determines if a point is ``inside'' a polygon by
drawing a ray outward from that point and counting the number of path
segments the ray crosses.  If the number is even, the point is outside
the object and shouldn't be filled.  Conversely, an odd number of
crossings means the point is inside and should be filled.  DR2D only
applies the even-odd rule to a polygon and its subpolygons, so no
other objects are considered in the calculations.

Taliesin, Inc. supplied the following algorithm to illustrate the
format of DR2D polygons.  OPLYs, CPLYs, AROWs, and ProVector's outline
fonts all use the same format:

        typedef union {
            IEEE num;
            LONG bits;
        } Coord;

        #define INDICATOR       0xFFFFFFFF
        #define IND_SPLINE      0x00000001
        #define IND_MOVETO      0x00000002

        /* A common pitfall in attempts to support DR2D has
                been to fail to recognize the case when an
                INDICATOR point indicates the following
                coordinate to be the first point of BOTH a
                Bezier cubic and a sub-polygon, ie. the
                value of the flag = (IND_CURVE | IND_MOVETO) */
        Coord   Temp0, Temp1;
        int     FirstPoint, i, Increment;

        /* Initialize the path */
        FirstPoint = 1;

        /* Draw the path */
        i = 0;
        while( i < NumPoints ) {
            Temp0.num = PolyPoints[2*i];    Temp1.num = PolyPoints[2*i + 1];
            if( Temp0.bits == INDICATOR ) {
                /* Increment past the indicator */
                Increment = 1;
                if( Temp1.bits & IND_MOVETO ) {
                    /* Close and fill, if appropriate */
                    if( ID == CPLY ) {
                    else {

                    /* Set up the new path */
                    FirstPoint = 1;
                if( Temp1.bits & IND_CURVE ) {
                    /* The next 4 points are Bezier cubic control points */
                    if( FirstPoint )
                        MoveTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                        LineTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                    CurveTo(    PolyPoints[2*i + 4], PolyPoints[2*i + 5],
                                PolyPoints[2*i + 6], PolyPoints[2*i + 7],
                                PolyPoints[2*i + 8], PolyPoints[2*i + 9] );
                    FirstPoint = 0;
                    /* Increment past the control points */
                    Increment += 4;
            else {
                if( FirstPoint )
                    MoveTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
                    LineTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
                FirstPoint = 0;

                /* Increment past the last endpoint */
                Increment = 1;

            /* Add the increment */
            i += Increment;

        /* Close the last path */
        if( ID == CPLY ) {
        else {
GRUP (0x47525550)   /* Group */

The GRUP chunk combines several DR2D objects into one.  This chunk is
only valid inside nested DR2D FORMs, and must be the first chunk in
the FORM.

struct GROUPstruct {

The NumObjs field contains the number of objects contained in this
group.  Note that the layer of the GRUP FORM overrides the layer of
objects within the GRUP.  The following example illustrates the layout
of the GRUP (and FILL) chunk.

       FORM { DR2D              /* Top-level drawing... */
                DRHD { ... }    /* Confirmed by presence of DRHD chunk */
                CMAP { ... }    /* Various other things... */
                FONS { ... }
                FORM { DR2D             /* A nested form... */
                        FILL { 1 }      /* Ah!  The fill-pattern table */
                        CPLY { ... }    /* with only 1 object */
                FORM { DR2D             /* Yet another nested form */
                        GRUP { ..., 3 } /* Ah! A group of 3 objects */
                        TEXT { ... }
                        CPLY { ... }
                        OPLY { ... }
                FORM { DR2D             /* Still another nested form */
                        GRUP { ..., 2 } /* A GRUP with 2 objects */
                        OPLY { ... }
                        TEXT { ... }

STXT (0x53545854)   /* Simple text */

The STXT chunk contains a text string along with some information on
how and where to render the text. 

struct STXTstruct {
    UBYTEPad0;/* Always 0 (for future expansion) */
    UBYTEWhichFont;      /* Which font to use */
    IEEECharW, CharH,  /* W/H of an individual char */
BaseX, BaseY,  /* Start of baseline */
Rotation;      /* Angle of text (in degrees) */

The text string is in the character array, TextChars[].  The ID of the
font used to render the text is WhichFont.  The font's ID is set in a
FONS chunk.  The starting point of the baseline of the text is (BaseX,
BaseY).  This is the point around which the text is rotated.  If the
Rotation field is zero (degrees), the text's baseline will originate
at (BaseX, BaseY) and move to the right.  CharW and CharH are used to
scale the text after rotation.  CharW is the average character width
and CharH is the average character height.  The CharW/H fields are
comparable to an X and Y font size.  

TPTH (0x54505448) /* A text string along a path */

This chunk defines a path (polygon) and supplies a string to render
along the edge of the path.

struct TPTHstruct {
    ULONG   ID;
    ULONG   Size;
    UBYTE   Justification;/* see defines, below */
    UBYTE   WhichFont;/* Which font to use */
    IEEE    CharW, CharH;/* W/H of an individual char*/
    USHORT  NumChars;   /* Number of chars in the string */
    USHORT  NumPoints;  /* Number of points in the path */
    char    TextChars[NumChars];/* PAD TO EVEN #! */
    IEEE    Path[2*NumPoints];  /* The path on which the text lies */

WhichFont contains the ID of the font used to render the text.
Justification controls how the text is justified on the line.
Justification can be one of the following values:

#define J_LEFT 0x00    /* Left justified */
#define J_RIGHT0x01   /* Right justified */
#define J_CENTER  0x02/* Center text */
#define J_SPREAD  0x03    /* Spread text across path */

CharW and CharH are the average width and height of the font
characters and are akin to X and Y font sizes, respectively.  A
negative FontH implies that the font is upsidedown.  Note that CharW
must not be negative.  NumChars is the number of characters in the
TextChars[] string, the string containing the text to be rendered.
NumPoints is the number of points in the Path[] array.  Path[] is the
path along which the text is rendered.  The path itself is not
rendered.  The points of Path[] are in the same format as the points
of a DR2D polygon.

A Simple DR2D Example

Here is a (symbolic) DR2D FORM:

    FORM { DR2D
            DRHD 16 { 0.0, 0.0, 10.0, 8.0 }
            CMAP  6 { 0,0,0, 255,255,255 }
            FONS  9 { 1, 0, 1, 0, "Roman" } 0
            DASH 12 { 1, 2, {1.0, 1.0} }
            ATTR 14 { 0, 0, 1, 0, 0, 0, 0, 0.0 }
            BBOX 16 { 2.0, 2.0, 8.0, 6.0 }
            FORM { DR2D
                  GRUP  2 { 2 }
                  BBOX 16 { 3.0, 4.0, 7.0, 5.0 }
                  STXT 36 { 0,1, 0.5, 1.0, 3.0, 5.0, 0.0, 12, "Hello, World" }
                  BBOX 16 { 2.0, 2.0, 8.0, 6.0 }
                  OPLY 42 { 5, {2.0,2.0, 8.0,2.0, 8.0,6.0, 2.0,6.0, 2.0,2.0 }

[Figure 4 - Simple DR2D drawing]


OFNT(0x4F464E54)   /* ID of outline font file */

ProVector's outline fonts are stored in an IFF FORM called OFNT.  This
IFF is a separate file from a DR2D.  DR2D's FONS chunk refers only to
fonts defined in the OFNT form.

OFHD(0x4F464844)   /* ID of OutlineFontHeaDer */

This chunk contains some basic information on the font.

struct OFHDstruct {
    char   FontName[32];   /* Font name, null padded */
    short  FontAttrs;   /* See FA_*, below */
    IEEE   FontTop,   /* Typical height above baseline */
   FontBot,        /* Typical descent below baseline */
   FontWidth;      /* Typical width, i.e. of the letter O */

#define FA_BOLD      0x0001
#define FA_OBLIQUE   0x0002
#define FA_SERIF0x0004

The FontName field is a NULL terminated string containing the name of
this font.  FontAttrs is a bit field with flags for several font attributes.  
The flags, as defined above, are bold, oblique, and serif.  The unused 
higher order bits are reserved for later use.  The other fields describe the 
average dimensions of the characters in this font.  FontTop is the average 
height above the baseline, FontBot is the average descent below the baseline, 
and FontWidth is the average character width.

KERN(0x4B45524C)   /* Kerning pair */

The KERN chunk describes a kerning pair.  A kerning pair sets the
distance between a specific pair of characters.  

struct KERNstruct {
    short   Ch1, Ch2; /* The pair to kern (allows for 16 bits...) */
    IEEE    XDisplace,  /* Amount to displace -left +right */
    YDisplace;  /* Amount to displace -down +up */

The Ch1 and Ch2 fields contain the pair of characters to kern.  These 
characters are typically stored as ASCII codes.  Notice that OFNT stores 
the characters as a 16-bit value.  Normally, characters are stored as 8-bit 
values.  The wary programmer will be sure to cast assigns properly to avoid 
problems with assigning an 8-bit value to a 16-bit variable.  The remaining 
fields, XDisplace and YDisplace, supply the baseline shift from Ch1 to Ch2.

CHDF(0x43484446)  /* Character definition */

This chunk defines the shape of ProVector's outline fonts.  

struct CHDFstruct {
    short   Ch;/* The character we're defining (ASCII) */
    short   NumPoints;  /* The number of points in the definition */
    IEEE    XWidth,     /* Position for next char on baseline - X */
            YWidth;/* Position for next char on baseline - Y */
 /* IEEE    Points[2*NumPoints]*/    /* The actual points */

#defineINDICATOR   0xFFFFFFFF   /* If X == INDICATOR, Y is an action */
#defineIND_SPLINE  0x00000001   /* Next 4 pts are spline control pts */
#defineIND_MOVETO  0x00000002   /* Start new subpoly */
#define IND_STROKE  0x00000004   /* Stroke previous path */
#define IND_FILL    0x00000008   /* Fill previous path */

Ch is the value (normally ASCII) of the character outline this chunk
defines.  Like Ch1 and Ch2 in the KERN chunk, Ch is stored as a 16-bit
value.  (XWidth,YWidth) is the offset to the baseline for the
following character.  OFNT outlines are defined using the same method
used to define DR2D's polygons (see the description of OPLY/CPLY for

Because the OFNT FORM does not have an ATTR chunk, it needed an
alternative to make fills and strokes possible.  There are two extra
bits used in font indicator points not found in polygon indicator
points, the IND_STROKE and IND_FILL bits (see defines above).  These
two defines describe how to render the current path when rendering

The current path remains invisible until the path is either filled
and/or stroked.  When the IND_FILL bit is set, the currently defined
path is filled in with the current fill pattern (as specified in the
current ATTR chunk).  A set IND_STROKE bit indicates that the
currently defined path itself should be rendered.  The current ATTR's
chunk dictates the width of the line, as well as several other
attributes of the line.  These two bits apply only to the OFNT FORM
and should not be used in describing DR2D polygons.