Picture compression format for 565 High Color

Hi guys,

I’m currently having fun coding some picture compression format specifically for Arduino and the like, optimized for 565 High Color (16bits) as well as B&W pictures. I wish to freely share the code with people when it’s ready. My format can define transparent color(s) to display sprites, also a quantized palette to optimize size, a collection of images sharing the same palette and size so it is possible to animate them like a GIF, or use them as an alphabet, etc… The decoding part is also very small and fast, use almost no more memory than the compressed data itself and is 100% independent from a given graphics library. All I need is a way to address a pixel at a given position and/or draw a line horizontally or vertically.

Now, I’m wondering if there is such a thing as a need for more colorful images than High Color in the Arduino world, like RGB 666 18 bits or True Color RGB 24/32 bits. The only thing I can imagine is a led display when each RGB led can be addressed. But I never came across a microcontroller screen that can do more than 16bits colors. Is there such a thing?

Do you think I should integrate more colors definition in the format from the start? I did some experiment with 18bits and 32bits and it works well as a compressed data format, but it’s cluttering a bit the code and I wonder if it is useful/necessary if no LCD/TFT screen ever use it anyway.

Looking forward to your comments. Thanks!

1 Like

It is very interesting

You correctly said that this is used in LED screens, where each pixel has separate control. This works on LED matrices with the HUB75 interface, which are widely used in commercial advertising. In the microcontroller environment, there are several libraries that work with such matrices. For example ESP32-HUB75-MatrixPanel-DMA library for ESP32, it allows you to display an image with a color depth of up to 24 bits. I also have a library for such panels, but it only works with the color565 format.

I think that for such screens the idea of ​​image compression could be very useful. I would be very interested in trying your code.

I would start with the 565 format only and add "hooks" to add additional formats.
When that code is stable go for the next format.

I did both :smiley: Every format handled is a “hook”. I heavily used object oriented code, so adding stuff is just a matter of deriving the base class to implement specifics. The decoding pixels loop is common to all the formats. With the use of #define and callback functions, my library is capable of implementing the decoding code only needed for a type of picture. So, if you know in advance what type of encoded picture you read, you can limit the quantity of decoding code in your program thus saving precious memory.

And I would be happy to give it to you for testing soon, it is a bit too early to publish it I think. I’m still adding stuff! But testers will be very welcome.

Here's a bit of details about what I have already implemented:

Compression ratio seems to be a bit better than the various examples I could find. For small images, I can go head to head with PNG or GIF (mainly because my header is much smaller). The reduction of number of colors and the palette quantization helps also a lot to do better than existing RGB 24/32 bits formats. I use various adaptative RLE algorithms to compress in the minimum number of bits, depending on the kind of pictures you have and the number of different colors it contains. Compression is complicated, but decompression is straightforward, fast and small. For example, a 2 colors picture will be compressed a bit differently than a 256 colors one, or a 15 bits 555 one. If you want me to do a test, post some pictures and I’ll give you the compression ratio I obtain with my current implementation. Currently I can optimize the compression for images with a maximum of 2, 4, 8, 16, 128 and 256 colors using a quantized 565 palette, and also 32768 colors in 555 format (15 bits), 666 format (18 bits) and 777 format (21 bits). All formats can handle transparency (alpha channel for 666 and 777, single transparent color for others).

The image can be encoded horizontally or vertically, because sometimes a vertical encoding is significantly smaller for RLE algorithms, depending on the picture to draw.

The image is drawn directly from the compressed buffer, I do not need additional memory to uncompress first the full picture, not even a full line. To do that I have 2 callback functions, DrawHorz and DrawVert, that can be implemented on the user side with any library available on any hardware that can draw a line, or simply set a pixel at a given coordinate.

The compressed buffer can be read directly byte by byte from a SD card using open/read/seek/close calls. That way, even less memory is needed. Of course it should be slower that way than directly from a PROGMEM or allocated RAM buffer. Again I use several callback functions, by implementing them you can read from any kind of device that have a interface similar to a file.

Finally, a compressed buffer can contains a collection of images, sharing the same palette colors. They can be animated like a GIF or displayed individually. They can also be of different sizes, so writing some text with a color alphabet with variable spacing composed of small pictures is straightforward to implement using a single compressed file as a source, with a picture’s index corresponding to a given letter.

Let me know what you think :smiley:

I use the 565 bitmaps for display on a TFT screen using a Mega 2560. It isn't really fast enough to be useful even without conversion from another format. This thread may be helpful.

What are you using the bitmaps for?

I’m glad my project sparked some interest. Since I started that thread I have implemented several compression schemes:

Compressed 888 + Alpha, 777 + Alpha, 666 + Alpha, 555

Palletized & compressed 256, 128, 64, 32, 16, 8, 4 and 2 colors

Also there is uncompressed 565, which is basically the “raw” 565 data. For example, for compressed 555 I use the additional bit to mark a following number of repetitions, a kind of RLE algorithm.

I compared the result with some other RLE implementation in the Arduino world, and it seems I do a bit better. But my process is not fully automatic, because depending on some images, sometime it is better to pick a particular compression scheme. So I wrote a program to pick the best result for a given usage. Here’s for example the Arduino Community logo, which has a transparent background and a size of 500x251, encoded as a PNG in 51491bytes.
https://support.arduino.cc/hc/article_attachments/12416033021852

You can see that this particular image compress well as a 8 color palletized image, in 6759 bytes, preserving the transparency.

Now a small monochrome image on a white background like that 150x150 JPEG image of 3362 bytes
https://support.arduino.cc/hc/article_attachments/12415993577116

Again, the 8 colors work best for this image, even removing the quite heavy JPEG compression artefacts at the same time, thanks to the palletization.

I compared also to an existing Arduino library, https://github.com/MHotchin/RLEBitmap
Picking the 2 examples given with a resulting size, the “chanceflurries” icon compress to 755 bytes according to the author. I do 512 bytes, a bit better…

Also, the moon example, 128x128 pixels in 16 colors compress to 8400 bytes, and again I do a bit better with 7731 bytes

I can even do a compression in 8 colors, with a result of 4939 bytes removing a little of the pixels information, but not much.

The current compression program that is quite a heavy Windows executable and is intended to be used manually picking the best algorithm. But the byte array result is read by a very simple algorithm, very light and that only need a implementation as a callback function, receiving a coordinate, a line length, a color and a direction (horizontal or vertical). So this would be straightforward to implement with any graphic library, or even doing it manually, for example lighting up an array of leds with a simple loop. So far, I implemented the decoding part in C#, but in a way that is extremely close to C++, so it will be no problem to translate it in the Arduino language later. And I’m pretty sure it will be very lightweight. I also have no dependencies at all for the decoding algorithm.

As my implementation can do sprites with some transparency, I thought it would be nice to consider the sprites as letters, thus having a way to write with any kind of font that Windows can display. I used as an encoding scheme CP437 (extended ascii) so many Western European languages can be written + a few additional characters, unlike some other implementations that limit to non-accentued characters. See https://www.lookuptables.com/text/extended-ascii-table

Here’s the result using a fancy font, only 16 pixels high and of course using antialiasing to have the letters looking nice, using only 4 colors (3 + transparency)
Screenshot 2025-01-03 075142

You can use even fancier fonts with complex ligatures, like this one
Screenshot 2025-01-03 080146

Enlarge for details:

And showing the various letters:

As you can see, at the end it is just a juxtaposition of sprites, taking into account the various parameters of a font (leading, kerning, baseline…). Each letter is a highly compressed buffer of bytes. in 2, 4 or 8 colors gradient

As a bonus, my decoding algorithm can read directly from an external media, so reading directly an image from a microSD card is possible, without having to instanciate more than a single line of pixels at a time.
Hope you enjoyed the ride :blush:

2 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.