Steve's Second Image Format
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Steven vanZyl 50a8906c82 to-bytes seems to be working 1 week ago
.gitignore Started S2IF in Racket 1 week ago
LICENSE Updated readme and license 1 week ago Updated readme and license 1 week ago
s2if.rkt to-bytes seems to be working 1 week ago

Thoughts on SSIF

Steve's Simple Image Format (SSIF) was for me a foray into completely unknown territory. I had done little to nothing with either graphics, or binary formats up until that point, and it was overall a fantastic learning experience for me. Overall I was an am very proud of it.

But I do think that I made a number of mistakes, and I want to take another go at making an image format in an attempt to maybe make some new mistakes and learn some more things along the way :)

Mistakes I Made Last Time

  1. Not starting with a converter meant that I didn't have any real images to
  2. test on for a long time.
  3. Compression algorithm was effective, but somewhat problematic.
  4. Limited colorspace occasionally caused neat effects, but wasn't a great
  5. design choice.
  6. Weird tooling.
  7. An over-emphasis on small file size for no particular reason.
  8. Not writing a viewer sooner.
  9. ARGB is more common than RGBA so that makes things easier.

Language Choice

So I have decided that I am going to use some form of LISP for this project, since I want to learn the ways of LISP better. That probably means Scheme. But which one to pick?

I think I'll go with Racket since that seems to have decent PNG support.


Has all the bells and whistles I could ever need built right in, but feels very "academic" and like a teaching language, not something truly meant for real work.

That's honestly a nitpick but still it bothers me.


The most mature and "real-language" feeling of the choices. Also learning Guile specifically has some benefits such as making my way toward learning more about Guix.


It's a neat language, and C interop is a cool feature. But I don't really want to deal with C interop.


Honorable mention to Clojure because I still find this language to be fascinating and it's probably the most "enterprise-y" of the LISP family.

Compression Algorithm

    So this time I want to employ a two-fold compression algorithm with a variety of flags for levels of compression. The two parts of the compression algorithm are:
  • Color indexing
  • Color runs (from SSIF)

Color Indexing

This is the new method that should provide much more flexiblity than the previous method of compression. Colors are stored in an index at the beginning of the file, and then referenced by their index number. Colors at occur frequently then can be referenced by a single byte. The different compression levels allow for slightly different colors to be averaged and considered the same, reducing the index. The index can hold at most 254 different colors, since there are two bytes that are reserved for indicating color runs and unindexed colors.

Color Runs

    Color runs function very similarly to how they did in SSIF but with a couple of key differences. In general the format is:
  • A magic byte 0xFE
  • The length of the run
  • Then a color, mostly likely an indexed color but not always.

Uncompressed Pixels

    Pixels can also be represented in an uncompressed way, allowing for more accurate images at the cost of filesize. The format is:
  • A magic byte 0xFF
  • 4 bytes for ARGB respectively

File Format

    7 bytes total length
  • 2 byte magic number 0xAEAE
  • 1 byte sequential version number, in this case 0x02
  • 2 byte width of the image
  • 2 byte height of the image


The index contains at most 254 color assignments. First is a single byte that is the length of the index. Then there are that many blocks of 4 byte colors, for ARGB respectively.

The index is, at most 1017 bytes long (4 * 254 + 1), and ends after the final color has been read in the length.


The body of the file contains the pixel data and runs until the end of the file. Generally it is individual bytes that map to the color index above it. However there are two special cases for color runs and uncompressed pixels:

  • If a zero byte 0xFF is found that indicates a color that has not been
  • indexed. The following 4 bytes are it's RGBA data.
  • If a one byte 0xFE is found that indicates the beginning of a color run.
  • This is then followed by a one byte length of the run, then a color either indexed or literal.

See the section above on comporession algorithms for details on these structures.