The FLIC file format
In this paper, you will find the file format specifications for a class of animation files that fall in the "FLIC" file category. These include:
FLI files (Autodesk Animator) | FLC files (Autodesk Animator Pro) |
FLH & FLT files (DTA) | CEL files (Autodesk Animator) |
FLX files (Tempra Pro, Mathematica Inc.) | FLX files (U-Lead, 3DStudio MAX) |
Extensions by Pro Motion (Cosmigo) | Extensions by EGI (CompuPhase) |
The original FLIC file format was described by its author (Jim Kent) in Dr. Dobb's Journal, March 1993. The original purpose of this document was to document the modifications and extensions that EGI (a FLIC compiler and player engine) adds to the FLIC file format, but since then it has grown to include all other extensions and variations as well. As you may not have Jim Kent's article, all standard file information is included in this paper as well.
There are two "official" types of FLIC files: FLI and FLC (the names of these types refer to the filename extensions). The FLI files are the older format and are limited to a resolution of 320×200. The FLC file format adds configurable resolution and better compression.
FLX files are a slight variation on the standard FLC format. Actually, there are two FLX file formats with slight differences. Both formats use 32768 colours in a packed RGB format (15-bpp, each pixel takes two bytes). The first FLX format was defined for "Tempra Pro" by Mathematica Inc. The format used by 3DStudio MAX (Discreet Inc.) also has the extension FLX but their format is more like that of 15-bpp FLH files. Where distinction is needed, this paper refers to "Tempra FLX" or to "Autodesk FLX" formats (Discreet is affiliated to Autodesk).
Both "Tempra FLX" and "Autodesk FLX" formats use the RLE compression that was originally designed for 8-bpp FLC files, which is a sub-optimal solution. To improve the compression and to make the file format more flexible with regard to colour depth, Dave K. Mason defined new chunk types for his DTA program, in addition to tagging the new format with a new "type" ID and new extensions (FLH and FLT) as to avoid confusion. A few other programs, notably EGI, also support the DTA chunks and type IDs.
CEL files, finally, are actually either FLI or FLC files with a different extension and one more chunk in the "prefix data" (a kind of optional header).
Overview
A FLIC file stores a number of frames. Every frame contains an image and possibly a palette, a label, or other data. Usually, a FLIC file contains a ring frame at the end, so that the animation can be played repeatedly without a perceptible pause between the last frame and the first (the unpacking of the first frame, a complete image, is generally slower than a delta frame update). A FLIC file that contains segments (EGI extension) optionally contains a ring frame per segment.
FLIC files are structured in a hierarchy of chunks. A chunk contains a fixed part and a variable part. The fixed part of every chunk contains the type and the size of the chunk. The rest of the chunk has no fixed format; it all depends on the chunk type. The purpose of the chunked structure is to allow new chunks to be added without breaking existing FLIC players. A reader that does not understand some chunk type can just skip it (using the information from the fixed part of the chunk). The figure below shows the hierarchy of chunks in a FLIC file.
All "word" (two bytes) or "double word" (four bytes) values are stored in Little Endian (this is the byte order used by the Intel 80x86 and Pentium processor series).
File header general file information Prefix chunk FLC files only Cel data chunk CEL files (identical to FLC files) Frame chunk overlay frame, EGI files only <image data> overlay pixel data Cel data chunk overlay origin and transparent colour <label data> overlay label Path map EGI files only Segment table chunk EGI files only Segment chunk information of every segment, EGI files only <label data> symbolic names of segments, EGI files only Huffman table chunk EGI files only Script chunk EGI files only Frame chunk standard frame Postage stamp icon, FLC files only <image data> compressed or uncompressed Cel data chunk frame origin, EGI files only <palette data> colour data <image data> compressed in various ways <mask data> transparency information, EGI files only <label data> symbolic or numeric labels, EGI files only Region chunk region of frame changes, EGI files only Wave audio chunk digitized audio, EGI files only User string chunk general purpose data, EGI files only Key palette key frame data, EGI files only Key image key frame data, EGI files only Ring frame chunk frame that cycles back <palette data> see above <image data> see above <mask data> see above <label data> see above Region chunk see above Wave audio chunk see above User string chunk see above <palette data> is one of either: "256" colour palette palette with 8-bpp RGB entries "64" colour palette palette with 6-bpp RGB entries <image data> is one of either: Black frame full black frame Uncompressed full frame uncompressed pixel block Full frame RLE compressed, EGI also supports Huffman/BWT Delta frame (old style) RLE compressed Delta frame (new style) RLE compressed, EGI also supports Huffman/BWT HiColor/True Color frame DTA and EGI only <mask data> is one of either: Bitmap mask chunk EGI files only Multilevel mask chunk EGI files only Region mask chunk EGI files only <label data> is one of either: Label chunk EGI files only Extended label chunk EGI files only
The file header
A FLIC file starts with a 128-byte header, see the figure below for a C structure. The type field contains:
0xAF11 | For FLI files |
0xAF12 | For standard FLC files (with an 8-bit colour depth). FLX files (both "Tempra FLX" and "Autodesk FLX") also use this header type. |
0xAF44 | For FLIC files with a colour depth other than 8. The DTA program (by Dave K. Mason) makes FLIC files with a colour depth of 1, 15, 16 or 24 bits per pixel. EGI version 3.0 (and later) supports 16-bpp FLIC files. |
0xAF30 | For FLIC files that use Huffman or BWT compression. These FLIC files can also have a colour resolution that is different from 8 bits per pixel. |
0xAF31 | For FLIC files that use "frame shift" compression. These FLIC files may use additional compression schemes like Huffman and BWT; They can also have a colour resolution that is different from 8 bits per pixel. |
??? | Password protected FLIC files have a scrambled type field, so that previous versions of EGI and other FLIC readers will identify them as invalid (or unknown) FLIC files. |
For FLI files, the delay between two frames is in increments of 1/70 second; FLC files specify the speed in milliseconds. The header for a FLI file does not contain the offset of the first and second frames; the first frame is assumed to start directly behind the file header (no "prefix", "segment table" or "Huffman table" chunks) and the offset to the second frame is easily computed once you read in the header of the first frame. The purpose of storing the offset to the second frame is that, after playing the ring frame, the next frame to display is the second frame of the animation.
Autodesk Animator Pro also stores the MS-DOS formatted date and time stamps of the initial creation and last update of the FLIC file, as well as the serial number of the copy of the Animator Pro program that made/updated the file (in the creator and updater fields). Other utilities set the creator field to:
0 | Unknown (Pro Motion, DTA, VFD or other) |
0x45474900 | EGI |
0x464c4942 | FlicLib |
0x42494c46 | FlicLib - release 2 |
0x41544542 | Micrografx Simply3D |
0x30314c46 | Discreet 3DStudio MAX |
The frames field in the FLIC file header does not include the ring frame that loops back to the beginning of the animation.
The flags field is used internally by Autodesk Animator Pro. If a FLIC file is written to disk and closed correctly, Animator Pro sets this field to 3. Several other utilities set it to zero.
typedef struct { DWORD size; /* Size of FLIC including this header */ WORD type; /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */ WORD frames; /* Number of frames in first segment */ WORD width; /* FLIC width in pixels */ WORD height; /* FLIC height in pixels */ WORD depth; /* Bits per pixel (usually 8) */ WORD flags; /* Set to zero or to three */ DWORD speed; /* Delay between frames */ WORD reserved1; /* Set to zero */ DWORD created; /* Date of FLIC creation (FLC only) */ DWORD creator; /* Serial number or compiler id (FLC only) */ DWORD updated; /* Date of FLIC update (FLC only) */ DWORD updater; /* Serial number (FLC only), see creator */ WORD aspect_dx; /* Width of square rectangle (FLC only) */ WORD aspect_dy; /* Height of square rectangle (FLC only) */ WORD ext_flags; /* EGI: flags for specific EGI extensions */ WORD keyframes; /* EGI: key-image frequency */ WORD totalframes; /* EGI: total number of frames (segments) */ DWORD req_memory; /* EGI: maximum chunk size (uncompressed) */ WORD max_regions; /* EGI: max. number of regions in a CHK_REGION chunk */ WORD transp_num; /* EGI: number of transparent levels */ BYTE reserved2[24]; /* Set to zero */ DWORD oframe1; /* Offset to frame 1 (FLC only) */ DWORD oframe2; /* Offset to frame 2 (FLC only) */ BYTE reserved3[40]; /* Set to zero */ } FLIC_HEADER;
The modifications that EGI made to the FLIC file header are subtler. Not only are there a few added fields, but you must also decide whether the fields are valid:
- First verify that the FLIC file is created by EGI. If the "creator" field of the FLIC file header is not equal to 0x45474900, the ext_flags field is not valid.
- The ext_flags field indicates what extensions were used. If this value is
zero (no extensions) the file is a "compatible" FLC file.
bit meaning 0 file contains segments (segment table present) 1 file contains key images at regular intervals 2 all frames have an identity palette 3 file is Huffman compressed 4 file is BWT-Huffman compressed 5 bitmap masks are present 6 multilevel masks are present 7 region data is present 8 file is password protected 9 animation file contains digitized audio 10 animation file contains a script 11 region masks are present 12 animation file contains overlays 13 file is frame-shift compressed - If the script contains "segment" instructions, the EGI compiler produces a segment table. The segment table will come right after the FLIC header. Bit 0 of the "ext_flags" field indicates whether there is a segment table in the file.
- In a segmented file, the "frames" field is set to the number of frames in the first segment (excluding the optional ring frame). Likewise, the fields for the offsets to the first and second frames ("oframe1" and "oframe2") are set to those of the first segment.
- The totalframes field is the total number of frames in all segments, including all ring frames.
Chunks and subchunks
Every chunk starts with a 6-byte header, that contains the size (four bytes) and the type (two bytes) for the chunk. All chunks sizes should be rounded up to an even number of bytes. The values of the main level chunks are above 0xF100, values of the subchunks are below 100 (decimal). As you can see in figure "Overview of the FLIC file structure", nearly all of the subchunks appear in the "frame" chunk.
Chunks types are indicated in the FLIC file with a two-byte value, but Jim Kent's article tagged names on those values (these names appear to be extracted from the Animator Pro source code). For ease of writing, I use the same names, in the same glorious ALL UPPER CAPS" that has become common in discussing the FLIC file format.
3 CEL_DATA registration and transparency 4 COLOR_256 256-level colour palette 7 DELTA_FLC (FLI_SS2) delta image, word oriented RLE 11 COLOR_64 64-level colour palette 12 DELTA_FLI (FLI_LC) delta image, byte oriented RLE 13 BLACK full black frame (rare) 15 BYTE_RUN (FLI_BRUN) full image, byte oriented RLE 16 FLI_COPY uncompressed image (rare) 18 PSTAMP postage stamp (icon of the first frame) 25 DTA_BRUN full image, pixel oriented RLE 26 DTA_COPY uncompressed image 27 DTA_LC delta image, pixel oriented RLE 31 LABEL frame label 32 BMP_MASK bitmap mask 33 MLEV_MASK multilevel mask 34 SEGMENT segment information 35 KEY_IMAGE key image, similar to BYTE_RUN / DTA_BRUN 36 KEY_PAL key palette, similar to COLOR_256 37 REGION region of frame differences 38 WAVE digitized audio 39 USERSTRING general purpose user data 40 RGN_MASK region mask 41 LABELEX extended frame label (includes symbolic name) 42 SHIFT scanline delta shifts (compression) 43 PATHMAP path map (segment transitions) 0xF100 PREFIX_TYPE prefix chunk 0xF1E0 SCRIPT_CHUNK embedded "Small" script 0xF1FA FRAME_TYPE frame chunk 0xF1FB SEGMENT_TABLE segment table chunk 0xF1FC HUFFMAN_TABLE Huffman compression table chunk
The rest of the document discusses these chunks and subchunks in detail. On the right of the caption for each chunk type is the product for which this chunk is valid. Autodesk Animator is not mentioned explicitly, because there exist (to my knowledge) no chunks that only Animator supports.
typedef struct { DWORD size; /* Size of the chunk, including subchunks */ WORD type; /* Chunk type: 0xF100 */ WORD chunks; /* Number of subchunks */ BYTE reserved[8]; /* Reserved, set to 0 */ } PREFIX_HDR;
typedef struct { DWORD size; /* Size of the chunk, always 64 */ WORD type; /* Chunk type: 3 */ short center_x; /* Coordinates of the cel centre or origin */ short center_y; WORD stretch_x; /* Stretch amounts */ WORD stretch_y; WORD rot_x; /* Rotation in x-axis (always 0) */ WORD rot_y; /* Rotation in y-axis (always 0) */ WORD rot_z; /* z-axis rotation, 0-5760=0-360 degrees */ WORD cur_frame; /* Current frame in cel file */ BYTE reserved1[2]; /* Reserved, set to 0 */ WORD transparent; /* Transparent colour index */ WORD overlay[16]; /* Frame overlay numbers */ BYTE reserved2[6] /* Reserved, set to 0 */ } CEL_DATA;
The CEL_DATA chunk is usually not present in FLC files. Intermediate files used by Autodesk Animator Pro with the .CEL extension usually contain a CEL_DATA in the prefix chunk. The CEL files have the same format as FLC files.
EGI version 3.0 and later may generate CEL_DATA chunks inside normal frames; i.e. outside the prefix chunk. EGI uses the CEL_DATA chunk to hold the origin of the frame. Sprite animation toolkits can use the origin of a frame for run-time "registration" of the frame. (Registration is a term that animators use to mean the correct positioning of a frame in relation to the other frames and to the background image.)
EGI uses only the center_x and center_y fields of the structure. The center_x field gives the horizontal offset of the origin relative to the left edge of the frame. The center_y field is the vertical offset of the origin relative to the top edge, where a positive value points downwards.
In EGI 4.0, the overlay array holds up to 16 labels of overlays that can apply to a frame. When an element in this array is zero, this means "no overlay". The FLIC player chooses one of these overlays (or none) as the default overlay. Which overlay it chooses depends on the "overlay index" that one selects in the player. If the overlay index is zero, no overlay is displayed; if it is 1, the first overlay listed in the array is used, and so on.
EGI supports embedded run-time scripts in FLIC files that are written in the "pawn" language. The compiled P-code is added to the FLIC file. The format of the compiled P-code and an instruction reference can be found in the Small manual (available on-line as a PDF document on the Small language page mentioned earlier).
Specific functions in the run-time script execute on "events" in the FLIC animation, such as reaching the last frame of the segment (this is the frame before the ring frame). The currently defined event functions are:
Function | Description |
@FlicClose() | Called when the animation is closed |
@FlicFrame(framenumber) | Called after each frame is decoded |
@FlicLabel(label) | Called when a label was reached |
@FlicLastFrame() | Called when the animation reaches its last frame of the current segment |
@FlicLButtonDown(x, y) | Called when the left mouse button is pressed in the animated frame |
@FlicLButtonUp(x, y) | Called when the left mouse button is released in the animated frame |
@FlicOpen() | Called when the animation is opened |
@FlicPlay() | Called when the animation is starts playing |
@FlicRButtonDown(x, y) | Called when the right mouse button is pressed in the animated frame |
@FlicRButtonUp(x, y) | Called when the right mouse button is released in the animated frame |
@FlicSegment(segmentnumber) | Called when a new segment has just started |
@FlicStop() | Called when the animation is stopped explicitly |
typedef struct { DWORD size; /* Size of this chunk, including subchunks */ WORD type; /* Chunk type: 0xF1FB */ WORD segments; /* Number of SEGMENT chunks that follow */ } SEGMENT_TABLE;
If a segment table is present, it overrules the values in the header (number of frames, offsets to the first and second frames).
The segment table contains a series of SEGMENT chunks, one for every segment in the FLIC file. As of EGI version 4.0, the segment table also stores LABELEX chunks that hold symbolic names for the segments. The numeric value of each LABELEX chunk must match the (numeric) label of exactly one of the SEGMENT chunks. The name of the LABELEX chunk then gives the symbolic name for the segment.
typedef struct { DWORD size; /* Size of this chunk (always 32 bytes) */ WORD type; /* Subchunk type: 34 */ WORD label; /* Label of the segment (user value) */ BYTE reserved1[2]; /* Reserved, set to zero */ WORD cont_image; /* "continued from" image id */ WORD last_image; /* id of the last image of this segment */ WORD flags; /* Flags */ WORD frames; /* Number of frames in the segment */ DWORD oframe1; /* File offset to frame 1 of the segment */ DWORD oframe2; /* File offset to frame 2 */ WORD next_segment; /* segment number of segment to proceed with */ WORD repeat; /* repeat count (0=indefinitely) */ BYTE reserved2[2]; /* Reserved, set to zero */ } SEGMENT;
The flags field contains four bit flags:
Bit Meaning
0 the segment contains a ring frame 1 the first frame contains a full image 2 the "next_segment" field of the structure is valid 3 audio must be synchronized with the animation
If bit 1 is set, the first frame contains either a full image, or a delta image plus a key image. In other words, bit 1 indicates whether the segment is a "launch point". See the EGI compiler instruction launch_point for more details.
The frames field does not include the ring frame (if a ring frame is present).
In a FLIC file has multiple segments and some of the segments are "continued" from other segments (see the compiler instruction continued_from), a frame may contain both a full image (the key image) and a delta image. The EGI player uses the cont_image and last_image fields to determine whether it can use the delta image (whose decoding is usually quicker than a full image), or whether it must decode the full image instead.
When creating the FLIC file, the EGI compiler assigns each picture a unique number. It stores the number of the last image in a segment in the last_image field of the SEGMENT structure. When you add a continued_from instruction to the segment, the compiler copies the last_image field of the segment that was specified in the continued_from instruction to the cont_image field of the current segment. The compiler then proceeds to create a delta image given the first image of the current segment and the last image of the "continued from" segment. A segment that is not continued from another segment has a value of 0xFFFF in the cont_image field.
The EGI player goes the opposite route. Assuming that the player just displayed the last frame of a segment, and at that moment you switch to another segment: if the last_image field of the current segment is the same as the cont_image of the next field, the player decodes the delta image of the new segment. Otherwise it decodes the key image.
typedef struct { DWORD size; /* Size of this chunk */ WORD type; /* Chunk type: 0xF1FC */ WORD codelength; /* Maximum length of the Huffman codes */ WORD numcodes; /* The number of codes */ BYTE reserved[6]; /* Must be set to zero */ /* The following three fields are repeated "numcodes" times */ WORD code; /* Huffman code */ BYTE length; /* Length of the Huffman code */ BYTE value; /* Byte value represented by the Huffman code */ } HUFFMAN_TABLE;
See the paper "EGI compression schemes" for information on the Huffman compression algorithm. This chunk is only present if bit 3 of the ext_flags of the FLIC file header is set.
This chunk is defined by Autodesk Animator, but Pro Motion and EGI extend it.
typedef struct { DWORD size; /* Size of the chunk, including subchunks */ WORD type; /* Chunk type: 0xF1FA */ WORD chunks; /* Number of subchunks */ WORD delay; /* Delay in milliseconds */ short reserved; /* Always zero */ ushort width; /* Frame width override (if non-zero) */ ushort height; /* Frame height override (if non-zero) */ } FRAME_TYPE;
The delay value overrules the speed field in the FLIC header; a value of zero indicates that the normal speed (from the FLIC header) applies. The delay field is an extension to the FLIC format, introduced by Cosmigo's Pro Motion.
The frame size override (width and height) is an EGI extension (starting with EGI 4.0). It is currently only used for overlays.
The two subchunks with types 4 and 11 are identical, except that the range of the RGB values is 0-63 for COLOR_64 (type 11) and 0-255 for COLOR_256 (type 4). The 0-63 range of COLOR_64 reflects the 6-bit "CLUT" hardware for the original VGA card (sometimes referred to as the 18-bit DAC, where the 18-bits are divided into 3 channels).
The data in the chunk is organized in packets. The first word following the chunk header is the number of packets in the chunk. Each packet consists of a 1-byte skip count, a 1-byte copy count and a series of 3-byte RGB values (the "copy count" gives the number of RGB triplets). If the copy count is zero, there are 256 RGB triplets in the packet. For example, the data to change colours 2, 7, 8 and 9 would appear as:
2 /* two packets */ 2,1,r,g,b /* skip 2 entries, change 1 entry */ 4,3,r,g,b,r,g,b,r,g,b /* skip 4, change 3 */
The FLIC file format assumes that when the palette changes, the entire frame is refreshed. Note that in a 256-colour FLIC file, pixel values are palette-indices. When the colour definition of a palette entry changes, all pixels with the corresponding palette entry must take over the new colour, regardless of whether they are refreshed in a DELTA_FLI or a DELTA_FLC chunk. Note that if your video hardware is physically in a palette mode (256-colours), this global colour refreshing is automatic. At the time that the FLIC format was developed, 256-colour palette-mode video cards were state-of-the-art for desktop PCs.
All pixels in the frame have colour 0. This chunk has no data following the chunk header.
The data that follows the chunk header are the pixels of an uncompressed image of the frame. The number of pixels are exactly width×height of the frame. The first pixel is for the upper left corner of the frame.
Note that FLX files (15-bpp) use 2 bytes per pixel.
The data following the chunk header is a full image that is compressed with byte oriented RLE. The first image of a segment and key frames are usually stored in a BYTE_RUN chunk.
Each line of the image is compressed separately, starting from the top of the image. The first byte of each line is the packet count. It is a holdover from the FLI format and it should be ignored, because it is now possible to have more than 255 packets on a line (for FLC files). Instead, the width of the frame image now is the criterion for decoding: continue decompressing until the number of uncompressed pixels becomes equal to the image width. However, some players still rely on this pack count, so FLIC compilers should still generate them and only set it to zero when the packet count indeed does exceed 255.
Each RLE packet consists of a count byte and one or more data bytes. If the count byte is negative, its absolute value is the number of data bytes (following the count byte) to copy to the image: a literal run. If the count byte is positive, the single data byte that follows must be replicated in the image "count" times: a replicate run.
This chunk is used for FLI type files; the newer FLC format replaces this chunk with DELTA_FLC (see below). The chunk contains the differences (the "delta") between the current frame and the previous frame.
The first 16-bit word behind the chunk header is the line number (starting from the top of the image) for the first line in the image that differs from the previous frame. In other words, it is a line skip count. The second word is the number of lines in the chunk. The data for the lines themselves follow these two words.
Each line starts with a one-byte packet count for the line. Unlike the BYTE_RUN chunk, this value is significant because the "delta" need not to store the complete width of the frame image. The packets themselves follow the packet count. Each packet consists of a column skip count, an RLE count byte and zero or more data bytes. The column skip count is a one-byte value; it holds the number of pixels to skip (the number of pixels that did not change between the current frame and the previous frame) from the current position in the line. It can have a value of zero. The RLE count byte and the data bytes are similar to the BYTE_RUN encoding: if the count is positive, that number of bytes is to be copied from the data to the image (literal run); if the count is negative, its absolute value is the replication count of the one data byte that follows (replicate run).
Two pitfalls:
- The negative/positive meaning of the RLE count byte is reversed from that of the BYTE_RUN encoding.
- The RLE count byte can be zero. This occurs when the number of pixels to skip is more than 255. In this case, skip count is split over two packets. The first of these contains a skip count of 255, a RLE count of 0, and no data following the RLE count. The skip count of the second packet holds the remaining number of pixels to skip.
Also note that the "FLIC file format" document that originates from Autodesk mentions an extra byte that prefixes each line. This byte, the "starting x position of the data on a line", does not exist.
This chunk is similar to the DELTA_FLI chunk, but it compresses the delta image differently. The data following the chunk header is organized into lines and each line is organized into packets. Every line starts with one or more word-sized "opcodes".
The first word following the chunk header is the number of lines in the chunk. This count does not include "skipped" lines. Each line starts with one or more opcodes. One of the opcodes (the last) is the packet count. The highest two bits of the opcode word give its type:
Bit 15 Bit 14 Description
0 0 The opcode is the packet count for the line, it can be zero 0 1 Undefined 1 0 Store the opcode's low byte (bits 0-7) in the last pixel of the line 1 1 The absolute value of the opcode is the line skip count
The RLE compression of DELTA_FLC is similar to that of DELTA_FLI, but it is word oriented. The first byte of each packet is the column skip count; the second byte is the RLE count byte. Zero or more data words follow the RLE count byte. If the count is positive, that number of words of data is copied to the image; if the count is negative, one data word follows and the absolute value of the count tells how many times that word must be replicated in the image.
The two pitfalls from the DELTA_FLI chunk apply here too (see above). Additionally:
- The packet count for a line can be zero. This occurs when the frame image has an odd width and only the last pixel of a line changes. In that case, there is a "last byte" opcode for the line, but no further packets.
- For "Autodesk FLX" files, the skip count of each packet should be considered the number of "pixels" to skip, not bytes. For "Tempra FLX" files, the skip count is the number of bytes (and it therefore should always be an even value).
typedef struct { DWORD size; /* Size of this chunk */ WORD type; /* Chunk type: 18 */ WORD height; /* Height of the postage stamp */ WORD width; /* Width of the postage stamp */ WORD xlate; /* Colour translation, always 1 */ } PSTAMP;
A "postage stamp" is a reduced image of the first frame of an animation. A postage stamp created by Autodesk Animator Pro has the preferred size of 100*63 (the actual size can vary) and uses a universal palette.
The palette is an RGB cube with 6 levels of each red, green and blue. This
totals in 216 colours. An arbitrary RGB value (where each component is in the
range of 0-255) is mapped into the six-cube space using:
The PSTAMP chunk embeds another chunk. This is usually a BYTE_RUN chunk or a FLI_COPY chunk (for compressed and uncompressed images respectively). If the type of the embedded chunk is 18 again (the type of PSTAMP is also 18), that chunk contains a 256-byte colour translation table. The translation table maps every palette index of the first frame to the universal six-cube colour space. To display the postage stamp, a program decodes the image of the first frame and maps every pixel of that image to the six-cube colour palette (using the translation table).
These chunks are generated by the DTA program by Dave K. Mason for FLIC files with 15-bit, 16-bit or 24-bit colour depth, and by EGI starting with version 3.0. FLIC files with these formats usually have a .FLH or a .FLT extension. EGI supports only the 16-bit variety of these chunks.
In 24-bit FLIC files, pixels are made up of three bytes in the order B,G,R. In 16-bit files the colour values are packed into words (two-byte units) in "rrrrrggg gggbbbbb" format. 15-Bit FLIC files have pixels packed into the format "0rrrrrgg gggbbbbb". (The "blue" bits are in the lowest bit positions of each word.)
The DTA_BRUN chunk is numbered 25 (instead of 15 for a standard BYTE_RUN). The Repeat/Copy value is still a byte, but it refers to a number of pixels to copy or repeat, not bytes.
The DTA_LC chunk is numbered 27. Internally it is like chunk type 7 (DELTA_FLC). It starts with a number-of-lines value. For each line, the packet is a signed word value. If it is negative then it represents a lines-to-skip value and it does not count toward the number-of-lines. Each non-negative packet begins with a skip counter. This is still a byte, but it represents a number of pixels to skip, not bytes. Next comes a repeat/copy packet. It represents a number of pixels to copy or skip, not words. (Though in a 16-bit FLIC file, the pixels are words.)
The copy chunk is numbered 26, although the standard "copy" chunk type 16 (FLI_COPY) would do equally well: there is no decoding for copy chunks.
typedef struct { DWORD size; /* Size of this chunk (usually 10) */ WORD type; /* Chunk type: 31 */ WORD label /* Label of the frame (user value) */ BYTE reserved[2] /* Reserved, set to zero */ } LABEL; typedef struct { DWORD size; /* Size of this chunk */ WORD type; /* Chunk type: 41 */ WORD label /* Label of the frame (user value) */ BYTE reserved[2] /* Reserved, set to zero */ BYTE name[variable] /* symbolic name of the label */ } LABELEX;
The EGI API function FlicPlay() sends a notification message if it encounters a label subchunk in a frame. The message is sent (using either the Microsoft Windows function SendMessage() or using a direct callback) after decoding (and displaying) the frame. If the FLIC file has an embedded script, a label invokes the public function @FlicLabel() in the script.
A bug in early versions of the EGI player caused it to assume that LABEL chunks would always be exactly 10 bytes. Extending the label chunk with additional could therefore cause compatibility problems with those early players. Therefore, EGI defined a new, separate chunk for extended labels.
EGI versions as of version 4.0 store the symbolic name of a label (if any) in the chunk. The length of the name is the length of the chunk minus the fixed size of the chunk and minus any padding. The fixed size of the chunk is 10 bytes. The chunk is padded to an even size in bytes with zero bytes. So a label name like "eat" is stored as four characters 'e', 'a', 't' and '\0'. A label name like "walk" is stored in four characters, the string is not zero-terminated.
For standard 256-colour FLIC files, the KEY_IMAGE chunk is the same as BYTE_RUN, but a frame that has a KEY_IMAGE chunk also contains a DELTA_FLC from a previous segment. A player can decide (based on the transition it makes from segment to segment) whether to decode the KEY_IMAGE and skip the DELTA_FLC, or to decode the DELTA_FLC and skip the KEY_IMAGE. See the SEGMENT chunk for more information.
For 16-bit FLIC files, the KEY_IMAGE chunk has the same encoding as DTA_BRUN. Similarly to the 256-colour FLIC files, the KEY_IMAGE chunk only appears in combination with a DTA_LC chunk.
The same as COLOR_256, but it is only present for key images.
The chunk MLEV_MASK is defined as of EGI version 2.0; the chunk BMP_MASK exists since EGI 1.0.
The chunk starts (right after the chunk header) with four 16-bit words that give the bounding box of the mask. You can use this bounding box to speed up drawing operations, because you do not have to draw anything outside this box (all pixels are transparent outside the bounding box). The values are stored in the following order: x y width height.
The mask data starts behind the bounding box. Each line is encoded separately. A line starts with a "line skip count", which indicates how many lines remain unchanged from the previous mask. It is a two-byte value. It is zero for every line in a full mask.
The bitmap data (for the mask bitmap) comes after the line skip count. The bitmap data is compressed with the same RLE encoding as the BYTE_RUN image. This means that if the first (type) byte is positive, it is followed by a single pixel that must be replicated; if it is negative, it is followed by a series of bytes that must be copied literally. In contrast to the BYTE_RUN compression, no "packet count" byte is stored at the beginning of a scan line. Each scan line of the mask is compressed individually. Huffman and BWT compression do not apply to masks.
The data of a bitmap mask (after compression) is a 1 bit per pixel (8 pixels per byte) bitmap with the same size as the frame image. Each bit that is set ("1") represents a transparent pixel in the frame image. Each bit that is cleared ("0") represents an opaque pixel.
A multilevel mask is an 8 bits per pixel bitmap (with the same size as the frame image). Each byte in the mask represents the transparency level of the corresponding pixel in the frame image. A value of 255 is fully transparent, and 0 is fully opaque. In EGI version 2.x, you could actually use only two levels (0 and 255); as of EGI version 3.0, you can define up to 15 additional transparency levels.
Note that you can only specify the number of transparency levels; the opacity value of each level of the multilevel masks is not stored. The FLIC file format does not indicate the purpose the additional mask levels either. The number of transparency levels is in the transp_num field in the FLIC file header (EGI versions prior to 3.0 set this field to zero). The multilevel transparency masks were designed to be used with AniSprite, which allows you to set the opacity for each level (and lets you decide whether the additional levels are for "luma" masks or for "alpha" masks); please look up the AniSprite documentation for more information.
A frame can contain both a full image and a delta image. This occurs, for example when a segment in a FLIC file is both "continued_from" another segment and a launch point, or when the "key_frames" instruction is used. The mask for a frame that contains both a delta image and a full image is always a full mask.
See also the RGN_MASK chunk.
typedef struct { DWORD size; /* Size of this chunk */ WORD type; /* Chunk type: 37 */ WORD number; /* Number of rectangles that form the region */ /* The following four fields are repeated "number" times */ WORD x; /* Pixel coordinates of a rectangle */ WORD y; WORD width; /* Size of the region in pixels */ WORD height; } REGION;
The EGI compiler stores the region of frame differences (see the dif_region instruction of the compiler's script language) by means of a grid. In the current release, the grid is composed of square tiles of 16*16 pixels. When a single pixel in a tile changes, all 256 pixels in that tile are flagged as "modified".
Two neighbouring tiles that are both flagged as "modified" are combined into a single new tile. The edges of the resulting rectangular tiles are always snapped to the grid points. Therefore, each rectangle in a region of frame differences needs not to enclose the object at pixel precision. However, no rectangle will exceed the bounding box of all differences between two frames.
The purpose of a coarse grid is to avoid a large amount of very small areas. The cost of the overhead for every call to the BitBlt() function for all these areas might be higher than the cost of blitting several pixels too many.
As of EGI version 2.0, the maximum number of tiles in a region is in the field max_regions in the FLIC file header.
typedef struct { DWORD size; /* size of this chunk */ WORD type; /* Chunk type: 38 */ WORD flags; WORD samplefreq; /* sample frequency */ DWORD overlap; /* sample overlap (in bytes) */ BYTE reserved[6]; /* always zero */ } WAVE;
The flags value is a bit field.
Bit Description
0 Sample size is 16-bit (otherwise 8-bit) 1 Format is signed PCM (otherwise unsigned PCM) 2 Audio is stereo 3 First block of the audio stream (open/reset sound card/driver) 4 Last block of the audio stream (close sound card/driver)
The digitized audio data follows the chunk. The format depends on the various flags. If audio is 16-bit, there are two bytes per sample, in Little Endian (Intel format). In unsigned PCM, the "silence" level is 128 for 8-bit samples and 32768 for 16-bit samples. For stereo audio the samples for the left and right channels are interleaved; the sample for the left channel precedes the sample for the right channel.
Currently, EGI only supports 8-bit unsigned PCM or 16-bit signed PCM. These are the same formats as used in the .WAV and the .VOC file formats.
An audio stream is often be stored in a single block. The audio starts at the frame in which the block occurs, and it continues to "sound" during the displaying of the next frames. Audio and animation are not necessarily synchronized. For these single block audio streams, both bits 3 and 4 in the flags field are set, and overlap is always zero.
The audio stream can also be cut in various blocks. The first block contains enough samples to get from the current frame to the first key frame. The key frame contains another audio block with enough samples to get to the next key frame, etc. Here, the bits 3 and 4 of the flags field help to glue the blocks together to a sequential audio stream. Audio and animation should be synchronized to play back correctly. (The EGI player synchronizes animation and sound by default if the sound is cut into blocks.)
To avoid gaps in the audio when synchronization is not perfect, each block contains more samples than strictly necessary. In the current implementation, EGI adds enough samples to go to one frame after each key frame. When sending a (next) block to the audio device, you should ignore the first overlap bytes from the audio data.
You can jump in the middle of an animation, and this way, you can also jump in the middle of a multiple block audio stream. In this case, to get a proper synchronization, ignore the overlap field for the first audio block that you encounter.
A zero-terminated ASCII string follows the chunk header. The USERSTRING chunk allows an application to store general-purpose strings (both for copyright information as for references to other files or data) in a frame.
The chunk starts (right after the chunk header) with four 16-bit words that give the bounding box of the mask (this is similar to the BMP_MASK and RGN_MASK chunks). The values are stored in the following order: x y width height.
The mask data starts behind the bounding box and consists of a list of non-overlapping rectangles. This set of rectangles describes the opaque region of the frame. Each rectangle is composed of four 16-bit words, again in the order x y width height. The number of rectangles in the region can be determined from the size of the chunk.
There is no delta encoding for a region mask, except that if a RGN_MASK chunk is absent from a frame it should be considered equal to the region mask of the previous frame.
Note that the REGION chunk contains a coarse region of changes between two frames. The RGN_MASK chunk is a region of opaque areas of a single frame and it is precise at pixel level.
See also the BMP_MASK and MLEV_MASK chunks.
Shift chunks are generated when the frame_shift() compression option is present in the EGI script. As noted in the manual, the frame shift compression is effective on animations where the background is panned or where objects move over a fixed background, and in particular objects that include transparency masks.
The shift chunks give pixel counts by which the scan lines in the current image must be shifted horizontally and/or vertically to better resemble the "next" image. There are separate shift chunks for the image and for the mask; only multilevel masks (the MLEV_MASK chunk) are applicable for frame shift compression. That is, if you have region masks, the mask cannot be frame-shifted, but the image in each frame can still benefit from frame shift compression.
The chunk header has the following format:
typedef struct { DWORD size; /* size of this chunk */ WORD type; /* Chunk type: 42 */ BYTE img_id; /* image/mask identifier */ BYTE flags; /* flags, reserved */ WORD prio_list; /* size (in bytes) of the priority list */ } FRAMESHIFT;
The first after the fixed chunk header, img_id is zero if the frame shifts apply to the image and 2 if the frame shifts are for the (multilevel) mask. The following byte is for general purpose flags; it is currently always zero. The next 16-bit word, prio_list is the size (in bytes) of the "priority list", which is one of the three lists that follow the header.
Following the header are two lists with byte values and a third list with word values a variable-length encoding (technically, it is also a list of bytes). The first two lists have a length that is equal to the height of the frame, the length of the third list is in the header (described above). All three lists are compressed separately with the compression algorithm that is also used for the BYTE_RUN chunk.
The first list gives the vertical shifts and the second list gives the horizontal shifts. Each value in either of these two lists is a signed 8-bit number and indicates a shift count. The third list gives the order in which the shifts should be applied. It is variable length because only those lines for which the order is important are listed.
See the paper "EGI compression schemes" for details of the frame shift algorithm.
For animations that use frame shift compression, the maximum frame size is restricted to 16384 × 16384 pixels.
The "path map" is a planning table that can tell you how to go from one segment to any other segment, while using only "known to be good" transitions. A FLIC that has segments has optional cont_image and last_image markers. When the cont_image marker of one segment matches the last_image marker of another segment, that indicates a fluent transition between the two. The path map forms a matrix of all such transitions so that the animation path from one segment to another segment is easy to look up.
The goal of the path map is to make "pose to pose" animation easier, especially when the next pose of the character is determined dynamically or interactively. When pursuing to go from one segment to another segment in a fluent manner, it may be necessary to go via one or more intermediate segments (transitions). The path map records the shortest fluent way to get from the "current" segment to any other segment.
The path map is a square matrix with a number of rows and columns that is equal to the number of segments. That is, the path map of an animation with 4 segments is a 4×4 matrix. Each entry in the path map is the segment segment number (a two-byte value) of the next segment to take, when using the current segment to index the matrix column and the desired "goal" segment to index the matrix row. When the value in a matrix cell is -1, no fluent transition is available: either there is no fluent path between the two segments, or you have already arrived at the goal segment.
As is apparent, one entry in the path map does not indicate a complete path, but only the next step to take to come closer to the goal segment. Once you arrive at that "next segment", you do another look-up and get a new "next step" that is closer to the desired goal segment (and so on). One row in the path map matrix does contain an entire path, by the way, but not in a sequential way. In fact, one row of the matrix contains all paths that lead to one destination segment.
Frequent errors
Unfortunately, several utilities create FLIC files that do not completely comply with the standard. Most FLIC players attempt to detect and silently correct these errors. Below is a list of frequent errors in FLIC files.
- The colour depth is set to zero. The colour depth must usually be 8. DTA and EGI (starting with version 3.0) support FLIC files with a different colour depth and use a different "type" value in the FLIC file header. FLX files (15-bpp FLC files that use the same chunks as for 8-bpp FLC files) should have the value 16 in the header field for the colour depth, even though they are actually 15-bpp.
- The offsets to the first and second frames are zero in an FLC file. In fact, it appears that these are FLI files with a FLC header.
- The offset to the second frame is set to an incorrect value. I have seen FLIC files where the offset to the second frame was set to zero, to the offset to the first frame, to the offset to the ring frame, or to a seemingly random value.
- DELTA_FLC chunks are present in files with a FLI header. Since most players are combined FLI/FLC players, this is usually not a problem. However, a FLIC file with DELTA_FLC chunks should have a FLC header.
- Chunk sizes are not rounded to an even number of bytes. Only very few FLIC players have problems with frames with an odd number of bytes, but it goes against the "standard".
- The sizes of all sub-chunks in a frame chunk do not add together to the size in the frame chunk header. Sometimes the frame chunk appears to be padded with extra data behind the last frame. Sometimes the size a subchunk is incorrect (I have seen this with the FLI_COPY chunk).
- Garbage in reserved fields of a chunk or of the header. Reserved fields in a chunk should be set to zero. Otherwise, the chunks become difficult to extend. Players must do their best in estimating (or guessing) whether a field is a valid value for a chunk extension or whether it is garbage.
- DELTA_FLC chunks with a "line count" of zero. This is essentially an 8-byte chunk: 6 bytes for the chunk header and two bytes for the count of lines in the chunk. Since this count is zero, no lines with compressed data follows. Although this is (or should be) valid, the AAPLAY engine crashes when it encounters such blocks. Therefore, utilities should avoid creating these chunks.
- DELTA_FLI is not documented correctly.
The document that originates from Autodesk, and which was published with small
modifications in Dr. Dobb's Journal (see below), claims that:
"Each line begins with two bytes. The first byte contains the starting x position of the data on the line, and the second byte the number of packets for the line."
However, each line starts with only one byte, which is the number of packets for the line. - Using 0xF5FA as a "frame" chunk type. The frame chunk type should be 0xF1FA (source Mike Melanson, http://www.pcisys.net/~melanson/). The particular FLIC file that Mike Melanson referred me to was created by Discreet 3D Studio MAX. I expect that the alternate frame type records some implicit (unknown) extra information.
References
- Kent, J.; "The FLIC File format"; Dr. Dobb's Journal, March 1992 (Volume 18, issue 3).
- A description of the FLIC file format by the original creator of the Autodesk Animator software.
- "EGI compression schemes".
- An on-line paper with descriptions ans additional pointers to the extended compression schemes that EGI brings to the FLIC file format.