@opendisplay/epaper-dithering 2.2.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -23
- package/dist/index.cjs +158 -380
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -16
- package/dist/index.d.ts +31 -16
- package/dist/index.js +158 -381
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@opendisplay/epaper-dithering)
|
|
4
4
|
|
|
5
|
-
High-quality dithering algorithms for e-paper/e-ink displays,
|
|
5
|
+
High-quality dithering algorithms for e-paper/e-ink displays, powered by a Rust/WASM core. Works in both browser and Node.js environments.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
+
- **Rust/WASM Core**: Compiled Rust logic bundled inline — no async init, no external files, works everywhere
|
|
9
10
|
- **9 Dithering Algorithms**: From fast ordered dithering to high-quality error diffusion
|
|
10
11
|
- **8 Color Schemes**: MONO, BWR, BWY, BWRY, BWGBRY (Spectra 6), GRAYSCALE\_4/8/16
|
|
11
|
-
- **Measured Palettes**: Use real display-calibrated colors for accurate dithering (SPECTRA\_7\_3\_6COLOR, BWRY\_3\_97, and more)
|
|
12
|
-
- **
|
|
12
|
+
- **Measured Palettes**: Use real display-calibrated colors for accurate dithering (SPECTRA\_7\_3\_6COLOR\_V2, BWRY\_3\_97, and more)
|
|
13
|
+
- **OKLab Color Matching**: Weighted Cartesian OKLab — preserves hue without the achromatic-attractor bug that plagues LCH-weighted approaches
|
|
14
|
+
- **Pre-dither Adjustments**: Per-image exposure, saturation, shadows, highlights, dynamic-range compression, and gamut compression — all orthogonal knobs
|
|
13
15
|
- **Serpentine Scanning**: Alternates row direction to eliminate directional artifacts
|
|
14
|
-
- **Universal**: Works in browser (Canvas API) and Node.js (
|
|
15
|
-
- **Zero Dependencies**:
|
|
16
|
-
- **Fast**: 256-entry sRGB LUT, pre-computed palette LAB arrays, typed array pixel buffers
|
|
16
|
+
- **Universal**: Works in browser (Canvas API) and Node.js (≥18)
|
|
17
|
+
- **Zero Dependencies**: WASM binary bundled inline, no image library required
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -44,7 +45,7 @@ const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
|
|
44
45
|
const dithered = ditherImage(
|
|
45
46
|
{ width: imageData.width, height: imageData.height, data: imageData.data },
|
|
46
47
|
ColorScheme.BWR,
|
|
47
|
-
DitherMode.FLOYD_STEINBERG,
|
|
48
|
+
{ mode: DitherMode.FLOYD_STEINBERG },
|
|
48
49
|
);
|
|
49
50
|
|
|
50
51
|
// Render result
|
|
@@ -65,10 +66,10 @@ Standard `ColorScheme` values use ideal sRGB colors (e.g. white = 255,255,255).
|
|
|
65
66
|
import { ditherImage, SPECTRA_7_3_6COLOR, BWRY_3_97 } from '@opendisplay/epaper-dithering';
|
|
66
67
|
|
|
67
68
|
// Automatically applies tone compression to fit the display's actual dynamic range
|
|
68
|
-
const dithered = ditherImage(imageBuffer, SPECTRA_7_3_6COLOR, DitherMode.BURKES);
|
|
69
|
+
const dithered = ditherImage(imageBuffer, SPECTRA_7_3_6COLOR, { mode: DitherMode.BURKES });
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
Available measured palettes: `SPECTRA_7_3_6COLOR`, `BWRY_3_97`, `MONO_4_26`, `BWRY_4_2`, `SOLUM_BWR`, `HANSHOW_BWR`, `HANSHOW_BWY`.
|
|
72
|
+
Available measured palettes: `SPECTRA_7_3_6COLOR_V2`, `SPECTRA_7_3_6COLOR`, `BWRY_3_97`, `MONO_4_26`, `BWRY_4_2`, `SOLUM_BWR`, `HANSHOW_BWR`, `HANSHOW_BWY`.
|
|
72
73
|
|
|
73
74
|
### Node.js (with sharp)
|
|
74
75
|
|
|
@@ -84,7 +85,7 @@ const { data, info } = await sharp('photo.jpg')
|
|
|
84
85
|
const dithered = ditherImage(
|
|
85
86
|
{ width: info.width, height: info.height, data: new Uint8ClampedArray(data) },
|
|
86
87
|
ColorScheme.BWR,
|
|
87
|
-
DitherMode.BURKES,
|
|
88
|
+
{ mode: DitherMode.BURKES },
|
|
88
89
|
);
|
|
89
90
|
|
|
90
91
|
const rgbaBuffer = Buffer.alloc(dithered.width * dithered.height * 4);
|
|
@@ -101,14 +102,24 @@ await sharp(rgbaBuffer, { raw: { width: dithered.width, height: dithered.height,
|
|
|
101
102
|
|
|
102
103
|
## API Reference
|
|
103
104
|
|
|
104
|
-
### `ditherImage(image, colorScheme,
|
|
105
|
+
### `ditherImage(image, colorScheme, options?)`
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
```typescript
|
|
108
|
+
ditherImage(image: ImageBuffer, palette: ColorScheme | ColorPalette, options?: DitherOptions): PaletteImageBuffer
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| `options` field | Type | Default | Description |
|
|
107
112
|
|---|---|---|---|
|
|
108
|
-
| `image` | `ImageBuffer` | — | RGBA input image |
|
|
109
|
-
| `colorScheme` | `ColorScheme \| ColorPalette` | — | Target palette (enum or measured) |
|
|
110
113
|
| `mode` | `DitherMode` | `BURKES` | Dithering algorithm |
|
|
111
114
|
| `serpentine` | `boolean` | `true` | Alternate row direction to reduce artifacts |
|
|
115
|
+
| `exposure` | `number` | `1.0` | Linear-RGB exposure multiplier. `2.0` = +1 stop, `0.5` = −1 stop |
|
|
116
|
+
| `saturation` | `number` | `1.0` | OKLab saturation multiplier. `0.0` = grayscale, `>1` = boost. Hue-preserving |
|
|
117
|
+
| `shadows` | `number` | `0.0` | Shadow lift strength (S-curve lower half). `0.0` = off, `1.0` = strong |
|
|
118
|
+
| `highlights` | `number` | `0.0` | Highlight compression strength (S-curve upper half). `0.0` = off, `1.0` = strong |
|
|
119
|
+
| `tone` | `number \| 'auto' \| 'off'` | `'auto'` | Dynamic range compression. `'auto'` = histogram-based; numeric = fixed strength. Ignored for `ColorScheme` |
|
|
120
|
+
| `gamut` | `number \| 'auto' \| 'off'` | `'auto'` | Pre-dither gamut compression. `'auto'` = activate when image exceeds palette gamut; numeric = fixed. Ignored for `ColorScheme` |
|
|
121
|
+
|
|
122
|
+
Pre-processing pipeline: `exposure → saturation → shadows/highlights → tone → gamut → dither`. Each step is a no-op at its identity value.
|
|
112
123
|
|
|
113
124
|
Returns `PaletteImageBuffer`.
|
|
114
125
|
|
|
@@ -163,23 +174,35 @@ interface ColorPalette {
|
|
|
163
174
|
}
|
|
164
175
|
```
|
|
165
176
|
|
|
166
|
-
##
|
|
177
|
+
## Preview Tool
|
|
178
|
+
|
|
179
|
+
An interactive browser tool for comparing dithering modes and palettes:
|
|
167
180
|
|
|
168
|
-
|
|
181
|
+
**Hosted** (always latest release): https://opendisplay.github.io/epaper-dithering/
|
|
169
182
|
|
|
183
|
+
**Local** (against your working branch):
|
|
170
184
|
```bash
|
|
171
185
|
cd packages/javascript
|
|
172
186
|
bun run dev
|
|
173
|
-
#
|
|
187
|
+
# → http://localhost:3456/dev.html
|
|
174
188
|
```
|
|
175
189
|
|
|
176
|
-
Features:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
190
|
+
Features: drag & drop or paste from clipboard, live re-render on every setting change, timing display, palette swatch preview.
|
|
191
|
+
|
|
192
|
+
## Development
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
bun install
|
|
196
|
+
|
|
197
|
+
# When Rust source changes, rebuild the WASM (from repo root):
|
|
198
|
+
wasm-pack build packages/rust/wasm --target bundler --out-dir ../../javascript/src/wasm-core
|
|
199
|
+
|
|
200
|
+
bun run test # vitest
|
|
201
|
+
bun run build # tsup → dist/
|
|
202
|
+
bun run type-check
|
|
203
|
+
```
|
|
181
204
|
|
|
182
205
|
## Related Projects
|
|
183
206
|
|
|
184
|
-
- **Python**: [`epaper-dithering`](https://pypi.org/project/epaper-dithering/) — Python
|
|
207
|
+
- **Python**: [`epaper-dithering`](https://pypi.org/project/epaper-dithering/) — Python package, shares the same Rust core
|
|
185
208
|
- **OpenDisplay**: [`py-opendisplay`](https://github.com/OpenDisplay-org/py-opendisplay) — Python library for OpenDisplay BLE devices
|