DevIL Logo
      A full featured cross-platform image library.


About

Features

News

Download

SourceForge Home

Documentation
Tutorials

Logos

Links

Projects

Contact Us

.OIL SpecificationsLast Revised: 3:25 PM 02/09/2001
The .oil file format was developed to be a robust solution to the lack of truecolour animated images (.mng is a possible one, but I've never even seen a .mng file, the library is still in beta and the format lacks some desirable features). From this auspicious beginning, .oil blossomed into a full-fledged image format, designed to support future additions (such as new types of compression) without breaking earlier files.

Or, as Aggrav8d of #flipCode said:
Say something like, "It was conceived by a comity formed by the clones of history's greatest minds, inscribed on sheets of silk by immaculate virgins using ink made by blind monks who used ground down charred pieces of the true cross. It was prompty lost in a sea of paganistic anarchy for a thousand years, kept secret by templar knights and guarded by the last great Chinese dragon and curses more powerful than those of Tutankhamen, until DooMWiz performed an ancient series of rituals and rights-of-passage until finally he was allowed the right to glimpse its wonderous magnificence. He promptly stole it."

.oil files are always in little endian format.

File Header
The .oil format is powerful, yet easy to read and begins with the obligatory file header.

typedef struct OILHEAD
{

char ByteHead[4];
ILuint MagicNum;
ILushort Version;
ILuint NumImages;
ILuint DirOffset;
ILuint AnimInfoOffset;
char HeadString[HEAD_STRING_LEN];
} OILHEAD;

ByteHead: This is a string that spells "OIL" (with the terminating zero).

MagicNum: This unsigned long "magic" number is 0x693D71 (or 6897009 in decimal format). Do not ask how this number was generated, as it was a horrid process that noone should ever submit themselves to.

Version: Simply states what version of the .oil format this file is. Unless the .oil format undergoes some major revision, more than likely, this number will stay at 1.

NumImages: Since the .oil format supports animation, this value is the number of images in the entire file, minus mipmaps, as they are considered "subsets" of an image.

DirOffset: Offset from the beginning of the file to the directory. The directory will be explained later in this document.

AnimInfoOffset: If 0, there is no animation info, else refer to the section titled "Animation Info".

HeadString: This is a human-readable string that just describes the type of file it is. If you want to make absolutely certain it's an .oil file and aren't convinced up to now, check this string. You can skip it if you want -- just skip to DirOffset. The string is currently:
"This is a graphics file based on the Open Image Library file format specification."
The length of this string is 83 bytes long (HEAD_STRING_LEN) and includes the terminating null character.

The Directory
To accomodate for animation quite easily, .oil files have a directory at DirOffset of the OILHEAD struct. This directory basically just tells where all of the images are located throughout the file. With this kind of system, there is no need to keep images in order in the file, though it is probably desirable for sequential access. You can even put the directory at the end of the file if you so desire. There are as many directory entries as there are number of images, so use the NumImages member of the OILHEAD struct to determine how many directory entries to load. The directory entry is described as such:

typedef struct DIRENT
{
char Name[DIRNAME_LEN];
ILuint Offset;
ILuint ImageSize;
} DIRENT;

DIRNAME_LEN is 255 characters, and Name is the filename of the file that this image was taken from or even just the regular name of this image. There is no significance to this name, except as a convenience to the author.

Offset is the number of bytes from the beginning of the file to this image.
ImageSize is the total size in bytes of the image, including mipmaps and anything else that may be present in the image.

The Image
Finally, we are down to the image itself. An image begins with its own little header:

typedef struct IMAGEHEAD
{
ILuint Width;
ILuint Height;
ILuint Depth;
ILubyte NumChan;
ILubyte Bpc;
ILubyte Type;
ILubyte Compression;
ILubyte NumMipmaps;
ILuint Duration;
ILuint SizeOfData;
} IMAGEHEAD;

Width: Specifies the number of pixels in the x direction.

Height: Specifies the number of pixels in the y direction.

Depth: Specifies the number of pixels in the z direction.

NumChan: Number of colour channels per pixel -- typically equated to bytes per pixel (or bits per pixel / 8). This number is usually 1, 3 or 4, but any number is theoretically support in the format, though support for it will not be available in any immediate fashion.

Bpc: Bytes per channel -- usually, this is 1, showing that each channel only occupies one byte (one byte for red, one for green, one for blue, etc.). The other common value for this field is 2, usually signifying 64 bits per pixel.

Type: Type is what type the image format is.

If Type is 1, then the image has a palette.
If Type is 2, then the image is only luminance values (greyscale).
If Type is 3, then the image's data is in bgr (blue-green-red) format.
If Type is 4, then the image's data is in bgra (blue-green-red-alpha) format.

Compression: Tells how the image data has been compressed. This field is what allows us to have virtually any kind of compression. Applications can even try to compress an image various ways before deciding on the best compression style for that particular image before compressing the image. With this field in place, we even have the option of lossy compression! The .oil specifications were designed with lossless compression in mind, but lossy compression may be ideally suited to certain types of images. There are currently four "official" compression schemes right now:

Compression Type:
0: No compression. Image data is to be read directly.

1: Run-length encoding. This version of rle is adapted from the .tga specification, which can be found at Wotsit's Format.

2: lzo compression. Just uses the lzo1x_decompress and lzo1x_compress functions from miniLZO. miniLZO can be found at the LZO Homepage.

3: zlib compression. Just uses the uncompress and compress functions from zlib. zlib can be found at the zlib Homepage.

Source examples for all three of these can be found in the DevIL sources, in oil.c.

NumMipmaps: Tells how many mipmaps immediately follow the image data. These are discussed in greater detail later in this document.

Duration: Specifies the number of milliseconds this image ("frame") should be displayed if part of an animation.

SizeOfData: Actual size of the image data on disk. This is the compressed size, if the image was compressed, or if not, it is the size of the image data in memory and on disk. This field is particularly useful for skipping the correct number of bytes if you do not understand the compression type used in this image (such as new compression engines being used in future versions of DevIL or other programs). The main use of this field though is for decompression of the image data, because you don't want to read too much when decompressing, so you don't overstep an array's boundaries.

Palettes
Only if the Type field of the image's header (IMAGEHEAD) is 1, then the image has a palette. The palette is always in bgra (blue-green-red-alpha) format. Immediately following the SizeOfData member of the image's header is the size of the entire palette in number of bytes as an unsigned long. For instance, if there are 256 palette entries, at 4 bytes per entry (bgra), 1024 should be written here. If the Type field of the image's header is not 1, this unsigned long value is not present.

Image Data
All multichannel image data is in blue-green-red format instead of red-green-blue, like some other image formats. The data is interleaved, meaning that we do not separate data into channels. In other words, our data looks like bgrbgrbgr instead of bbbgggrrr. Luminance data (type 2) is just read as a series of values, as is colour indexed data. How many bytes you read per pixel is dependent on both the number of channels and the bytes per channel. Just multiply these two values to determine how many bytes you must read per pixel. For programs that can only make sense out of one byte per channel, assume that the data is only in the top byte.

Mipmaps
Immediately following the (compressed or uncompressed) image data is the mipmaps. Mipmaps have the exact same format as their parents and even share the same image header, though the Duration and NumMipmaps members are ignored for mipmaps. The duration of the mipmap is the same duration as its parent, and mipmaps are not allowed to have mipmaps of their own.

Animation Info
This "directory" at the offset specified by AnimInfoOffset in OILHEAD is a simple list of the animation frames and their duration. This feature should be useful if you are wanting to reuse frames and do not wish to waste space by including them twice.

typedef struct FRAME
{
ILuint FrameNum;
ILuint Duration;
} FRAME;

typedef struct ANIM_INFO
{
ILuint NumFrames;
FRAME Frames[NumFrames];
} ANIM_INFO;
The first four bytes (NumFrames) make up an unsigned long that tells how many frames follow. Immediately after, there are NumFrames "frames" that make up the animation info. Each frame is just 8 bytes. The first unsigned int (FrameNum) is the frame number to display, and the second unsigned int (Duration) is the duration (in milliseconds) to show the frame for.

That should be all for the .oil format. Any comments, questions or suggestions should be sent to Denton Woods.