Image Decoders and Decompressors

Copyright 2010 Attila Tarpai (tarpai76 gmail)

When I wrote image decoders for my homebrew OS and native GUI to display common image types (BMP, GIF, JPEG, PNG, TIFF), I've not only had to deal with bits'n'bytes'n'pixels but stumbled upon different compression algorithms too (LZW, Huffmann, Deflate, RLE), because these decoders were written from scratch, without any external library dependencies. So a few decompressors were implemented too. 3 decompressor and 5 different image decoders took about 6 month of free-time development.. dealing with it programmatically is a very challenging task.

The design principles in osdev are clear, these decoders should:

The result is a bmp-format, which is the input of image transfer for the 2D BitBLT engine. This is a snapshot of the 'os', GIF, JPEG and PNG decoders in action in the virtual machine. All decoders here make RGB565 (16-bpp) bmp format, because it is the actual graphics mode now:

Re-entrant image decoders in action: png-, jpeg- and a gif-animation.

A client (a task) should be able to use an image decoder very simply and uniformly for jpeg or png, etc. These are the design principles from the client side:

1. Allocate a decoder object

This is a per-image relationship. All decoders are re-entrant: multiple tasks can call the same Decode() function at approx the same time, each using it's own image file to work on, each can then use the same decompression code, so all decompressors are re-entrant too...

Some objects are big, eg. each GIF-decoder instance has to have its own LZW-dictionary, which is now 32KB in size. It's a design decision who and how allocates this 32KB:

2. Dynamic memory requirement

Decoding an image file will 100% need some dynamic memory allocation. Depending on image size, format, the decompressed image will need different amount of memory. Only the decoder knows the exact size that is required for the decoding process.

Calling malloc() again is therefore inevitable. I use the following approach:

3. call Decode()

The decoder does the job by writing the bmp into memory, ie. fill in the previously allocated bmp's image data area. Alternatively make a QuickDecode() and pass malloc pointer so decoder deals with allocation. In this case all decoder is designed to make this call to malloc() only once (one-shot-malloc), because system calls are a little expensive - and we need blazing speed here..

4. Blit or Save

Client then does whatever is intended to do with the resulting bmp. I can think of the following:

My goal was the first one during GUI development, but also wrote a few front-ends to make eg. a png2bmp.exe command line tool.

What has been implemented and tested

[details on decoders and C code]

Re-usable decompressors

Re-entrant image decoders