Recently I implemented a BMP image decoder in Elm. This Elm function takes an image as byte-array (
Bytes.Bytes) and returns the values of the pixels in this image.
For a current project, I was looking for a way to decode 2-dimensional raster images in Elm to get values of individual pixels out of an image. I googled and searched the Elm forum for existing solutions but did not come up with anything. So I built a BMP image file decoder from scratch.
So what is the scenario exactly? The user loads an RGB image file containing a screenshot into our program. The program then needs to read the intensities of the individual pixels' red, green, and blue components for further processing. What's more, the user also should be able to load the same image file into popular raster graphics editors like MS Paint. Following this constraint, I could not use the simplest possible image file format. Instead, I needed to support one of the popular file formats like PNG, BMP, or JPEG.
Looking a bit closer at those image file formats, it seemed that BMP would be the simplest one to implement a decoder for. I found the Wikipedia article on the BMP file format explains it well. In that article, we can also see that BMP supports diverse kinds of pixel formats. But for our application, we only need to support the 24-bit pixel (24bpp) format.
Even with this relatively limited set of supported formats, I expected plenty of opportunities to introduce bugs while writing a decoder implementation. So it was clear from the beginning that I would need to run some tests on the decoder to trust it works correctly. I began with coding tests for some image files. These tests are written in Elm, so we can run them with the elm-test tool to see if the decoding function works correctly. The tests model the flat file on one side and the pixels values on the other side. The reference image files are encoded as base64 strings to get them into the Elm source code. The testing module is published at https://github.com/Viir/bots/blob/82ef37f3089a3e40bd496f507b691a5c25011b33/implement/devtools/read-from-image/tests/DecodeBMPImageTest.elm
To figure out the decoding, I found the Wikipedia article easy enough to follow. One thing which took me by surprise was the behavior of
Bytes.Decode.decode in the Elm libraries. This function can return different results for the same inputs since it suppresses stack overflows.
The final image decoding function to get the RGB pixel values out of an image file is published at https://github.com/Viir/bots/blob/82ef37f3089a3e40bd496f507b691a5c25011b33/implement/devtools/read-from-image/src/DecodeBMPImage.elm
(This post was originally published at https://michaelrätzel.com/blog/bmp-image-file-decoding-in-elm)