X-Git-Url: https://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/f58512db610bb5ce272a142a552b8bf75b8c2f6e..fc46251027e2c65bff081cc04939ff13015501aa:/doc/txt/image-format.txt?ds=sidebyside diff --git a/doc/txt/image-format.txt b/doc/txt/image-format.txt new file mode 100644 index 0000000..0a558dd --- /dev/null +++ b/doc/txt/image-format.txt @@ -0,0 +1,154 @@ +LBX Images: + +This document describes the LBX image format used in Moo2. The goal is that +the code to decode images in liblbx can be written against this specification, +thus guiding the implementation. + +All multi-byte integer types are stored in the image file from +least-significant to most-significant byte. This document uses the names +"uint8", "uint16" and "uint32" to refer to 8-bit, 16-bit and 32-bit unsigned +integers, respectively. + +LBX Images begin with a 12-byte header which has the following layout: + + OFFSET TYPE DESCRIPTION + ----------------------------------------------------- + 0 uint16 Image width in pixels + 2 uint16 Image height in pixels + 4 uint16 Unknown (always 0?) + 6 uint8 Number of frames + 7 uint8 Unknown (always 0?) + 8 uint8 Lead-in (less than the frame count) + 9 uint8 Chunk size + 10 uint16 Flags + +Immediately following the header is an sequence of uint32 offsets (one for +each frame) indicating the start of each frame, followed by a final uint32 +offset which marks the end of the file. + +The frame count, lead-in and chunk size describe how the image is animated, +which is described later. The flags value is the bitwise-OR of zero or more +of the following values: + + VALUE NAME DESCRIPTION + ---------------------------------------------------------------------------- + 0x0100 raw If set, this flag enables a simpler method of storing + frame data in the image. + 0x0400 overwrite If set, behave as if the chunk size was 1 instead of its + actual value. + 0x0800 building Unknown. This flag is set on the building images + (perhaps to enable a special blending mode for shadows). + 0x1000 palette If set, the image contains embedded palette data. + 0x2000 loop If set, behave as if the lead-in was 0 instead of its + actual value. + +If the palette flag is set, the palette data follows the frame offsets. The +palette has a 4-byte header: + + OFFSET TYPE DESCRIPTION + ----------------------------------------------------- + 0 uint16 Index of the first embedded palette entry + 2 uint16 Number of embedded palette entries. + +The sum of these two values must not exceed 256. Immediately following the +palette header are the palette entries. Each palette entry is 4 bytes with +the following layout: + + OFFSET TYPE DESCRIPTION + ----------------------------------------------------- + 0 uint8 Always 1. + 1 uint8 Red component (0-63) + 2 uint8 Green component (0-63) + 3 uint8 Blue component (0-63) + +Note that component values are stored as an 8-bit integer but only range +from 0-63 (6 bits per channel), with (0, 0, 0) being the darkest black and +(63, 63, 63) the brightest white. The values in the image's embedded palette +supersede the values in the "main" palette. + +Image data: + +Each LBX image consists of one or more frames. The start of each frame +can be found by seeking to the appropriate offset in the file, as described +above. + +There are two main methods of encoding frame data. The simplest is the raw +encoding, which is used when the "raw" flag (0x0100) is set in the header. +In raw encoding, the pixel data for the entire frame is stored verbatim in +row-major order (one uint8 value per pixel). There are no headers to parse +within the frame, and this format does not support transparency as each frame +specifies a palette index for every pixel in the image. + +For example, a 3x2 raw image has exactly 6 bytes per frame, with the pixels +laid out as in the following format. The numbers represent byte offsets +within the file: + + +---+---+---+ + | 0 | 1 | 2 | + +---+---+---+ + | 3 | 4 | 5 | + +---+---+---+ + +The more common method is a line-based encoding, which is more flexible. +In this format, a cursor is maintained to track where pixel data is to be +written. The X value of the cursor specifies the number of pixels right +from the left edge of the image, and the Y value of the cursor specifies +the number of pixels down from the top edge of the image. + +Each frame in this encoding begins with a header. + + OFFSET TYPE DESCRIPTION + ----------------------------------------------------- + 0 uint16 Always 1. + 2 uint16 Initial Y position of cursor. + +The cursor has an initial X value of 0. Immediately following the header +is a sequence of one or more drawing commands. Each command consists of a +2-byte header. + + OFFSET TYPE DESCRIPTION + ----------------------------------------------------- + 0 uint16 Length: number of pixels to follow + 2 uint16 Cursor offset + +If length is non-zero, first the offset is added to the current X value of the +cursor, then the pixel data (1 byte per pixel) follows and is drawn starting +at the current cursor position, with successive pixels increasing in X value. +The cursor is updated as pixels are drawn. In general, new pixel data +supersedes any previous value for a particular pixel. Pixels that have no +data specified are transparent. Animated images may re-use pixel values from +the previous frame, see below for details. + +If length is odd, there is a padding byte after the last pixel which must be +skipped. + +If length is 0, then the offset is added to the Y value of the cursor, the X +value of the cursor is reset to 0, and no pixel data follows. + +Notwithstanding the above, if both length is 0 and offset is exactly 1000, +then there are no further drawing commands in this frame. + +Animation: + +There are 3 parameters relevant to animations found in the image header: the +frame count, the lead-in, and the chunk size. Frame count specifies the total +number of frames in the image, chunk size affects how frames are decoded and +the lead-in affects how animations are displayed. + +Normally, each decoded frame is drawn on top of the previous frame, with the +first frame drawn on a fully transparent slate. So if the first frame has any +unspecified pixel values, those pixels are transparent, while if the second +frame has any unspecified pixel values, those pixels retain the value from the +first frame (transparent or otherwise). + +However, if the chunk size is set to a non-zero value, then the slate is reset +to a fully transparent state when decoding a frame number which is divisible +by the chunk size. So if this is set to 1, every frame must specify all +non-transparent pixels as they will each be drawn on top of a transparent +slate (just like the first frame). This parameter is the only way for pixels +which are opaque in one frame to become transparent in the next. + +The lead-in specifies the frame to be displayed after the last frame in an +animation. If this is different from the last frame, then the result is a +looping animation. If this is the same as the last frame, then the result is +an animation that stops at the end.