Smaller Than a Bitmap: Vector Drawing Code Shrinks OLED Flash (Measured)
Every monochrome OLED pixel stored in a traditional C bitmap consumes flash memory, regardless of whether that pixel is illuminated. By converting structural graphics into algorithmic drawing commands, we can dramatically compress microcontroller program storage.
The Problem: The Inflexible Bitmap Storage Math
In standard monochrome graphics (such as Adafruit drawBitmap or U8g2 drawXBMP), every pixel is stored in a 1-bit grid. The flash byte requirement is strictly fixed by dimensions:
bytes = (width / 8) * height
Under this mathematical reality:
- A full-screen 128x64 display requires exactly 1,024 bytes in flash.
- A 10-frame animation of 128x64 frames requires 10,240 bytes (over 10 KB).
- A 30-frame spinner requires 30,720 bytes.
On an Arduino Uno (ATmega328P), total flash is only 32,256 bytes, and the bootloader plus Adafruit display library already consume approximately 9 KB to 13 KB. A 20-frame raw bitmap animation will completely overflow the microcontroller's ROM.
The Vector Solution: Algorithmic Drawing Primitives
The vast majority of maker OLED graphics are not photographs—they are status icons, battery gauges, warning symbols, user interface frames, and logos. These graphics consist of solid rectangles, outlines, and geometric lines.
Rather than storing 1,024 discrete 1s and 0s in flash, we can instruct the display controller with drawing primitives:
- Filled Rectangles (
fillRect/drawBox): Encoded with(x, y, width, height). A 32x32 solid square takes 128 bytes as a bitmap, but as a single drawing command takes only 4 bytes in an instruction table! - Diagonal Lines (
drawLine): Outlines and angled accents encoded as coordinate endpoints. - Horizontal Run-Length Encoding (RLE): Consecutive spans of pixels encoded as start and length offsets.
Measured Real-World Benchmarks
Here are actual measured flash memory consumption results comparing raw bitmap storage against the automated vector mode in OLED Bitmap Converter:
| Graphic Type | Resolution | Raw Bitmap Size | Vector Code Size | PROGMEM Saved |
|---|---|---|---|---|
| Battery & WiFi UI Icons | 32x32 px | 128 bytes | 44 bytes | 65.6% saved |
| Geometric Maker Logo (Outlines) | 64x64 px | 512 bytes | 186 bytes | 63.7% saved |
| Full-Screen UI Dashboard Frame | 128x64 px | 1,024 bytes | 290 bytes | 71.7% saved |
| 8-Frame Loading Spinner GIF | 64x64 px (8f) | 4,096 bytes | 2,110 bytes | 48.5% saved |
| Dithered Photo (Floyd-Steinberg) | 128x64 px | 1,024 bytes | 1,480 bytes (vector overhead) | 0% (Bitmap is smaller) |
When Vector Wins vs When Bitmap Wins
Vector compilation is not a universal silver bullet. In random, high-entropy images (such as dithered portraits or noise textures), every individual dot would require its own separate drawing call, increasing both code size and execution time.
This is why the Auto mode in OLED Bitmap Converter operates transparently:
- It compiles the graphic using four independent methods: Raw Bitmap, Rectangles (
fillRect), Rectangles + Lines, and Run-Length Rows. - It factors in the exact bytecode overhead of the drawing loop subroutine on your chosen target board (e.g. Arduino Uno vs ESP32).
- It performs an automated pixel-for-pixel mathematical check against the original image to guarantee zero rendering distortion.
- If the vector version is smaller, it selects it; if the bitmap is smaller (as with complex photos), it keeps the bitmap.
Benchmark Your Own Artwork
Upload your logo, UI frames, or GIF to see live flash comparison numbers for your specific microcontroller.
Test in OLED Bitmap Converter