TFT_eSPI Images: Fix RGB565 Colors and Byte Order

If an image has the right shape but the wrong colors in TFT_eSPI, test two things separately: the display's RGB/BGR configuration and the byte order of the image data. Changing both at once makes it hard to tell which setting fixed the problem.
A small pattern is more useful here than a photograph. Red, green, blue, white, and black give you values you can recognize without guessing what the original image looked like.
Draw solid colors first
Add this function to a sketch that already initializes the display correctly. Call it after tft.init() and your rotation setting. It assumes the display object is named tft and the screen is at least 100 × 20 pixels.
void drawColorReference() {
const uint16_t colors[] = {
TFT_RED, TFT_GREEN, TFT_BLUE, TFT_WHITE, TFT_BLACK
};
for (int i = 0; i < 5; ++i) {
tft.fillRect(i * 20, 0, 20, 20, colors[i]);
}
}
Synthetic test pattern, 100 × 20: five equal bands — red, green, blue, white, black. Not a hardware photograph. Download the reference PNG and convert it through your converter to compare the asset path with solid-color rendering.
If these rectangles are wrong, work on the display configuration before converting another image. The selected driver, panel dimensions, and color order belong to the hardware setup. TFT_eSPI's User_Setup.h documents the relevant options, including TFT_RGB_ORDER for supported drivers.
If red appears blue and blue appears red while green remains green, RGB/BGR order is a useful suspect. Check the setup for the actual panel rather than assuming every module sold under the same controller name behaves identically.
Then test the image path
Once the solid rectangles are correct, send an RGB565 array through pushImage(). This second pattern is drawn lower on the screen, so allow at least 100 × 32 pixels in total:
void drawImageReference() {
static const uint16_t strip[] = {
0xF800, 0x07E0, 0x001F, 0xFFFF, 0x0000,
0xF800, 0x07E0, 0x001F, 0xFFFF, 0x0000
};
const bool previous = tft.getSwapBytes();
tft.setSwapBytes(false);
tft.pushImage(0, 24, 5, 2, strip);
tft.setSwapBytes(true);
tft.pushImage(0, 30, 5, 2, strip);
tft.setSwapBytes(previous);
}This produces two tiny 5 × 2 patterns, one with each swap setting. Inspect which matches the reference colors. Use a magnifier if needed; keeping the array small makes every value easy to audit.
The library declares setSwapBytes() specifically for pixel byte order in its API header. The correct setting depends on how the asset is stored and which API path reads it. Treat the result as a check for that asset representation, not a universal setting for every bitmap in the project.
Byte swapping is not red/blue swapping
In RGB565, red is 0xF800. Its bytes are F8 and 00; reversing them produces 0x00F8. That changes the packed numeric value. It does not simply exchange the red and blue channels.
This is why two symptoms need different checks:
| What you see | What to check first |
|---|---|
| Solid red and blue rectangles are exchanged | Panel RGB/BGR configuration |
| Solid rectangles look right, imported colors look wrong | Asset encoding and byte order |
| Rows wrap or form diagonal strips | Width, height, and array length |
| Everything is blank, including solid rectangles | Display initialization, wiring, backlight, and driver |
Export the asset for TFT_eSPI
In Lopaka, select TFT_eSPI before importing the image, then export the data with its generated draw call. If you change the target library later, regenerate the asset instead of carrying the old byte array into the new firmware.
For the complete layout workflow, use the custom TFT display UI guide. The TFT_eSPI integration guide covers the hardware setup and the generated drawing code.
Prepare the diagnostic pattern first, then your logo, then the full screen. If only the full screen fails, you have narrowed the problem to that asset or the memory path it uses.
Common questions
Should I always call setSwapBytes(true)?
No. Match the setting to the array and drawing path. Restore it after a local test so later rendering does not inherit a setting you forgot about.
Can an RGB565 array keep PNG transparency?
RGB565 has no alpha component. A transparent PNG needs a chosen background or a drawing method with explicit transparency support. A dark border around a logo can come from flattening it against the wrong background.
Is this different for ST7789 and ILI9341?
The test works with either when TFT_eSPI already drives the panel. Their setup details still differ. Keep the panel configuration that makes solid colors correct, then diagnose imported assets independently.