DitherInk

Black & White e-paper dithering

Black and white panels show two levels, so every gray in your photo has to be built from patterns of black and white dots. Dithering spreads the rounding error to nearby pixels, which keeps gradients and faces readable at normal viewing distance.

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

Frame buffer format

1 bit per pixel (mono_1bpp). 8 pixels per byte, most significant bit is the leftmost pixel. 1 = white, 0 = black. Rows are stored top to bottom and each row is padded to a whole byte.

Supported sizes

SizeResolution.bin size
1.54"200×2005,000 bytes (4.9 KB)
2.13"250×1223,904 bytes (3.8 KB)
2.66"296×1525,624 bytes (5.5 KB)
2.7"264×1765,808 bytes (5.7 KB)
2.9"296×1284,736 bytes (4.6 KB)
3.7"480×28016,800 bytes (16.4 KB)
3.7"416×24012,480 bytes (12.2 KB)
4.2"400×30015,000 bytes (14.6 KB)
4.26"800×48048,000 bytes (46.9 KB)
5.83"648×48038,880 bytes (38.0 KB)
7.5"800×48048,000 bytes (46.9 KB)
7.5"880×52858,080 bytes (56.7 KB)
10.2"960×64076,800 bytes (75.0 KB)
11.6"960×64076,800 bytes (75.0 KB)
12.48"1304×984160,392 bytes (156.6 KB)
13.3"960×68081,600 bytes (79.7 KB)

Convert an image to black & white →

Ad

Tips

High-contrast images, line art and text work best. For photos, Auto mode lifts the shadows a little so dark areas do not collapse into solid black.

Example code (4.26" 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, mono_1bpp
// Color codes: black=0, white=1
// The buffer is already in the panel's native orientation.

void showImage() {
  epd_write_frame(my_image, MY_IMAGE_SIZE);  // your driver's full-frame write
  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 = 48000;
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 == 48000
bits = np.unpackbits(data).reshape(480, -1)[:, :800]  # 1 = white, 0 = black