I took a break from arguing with our GIF decoder to take a quick look at the BBM format as I have a few files in that format containing colour palettes I wished to extract. When I looked into this, I found a BBM file is essentially an LBM file without any image data, so I set to work at writing a new palette serializer for reading and writing the palette files. This article describes how to read the palettes from BBM and LBM files.
Note: I only cover loading of color palette data in this article. The image data I don't even look at - this article does not represent a full LBM decoder.
The sample code presented in this article took all of an hour to write and has been tested on a pretty small selection of images. It only handles 8bit LBM files (possibly lower depths too, but I have not tested this). And, who knows, I might be misinterpreting the specification or missing a chunk of vital information.
Information is sketchy so I may well be wrong in particulars, but a BBM file essentially seems to be a LBM without a full image - in the files I've experimented with, there are header chunks describing a bitmap, but no real data. A LBM file is of course a full graphic, most popular (I think) by DeluxePaint on both the Amiga and MS DOS. As I said though, this information is my understanding and might be totally wrong. Luckily enough, or our purposes it doesn't matter. However, to keep things simple, for the rest of this article I'm going to refer to LBM, but you can consider this interchangeable with BBM.
The LBM format is more formally known as ILBM - IFF Interleaved Bitmap. It is built upon "EA IFF 85" Standard for Interchange Format Files. Both formats were devised by Electronic Arts as a standard means of sharing data between systems, their example of writing a theme song with a Macintosh score editor and incorporating it into an Amiga game summing it up neatly.
An IFF file is comprised of chunks of data. Each chunk is prefixed with a four character ID, the size of the data in the chunk, and then the chunk data itself.
There is one oddity in that if the size of the chunk is odd, an extra padding byte is added to the chunk data to make it even. This padding byte is not included in the size field, so if it's odd you must make sure you read (and discard) the padding byte.
Oh yes, and there's one other important detail. I don't know if this is specific to all IFF format files, or just LBM, but integers and longs are in big-endian format, so we need to convert these when we read them.
The only section of the LBM file we are interested in is the
CMAP chunk, which describes an 8bit colour palette. According
to the specification however, it's optional so it's entirely
possible that not all LBM files contain a
CMAP. Also, only
8bit (or lower?) LBM files will contain a
CMAP, as they only
support RGB channels. 24bit or 32bit images won't have one as
there's no scope for storing alpha channels.
The data section of a
CMAP chunk is as simple as it gets - one
set of 3 bytes for each colour describing the red, green and
blue channels. The size attribute is the number of colours * 3.
Although I'm not reading other chunks, I still have to pay attention to some of them.
FORM chunk describes an IFF document. So, if the
file we read doesn't start with this, it's not a valid IFF file
and we shouldn't continue reading.
The second chunk we at least want to identify is the chunk that
states if this is an actual image. The specification states that
this should be
ILBM, but the sample images I've worked with
use a different header which is
PBM for Planar BitMap. Note
there's a trailing space on this ID as the specification states
ID's are four ASCII characters long. As in both cases the
section is the same, I look for either of these.
Anything else will be discarded.
After opening the file, the first thing we do is read the first
four bytes and convert these to an ASCII string. If the string
FORM, we know we have an IFF document and continue
reading. Otherwise, we throw an
Next we read the size of the data contained within the
chunk. As we aren't checking for nested chunks nor reading all
the data, we can safely ignore this.
Time for another sanity check, this time to verify we are
reading an image, be it Planar (
PBM ) or Interleaved (
For some reason this chunk doesn't include a size, so we don't attempt to read any more bytes as the next byte is the start of a new chunk.
The reset of the routine is going to load one chunk of data from the file at a time, and either discard it or process it.
First, we read 4 bytes that will be the ID of the chunk. We also need the size of the chunk, regardless of whether we use it or not, so we'll read that too.
As we are only interested in
CMAP chunks, if the pending chunk
has any other type of ID, we skip the remainder of the chunk, as
chunkLength read earlier. If we can, we just
move the current position in the stream ahead, but if we can't
(can't think why not!) then we just read and discard bytes until
Aha! We finally found the
CMAP chunk. Now it's just a
straightforward reading of colours.
chunkLength is the number
of colours / 3 (as each colour is represented by 3 bytes), so a
simple loop to read each triplet and add them to our results
collection is all we need.
Then, we exit out of the
while loop - no pointing reading the
entire file now that we have what we wanted.
If we are still in the loop however, then we need to check our
chunkLength value. If it's odd, we read and discard the
padding byte - otherwise you'll be out of alignment and won't
hit any more chunk headers, except by accident.
At the start of the article I mentioned that the numeric data types in an LBM image are stored as big-endian. On the Windows platform, we use little-endian. So when we try to read the chunk length from the file... well, it's just not going to work.
As bit shifting still jellies my brain, I took to Stack Overflow which provided me with a function for converting four bytes of big-endian data into a little-endian integer.
So in our sample, we read our 4 bytes, shift their bits around and return the result.
That was fairly straightforward! Well, if I ignore the endian
conversion. And I'm sure if I decided to read the
and actually start decoding the image itself I'd be tearing out
my hair, but the bit I actually wanted could hardly have been
As usual, a fully working sample is attached to this post.
As I wrap up this post it occurred to me I forgot to add
anything in for if it's a valid LBM image, but doesn't contain a
CMAP section. Although the clue would be in the empty list
I also didn't even begin to look at writing a BBM file... this will probably be the next thing I take a look at. Unless I get distracted by Microsoft's (old?) palette format which I discovered is also a variant of IFF and should be just as easy to read.
- 2014-01-11 - First published
- 2020-11-21 - Updated formatting
Like what you're reading? Perhaps you like to buy us a coffee?