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 a program built with Elm. This program needs to read the intensities of the red, green, and blue components of the individual pixels 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.
After briefly looking at the encoding of popular image file formats, I concluded that BMP would be the simplest one to implement a decoder for. I found a good description of the format on Wikipedia. In the Wikipedia article, we can also see that BMP supports diverse kinds of pixel formats. But for our application, it is sufficient only to support the 24-bit pixel (24bpp) format.
Even with this relatively simple choice of supported format, I expected plenty of opportunities to introduce bugs in 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 that 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)