DitherInk

3-color (Black/White/Red) e-paper dithering

Three-color panels with red can show black, white and red. Red is a strong accent color, so DitherInk places red dots only where the image is clearly red or warm, and uses black and white for everything else.

Palette and color codes

The code is the value DitherInk writes for each pixel in .bin, .h, .png and .bmp files. The preview color is the brightened color used for matching and on-screen previews. The index color is the pure color stored in the PNG and BMP palette.

ColorCodePreview colorIndex color (PNG/BMP)
Black0#19191e#000000
White1#ffffff#ffffff
Red2#b92d28#ff0000

Frame buffer format

2 planes of 1 bit per pixel (tri_2plane). The black/white plane comes first (1 = not black), followed by the color plane (1 = color). Each plane packs 8 pixels per byte. Rows are stored top to bottom and each row is padded to a whole byte.

Supported sizes

SizeResolution.bin size
1.54"200×20010,000 bytes (9.8 KB)
2.13"250×1227,808 bytes (7.6 KB)
2.66"296×15211,248 bytes (11.0 KB)
2.9"296×1289,472 bytes (9.3 KB)
4.2"400×30030,000 bytes (29.3 KB)
5.83"648×48077,760 bytes (75.9 KB)
7.5"800×48096,000 bytes (93.8 KB)
7.5"880×528116,160 bytes (113.4 KB)
11.6"960×640153,600 bytes (150.0 KB)
12.48"1304×984320,784 bytes (313.3 KB)

Convert an image to 3-color (black/white/red) →

Ad

Tips

These panels refresh slowly (often 10 to 20 seconds) and are best for images that change a few times a day. Most drivers expect two separate buffers: one for black and white and one for red.

Example code (7.5" 800×480)

Arduino / ESP-IDF with the .h file

#include "my_image.h"  // downloaded from DitherInk

// MY_IMAGE_WIDTH x MY_IMAGE_HEIGHT = 800 x 480, tri_2plane
// Color codes: black=0, white=1, red=2
// The buffer is already in the panel's native orientation.

void showImage() {
  // With "Split planes" enabled you get two arrays:
  epd_write_planes(my_image_black, my_image_color, MY_IMAGE_PLANE_SIZE);
  epd_refresh();
}

Replace epd_write_frame and epd_refresh with the functions your display driver provides. Most drivers have a function that takes a full frame buffer.

ESP32 loading the .bin file from flash

#include <LittleFS.h>

constexpr size_t FRAME_BYTES = 96000;
static uint8_t frame[FRAME_BYTES];

bool loadFrame(const char* path) {
  File f = LittleFS.open(path, "r");
  if (!f || f.size() != FRAME_BYTES) return false;  // wrong size = wrong panel settings
  f.read(frame, FRAME_BYTES);
  f.close();
  return true;
}

Python (Raspberry Pi) reading the .bin file

import numpy as np

data = np.fromfile("my_image.bin", dtype=np.uint8)
assert data.size == 96000
plane = data.size // 2
not_black = np.unpackbits(data[:plane]).reshape(480, -1)[:, :800]  # 1 = white or color
is_color = np.unpackbits(data[plane:]).reshape(480, -1)[:, :800]   # 1 = color