ST7735 Command Set Encoder

Enter a file here:

Warning: run time for large images (> 256 × 256?) is awful.

Input Image

Select an Image

Encoded Regions

Flat Rectangles/Lines   Bitmapped

Intermediate Steps

Output Data

Verbose (Text)
C Header
Sort flats by size (default: by color)
File name:


Output File Format

Options, Notes

Long format: Set Header to 0x371f5354 ("ST" 7735+1). Replace Widths, Heights and locations (xStart, yStart) with WORDs; maximum image size 65536 x 65536. Not yet implemented.

Text format: Full 24-bit color is given, exact to the original image. The other formats use 16-bit (5-6-5 RGB); the extra bits in the original image are discarded (rounded down).

C header format: Header DWORD is #define'd. Width and Height #define'd to SCREEN_WIDTH (160) and SCREEN_HEIGHT (128). Recommend converting an image under 160 × 128. All BYTEs are uint8_t, WORDs are uint16_t, etc. Termination byte is obligatory (C arrays don't have an EOF!).

Execution speed: Without a locate command on the ST7735 display controller, and the set-region command being fairly lengthy, the best writing option is to fill rectangular regions. Regions should generally be non-overlapping to avoid redraw, but some is acceptable. Each set-region command takes 10 bytes of SPI transfers—make the most of it. This makes drawing transparent images, diagonal lines, etc. fairly painstaking.

Compression/Encoding: This "compressor" tool only generates line and rectangle commands, when they are of adequate size. Everything else is considered "random" data and expressed as bitmap regions. This is effective on mostly-flat images—line drawings and such, and ineffective on high color images which generate mostly bitmap regions. A hand-written image (or a much smarter encoder..) could take better advantage of the command set.

Transparency is implicitly part of the format: any pixels that aren't drawn by command, are left unchanged.

Future Improvements, Speculation

I would love to be wrong about the ST7735's limited command set... (On that note, it appears there is one pair of undocumented commands, but they're not very useful here.)

Depending on implementation, palette data may not be checked for bounds. In case of un-checked overrun, most likely some instruction data will be read as colors. This suggests interesting opportunities for highly optimized files...

If a few more shapes are implemented, this could become a proper vector format of sorts. Downside: antialiasing would require a canvas (and a lot of processing), or video memory, both impractical on a small MCU.

An RLE Bitmap Rectangle command might be nice. This would RLE compress a given block, using a scheme similar to, say, Windows RLE. This gives another step between encoding large flat regions, and giving up and encoding whole raw bitmap regions. The fill-rectangle threshold would be higher, and RLE vs. raw would be decided on a per-block basis. Alternately, RLE can be used for much, or all of, the image, simplifying the command set.

Adding a set-color command or flag may prove useful. In that case, the decoder sets a state variable (current color), and draws flat-colored commands in that color. Maybe this can be switched too, so you can go back to using per-command colors on single pixel writes. Or just use even more command slots, etc.

A few bits could be saved by packing the Command, Len, xW and yH bytes, and maybe others. Unused bits could be packed away, or Huffman coding used. For larger, more complicated (high color) images, just using a zlib format is probably better.

For the C header format, the palette could be generated as a separate array, referenced by label. This would allow common palette(s) to be used by many images; then, the "image", as such, is just a string of commands. Similarly, an "indirect bitmap" command could be used, which references a separate bitmap array. This would allow patterns or sprites to be reused by many images, without incurring overhead—something of a 2D zip compression method.

Commands could also be packed in a different way. All draw commands except Point have the redundant information that zero width or height is a no-op. This should probably be wrapped to 256, as done with Width and Height. They could however be used as a flag to read one of (x, y, color) as a command byte, and that command byte is repeated going forward until changed again. This would save significantly on command bytes, in the same way that runs of colors can save using a color-change command. Point itself can't hold any such info, though; maybe it could be left until the end, with the remaining (x, y, color) triples assumed to be Points, and EOF is required for termination. Header format would have to include an array length #define, or use sizeof.