Proje Defteri

OLED Bitmap Generator

Draw pixel icons for SSD1306 and other monochrome displays, then take the code as Adafruit GFX, U8g2 (XBM), vertical byte order or plain hex. You can also paste an array you already have and edit it.

1:1 preview

Before pasting, pick the correct grid size above and the output format that matches the array's byte order.

How an OLED bitmap array works

On a monochrome OLED such as the SSD1306, a pixel has only two states: lit or dark. An image is therefore stored as bits rather than pixels, and eight pixels fit into a single byte. A full 128x64 screen is 8192 bits, or 1024 bytes. The icon you draw here becomes an unsigned char array the same way.

What decides the order those bits land in is the byte order, and libraries have never agreed on one. In horizontal order a byte carries eight neighbouring pixels of one row, and each row is padded with zeros to a byte boundary. In vertical order a byte carries eight stacked pixels of one column, which is how the SSD1306 controller keeps its own memory: in pages of eight rows. That is the order you need when you push raw data to the panel.

Which output format should I pick?

Adafruit GFX is the most common one and works directly with display.drawBitmap(x, y, array, width, height, colour). Bit 7 of each byte is the leftmost pixel of the row.

XBM / U8g2 is horizontal as well, but the bit direction is reversed: the leftmost pixel is bit 0. The U8g2 drawXBMP function expects this. Mixing the two up makes the image look mirrored in blocks of eight pixels, and it is the single most common mistake with bitmaps.

Vertical byte order is only needed when you write to the panel without a graphics layer, when you use u8x8 tile functions, or with libraries that expect a raw buffer. Plain hex is useful when you want to drop the bytes into your own template without a C declaration.

Not wasting memory

An Arduino Uno or Nano has 2048 bytes of SRAM in total. A full-screen 128x64 bitmap is 1024 bytes on its own, so without PROGMEM half of your memory goes to one image and the board starts locking up for no visible reason. Declare the array as const unsigned char PROGMEM to keep it in flash; both Adafruit GFX and U8g2 handle reading it back. On ESP32 and ESP8266 boards the limit is not a problem, but the keyword costs nothing.

In practice, drawing small icons and placing them on the screen beats one full-screen image: it uses far less space and keeps the interface flexible. A 16x16 icon is only 32 bytes, so you can build a whole symbol set and compose the screen from parts.

Converting an image file instead

If you have a file rather than a drawing, use our image2cpp image to C array converter, which turns PNG, JPG and BMP files into arrays with threshold and dithering options. For animation, the GIF to OLED animation tool exports frames one by one. If you are choosing a screen font, the U8g2 font list shows how much space each font takes.

Frequently asked questions

What is the difference between the Adafruit GFX and U8g2 output?

Both use horizontal byte order, but the bit direction is reversed. Adafruit GFX drawBitmap reads the most significant bit (bit 7) of each byte as the leftmost pixel of the row. U8g2 drawXBMP follows the XBM convention and treats the least significant bit (bit 0) as the leftmost pixel. Picking the wrong one makes the image come out mirrored in blocks of eight pixels.

When do I need vertical byte order?

The SSD1306 controller stores its own memory in pages of eight rows, so one byte holds eight stacked pixels of a single column. Choose vertical order when you write to the panel over I2C or SPI without a graphics library, when you work with u8x8 tile functions, or when a library such as SSD1306Ascii expects a raw buffer. The Adafruit GFX and U8g2 drawing functions will not read a vertical array correctly.

Can I paste an existing C array and edit it?

Yes. Pick the array's real width and height in the size list and the matching byte order in the output format, then paste the array into the import box. The tool compares the byte count with the selected size and tells you the expected and actual counts when they differ. Hexadecimal, decimal and binary literals are accepted, as are full C declarations and XBM blocks.

Why should I use the PROGMEM keyword?

On AVR boards such as the Arduino Uno, Nano and Mega, constant arrays are copied into SRAM by default. A full 128x64 monochrome bitmap is 1024 bytes and the Uno only has 2048 bytes of SRAM, so a single full-screen image eats half of it and the board starts failing in ways that are hard to trace. PROGMEM keeps the array in flash instead. ESP32 and ESP8266 boards do not have this pressure, but the keyword is harmless and keeps the code portable.

My icon comes out skewed or scrambled on the display, why?

There are three common causes. Bit direction: exporting XBM where the sketch expects Adafruit GFX makes the image mirror in blocks of eight. Width: if the width you pass to drawBitmap differs from the width you generated the array at, every row shifts and you get a diagonal smear. Byte direction: feeding a vertically packed array to a function that expects horizontal order makes the image unrecognisable.

What if I want to convert an existing image file?

This tool is for drawing on an empty grid and for fixing an existing array pixel by pixel. If you have a PNG, JPG or BMP file, our image2cpp tool converts it straight to a C array with threshold and dithering options. For animation, the GIF to OLED animation tool exports the frames one by one.

Will I lose my drawing?

The drawing is saved to your browser's local storage on every change, so a refresh picks up where you left off. Nothing is sent to a server; it stays on your device. In a private window, or when site data is blocked, saving is skipped and the tool still works normally.