image2cpp - Image to C Array Converter (Mono and Color)

image2cpp (LCD Assistant) is a free online tool that turns an image into a C array or byte array, and turns an existing array back into an image. It covers both kinds of display used in Arduino, ESP32 and Raspberry Pi projects: 1 bit monochrome for SSD1306 / SH1106 OLEDs and the Nokia 5110, and RGB565 or RGB888 color for ST7735, ST7789, ILI9341 and ILI9488 TFT panels. Output as Adafruit GFX, U8g2, plain bytes or MicroPython / CircuitPython.

1. Select image

All operations are performed locally in your browser; your photos are not uploaded or stored anywhere online.

or

1. Paste plain byte array


x px
Read images appear in the preview panel

2. Image Settings

    Only images file type are allowed
    No files selected
    Fills the canvas size fields above in one go. If no image is loaded yet, the values go to the width and height fields of the paste-bytes box instead.
    100%
    100%
    0 - 255; if the brightness of a pixel is above the given level the pixel becomes white, otherwise they become black. When using alpha, opaque and transparent are used instead.
    Centering the image only works when using a canvas larger than the original image.

    3. Output

    Wraps the array in a LovyanGFX.hpp include and a ready lcd.pushImage(0, 0, width, height, array) call. Use it with the color RGB565 draw mode; if the colors come out wrong, add or drop the lcd.setSwapBytes(true) line in the output.
    Generates output in bytearray format for Micropython and CircuitPython.
    This oled_example adds some extra Arduino code around the output to easily copy and paste it. If multiple images are uploaded, it creates a byte array for each one and adds a counter to the identifier.
    Adds some extra Arduino code around the output for easy copy-paste. If multiple images are loaded, generates a single byte array.
    Creates a GFXbitmapFont formatted output. Used by a modified version of the Adafruit GFX library. GitHub project and example bitmap-font
    First ASCII character value is used only if a glyph identifier of length equal to 1 is not provided for each image. The value itself will be incremented by 1 for each glyph.

    If your image looks all messed up on your display, like the image below, try using a different mode.

    info image
    Useful when working with the u8g2 library: tick it in the monochrome modes to get XBM bit order. Leave it off for color RGB565 output. What breaks the colors there is not the bit order but the byte order, and the place to fix it is the library: tft.setSwapBytes(true) for TFT_eSPI and LovyanGFX, while Adafruit GFX drawRGBBitmap() expects standard byte order. This is the single most common real world failure with RGB565 arrays.
    Set it to 0 to wrap on the image's own width instead: an 8 px wide image then breaks the line every 8 bytes.
    Adds a dot and hash comment after each row of bytes, drawing the pixel row it encodes. The fastest way to check an array by eye. In this mode every output line is one image row, so "bytes per row" is ignored.
    -
    Load an image or paste a byte array to see the flash cost.

    What a monochrome bitmap array actually is

    A monochrome bitmap stores one bit per pixel: 1 means the pixel is lit, 0 means it is off. There is no grey and no color, which is why a whole 128x64 OLED frame is only 1,024 bytes. Converting an image to a bitmap array therefore comes down to two decisions, and nearly every broken picture on a display comes from getting one of them wrong: which eight pixels get packed into each byte, and in which order the bits inside that byte are counted.

    Horizontal byte orientation, one row at a time

    In horizontal mode the converter walks the image left to right, top to bottom, and packs eight neighbouring pixels of the same row into one byte. The leftmost pixel of the group lands in the most significant bit. A row of 128 pixels becomes 16 bytes and the next row starts immediately after, so the array is simply the picture read like a page of text. This is what Adafruit_GFX drawBitmap() and U8g2 drawXBMP() expect.

    One consequence catches almost everyone out: a row always ends on a byte boundary. If the image is 60 pixels wide, each row still costs 8 bytes and the last 4 bits are padding. Pass 60 as the width to drawBitmap() and the library reads 60 bits per row from an array that stores 64, so every row drifts 4 pixels further along and the picture shears into a diagonal. Keeping monochrome widths a multiple of 8 removes that whole class of bug.

    Vertical (page) byte orientation, eight rows at a time

    In vertical mode one byte holds eight pixels stacked on top of each other in a single column, with the topmost pixel in the least significant bit. The image is cut into horizontal bands eight pixels tall, called pages, and each page is stored as a run of bytes across the screen.

    That looks arbitrary until you see the hardware. The SSD1306 controller's own video memory is laid out exactly this way: a 128x64 panel is 8 pages of 128 bytes, and one byte written over I2C lights a vertical column of 8 pixels in a single transaction. The Nokia 5110 (PCD8544) works the same way. Vertical mode exists so you can dump the array straight into that memory with no repacking on the microcontroller. Horizontal mode exists because it matches how a drawing library thinks about rows, which is what lets it clip a bitmap and place it at any y coordinate rather than only on page boundaries.

    OrientationOne byte holdsBit 7 (MSB) is128x64 arrayUsed by
    Horizontal, MSB first8 pixels across one rowthe leftmost pixel of the group1,024 bytesAdafruit_GFX drawBitmap(), MicroPython MONO_HLSB
    Horizontal, LSB first (XBM)8 pixels across one rowthe rightmost pixel of the group1,024 bytesU8g2 drawXBM(), .xbm files
    Vertical (page)8 pixels down one columnthe bottom pixel of the group1,024 bytesraw SSD1306 / SH1106 / PCD8544 buffers

    Which output format should I use?

    The draw mode decides how pixels are packed into bytes. Pick it by display controller, not by taste, or the picture will come out sliced or mirrored.

    Draw modeBytes / pixelTypical displaysDraw call
    Mono, horizontal1 bitSSD1306, SH1106, SSD1309 OLEDdrawBitmap() / drawXBM()
    Mono, vertical1 bitNokia 5110 (PCD8544), SSD1306 page moderaw buffer write
    Color RGB5652 bytesST7735, ST7789, ILI9341, ILI9488, GC9A01drawRGBBitmap() / pushImage()
    Alpha mask1 bitany, as a transparency mask beside a color bitmapmasked blit
    Color RGB8883 bytes24 bit framebuffers, ESP32 RGB LCD panelspanel driver
    Color HSV3 bytesLED matrices, effects that shift hue at runtimecustom

    Using the array in your sketch

    Monochrome OLED, Adafruit SSD1306:

    #include <Adafruit_SSD1306.h>
    Adafruit_SSD1306 display(128, 64, &Wire, -1);
    
    // paste the array from the Output box above
    const unsigned char myBitmap [] PROGMEM = { /* ... */ };
    
    void setup() {
      display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
      display.clearDisplay();
      display.drawBitmap(0, 0, myBitmap, 128, 64, SSD1306_WHITE);
      display.display();
    }

    Monochrome with U8g2 (XBM byte order):

    u8g2.firstPage();
    do {
      u8g2.drawXBMP(0, 0, 128, 64, myBitmap);
    } while (u8g2.nextPage());

    Color RGB565 on an ST7735 / ILI9341, Adafruit GFX:

    #include <Adafruit_ILI9341.h>
    
    // RGB565 output: 2 bytes per pixel, so uint16_t not unsigned char
    const uint16_t myImage [] PROGMEM = { /* ... */ };
    
    tft.drawRGBBitmap(0, 0, myImage, 160, 128);

    Color RGB565 with TFT_eSPI (faster, ESP32 friendly):

    #include <TFT_eSPI.h>
    TFT_eSPI tft = TFT_eSPI();
    
    tft.setSwapBytes(true);            // drop this line if colors look inverted
    tft.pushImage(0, 0, 160, 128, myImage);

    Adafruit GFX, U8g2, raw SSD1306 and the classic LCD Assistant

    These four consume the same pixels but not the same bytes. Generate the output that matches the call you are actually making.

    Adafruit_GFX drawBitmap: horizontal, MSB first, PROGMEM

    drawBitmap() is the standard call for every display library that inherits from Adafruit_GFX, so one array works on an SSD1306 OLED, an SH1106, a monochrome ST7735 mode or an e-paper panel. It reads horizontal bytes MSB first, and it reads them through pgm_read_byte(), which is why the array has to be declared PROGMEM on AVR boards. Drop the PROGMEM on an Uno and a full screen bitmap eats half your RAM instead of sitting in flash. On ESP32 and RP2040 PROGMEM is a no-op and harmless, so leave it in for portability. The last argument is the color, and passing the background color instead of the foreground one punches the shape out of what is already on screen.

    // Draw Mode: Mono - Horizontal, output format: Arduino code
    const unsigned char logo [] PROGMEM = { 0x00, 0x7e, 0x81, /* ... */ };
    
    display.clearDisplay();
    display.drawBitmap(0, 0, logo, 128, 64, SSD1306_WHITE);  // lit pixels
    display.drawBitmap(0, 0, logo, 128, 64, SSD1306_BLACK);  // knock it out instead
    display.display();

    U8g2 drawXBMP and the XBM bit order trap

    U8g2 draws bitmaps in XBM, the old X11 bitmap format, and XBM numbers the bits inside a byte the other way round: bit 0 is the leftmost pixel, not the rightmost. The bytes are still horizontal and the array is exactly the same length, only each byte is bit reversed. Hand a GFX array to drawXBMP() and the overall shape survives, but every group of 8 pixels is mirrored in place, which looks like your image has been finely shredded and reassembled. That is what the Swap bits in byte checkbox in the Output section is for: tick it for U8g2, leave it clear for Adafruit_GFX.

    // Draw Mode: Mono - Horizontal, with "Swap bits in byte" ticked
    static const unsigned char logo_xbm[] U8X8_PROGMEM = { 0x00, 0x7e, 0x81, /* ... */ };
    
    u8g2.firstPage();
    do {
      u8g2.drawXBMP(0, 0, 128, 64, logo_xbm);
    } while (u8g2.nextPage());

    drawXBMP() is the PROGMEM variant and drawXBM() is the same call for an array already in RAM. U8g2 also has a method named drawBitmap(), but it is not the Adafruit one and it does not take the same arguments, so do not swap the two from muscle memory.

    Raw SSD1306 page mode, writing the buffer yourself

    If you are not using a graphics library at all, for instance on a bare I2C driver or in ESP-IDF, you address the panel by page and column and push bytes straight into its memory. Use the Mono - Vertical draw mode for this: the array then already matches the controller's own layout and the transfer is a plain copy with no bit shuffling on the microcontroller.

    // Draw Mode: Mono - Vertical. 128x64 = 8 pages of 128 bytes
    const uint8_t frame[1024] = { /* ... */ };
    
    for (uint8_t page = 0; page < 8; page++) {
      ssd1306_cmd(0xB0 | page);  // page start address
      ssd1306_cmd(0x00);         // lower column address = 0
      ssd1306_cmd(0x10);         // higher column address = 0
      ssd1306_data(&frame[page * 128], 128);
    }

    The classic Windows LCD Assistant program

    LCD Assistant is a small Windows desktop program from the late 2000s that did this job long before browsers could. It opens a monochrome BMP file and writes out a C table, offering horizontal or vertical byte orientation and an 8 or 16 bit table width. Its output differs from what Arduino libraries want today in three ways. It accepts only an already 1 bit BMP, so you have to threshold the image in an editor first. It defaults to vertical orientation, which is the wrong one for drawBitmap(). And it emits a bare const unsigned char table with no PROGMEM qualifier, which you then have to add by hand on an AVR. It also has no dithering, no preview against a simulated panel, and no way to read an existing array back into a picture.

    If you came here looking for an LCD Assistant download, you do not need one. This page does the same conversion in the browser, accepts PNG, JPG, BMP, GIF and WebP directly instead of only 1 bit BMP, and shows the result on a simulated OLED before you paste anything into a sketch.

    Hex code, C array or XBM: which output do you need?

    Pixel packing and text formatting are two separate choices here. The Draw Mode decides the bytes; the Code output format decides how those bytes are printed. Three shapes cover almost everything people are looking for.

    • A C array. Arduino code gives you a complete const unsigned char name [] PROGMEM = { 0x00, 0xFF, ... }; declaration, ready to paste above setup(). This is what you want for Adafruit_GFX and U8g2.
    • Plain hex. Plain bytes prints the byte values with no declaration wrapped around them, so they drop into a Python list, a JSON file, a struct initialiser or another language entirely. Tick Remove '0x' and commas from output as well and you get a bare hex string, which is what "image to bitmap hex code" usually means in practice.
    • XBM. XBM is less a different byte packing than a different bit order with a C wrapper around it. Pick Mono - Horizontal, tick Swap bits in byte, and the array you get is XBM ordered and drops straight into drawXBMP(). A real .xbm file is the same table with two #define lines for width and height above it, so adding those two lines turns the output into a valid XBM file.

    The MicroPython and CircuitPython option wraps the same monochrome bytes in a bytearray for framebuf.FrameBuffer(..., framebuf.MONO_HLSB), which is the horizontal MSB first layout again under a different name. The binary .bin download is the raw bytes with no text formatting at all, for streaming from an SD card or SPIFFS.

    Bitmap comes out scrambled, mirrored, inverted or rotated

    Nearly every broken bitmap is one of a handful of failures, and the symptom tells you which one. Work down this table before you start changing your sketch.

    What you seeCauseFix
    Image is cut into 8 pixel horizontal stripes, each one smeared sidewaysByte orientation is wrong: vertical data sent to a horizontal draw call, or the reverseSwitch Draw Mode between Mono - Horizontal and Mono - Vertical and regenerate
    Shape is recognisable but every group of 8 pixels is flipped left to right, like fine shreddingBit order is wrong: MSB first data sent to an XBM call, or the reverseToggle Swap bits in byte. Ticked for U8g2 drawXBM, clear for Adafruit_GFX drawBitmap
    Picture drifts sideways a little more on every row and ends as a diagonal shearWidth mismatch, usually a width that is not a multiple of 8Make the canvas width a multiple of 8 and pass that same width to the draw call
    Everything is a negative: background lit, subject darkThe panel treats 1 as off, or the source image had a dark backgroundTick Invert image colors in Image Settings, or draw with SSD1306_BLACK
    Whole image is rotated 90 degrees or mirrored as a blockPanel orientation, not the arrayUse Rotate image and Flip image here, or call setRotation() in the library
    Only the top eighth of the screen has anything on itThe array is shorter than the frame, often a 128x8 export used as 128x64Check the canvas size and confirm the array length equals width / 8 * height
    Blank screen or random noiseArray is not where the library expects it, or the pointer type is wrongAdd PROGMEM on AVR, and keep RGB565 arrays as uint16_t rather than unsigned char

    A fast way to isolate the problem: paste the array back into the Paste plain byte array box at the top of this page with the same width, height and draw mode. If the preview here looks right but the panel does not, the array is fine and the bug is in your sketch. If the preview is scrambled too, the export settings are wrong.

    How much memory will it cost?

    A 1 bit bitmap needs width / 8 * height bytes. An RGB565 bitmap needs width * height * 2. That gap decides which board you can use:

    • 128x64 monochrome OLED: 1,024 bytes. Fits in an Arduino Uno's flash without thinking about it.
    • 160x128 RGB565 (ST7735): 40,960 bytes. Already larger than the Uno's entire 32 KB flash. Use an ESP32, an RP2040, or stream it from an SD card.
    • 320x240 RGB565 (ILI9341): 153,600 bytes. ESP32 or SD card only.

    If a color image will not fit, convert it to 1 bit with Floyd-Steinberg dithering instead. On a small panel the dithered version usually reads better than a heavily downscaled color one.

    Flash cost of common bitmap sizes

    For a 1 bit image the arithmetic is width / 8 * height, rounding the width up to the next multiple of 8. Set against a board's flash budget it looks like this:

    BitmapBytesShare of an Uno's 32 KB flashShare of an ESP32's 4 MB flash
    32x32 icon, 1 bit1280.4%negligible
    84x48 full screen, Nokia 51105281.6%negligible
    128x32 full screen, small OLED5121.6%negligible
    128x64 full screen, standard OLED1,0243.1%0.02%
    160x128 RGB565, ST773540,960does not fit1.0%
    320x240 RGB565, ILI9341153,600does not fit3.7%

    The practical reading: on an Uno you can afford one monochrome splash screen and a handful of icons, but not a library of them, and the bootloader plus the SSD1306 driver has already claimed several KB before your first bitmap. Twenty 32x32 icons cost 2,560 bytes together and are usually the better spend than one full screen image. On an ESP32 flash stops being the constraint and the question becomes RAM: Adafruit_SSD1306 keeps a 1,024 byte framebuffer in RAM regardless, while a full 320x240 RGB565 framebuffer would be 150 KB, which is why colour panels are normally drawn in strips rather than buffered whole.

    Panel presets and the live flash readout

    Instead of typing the canvas size by hand, pick your display from the Panel preset menu in section 2. The list covers the 128x64 and 128x32 SSD1306 OLEDs, 84x48 for the Nokia 5110, 128x160 and 128x128 for the ST7735, 240x240, 240x320 and 135x240 for the ST7789, 240x240 for the round GC9A01, 240x320 for the ILI9341 and 320x480 for the ILI9488. The choice fills the width and height field of every loaded image; with no image loaded yet the values land in the size fields of the paste-bytes box. Starting at the real resolution removes most of the skew and clipping problems that otherwise only show up on the panel.

    The Flash cost row in section 4 then shows how many bytes the current settings will produce, and it refreshes whenever you change the canvas size, the draw mode or the indexed palette checkbox. Above 30 KB it warns about the Arduino Uno, because that is what is left of its 32 KB flash once the bootloader and the libraries have taken their share. Above 1.3 MB a second warning appears: that is roughly the default ESP32 app partition, and the sketch has to fit in the same partition. Everything between those two thresholds is comfortable on an ESP32, an ESP8266 or an RP2040, and out of reach on an Uno.

    Preparing the source image: threshold or dither?

    A 1 bit display has two states per pixel, so every input pixel has to become on or off. There are two ways to make that decision, and picking the right one matters more than any library setting.

    Thresholding is the Binary option in the Dithering menu. It compares each pixel's brightness against the Brightness / alpha threshold value, 128 by default, and rounds it. This is the right choice for anything with hard edges: logos, line art, icons, text, QR codes, schematic symbols. The result is crisp and it stays crisp when the panel is tiny. If your logo vanishes or fills in solid, move the threshold rather than reaching for dithering.

    Dithering preserves the average brightness of an area by scattering on and off pixels in a pattern, trading resolution for apparent grey. Floyd-Steinberg pushes each rounding error out to neighbouring pixels and gives the most photographic result. Atkinson, the algorithm from the original Macintosh, deliberately discards part of the error, which loses a little detail at the extremes but produces cleaner, less noisy midtones and usually looks better on a small OLED. Bayer uses a fixed matrix, so it shows a visible crosshatch but is stable from frame to frame, which matters if you are animating.

    Why photos rarely work: a 128x64 OLED has 8,192 pixels, roughly a twentieth of a phone thumbnail, and dithering spends several of those pixels to imply a single grey level. A face at that size turns into texture. Crop hard to one subject, push the Contrast slider up before converting, and accept that the readable version is often a traced silhouette rather than the photograph. Raising contrast before thresholding rescues more images than switching dithering algorithms ever will.

    What is image2cpp, and how is this version different?

    image2cpp is a browser based image to C array converter written by Jasper van Loenen, open sourced on GitHub and hosted at javl.github.io/image2cpp. It became the default answer for turning a picture into an Arduino bitmap because it needs no install, runs entirely on the client, and handles both the horizontal and the vertical byte orders that the SSD1306 world actually uses. When a tutorial says "convert your image with image2cpp", that is the tool it means, and this page is built on the same open source core, which is credited in the footer.

    What that core does well is kept intact here: conversion happens locally with nothing uploaded, several images can be queued at once, plain byte and Arduino output are both available, and an existing array can be decoded back into a picture. What has been added on top:

    • Colour output. RGB565, RGB888, HSV and a 1 bit alpha mask, so the same page serves ST7735, ST7789, ILI9341, ILI9488 and GC9A01 panels and not only monochrome OLEDs.
    • More dithering. Floyd-Steinberg, Atkinson and Bayer alongside plain thresholding, with brightness and contrast sliders applied before the conversion runs.
    • A realistic preview. The result is rendered against a simulated panel, including classic green LCD, blue OLED and the yellow over blue OLED, so you can judge legibility before wiring anything up.
    • MicroPython and CircuitPython output as a bytearray, plus a binary .bin download for SD card and SPIFFS workflows.
    • A Turkish interface at /araclar/lcd-assistant/, and this written guide on both.

    None of that makes the original wrong. If you only need a monochrome array and you already have image2cpp bookmarked, it will do the job perfectly well. The reasons to use this one are colour panels, the extra dithering choices, and a preview that shows you what the OLED is really going to look like.

    Frequently asked questions

    Does image2cpp support color images?

    Yes. Besides the classic 1 bit per pixel monochrome mode, this converter exports RGB565 (2 bytes per pixel), RGB888 (3 bytes per pixel) and HSV arrays. Choose the draw mode Color RGB565 - 2 bytes per pixel to get an array you can push straight to an ST7735, ST7789, ILI9341 or ILI9488 TFT.

    What is RGB565 and why is it 2 bytes per pixel?

    RGB565 packs a color into 16 bits: 5 bits red, 6 bits green, 5 bits blue. Green gets the extra bit because the eye is most sensitive to it. That is 65,536 colors in half the memory of 24 bit RGB888, which is why nearly every small TFT driver and framebuffer on Arduino and ESP32 uses it.

    How do I display the generated array on an SSD1306 128x64 OLED?

    Set the canvas to 128x64, choose the mono horizontal draw mode and the Adafruit GFX output format, then call display.drawBitmap(0, 0, myBitmap, 128, 64, SSD1306_WHITE) followed by display.display().

    Should I use horizontal or vertical byte order?

    Horizontal for Adafruit GFX drawBitmap and U8g2 drawXBM, which covers most SSD1306 and SH1106 code. Vertical for page addressed drivers such as the Nokia 5110 (PCD8544) and raw SSD1306 page buffers. If the image comes out cut into horizontal stripes, you picked the wrong one.

    Why does my bitmap look scrambled or skewed on the display?

    Almost always a width mismatch. The array width has to match the width you pass to drawBitmap, and for 1 bit modes it should be a multiple of 8, otherwise every row is padded and the picture shears diagonally. Check byte order second, and setSwapBytes() third if you are on RGB565 and the colors look wrong rather than the shape.

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

    Adafruit GFX expects a PROGMEM byte array drawn with drawBitmap, where a 1 bit means an on pixel. U8g2 uses XBM ordering with drawXBM, where the bits inside each byte are reversed. Picking the wrong one mirrors every group of 8 pixels.

    Can I turn a C array back into an image?

    Yes. Paste an existing byte array into the Paste plain byte array box, set the right width, height and draw mode, and the preview renders it back as a picture. Handy for checking a bitmap you inherited from someone else's sketch.

    Can I convert an animated GIF?

    Not here. This converter reads a single still frame, because it draws the file into an HTML canvas and a canvas holds one frame at a time. Hand it an animated GIF and you get frame one only. For animations use the GIF to OLED animation converter, which parses the GIF format directly and returns every frame as its own array plus a ready sketch that loops through them.

    What image size should I use?

    Match the display: 128x64 or 128x32 for common SSD1306 OLEDs, 84x48 for Nokia 5110, 160x128 for ST7735, 240x240 for GC9A01 round panels, 320x240 for ILI9341. Remember that a full screen 320x240 RGB565 image is 153,600 bytes, far more than an Arduino Uno has, so color bitmaps that size need an ESP32 or storage on an SD card.

    How do I convert an image to a bitmap for Arduino and U8g2?

    Load the image, set the canvas to your panel size, pick Mono - Horizontal, 1 bit per pixel as the draw mode, then tick Swap bits in byte because U8g2 uses XBM bit order. Generate the code, paste the array into your sketch as static const unsigned char name[] U8X8_PROGMEM and draw it with u8g2.drawXBMP(x, y, width, height, name) inside the firstPage and nextPage loop. Leave Swap bits in byte unticked if you are using Adafruit_GFX drawBitmap() instead.

    How do I convert a JPG or a photo into a monochrome bitmap?

    Drop the JPG in, then decide between thresholding and dithering before anything else. For a logo or line art use the Binary dithering option and move the Brightness / alpha threshold until the shape is clean. For a photograph use Floyd-Steinberg or Atkinson dithering and raise the Contrast slider first, because a 128x64 panel has only 8,192 pixels and low contrast material turns into grey texture. Crop tight to a single subject rather than converting the whole frame.

    How do I get plain hex code instead of a C array?

    Set Code output format to Plain bytes and the output loses the C declaration, leaving just the byte values. Tick Remove '0x' and commas from output as well and you get a bare hex string you can paste into Python, JSON, a struct initialiser or a hex editor. The Binary (.bin) download button gives you the same bytes as an actual file if you plan to stream the bitmap from an SD card instead of compiling it in.

    Is this an XBM converter?

    Yes, with one setting. Choose Mono - Horizontal and tick Swap bits in byte: XBM is the same horizontal packing with the bits inside each byte reversed, so the array you get is XBM ordered and works directly with drawXBM() and drawXBMP(). To make a real .xbm file, put two #define lines for the width and the height above the table and save it with an .xbm extension.

    Does it work with Adafruit GFX and Adafruit displays?

    Yes, and that is the default path. Mono - Horizontal plus the Arduino code output format produces exactly what drawBitmap() wants, and because every Adafruit display library inherits from Adafruit_GFX the same array works on SSD1306 and SH1106 OLEDs, monochrome e-paper, and Adafruit TFT breakouts. For colour on an ST7735 or ILI9341, switch to Color RGB565 and use drawRGBBitmap() with a uint16_t array.

    Can I make SSD1306 OLED fonts here?

    Partly. The Adafruit GFXbitmapFont output format builds a GFX font table from a strip of glyph images, with fields for the first ASCII character and the x advance, so you can produce a custom font for setFont(). It is not a glyph editor though. For the 5x8 custom characters of a 16x2 or 20x4 HD44780 character LCD, which is a different thing entirely, use the LCD custom character generator.

    Do I need to download LCD Assistant?

    No. LCD Assistant is a Windows desktop program that converts a 1 bit BMP into a C table, and this page does the same job in the browser with no install, on any operating system. It also accepts PNG, JPG, GIF and WebP rather than only pre-thresholded BMP files, defaults to the horizontal orientation that drawBitmap() expects, and adds PROGMEM to the output for you.

    What is the difference between a BMP file and a bitmap array?

    A BMP file is a container: a header describing width, height, colour depth and row padding, followed by the pixel data. A bitmap array is just the pixel data, packed the way one specific display expects it and written out as C source. A microcontroller has no file system in flash and no BMP decoder, so what it needs is the array. That is what this converter produces, which is why "image to bmp array" and "image to bitmap array" end up meaning the same request.

    When is the indexed palette mode worth using?

    The Indexed palette checkbox, which appears once you pick the RGB565 draw mode, quantises the image to at most 256 colors and emits a uint16_t name_palette[] plus a uint8_t name_pixels[] instead of one array. One byte per pixel instead of two makes the result roughly half the size: a 240x240 image costs 58,112 bytes instead of 115,200. On logos, icons, UI artwork and flat colour graphics the difference is invisible, because those images already use fewer than 256 colors. On photos and gradients banding starts to show, and there the full RGB565 array is the better choice. The cost on the drawing side is that you can no longer hand the array straight to pushImage: you have to walk one row at a time, look each index up in the palette and fill a uint16_t buffer, which is exactly what the helper loop at the end of the output does.

    Related tools