Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Marcussacapuces91/doc-TFT_eSPI/llms.txt

Use this file to discover all available pages before exploring further.

TFT_eSPI provides several complementary mechanisms for putting image data on the display. Monochrome icons stored as packed bit arrays in flash (PROGMEM) are drawn with drawBitmap or drawXBitmap. Full-color images — whether generated at runtime or decoded from JPEG/PNG — are pushed as raw RGB565 pixel buffers with pushImage or pushRect. You can also read a region of the display back into a buffer with readRect, enabling sprite-style operations. Understanding which API to use for each scenario is key to getting the best performance and the correct byte order.

Monochrome PROGMEM Bitmaps

drawBitmap

Draw a 1-bit-per-pixel bitmap stored in PROGMEM. Each 1 bit maps to the foreground color; each 0 bit is either skipped (transparent) or mapped to a background color.
void drawBitmap(int16_t x, int16_t y,
                const uint8_t *bitmap,
                int16_t w, int16_t h,
                uint16_t fgcolor);

void drawBitmap(int16_t x, int16_t y,
                const uint8_t *bitmap,
                int16_t w, int16_t h,
                uint16_t fgcolor, uint16_t bgcolor);
x
int16_t
required
X coordinate of the top-left corner.
y
int16_t
required
Y coordinate of the top-left corner.
bitmap
const uint8_t*
required
Pointer to a byte array in PROGMEM. Bits are packed MSB-first, row by row.
w
int16_t
required
Bitmap width in pixels.
h
int16_t
required
Bitmap height in pixels.
fgcolor
uint16_t
required
Color used where the bit is 1.
bgcolor
uint16_t
Color used where the bit is 0. When omitted those pixels are skipped (transparent over the existing display content).

drawXBitmap

Same as drawBitmap but expects data in XBM format, where bits are packed LSB-first (as produced by the GIMP Export as XBM option).
void drawXBitmap(int16_t x, int16_t y,
                 const uint8_t *bitmap,
                 int16_t w, int16_t h,
                 uint16_t fgcolor);

void drawXBitmap(int16_t x, int16_t y,
                 const uint8_t *bitmap,
                 int16_t w, int16_t h,
                 uint16_t fgcolor, uint16_t bgcolor);
Parameters are identical to drawBitmap. The only difference is the LSB-first bit order of the data.
drawBitmap vs drawXBitmapdrawBitmap expects MSB-first packed data (as produced by most online bitmap converters for Arduino). drawXBitmap expects LSB-first XBM data (as exported directly from GIMP or the xbm format standard). If your image appears mirrored, you are using the wrong function.

Example — PROGMEM Icon

#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();

// 16×16 checkmark icon, 1 bit per pixel, MSB-first, row-major
// Generated with an Arduino bitmap converter tool
static const uint8_t checkmark[] PROGMEM = {
  0x00, 0x00,  // Row  0: 0000 0000 0000 0000
  0x00, 0x00,  // Row  1
  0x00, 0x06,  // Row  2: ............. ##.
  0x00, 0x0C,  // Row  3: ............ ##..
  0x00, 0x18,  // Row  4: ........... ##...
  0x00, 0x30,  // Row  5: .......... ##....
  0x60, 0x60,  // Row  6: .##....... ##....   (simplified illustration)
  0x70, 0xC0,  // Row  7
  0x39, 0x80,  // Row  8
  0x1F, 0x00,  // Row  9
  0x0E, 0x00,  // Row 10
  0x04, 0x00,  // Row 11
  0x00, 0x00,  // Row 12
  0x00, 0x00,  // Row 13
  0x00, 0x00,  // Row 14
  0x00, 0x00,  // Row 15
};

void setup() {
  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);

  // Draw with transparent background (0-bits are skipped)
  tft.drawBitmap(10, 10, checkmark, 16, 16, TFT_GREEN);

  // Draw with explicit background (0-bits filled black)
  tft.drawBitmap(40, 10, checkmark, 16, 16, TFT_GREEN, TFT_BLACK);
}

void loop() {}

Full-Color Pixel Buffers

pushImage

Copy a block of RGB565 pixel data from a RAM (or PROGMEM) buffer directly to a rectangular region of the display. This is the primary method for drawing decoded images, sprite frames, and generated pixel art.
// 16-bit RGB565 buffer (RAM)
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               uint16_t *data);

// 16-bit RGB565 buffer with a single transparent color key
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               uint16_t *data, uint16_t transparent);

// 16-bit RGB565 const (PROGMEM) buffer
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               const uint16_t *data);

// 16-bit RGB565 const buffer with transparency
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               const uint16_t *data, uint16_t transparent);

// 8-bit palette-indexed or raw 8-bit buffer
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               uint8_t *data, bool bpp8 = true,
               uint16_t *cmap = nullptr);

// 8-bit buffer with transparent index
void pushImage(int32_t x, int32_t y, int32_t w, int32_t h,
               uint8_t *data, uint8_t transparent,
               bool bpp8 = true, uint16_t *cmap = nullptr);
x, y
int32_t
required
Top-left corner of the destination rectangle.
w, h
int32_t
required
Width and height of the image in pixels.
data
uint16_t* / uint8_t*
required
Pointer to the pixel buffer. For RGB565 images each pixel is a uint16_t. For 8-bit images each byte is a palette index or raw 8-bit color.
transparent
uint16_t / uint8_t
Color key: pixels with this value are not written to the display, leaving the existing background visible.
bpp8
bool
For 8-bit buffers: true = 8-bit palette index (use cmap), false = 1-bit per pixel.
cmap
uint16_t*
Optional 256-entry RGB565 color map for 8-bit indexed images.
// Push a 50×50 full-color image buffer to position (10, 10)
uint16_t imgBuf[50 * 50];
// ... fill imgBuf with RGB565 data ...
tft.pushImage(10, 10, 50, 50, imgBuf);

// Push with magenta (0xF81F) as the transparent color key
tft.pushImage(70, 10, 50, 50, imgBuf, (uint16_t)0xF81F);

pushRect

Push a uint16_t pixel buffer to a display rectangle. Functionally equivalent to the plain pushImage overload — use whichever name feels more descriptive in your code.
void pushRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data);
x, y
int32_t
required
Top-left corner of the destination.
w, h
int32_t
required
Width and height in pixels.
data
uint16_t*
required
RGB565 pixel buffer, w × h entries.
tft.pushRect(0, 0, 320, 240, frameBuffer);  // Blit a full-screen frame

Reading Pixels from the Display

readRect

Read a rectangular region of the display into a uint16_t buffer. Useful for saving a background region before drawing a sprite, then restoring it afterward.
void readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data);
x, y
int32_t
required
Top-left corner of the source rectangle.
w, h
int32_t
required
Width and height in pixels.
data
uint16_t*
required
Destination buffer. Must be at least w × h entries of uint16_t.
uint16_t saved[32 * 32];
tft.readRect(100, 100, 32, 32, saved);   // Save 32×32 region

// ... draw a sprite over it ...

tft.pushRect(100, 100, 32, 32, saved);   // Restore the background
readRect requires the display controller to support read-back over SPI and is significantly slower than write operations. Not all TFT controllers or wiring configurations support it.

Bitmap Helper Functions

setBitmapColor

Override the foreground and background colors that drawBitmap / drawXBitmap use. This is an alternative to passing colors directly to each call.
void setBitmapColor(uint16_t fgcolor, uint16_t bgcolor);
fgcolor
uint16_t
required
Color for 1-bits.
bgcolor
uint16_t
required
Color for 0-bits.
tft.setBitmapColor(TFT_GREEN, TFT_BLACK);
tft.drawBitmap(10, 10, myIcon, 32, 32, TFT_GREEN); // explicit color still overrides

setSwapBytes and getSwapBytes

TFT_eSPI internally stores colors in the display-native byte order. Image data loaded from external sources (e.g. JPEG decoders, BMP files) may have the bytes swapped. Calling setSwapBytes(true) tells pushImage to swap the high and low bytes of every pixel as it writes.
void setSwapBytes(bool swap);
bool getSwapBytes(void);
swap
bool
required
true — swap bytes before writing (required for most externally-sourced RGB565 bitmaps). false — no swap (default).
tft.setSwapBytes(true);
tft.pushImage(0, 0, 320, 240, bmpData);   // Bytes swapped as written
tft.setSwapBytes(false);                  // Restore default
If your pushed image looks like random noise or has corrupted colors, try toggling setSwapBytes. The byte order of RGB565 data varies between tools: Arduino bitmap converters often produce native order, while BMP file RGB565 data is typically byte-swapped.

drawBitmap vs pushImage — Choosing the Right Function

drawBitmap / drawXBitmap

Use when: you have a monochrome (1-bit) icon or logo stored in PROGMEM as a packed byte array. Memory-efficient — 1 pixel per bit. Supports transparent (skip) or filled background rendering.

pushImage / pushRect

Use when: you have a full-color RGB565 image decoded at runtime or stored in a uint16_t array. Every pixel has its own 16-bit color value, so the buffer is w × h × 2 bytes. Fastest path to the display for pre-decoded imagery.

Complete Example — Animated Sprite

#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();

// 8×8 smiley face icon (MSB-first, 1 bit per pixel)
static const uint8_t smiley[] PROGMEM = {
  0b00111100,  // ..####..
  0b01000010,  // .#....#.
  0b10100101,  // #.#..#.#  (eyes)
  0b10000001,  // #......#
  0b10100101,  // #.#..#.#  (mouth corners)
  0b10011001,  // #..##..#
  0b01000010,  // .#....#.
  0b00111100,  // ..####..
};

// 20×20 pixel sprite buffer — filled programmatically
static uint16_t spritePixels[20 * 20];

// Fill the sprite with a simple red-gradient
void buildSprite() {
  for (int row = 0; row < 20; row++) {
    for (int col = 0; col < 20; col++) {
      uint8_t r = (col * 31) / 19;   // 0..31 for 5-bit red
      uint8_t g = (row * 63) / 19;   // 0..63 for 6-bit green
      spritePixels[row * 20 + col] = tft.color565(r << 3, g << 2, 0);
    }
  }
}

void setup() {
  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);

  buildSprite();

  // ── Step 1: Push the color sprite ────────────────────────────────────────
  tft.pushImage(10, 10, 20, 20, spritePixels);

  // ── Step 2: Overlay a monochrome icon at top-left ─────────────────────────
  tft.drawBitmap(5, 5, smiley, 8, 8, TFT_WHITE);         // white face, transparent bg
  tft.drawBitmap(5, 20, smiley, 8, 8, TFT_YELLOW, TFT_NAVY); // yellow on navy bg

  // ── Step 3: Demonstrate readRect / pushRect round-trip ────────────────────
  uint16_t saved[20 * 20];
  tft.readRect(10, 10, 20, 20, saved);         // capture the sprite region

  tft.fillRect(10, 10, 20, 20, TFT_BLACK);     // erase it
  delay(500);

  tft.pushRect(10, 10, 20, 20, saved);         // restore it
}

void loop() {}
1

Store your bitmap in PROGMEM

Declare the array with the PROGMEM keyword and static const uint8_t. This keeps the data in flash rather than consuming precious RAM.
2

Choose the correct bit order

Use drawBitmap for MSB-first data (most Arduino converters) and drawXBitmap for LSB-first XBM data (GIMP exports).
3

Check byte order for color images

Call tft.setSwapBytes(true) before pushImage if your decoded RGB565 data comes from a BMP file or a JPEG decoder library that outputs big-endian pixels.
4

Provide an explicit bg_color where possible

Passing TFT_BLACK (or whatever your background is) instead of relying on pixel read-back avoids slow SPI read operations and speeds up rendering noticeably.

Build docs developers (and LLMs) love