QuickDraw Viewer – Matriochkas image formats

Matrioshka-doll

I have just added support for SGI file embedded inside QuickTime opcodes in my QuickDraw Viewer, and I though it might be helpful to explain how this menagerie of image formats interact.

The original QuickDraw image format was quite innovative, as it was not a representation of the image buffer, but a sequence of drawing instructions, a vector format. It supported bitmap operations, and those bitmaps were typically stored in a compressed form, with the PackBits algorithm. This algorithm implements the simplest form of run length encoding, but has the advantage of being very fast as it basically relies on sign logic present in processors. Other image formats like Targa or ILBM also relied on PackBit compression.

When Apple introduced QuickTime in 1991, it included video compression algorithms like Road Pizza which could also be used for still images. So an additional QuickDraw operation was added that would embed a QuickTime image. That image could be encoded with different codecs, different compression algorithms, in the same way a QuickTime movie could be encoded with different codecs.

With QuickTime 2, Apple introduced the ability for QuickTime to use existing image formats as codecs, so you could use a JPEG file inside a QuickTime structure, and store that structure inside a QuickDraw image. This basically meant that the system could magically convert an foreign image file into a native Mac one, which could be parsed by any Mac application, as long as said application did use the standard functions to decode the picture.

The thing people usually call a JPEG file is a actually JFIF file which uses the JPEG compression. So in effect, the resulting file would be a QuickDraw image, with a QuickTime opcode, with a JFIF container with a JPEG image.

Now it might look a bit complicated, but it worked and allowed old applications to handle files which used newer compressions which was nice. So when I started implementing my QuickDraw viewer, my approach was to just find where the JPEG file is embedded, extract it and pass it to the rendering system (in my case Core Graphics). I did the same when I first implemented the parser in Java. Right?

Well, it works for some image formats, but not others. The reason for this is that basically an image file is two things: some form of container that contains meta-data about the image (dimensions, depth, codec) and a blob of data encoded in the way described in the container. Apple’s engineer noted that they were wrapping a container within a container, and transmitting redundant information, so when some image formats are embedded inside the QuickTime container, they strip the header – the embedded file is incomplete.

The simplest case is the SGI file format. It specifies a 512 byte header, which is mostly empty, the last 404 bytes are zeroes. So when an SGI file is embedded within a QuickTime container, these first 512 bytes are stripped. So my code has to reconstructs this missing header from meta-data in the enclosing container, this includes the file signature, height, width, depth etc. BMP files are stripped in a similar way, although I’m not sure the 36 byte this saves was worth the hassle.

Things get weird a JPEG-2000 file is embedded inside a QuickTime image file (QTIF). It is longer. The reason, I think is that JPEG-2000 files use the ISO base media file format, which is a direct descendant of the QuickTime container format. And one does not simply embed a QuickTime container inside a QuickTime container, so the embedded data becomes an atom, with a type (jp2c) and a length. So in this case, the parser skips the first 8 bytes. I’m not sure if you can find a JPEG-2000 embedded inside a QuickDraw file, a least I was never able to produce one.

Now it might sound like all the QuickTime codecs are actually just another file format embedded inside, this is not the case, video codecs are typically raw, i.e. there are just bytes embedded.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.