@opendisplay/epaper-dithering 0.1.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 +239 -0
- package/dist/index.cjs +328 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +320 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @opendisplay/epaper-dithering
|
|
2
|
+
|
|
3
|
+
High-quality dithering algorithms for e-paper/e-ink displays, implemented in TypeScript. Works in both browser and Node.js environments.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **9 Dithering Algorithms**: From fast ordered dithering to high-quality error diffusion
|
|
8
|
+
- 🎯 **6 Color Schemes**: MONO, BWR, BWY, BWRY, BWGBRY (Spectra 6), GRAYSCALE_4
|
|
9
|
+
- 🌐 **Universal**: Works in browser (Canvas API) and Node.js (with sharp/jimp)
|
|
10
|
+
- 📦 **Zero Dependencies**: Pure TypeScript, no image library dependencies
|
|
11
|
+
- ⚡ **Fast**: Optimized typed array operations
|
|
12
|
+
- 🔒 **Type-Safe**: Full TypeScript support with exported types
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @opendisplay/epaper-dithering
|
|
18
|
+
# or
|
|
19
|
+
bun add @opendisplay/epaper-dithering
|
|
20
|
+
# or
|
|
21
|
+
yarn add @opendisplay/epaper-dithering
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Browser (Canvas API)
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { ditherImage, ColorScheme, DitherMode } from '@opendisplay/epaper-dithering';
|
|
30
|
+
|
|
31
|
+
// Load image
|
|
32
|
+
const img = new Image();
|
|
33
|
+
img.src = 'photo.jpg';
|
|
34
|
+
await img.decode();
|
|
35
|
+
|
|
36
|
+
// Convert to ImageBuffer
|
|
37
|
+
const canvas = document.createElement('canvas');
|
|
38
|
+
canvas.width = img.width;
|
|
39
|
+
canvas.height = img.height;
|
|
40
|
+
const ctx = canvas.getContext('2d')!;
|
|
41
|
+
ctx.drawImage(img, 0, 0);
|
|
42
|
+
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
|
43
|
+
|
|
44
|
+
const imageBuffer = {
|
|
45
|
+
width: imageData.width,
|
|
46
|
+
height: imageData.height,
|
|
47
|
+
data: imageData.data,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Dither
|
|
51
|
+
const dithered = ditherImage(
|
|
52
|
+
imageBuffer,
|
|
53
|
+
ColorScheme.BWR,
|
|
54
|
+
DitherMode.FLOYD_STEINBERG
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Render result
|
|
58
|
+
const resultCanvas = document.createElement('canvas');
|
|
59
|
+
resultCanvas.width = dithered.width;
|
|
60
|
+
resultCanvas.height = dithered.height;
|
|
61
|
+
const resultCtx = resultCanvas.getContext('2d')!;
|
|
62
|
+
const resultData = resultCtx.createImageData(dithered.width, dithered.height);
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < dithered.indices.length; i++) {
|
|
65
|
+
const color = dithered.palette[dithered.indices[i]];
|
|
66
|
+
resultData.data[i * 4] = color.r;
|
|
67
|
+
resultData.data[i * 4 + 1] = color.g;
|
|
68
|
+
resultData.data[i * 4 + 2] = color.b;
|
|
69
|
+
resultData.data[i * 4 + 3] = 255;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
resultCtx.putImageData(resultData, 0, 0);
|
|
73
|
+
document.body.appendChild(resultCanvas);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Node.js (with sharp)
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import sharp from 'sharp';
|
|
80
|
+
import { ditherImage, ColorScheme, DitherMode } from '@opendisplay/epaper-dithering';
|
|
81
|
+
|
|
82
|
+
// Load image
|
|
83
|
+
const { data, info } = await sharp('photo.jpg')
|
|
84
|
+
.ensureAlpha()
|
|
85
|
+
.raw()
|
|
86
|
+
.toBuffer({ resolveWithObject: true });
|
|
87
|
+
|
|
88
|
+
const imageBuffer = {
|
|
89
|
+
width: info.width,
|
|
90
|
+
height: info.height,
|
|
91
|
+
data: new Uint8ClampedArray(data),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Dither
|
|
95
|
+
const dithered = ditherImage(imageBuffer, ColorScheme.BWR, DitherMode.BURKES);
|
|
96
|
+
|
|
97
|
+
// Convert back to RGBA
|
|
98
|
+
const rgbaBuffer = Buffer.alloc(dithered.width * dithered.height * 4);
|
|
99
|
+
for (let i = 0; i < dithered.indices.length; i++) {
|
|
100
|
+
const color = dithered.palette[dithered.indices[i]];
|
|
101
|
+
rgbaBuffer[i * 4] = color.r;
|
|
102
|
+
rgbaBuffer[i * 4 + 1] = color.g;
|
|
103
|
+
rgbaBuffer[i * 4 + 2] = color.b;
|
|
104
|
+
rgbaBuffer[i * 4 + 3] = 255;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Save
|
|
108
|
+
await sharp(rgbaBuffer, {
|
|
109
|
+
raw: {
|
|
110
|
+
width: dithered.width,
|
|
111
|
+
height: dithered.height,
|
|
112
|
+
channels: 4,
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
.png()
|
|
116
|
+
.toFile('dithered.png');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### `ditherImage(image, colorScheme, mode?)`
|
|
122
|
+
|
|
123
|
+
Apply dithering algorithm to image for e-paper display.
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
- `image: ImageBuffer` - Input image in RGBA format
|
|
127
|
+
- `colorScheme: ColorScheme` - Target e-paper color scheme
|
|
128
|
+
- `mode?: DitherMode` - Dithering algorithm (default: `DitherMode.BURKES`)
|
|
129
|
+
|
|
130
|
+
**Returns:** `PaletteImageBuffer` - Palette-indexed image with color information
|
|
131
|
+
|
|
132
|
+
### Color Schemes
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
enum ColorScheme {
|
|
136
|
+
MONO = 0, // Black & White (2 colors)
|
|
137
|
+
BWR = 1, // Black, White, Red (3 colors)
|
|
138
|
+
BWY = 2, // Black, White, Yellow (3 colors)
|
|
139
|
+
BWRY = 3, // Black, White, Red, Yellow (4 colors)
|
|
140
|
+
BWGBRY = 4, // Black, White, Green, Blue, Red, Yellow (6 colors)
|
|
141
|
+
GRAYSCALE_4 = 5, // 4-level grayscale
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Dither Modes
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
enum DitherMode {
|
|
149
|
+
NONE = 0, // No dithering (direct palette mapping)
|
|
150
|
+
BURKES = 1, // Burkes (default - good quality/speed balance)
|
|
151
|
+
ORDERED = 2, // Ordered (4×4 Bayer matrix - fast)
|
|
152
|
+
FLOYD_STEINBERG = 3, // Floyd-Steinberg (most popular)
|
|
153
|
+
ATKINSON = 4, // Atkinson (classic Macintosh style)
|
|
154
|
+
STUCKI = 5, // Stucki (high quality)
|
|
155
|
+
SIERRA = 6, // Sierra (high quality)
|
|
156
|
+
SIERRA_LITE = 7, // Sierra Lite (fast)
|
|
157
|
+
JARVIS_JUDICE_NINKE = 8, // Jarvis-Judice-Ninke (highest quality, slowest)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Algorithm Comparison
|
|
162
|
+
|
|
163
|
+
| Algorithm | Quality | Speed | Use Case |
|
|
164
|
+
|-----------|---------|-------|----------|
|
|
165
|
+
| NONE | Lowest | Fastest | Testing, solid colors |
|
|
166
|
+
| ORDERED | Low | Very Fast | Simple images, patterns |
|
|
167
|
+
| SIERRA_LITE | Medium | Fast | Quick previews |
|
|
168
|
+
| BURKES | Good | Medium | **Default - best balance** |
|
|
169
|
+
| FLOYD_STEINBERG | Good | Medium | Popular choice, smooth gradients |
|
|
170
|
+
| ATKINSON | Good | Medium | Classic retro aesthetic |
|
|
171
|
+
| SIERRA | High | Medium | Detailed images |
|
|
172
|
+
| STUCKI | Very High | Slow | Photos, high detail |
|
|
173
|
+
| JARVIS_JUDICE_NINKE | Highest | Slowest | Maximum quality |
|
|
174
|
+
|
|
175
|
+
## Types
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
interface RGB {
|
|
179
|
+
r: number; // 0-255
|
|
180
|
+
g: number; // 0-255
|
|
181
|
+
b: number; // 0-255
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
interface ImageBuffer {
|
|
185
|
+
width: number;
|
|
186
|
+
height: number;
|
|
187
|
+
data: Uint8ClampedArray; // RGBA format
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface PaletteImageBuffer {
|
|
191
|
+
width: number;
|
|
192
|
+
height: number;
|
|
193
|
+
indices: Uint8Array; // Palette index per pixel
|
|
194
|
+
palette: RGB[]; // Available colors
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
interface ColorPalette {
|
|
198
|
+
readonly colors: Record<string, RGB>;
|
|
199
|
+
readonly accent: string;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Helper Functions
|
|
204
|
+
|
|
205
|
+
### `getPalette(scheme: ColorScheme): ColorPalette`
|
|
206
|
+
|
|
207
|
+
Get color palette for a color scheme.
|
|
208
|
+
|
|
209
|
+
### `getColorCount(scheme: ColorScheme): number`
|
|
210
|
+
|
|
211
|
+
Get number of colors in a color scheme.
|
|
212
|
+
|
|
213
|
+
### `fromValue(value: number): ColorScheme`
|
|
214
|
+
|
|
215
|
+
Create ColorScheme from firmware integer value (0-5).
|
|
216
|
+
|
|
217
|
+
## Performance
|
|
218
|
+
|
|
219
|
+
Expected performance on an 800×600 image:
|
|
220
|
+
- **ORDERED**: ~50ms
|
|
221
|
+
- **SIERRA_LITE**: ~100ms
|
|
222
|
+
- **BURKES/FLOYD_STEINBERG**: ~150ms
|
|
223
|
+
- **SIERRA/ATKINSON**: ~200ms
|
|
224
|
+
- **STUCKI/JARVIS**: ~300ms
|
|
225
|
+
|
|
226
|
+
Performance varies by device and JavaScript engine.
|
|
227
|
+
|
|
228
|
+
## Related Projects
|
|
229
|
+
|
|
230
|
+
- **Python**: [`epaper-dithering`](https://pypi.org/project/epaper-dithering/) - Python implementation
|
|
231
|
+
- **OpenDisplay**: [`py-opendisplay`](https://github.com/OpenDisplay-org/py-opendisplay) - Python library for OpenDisplay BLE e-paper tags
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT © OpenDisplay
|
|
236
|
+
|
|
237
|
+
## Contributing
|
|
238
|
+
|
|
239
|
+
Contributions welcome! Please see the [monorepo](https://github.com/OpenDisplay-org/epaper-dithering) for development setup.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/enums.ts
|
|
4
|
+
var DitherMode = /* @__PURE__ */ ((DitherMode2) => {
|
|
5
|
+
DitherMode2[DitherMode2["NONE"] = 0] = "NONE";
|
|
6
|
+
DitherMode2[DitherMode2["BURKES"] = 1] = "BURKES";
|
|
7
|
+
DitherMode2[DitherMode2["ORDERED"] = 2] = "ORDERED";
|
|
8
|
+
DitherMode2[DitherMode2["FLOYD_STEINBERG"] = 3] = "FLOYD_STEINBERG";
|
|
9
|
+
DitherMode2[DitherMode2["ATKINSON"] = 4] = "ATKINSON";
|
|
10
|
+
DitherMode2[DitherMode2["STUCKI"] = 5] = "STUCKI";
|
|
11
|
+
DitherMode2[DitherMode2["SIERRA"] = 6] = "SIERRA";
|
|
12
|
+
DitherMode2[DitherMode2["SIERRA_LITE"] = 7] = "SIERRA_LITE";
|
|
13
|
+
DitherMode2[DitherMode2["JARVIS_JUDICE_NINKE"] = 8] = "JARVIS_JUDICE_NINKE";
|
|
14
|
+
return DitherMode2;
|
|
15
|
+
})(DitherMode || {});
|
|
16
|
+
|
|
17
|
+
// src/palettes.ts
|
|
18
|
+
var ColorScheme = /* @__PURE__ */ ((ColorScheme3) => {
|
|
19
|
+
ColorScheme3[ColorScheme3["MONO"] = 0] = "MONO";
|
|
20
|
+
ColorScheme3[ColorScheme3["BWR"] = 1] = "BWR";
|
|
21
|
+
ColorScheme3[ColorScheme3["BWY"] = 2] = "BWY";
|
|
22
|
+
ColorScheme3[ColorScheme3["BWRY"] = 3] = "BWRY";
|
|
23
|
+
ColorScheme3[ColorScheme3["BWGBRY"] = 4] = "BWGBRY";
|
|
24
|
+
ColorScheme3[ColorScheme3["GRAYSCALE_4"] = 5] = "GRAYSCALE_4";
|
|
25
|
+
return ColorScheme3;
|
|
26
|
+
})(ColorScheme || {});
|
|
27
|
+
var PALETTES = {
|
|
28
|
+
[0 /* MONO */]: {
|
|
29
|
+
colors: {
|
|
30
|
+
black: { r: 0, g: 0, b: 0 },
|
|
31
|
+
white: { r: 255, g: 255, b: 255 }
|
|
32
|
+
},
|
|
33
|
+
accent: "black"
|
|
34
|
+
},
|
|
35
|
+
[1 /* BWR */]: {
|
|
36
|
+
colors: {
|
|
37
|
+
black: { r: 0, g: 0, b: 0 },
|
|
38
|
+
white: { r: 255, g: 255, b: 255 },
|
|
39
|
+
red: { r: 255, g: 0, b: 0 }
|
|
40
|
+
},
|
|
41
|
+
accent: "red"
|
|
42
|
+
},
|
|
43
|
+
[2 /* BWY */]: {
|
|
44
|
+
colors: {
|
|
45
|
+
black: { r: 0, g: 0, b: 0 },
|
|
46
|
+
white: { r: 255, g: 255, b: 255 },
|
|
47
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
48
|
+
},
|
|
49
|
+
accent: "yellow"
|
|
50
|
+
},
|
|
51
|
+
[3 /* BWRY */]: {
|
|
52
|
+
colors: {
|
|
53
|
+
black: { r: 0, g: 0, b: 0 },
|
|
54
|
+
white: { r: 255, g: 255, b: 255 },
|
|
55
|
+
red: { r: 255, g: 0, b: 0 },
|
|
56
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
57
|
+
},
|
|
58
|
+
accent: "red"
|
|
59
|
+
},
|
|
60
|
+
[4 /* BWGBRY */]: {
|
|
61
|
+
colors: {
|
|
62
|
+
black: { r: 0, g: 0, b: 0 },
|
|
63
|
+
white: { r: 255, g: 255, b: 255 },
|
|
64
|
+
green: { r: 0, g: 255, b: 0 },
|
|
65
|
+
blue: { r: 0, g: 0, b: 255 },
|
|
66
|
+
red: { r: 255, g: 0, b: 0 },
|
|
67
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
68
|
+
},
|
|
69
|
+
accent: "red"
|
|
70
|
+
},
|
|
71
|
+
[5 /* GRAYSCALE_4 */]: {
|
|
72
|
+
colors: {
|
|
73
|
+
black: { r: 0, g: 0, b: 0 },
|
|
74
|
+
gray1: { r: 85, g: 85, b: 85 },
|
|
75
|
+
gray2: { r: 170, g: 170, b: 170 },
|
|
76
|
+
white: { r: 255, g: 255, b: 255 }
|
|
77
|
+
},
|
|
78
|
+
accent: "black"
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
function getPalette(scheme) {
|
|
82
|
+
return PALETTES[scheme];
|
|
83
|
+
}
|
|
84
|
+
function getColorCount(scheme) {
|
|
85
|
+
return Object.keys(PALETTES[scheme].colors).length;
|
|
86
|
+
}
|
|
87
|
+
function fromValue(value) {
|
|
88
|
+
if (value < 0 || value > 5) {
|
|
89
|
+
throw new Error(`Invalid color scheme value: ${value}`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/algorithms.ts
|
|
95
|
+
function getPaletteColors(scheme) {
|
|
96
|
+
const palette = getPalette(scheme);
|
|
97
|
+
return Object.values(palette.colors);
|
|
98
|
+
}
|
|
99
|
+
function findClosestPaletteColor(rgb, palette) {
|
|
100
|
+
let minDistance = Infinity;
|
|
101
|
+
let closestIdx = 0;
|
|
102
|
+
for (let i = 0; i < palette.length; i++) {
|
|
103
|
+
const pal = palette[i];
|
|
104
|
+
const distance = (rgb.r - pal.r) ** 2 + (rgb.g - pal.g) ** 2 + (rgb.b - pal.b) ** 2;
|
|
105
|
+
if (distance < minDistance) {
|
|
106
|
+
minDistance = distance;
|
|
107
|
+
closestIdx = i;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return closestIdx;
|
|
111
|
+
}
|
|
112
|
+
function errorDiffusionDither(image, colorScheme, kernel) {
|
|
113
|
+
const { width, height } = image;
|
|
114
|
+
const palette = getPaletteColors(colorScheme);
|
|
115
|
+
const pixels = new Float32Array(width * height * 3);
|
|
116
|
+
for (let i = 0; i < width * height; i++) {
|
|
117
|
+
pixels[i * 3] = image.data[i * 4];
|
|
118
|
+
pixels[i * 3 + 1] = image.data[i * 4 + 1];
|
|
119
|
+
pixels[i * 3 + 2] = image.data[i * 4 + 2];
|
|
120
|
+
}
|
|
121
|
+
const indices = new Uint8Array(width * height);
|
|
122
|
+
for (let y = 0; y < height; y++) {
|
|
123
|
+
for (let x = 0; x < width; x++) {
|
|
124
|
+
const idx = y * width + x;
|
|
125
|
+
const pixelIdx = idx * 3;
|
|
126
|
+
const oldPixel = {
|
|
127
|
+
r: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx]))),
|
|
128
|
+
g: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 1]))),
|
|
129
|
+
b: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 2])))
|
|
130
|
+
};
|
|
131
|
+
const newIdx = findClosestPaletteColor(oldPixel, palette);
|
|
132
|
+
const newPixel = palette[newIdx];
|
|
133
|
+
indices[idx] = newIdx;
|
|
134
|
+
const errorR = oldPixel.r - newPixel.r;
|
|
135
|
+
const errorG = oldPixel.g - newPixel.g;
|
|
136
|
+
const errorB = oldPixel.b - newPixel.b;
|
|
137
|
+
for (const { dx, dy, weight } of kernel) {
|
|
138
|
+
const nx = x + dx;
|
|
139
|
+
const ny = y + dy;
|
|
140
|
+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
141
|
+
const neighborIdx = (ny * width + nx) * 3;
|
|
142
|
+
pixels[neighborIdx] += errorR * weight;
|
|
143
|
+
pixels[neighborIdx + 1] += errorG * weight;
|
|
144
|
+
pixels[neighborIdx + 2] += errorB * weight;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { width, height, indices, palette };
|
|
150
|
+
}
|
|
151
|
+
function directPaletteMap(image, colorScheme) {
|
|
152
|
+
const { width, height } = image;
|
|
153
|
+
const palette = getPaletteColors(colorScheme);
|
|
154
|
+
const indices = new Uint8Array(width * height);
|
|
155
|
+
for (let i = 0; i < width * height; i++) {
|
|
156
|
+
const rgb = {
|
|
157
|
+
r: image.data[i * 4],
|
|
158
|
+
g: image.data[i * 4 + 1],
|
|
159
|
+
b: image.data[i * 4 + 2]
|
|
160
|
+
};
|
|
161
|
+
indices[i] = findClosestPaletteColor(rgb, palette);
|
|
162
|
+
}
|
|
163
|
+
return { width, height, indices, palette };
|
|
164
|
+
}
|
|
165
|
+
function orderedDither(image, colorScheme) {
|
|
166
|
+
const bayerMatrix = new Uint8Array([
|
|
167
|
+
0,
|
|
168
|
+
8,
|
|
169
|
+
2,
|
|
170
|
+
10,
|
|
171
|
+
12,
|
|
172
|
+
4,
|
|
173
|
+
14,
|
|
174
|
+
6,
|
|
175
|
+
3,
|
|
176
|
+
11,
|
|
177
|
+
1,
|
|
178
|
+
9,
|
|
179
|
+
15,
|
|
180
|
+
7,
|
|
181
|
+
13,
|
|
182
|
+
5
|
|
183
|
+
]).map((v) => v * 16);
|
|
184
|
+
const { width, height } = image;
|
|
185
|
+
const palette = getPaletteColors(colorScheme);
|
|
186
|
+
const indices = new Uint8Array(width * height);
|
|
187
|
+
for (let y = 0; y < height; y++) {
|
|
188
|
+
for (let x = 0; x < width; x++) {
|
|
189
|
+
const idx = y * width + x;
|
|
190
|
+
const dataIdx = idx * 4;
|
|
191
|
+
const threshold = bayerMatrix[y % 4 * 4 + x % 4];
|
|
192
|
+
const rgb = {
|
|
193
|
+
r: Math.min(255, image.data[dataIdx] + threshold),
|
|
194
|
+
g: Math.min(255, image.data[dataIdx + 1] + threshold),
|
|
195
|
+
b: Math.min(255, image.data[dataIdx + 2] + threshold)
|
|
196
|
+
};
|
|
197
|
+
indices[idx] = findClosestPaletteColor(rgb, palette);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { width, height, indices, palette };
|
|
201
|
+
}
|
|
202
|
+
function burkesDither(image, colorScheme) {
|
|
203
|
+
const kernel = [
|
|
204
|
+
{ dx: 1, dy: 0, weight: 32 / 200 },
|
|
205
|
+
{ dx: 2, dy: 0, weight: 12 / 200 },
|
|
206
|
+
{ dx: -2, dy: 1, weight: 5 / 200 },
|
|
207
|
+
{ dx: -1, dy: 1, weight: 12 / 200 },
|
|
208
|
+
{ dx: 0, dy: 1, weight: 26 / 200 },
|
|
209
|
+
{ dx: 1, dy: 1, weight: 12 / 200 },
|
|
210
|
+
{ dx: 2, dy: 1, weight: 5 / 200 }
|
|
211
|
+
];
|
|
212
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
213
|
+
}
|
|
214
|
+
function floydSteinbergDither(image, colorScheme) {
|
|
215
|
+
const kernel = [
|
|
216
|
+
{ dx: 1, dy: 0, weight: 7 / 16 },
|
|
217
|
+
{ dx: -1, dy: 1, weight: 3 / 16 },
|
|
218
|
+
{ dx: 0, dy: 1, weight: 5 / 16 },
|
|
219
|
+
{ dx: 1, dy: 1, weight: 1 / 16 }
|
|
220
|
+
];
|
|
221
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
222
|
+
}
|
|
223
|
+
function sierraDither(image, colorScheme) {
|
|
224
|
+
const kernel = [
|
|
225
|
+
{ dx: 1, dy: 0, weight: 5 / 32 },
|
|
226
|
+
{ dx: 2, dy: 0, weight: 3 / 32 },
|
|
227
|
+
{ dx: -2, dy: 1, weight: 2 / 32 },
|
|
228
|
+
{ dx: -1, dy: 1, weight: 4 / 32 },
|
|
229
|
+
{ dx: 0, dy: 1, weight: 5 / 32 },
|
|
230
|
+
{ dx: 1, dy: 1, weight: 4 / 32 },
|
|
231
|
+
{ dx: 2, dy: 1, weight: 2 / 32 },
|
|
232
|
+
{ dx: -1, dy: 2, weight: 2 / 32 },
|
|
233
|
+
{ dx: 0, dy: 2, weight: 3 / 32 },
|
|
234
|
+
{ dx: 1, dy: 2, weight: 2 / 32 }
|
|
235
|
+
];
|
|
236
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
237
|
+
}
|
|
238
|
+
function sierraLiteDither(image, colorScheme) {
|
|
239
|
+
const kernel = [
|
|
240
|
+
{ dx: 1, dy: 0, weight: 2 / 4 },
|
|
241
|
+
{ dx: -1, dy: 1, weight: 1 / 4 },
|
|
242
|
+
{ dx: 0, dy: 1, weight: 1 / 4 }
|
|
243
|
+
];
|
|
244
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
245
|
+
}
|
|
246
|
+
function atkinsonDither(image, colorScheme) {
|
|
247
|
+
const kernel = [
|
|
248
|
+
{ dx: 1, dy: 0, weight: 1 / 8 },
|
|
249
|
+
{ dx: 2, dy: 0, weight: 1 / 8 },
|
|
250
|
+
{ dx: -1, dy: 1, weight: 1 / 8 },
|
|
251
|
+
{ dx: 0, dy: 1, weight: 1 / 8 },
|
|
252
|
+
{ dx: 1, dy: 1, weight: 1 / 8 },
|
|
253
|
+
{ dx: 0, dy: 2, weight: 1 / 8 }
|
|
254
|
+
];
|
|
255
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
256
|
+
}
|
|
257
|
+
function stuckiDither(image, colorScheme) {
|
|
258
|
+
const kernel = [
|
|
259
|
+
{ dx: 1, dy: 0, weight: 8 / 42 },
|
|
260
|
+
{ dx: 2, dy: 0, weight: 4 / 42 },
|
|
261
|
+
{ dx: -2, dy: 1, weight: 2 / 42 },
|
|
262
|
+
{ dx: -1, dy: 1, weight: 4 / 42 },
|
|
263
|
+
{ dx: 0, dy: 1, weight: 8 / 42 },
|
|
264
|
+
{ dx: 1, dy: 1, weight: 4 / 42 },
|
|
265
|
+
{ dx: 2, dy: 1, weight: 2 / 42 },
|
|
266
|
+
{ dx: -2, dy: 2, weight: 1 / 42 },
|
|
267
|
+
{ dx: -1, dy: 2, weight: 2 / 42 },
|
|
268
|
+
{ dx: 0, dy: 2, weight: 4 / 42 },
|
|
269
|
+
{ dx: 1, dy: 2, weight: 2 / 42 },
|
|
270
|
+
{ dx: 2, dy: 2, weight: 1 / 42 }
|
|
271
|
+
];
|
|
272
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
273
|
+
}
|
|
274
|
+
function jarvisJudiceNinkeDither(image, colorScheme) {
|
|
275
|
+
const kernel = [
|
|
276
|
+
{ dx: 1, dy: 0, weight: 7 / 48 },
|
|
277
|
+
{ dx: 2, dy: 0, weight: 5 / 48 },
|
|
278
|
+
{ dx: -2, dy: 1, weight: 3 / 48 },
|
|
279
|
+
{ dx: -1, dy: 1, weight: 5 / 48 },
|
|
280
|
+
{ dx: 0, dy: 1, weight: 7 / 48 },
|
|
281
|
+
{ dx: 1, dy: 1, weight: 5 / 48 },
|
|
282
|
+
{ dx: 2, dy: 1, weight: 3 / 48 },
|
|
283
|
+
{ dx: -2, dy: 2, weight: 1 / 48 },
|
|
284
|
+
{ dx: -1, dy: 2, weight: 3 / 48 },
|
|
285
|
+
{ dx: 0, dy: 2, weight: 5 / 48 },
|
|
286
|
+
{ dx: 1, dy: 2, weight: 3 / 48 },
|
|
287
|
+
{ dx: 2, dy: 2, weight: 1 / 48 }
|
|
288
|
+
];
|
|
289
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/core.ts
|
|
293
|
+
function ditherImage(image, colorScheme, mode = 1 /* BURKES */) {
|
|
294
|
+
switch (mode) {
|
|
295
|
+
case 0 /* NONE */:
|
|
296
|
+
return directPaletteMap(image, colorScheme);
|
|
297
|
+
case 2 /* ORDERED */:
|
|
298
|
+
return orderedDither(image, colorScheme);
|
|
299
|
+
case 3 /* FLOYD_STEINBERG */:
|
|
300
|
+
return floydSteinbergDither(image, colorScheme);
|
|
301
|
+
case 4 /* ATKINSON */:
|
|
302
|
+
return atkinsonDither(image, colorScheme);
|
|
303
|
+
case 5 /* STUCKI */:
|
|
304
|
+
return stuckiDither(image, colorScheme);
|
|
305
|
+
case 6 /* SIERRA */:
|
|
306
|
+
return sierraDither(image, colorScheme);
|
|
307
|
+
case 7 /* SIERRA_LITE */:
|
|
308
|
+
return sierraLiteDither(image, colorScheme);
|
|
309
|
+
case 8 /* JARVIS_JUDICE_NINKE */:
|
|
310
|
+
return jarvisJudiceNinkeDither(image, colorScheme);
|
|
311
|
+
case 1 /* BURKES */:
|
|
312
|
+
default:
|
|
313
|
+
return burkesDither(image, colorScheme);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/index.ts
|
|
318
|
+
var VERSION = "0.1.0";
|
|
319
|
+
|
|
320
|
+
exports.ColorScheme = ColorScheme;
|
|
321
|
+
exports.DitherMode = DitherMode;
|
|
322
|
+
exports.VERSION = VERSION;
|
|
323
|
+
exports.ditherImage = ditherImage;
|
|
324
|
+
exports.fromValue = fromValue;
|
|
325
|
+
exports.getColorCount = getColorCount;
|
|
326
|
+
exports.getPalette = getPalette;
|
|
327
|
+
//# sourceMappingURL=index.cjs.map
|
|
328
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/enums.ts","../src/palettes.ts","../src/algorithms.ts","../src/core.ts","../src/index.ts"],"names":["DitherMode","ColorScheme"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Dithering algorithm modes\n * Values match firmware conventions (0-8)\n */\nexport enum DitherMode {\n NONE = 0,\n BURKES = 1,\n ORDERED = 2,\n FLOYD_STEINBERG = 3,\n ATKINSON = 4,\n STUCKI = 5,\n SIERRA = 6,\n SIERRA_LITE = 7,\n JARVIS_JUDICE_NINKE = 8,\n}","import type { RGB, ColorPalette } from './types';\n\n/**\n * E-paper display color schemes\n * Values match firmware conventions (0-5)\n */\nexport enum ColorScheme {\n MONO = 0,\n BWR = 1,\n BWY = 2,\n BWRY = 3,\n BWGBRY = 4,\n GRAYSCALE_4 = 5,\n}\n\nconst PALETTES: Record<ColorScheme, ColorPalette> = {\n [ColorScheme.MONO]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n },\n accent: 'black',\n },\n [ColorScheme.BWR]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.BWY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'yellow',\n },\n [ColorScheme.BWRY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.BWGBRY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n green: { r: 0, g: 255, b: 0 },\n blue: { r: 0, g: 0, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.GRAYSCALE_4]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n gray1: { r: 85, g: 85, b: 85 },\n gray2: { r: 170, g: 170, b: 170 },\n white: { r: 255, g: 255, b: 255 },\n },\n accent: 'black',\n },\n};\n\n/**\n * Get color palette for a color scheme\n */\nexport function getPalette(scheme: ColorScheme): ColorPalette {\n return PALETTES[scheme];\n}\n\n/**\n * Get number of colors in a color scheme\n */\nexport function getColorCount(scheme: ColorScheme): number {\n return Object.keys(PALETTES[scheme].colors).length;\n}\n\n/**\n * Create ColorScheme from firmware integer value\n */\nexport function fromValue(value: number): ColorScheme {\n if (value < 0 || value > 5) {\n throw new Error(`Invalid color scheme value: ${value}`);\n }\n return value as ColorScheme;\n}","import type { RGB, ImageBuffer, PaletteImageBuffer } from './types';\nimport { ColorScheme, getPalette } from './palettes';\n\n/**\n * Get RGB palette colors from color scheme\n */\nexport function getPaletteColors(scheme: ColorScheme): RGB[] {\n const palette = getPalette(scheme);\n return Object.values(palette.colors);\n}\n\n/**\n * Find closest palette color using Euclidean distance\n */\nexport function findClosestPaletteColor(rgb: RGB, palette: RGB[]): number {\n let minDistance = Infinity;\n let closestIdx = 0;\n\n for (let i = 0; i < palette.length; i++) {\n const pal = palette[i];\n // Euclidean distance in RGB space\n const distance =\n (rgb.r - pal.r) ** 2 + (rgb.g - pal.g) ** 2 + (rgb.b - pal.b) ** 2;\n\n if (distance < minDistance) {\n minDistance = distance;\n closestIdx = i;\n }\n }\n\n return closestIdx;\n}\n\n/**\n * Error diffusion kernel entry\n */\ninterface ErrorKernel {\n dx: number;\n dy: number;\n weight: number;\n}\n\n/**\n * Apply error diffusion dithering with specified kernel\n */\nfunction errorDiffusionDither(\n image: ImageBuffer,\n colorScheme: ColorScheme,\n kernel: ErrorKernel[]\n): PaletteImageBuffer {\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n\n // Convert RGBA to RGB float array for error accumulation\n const pixels = new Float32Array(width * height * 3);\n for (let i = 0; i < width * height; i++) {\n pixels[i * 3] = image.data[i * 4]; // R\n pixels[i * 3 + 1] = image.data[i * 4 + 1]; // G\n pixels[i * 3 + 2] = image.data[i * 4 + 2]; // B\n }\n\n const indices = new Uint8Array(width * height);\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const pixelIdx = idx * 3;\n\n // Get old pixel (clamp to 0-255)\n const oldPixel: RGB = {\n r: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx]))),\n g: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 1]))),\n b: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 2]))),\n };\n\n // Find closest palette color\n const newIdx = findClosestPaletteColor(oldPixel, palette);\n const newPixel = palette[newIdx];\n indices[idx] = newIdx;\n\n // Calculate quantization error\n const errorR = oldPixel.r - newPixel.r;\n const errorG = oldPixel.g - newPixel.g;\n const errorB = oldPixel.b - newPixel.b;\n\n // Distribute error to neighbors using kernel\n for (const { dx, dy, weight } of kernel) {\n const nx = x + dx;\n const ny = y + dy;\n\n if (nx >= 0 && nx < width && ny >= 0 && ny < height) {\n const neighborIdx = (ny * width + nx) * 3;\n pixels[neighborIdx] += errorR * weight;\n pixels[neighborIdx + 1] += errorG * weight;\n pixels[neighborIdx + 2] += errorB * weight;\n }\n }\n }\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Direct palette mapping without dithering (NONE)\n */\nexport function directPaletteMap(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n const indices = new Uint8Array(width * height);\n\n for (let i = 0; i < width * height; i++) {\n const rgb: RGB = {\n r: image.data[i * 4],\n g: image.data[i * 4 + 1],\n b: image.data[i * 4 + 2],\n };\n indices[i] = findClosestPaletteColor(rgb, palette);\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Ordered dithering using 4x4 Bayer matrix\n */\nexport function orderedDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const bayerMatrix = new Uint8Array([\n 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5,\n ]).map((v) => v * 16);\n\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n const indices = new Uint8Array(width * height);\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const dataIdx = idx * 4;\n\n // Get threshold from Bayer matrix\n const threshold = bayerMatrix[(y % 4) * 4 + (x % 4)];\n\n // Add threshold noise\n const rgb: RGB = {\n r: Math.min(255, image.data[dataIdx] + threshold),\n g: Math.min(255, image.data[dataIdx + 1] + threshold),\n b: Math.min(255, image.data[dataIdx + 2] + threshold),\n };\n\n indices[idx] = findClosestPaletteColor(rgb, palette);\n }\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Burkes dithering (divisor 200)\n * Kernel:\n * X 32 12\n * 5 12 26 12 5\n */\nexport function burkesDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 32 / 200 },\n { dx: 2, dy: 0, weight: 12 / 200 },\n { dx: -2, dy: 1, weight: 5 / 200 },\n { dx: -1, dy: 1, weight: 12 / 200 },\n { dx: 0, dy: 1, weight: 26 / 200 },\n { dx: 1, dy: 1, weight: 12 / 200 },\n { dx: 2, dy: 1, weight: 5 / 200 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Floyd-Steinberg dithering (divisor 16)\n * Most popular error diffusion algorithm\n * Kernel:\n * X 7\n * 3 5 1\n */\nexport function floydSteinbergDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 7 / 16 },\n { dx: -1, dy: 1, weight: 3 / 16 },\n { dx: 0, dy: 1, weight: 5 / 16 },\n { dx: 1, dy: 1, weight: 1 / 16 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Sierra dithering (divisor 32)\n * Kernel:\n * X 5 3\n * 2 4 5 4 2\n * 2 3 2\n */\nexport function sierraDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 5 / 32 },\n { dx: 2, dy: 0, weight: 3 / 32 },\n { dx: -2, dy: 1, weight: 2 / 32 },\n { dx: -1, dy: 1, weight: 4 / 32 },\n { dx: 0, dy: 1, weight: 5 / 32 },\n { dx: 1, dy: 1, weight: 4 / 32 },\n { dx: 2, dy: 1, weight: 2 / 32 },\n { dx: -1, dy: 2, weight: 2 / 32 },\n { dx: 0, dy: 2, weight: 3 / 32 },\n { dx: 1, dy: 2, weight: 2 / 32 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Sierra Lite dithering (divisor 4)\n * Fastest error diffusion algorithm\n * Kernel:\n * X 2\n * 1 1\n */\nexport function sierraLiteDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 2 / 4 },\n { dx: -1, dy: 1, weight: 1 / 4 },\n { dx: 0, dy: 1, weight: 1 / 4 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Atkinson dithering (divisor 8)\n * Created by Bill Atkinson for original Macintosh\n * Kernel:\n * X 1 1\n * 1 1 1\n * 1\n */\nexport function atkinsonDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 1 / 8 },\n { dx: 2, dy: 0, weight: 1 / 8 },\n { dx: -1, dy: 1, weight: 1 / 8 },\n { dx: 0, dy: 1, weight: 1 / 8 },\n { dx: 1, dy: 1, weight: 1 / 8 },\n { dx: 0, dy: 2, weight: 1 / 8 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Stucki dithering (divisor 42)\n * High quality error diffusion\n * Kernel:\n * X 8 4\n * 2 4 8 4 2\n * 1 2 4 2 1\n */\nexport function stuckiDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 8 / 42 },\n { dx: 2, dy: 0, weight: 4 / 42 },\n { dx: -2, dy: 1, weight: 2 / 42 },\n { dx: -1, dy: 1, weight: 4 / 42 },\n { dx: 0, dy: 1, weight: 8 / 42 },\n { dx: 1, dy: 1, weight: 4 / 42 },\n { dx: 2, dy: 1, weight: 2 / 42 },\n { dx: -2, dy: 2, weight: 1 / 42 },\n { dx: -1, dy: 2, weight: 2 / 42 },\n { dx: 0, dy: 2, weight: 4 / 42 },\n { dx: 1, dy: 2, weight: 2 / 42 },\n { dx: 2, dy: 2, weight: 1 / 42 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Jarvis-Judice-Ninke dithering (divisor 48)\n * Highest quality, slowest algorithm\n * Kernel:\n * X 7 5\n * 3 5 7 5 3\n * 1 3 5 3 1\n */\nexport function jarvisJudiceNinkeDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 7 / 48 },\n { dx: 2, dy: 0, weight: 5 / 48 },\n { dx: -2, dy: 1, weight: 3 / 48 },\n { dx: -1, dy: 1, weight: 5 / 48 },\n { dx: 0, dy: 1, weight: 7 / 48 },\n { dx: 1, dy: 1, weight: 5 / 48 },\n { dx: 2, dy: 1, weight: 3 / 48 },\n { dx: -2, dy: 2, weight: 1 / 48 },\n { dx: -1, dy: 2, weight: 3 / 48 },\n { dx: 0, dy: 2, weight: 5 / 48 },\n { dx: 1, dy: 2, weight: 3 / 48 },\n { dx: 2, dy: 2, weight: 1 / 48 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}","import type { ImageBuffer, PaletteImageBuffer } from './types';\nimport { DitherMode } from './enums';\nimport { ColorScheme } from './palettes';\nimport * as algorithms from './algorithms';\n\n/**\n * Apply dithering algorithm to image for e-paper display\n *\n * @param image - Input image buffer (RGBA format)\n * @param colorScheme - Target e-paper color scheme\n * @param mode - Dithering algorithm (default: BURKES)\n * @returns Palette-indexed image buffer\n *\n * @example\n * ```typescript\n * const dithered = ditherImage(imageBuffer, ColorScheme.BWR, DitherMode.FLOYD_STEINBERG);\n * ```\n */\nexport function ditherImage(\n image: ImageBuffer,\n colorScheme: ColorScheme,\n mode: DitherMode = DitherMode.BURKES\n): PaletteImageBuffer {\n switch (mode) {\n case DitherMode.NONE:\n return algorithms.directPaletteMap(image, colorScheme);\n case DitherMode.ORDERED:\n return algorithms.orderedDither(image, colorScheme);\n case DitherMode.FLOYD_STEINBERG:\n return algorithms.floydSteinbergDither(image, colorScheme);\n case DitherMode.ATKINSON:\n return algorithms.atkinsonDither(image, colorScheme);\n case DitherMode.STUCKI:\n return algorithms.stuckiDither(image, colorScheme);\n case DitherMode.SIERRA:\n return algorithms.sierraDither(image, colorScheme);\n case DitherMode.SIERRA_LITE:\n return algorithms.sierraLiteDither(image, colorScheme);\n case DitherMode.JARVIS_JUDICE_NINKE:\n return algorithms.jarvisJudiceNinkeDither(image, colorScheme);\n case DitherMode.BURKES:\n default:\n return algorithms.burkesDither(image, colorScheme);\n }\n}","export { ditherImage } from './core';\nexport { DitherMode } from './enums';\nexport {\n ColorScheme,\n getPalette,\n getColorCount,\n fromValue,\n} from './palettes';\nexport type { RGB, ImageBuffer, PaletteImageBuffer, ColorPalette } from './types';\n\nexport const VERSION = '0.1.0';"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RGB color representation
|
|
3
|
+
*/
|
|
4
|
+
interface RGB {
|
|
5
|
+
r: number;
|
|
6
|
+
g: number;
|
|
7
|
+
b: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Image buffer in RGBA format
|
|
11
|
+
* Compatible with Canvas ImageData and Node.js image libraries
|
|
12
|
+
*/
|
|
13
|
+
interface ImageBuffer {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
data: Uint8ClampedArray;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Palette-indexed image output
|
|
20
|
+
*/
|
|
21
|
+
interface PaletteImageBuffer {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
indices: Uint8Array;
|
|
25
|
+
palette: RGB[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Color palette definition
|
|
29
|
+
*/
|
|
30
|
+
interface ColorPalette {
|
|
31
|
+
readonly colors: Record<string, RGB>;
|
|
32
|
+
readonly accent: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Dithering algorithm modes
|
|
37
|
+
* Values match firmware conventions (0-8)
|
|
38
|
+
*/
|
|
39
|
+
declare enum DitherMode {
|
|
40
|
+
NONE = 0,
|
|
41
|
+
BURKES = 1,
|
|
42
|
+
ORDERED = 2,
|
|
43
|
+
FLOYD_STEINBERG = 3,
|
|
44
|
+
ATKINSON = 4,
|
|
45
|
+
STUCKI = 5,
|
|
46
|
+
SIERRA = 6,
|
|
47
|
+
SIERRA_LITE = 7,
|
|
48
|
+
JARVIS_JUDICE_NINKE = 8
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* E-paper display color schemes
|
|
53
|
+
* Values match firmware conventions (0-5)
|
|
54
|
+
*/
|
|
55
|
+
declare enum ColorScheme {
|
|
56
|
+
MONO = 0,
|
|
57
|
+
BWR = 1,
|
|
58
|
+
BWY = 2,
|
|
59
|
+
BWRY = 3,
|
|
60
|
+
BWGBRY = 4,
|
|
61
|
+
GRAYSCALE_4 = 5
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get color palette for a color scheme
|
|
65
|
+
*/
|
|
66
|
+
declare function getPalette(scheme: ColorScheme): ColorPalette;
|
|
67
|
+
/**
|
|
68
|
+
* Get number of colors in a color scheme
|
|
69
|
+
*/
|
|
70
|
+
declare function getColorCount(scheme: ColorScheme): number;
|
|
71
|
+
/**
|
|
72
|
+
* Create ColorScheme from firmware integer value
|
|
73
|
+
*/
|
|
74
|
+
declare function fromValue(value: number): ColorScheme;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Apply dithering algorithm to image for e-paper display
|
|
78
|
+
*
|
|
79
|
+
* @param image - Input image buffer (RGBA format)
|
|
80
|
+
* @param colorScheme - Target e-paper color scheme
|
|
81
|
+
* @param mode - Dithering algorithm (default: BURKES)
|
|
82
|
+
* @returns Palette-indexed image buffer
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const dithered = ditherImage(imageBuffer, ColorScheme.BWR, DitherMode.FLOYD_STEINBERG);
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare function ditherImage(image: ImageBuffer, colorScheme: ColorScheme, mode?: DitherMode): PaletteImageBuffer;
|
|
90
|
+
|
|
91
|
+
declare const VERSION = "0.1.0";
|
|
92
|
+
|
|
93
|
+
export { type ColorPalette, ColorScheme, DitherMode, type ImageBuffer, type PaletteImageBuffer, type RGB, VERSION, ditherImage, fromValue, getColorCount, getPalette };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RGB color representation
|
|
3
|
+
*/
|
|
4
|
+
interface RGB {
|
|
5
|
+
r: number;
|
|
6
|
+
g: number;
|
|
7
|
+
b: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Image buffer in RGBA format
|
|
11
|
+
* Compatible with Canvas ImageData and Node.js image libraries
|
|
12
|
+
*/
|
|
13
|
+
interface ImageBuffer {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
data: Uint8ClampedArray;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Palette-indexed image output
|
|
20
|
+
*/
|
|
21
|
+
interface PaletteImageBuffer {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
indices: Uint8Array;
|
|
25
|
+
palette: RGB[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Color palette definition
|
|
29
|
+
*/
|
|
30
|
+
interface ColorPalette {
|
|
31
|
+
readonly colors: Record<string, RGB>;
|
|
32
|
+
readonly accent: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Dithering algorithm modes
|
|
37
|
+
* Values match firmware conventions (0-8)
|
|
38
|
+
*/
|
|
39
|
+
declare enum DitherMode {
|
|
40
|
+
NONE = 0,
|
|
41
|
+
BURKES = 1,
|
|
42
|
+
ORDERED = 2,
|
|
43
|
+
FLOYD_STEINBERG = 3,
|
|
44
|
+
ATKINSON = 4,
|
|
45
|
+
STUCKI = 5,
|
|
46
|
+
SIERRA = 6,
|
|
47
|
+
SIERRA_LITE = 7,
|
|
48
|
+
JARVIS_JUDICE_NINKE = 8
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* E-paper display color schemes
|
|
53
|
+
* Values match firmware conventions (0-5)
|
|
54
|
+
*/
|
|
55
|
+
declare enum ColorScheme {
|
|
56
|
+
MONO = 0,
|
|
57
|
+
BWR = 1,
|
|
58
|
+
BWY = 2,
|
|
59
|
+
BWRY = 3,
|
|
60
|
+
BWGBRY = 4,
|
|
61
|
+
GRAYSCALE_4 = 5
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get color palette for a color scheme
|
|
65
|
+
*/
|
|
66
|
+
declare function getPalette(scheme: ColorScheme): ColorPalette;
|
|
67
|
+
/**
|
|
68
|
+
* Get number of colors in a color scheme
|
|
69
|
+
*/
|
|
70
|
+
declare function getColorCount(scheme: ColorScheme): number;
|
|
71
|
+
/**
|
|
72
|
+
* Create ColorScheme from firmware integer value
|
|
73
|
+
*/
|
|
74
|
+
declare function fromValue(value: number): ColorScheme;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Apply dithering algorithm to image for e-paper display
|
|
78
|
+
*
|
|
79
|
+
* @param image - Input image buffer (RGBA format)
|
|
80
|
+
* @param colorScheme - Target e-paper color scheme
|
|
81
|
+
* @param mode - Dithering algorithm (default: BURKES)
|
|
82
|
+
* @returns Palette-indexed image buffer
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const dithered = ditherImage(imageBuffer, ColorScheme.BWR, DitherMode.FLOYD_STEINBERG);
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare function ditherImage(image: ImageBuffer, colorScheme: ColorScheme, mode?: DitherMode): PaletteImageBuffer;
|
|
90
|
+
|
|
91
|
+
declare const VERSION = "0.1.0";
|
|
92
|
+
|
|
93
|
+
export { type ColorPalette, ColorScheme, DitherMode, type ImageBuffer, type PaletteImageBuffer, type RGB, VERSION, ditherImage, fromValue, getColorCount, getPalette };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// src/enums.ts
|
|
2
|
+
var DitherMode = /* @__PURE__ */ ((DitherMode2) => {
|
|
3
|
+
DitherMode2[DitherMode2["NONE"] = 0] = "NONE";
|
|
4
|
+
DitherMode2[DitherMode2["BURKES"] = 1] = "BURKES";
|
|
5
|
+
DitherMode2[DitherMode2["ORDERED"] = 2] = "ORDERED";
|
|
6
|
+
DitherMode2[DitherMode2["FLOYD_STEINBERG"] = 3] = "FLOYD_STEINBERG";
|
|
7
|
+
DitherMode2[DitherMode2["ATKINSON"] = 4] = "ATKINSON";
|
|
8
|
+
DitherMode2[DitherMode2["STUCKI"] = 5] = "STUCKI";
|
|
9
|
+
DitherMode2[DitherMode2["SIERRA"] = 6] = "SIERRA";
|
|
10
|
+
DitherMode2[DitherMode2["SIERRA_LITE"] = 7] = "SIERRA_LITE";
|
|
11
|
+
DitherMode2[DitherMode2["JARVIS_JUDICE_NINKE"] = 8] = "JARVIS_JUDICE_NINKE";
|
|
12
|
+
return DitherMode2;
|
|
13
|
+
})(DitherMode || {});
|
|
14
|
+
|
|
15
|
+
// src/palettes.ts
|
|
16
|
+
var ColorScheme = /* @__PURE__ */ ((ColorScheme3) => {
|
|
17
|
+
ColorScheme3[ColorScheme3["MONO"] = 0] = "MONO";
|
|
18
|
+
ColorScheme3[ColorScheme3["BWR"] = 1] = "BWR";
|
|
19
|
+
ColorScheme3[ColorScheme3["BWY"] = 2] = "BWY";
|
|
20
|
+
ColorScheme3[ColorScheme3["BWRY"] = 3] = "BWRY";
|
|
21
|
+
ColorScheme3[ColorScheme3["BWGBRY"] = 4] = "BWGBRY";
|
|
22
|
+
ColorScheme3[ColorScheme3["GRAYSCALE_4"] = 5] = "GRAYSCALE_4";
|
|
23
|
+
return ColorScheme3;
|
|
24
|
+
})(ColorScheme || {});
|
|
25
|
+
var PALETTES = {
|
|
26
|
+
[0 /* MONO */]: {
|
|
27
|
+
colors: {
|
|
28
|
+
black: { r: 0, g: 0, b: 0 },
|
|
29
|
+
white: { r: 255, g: 255, b: 255 }
|
|
30
|
+
},
|
|
31
|
+
accent: "black"
|
|
32
|
+
},
|
|
33
|
+
[1 /* BWR */]: {
|
|
34
|
+
colors: {
|
|
35
|
+
black: { r: 0, g: 0, b: 0 },
|
|
36
|
+
white: { r: 255, g: 255, b: 255 },
|
|
37
|
+
red: { r: 255, g: 0, b: 0 }
|
|
38
|
+
},
|
|
39
|
+
accent: "red"
|
|
40
|
+
},
|
|
41
|
+
[2 /* BWY */]: {
|
|
42
|
+
colors: {
|
|
43
|
+
black: { r: 0, g: 0, b: 0 },
|
|
44
|
+
white: { r: 255, g: 255, b: 255 },
|
|
45
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
46
|
+
},
|
|
47
|
+
accent: "yellow"
|
|
48
|
+
},
|
|
49
|
+
[3 /* BWRY */]: {
|
|
50
|
+
colors: {
|
|
51
|
+
black: { r: 0, g: 0, b: 0 },
|
|
52
|
+
white: { r: 255, g: 255, b: 255 },
|
|
53
|
+
red: { r: 255, g: 0, b: 0 },
|
|
54
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
55
|
+
},
|
|
56
|
+
accent: "red"
|
|
57
|
+
},
|
|
58
|
+
[4 /* BWGBRY */]: {
|
|
59
|
+
colors: {
|
|
60
|
+
black: { r: 0, g: 0, b: 0 },
|
|
61
|
+
white: { r: 255, g: 255, b: 255 },
|
|
62
|
+
green: { r: 0, g: 255, b: 0 },
|
|
63
|
+
blue: { r: 0, g: 0, b: 255 },
|
|
64
|
+
red: { r: 255, g: 0, b: 0 },
|
|
65
|
+
yellow: { r: 255, g: 255, b: 0 }
|
|
66
|
+
},
|
|
67
|
+
accent: "red"
|
|
68
|
+
},
|
|
69
|
+
[5 /* GRAYSCALE_4 */]: {
|
|
70
|
+
colors: {
|
|
71
|
+
black: { r: 0, g: 0, b: 0 },
|
|
72
|
+
gray1: { r: 85, g: 85, b: 85 },
|
|
73
|
+
gray2: { r: 170, g: 170, b: 170 },
|
|
74
|
+
white: { r: 255, g: 255, b: 255 }
|
|
75
|
+
},
|
|
76
|
+
accent: "black"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
function getPalette(scheme) {
|
|
80
|
+
return PALETTES[scheme];
|
|
81
|
+
}
|
|
82
|
+
function getColorCount(scheme) {
|
|
83
|
+
return Object.keys(PALETTES[scheme].colors).length;
|
|
84
|
+
}
|
|
85
|
+
function fromValue(value) {
|
|
86
|
+
if (value < 0 || value > 5) {
|
|
87
|
+
throw new Error(`Invalid color scheme value: ${value}`);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/algorithms.ts
|
|
93
|
+
function getPaletteColors(scheme) {
|
|
94
|
+
const palette = getPalette(scheme);
|
|
95
|
+
return Object.values(palette.colors);
|
|
96
|
+
}
|
|
97
|
+
function findClosestPaletteColor(rgb, palette) {
|
|
98
|
+
let minDistance = Infinity;
|
|
99
|
+
let closestIdx = 0;
|
|
100
|
+
for (let i = 0; i < palette.length; i++) {
|
|
101
|
+
const pal = palette[i];
|
|
102
|
+
const distance = (rgb.r - pal.r) ** 2 + (rgb.g - pal.g) ** 2 + (rgb.b - pal.b) ** 2;
|
|
103
|
+
if (distance < minDistance) {
|
|
104
|
+
minDistance = distance;
|
|
105
|
+
closestIdx = i;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return closestIdx;
|
|
109
|
+
}
|
|
110
|
+
function errorDiffusionDither(image, colorScheme, kernel) {
|
|
111
|
+
const { width, height } = image;
|
|
112
|
+
const palette = getPaletteColors(colorScheme);
|
|
113
|
+
const pixels = new Float32Array(width * height * 3);
|
|
114
|
+
for (let i = 0; i < width * height; i++) {
|
|
115
|
+
pixels[i * 3] = image.data[i * 4];
|
|
116
|
+
pixels[i * 3 + 1] = image.data[i * 4 + 1];
|
|
117
|
+
pixels[i * 3 + 2] = image.data[i * 4 + 2];
|
|
118
|
+
}
|
|
119
|
+
const indices = new Uint8Array(width * height);
|
|
120
|
+
for (let y = 0; y < height; y++) {
|
|
121
|
+
for (let x = 0; x < width; x++) {
|
|
122
|
+
const idx = y * width + x;
|
|
123
|
+
const pixelIdx = idx * 3;
|
|
124
|
+
const oldPixel = {
|
|
125
|
+
r: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx]))),
|
|
126
|
+
g: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 1]))),
|
|
127
|
+
b: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 2])))
|
|
128
|
+
};
|
|
129
|
+
const newIdx = findClosestPaletteColor(oldPixel, palette);
|
|
130
|
+
const newPixel = palette[newIdx];
|
|
131
|
+
indices[idx] = newIdx;
|
|
132
|
+
const errorR = oldPixel.r - newPixel.r;
|
|
133
|
+
const errorG = oldPixel.g - newPixel.g;
|
|
134
|
+
const errorB = oldPixel.b - newPixel.b;
|
|
135
|
+
for (const { dx, dy, weight } of kernel) {
|
|
136
|
+
const nx = x + dx;
|
|
137
|
+
const ny = y + dy;
|
|
138
|
+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
139
|
+
const neighborIdx = (ny * width + nx) * 3;
|
|
140
|
+
pixels[neighborIdx] += errorR * weight;
|
|
141
|
+
pixels[neighborIdx + 1] += errorG * weight;
|
|
142
|
+
pixels[neighborIdx + 2] += errorB * weight;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { width, height, indices, palette };
|
|
148
|
+
}
|
|
149
|
+
function directPaletteMap(image, colorScheme) {
|
|
150
|
+
const { width, height } = image;
|
|
151
|
+
const palette = getPaletteColors(colorScheme);
|
|
152
|
+
const indices = new Uint8Array(width * height);
|
|
153
|
+
for (let i = 0; i < width * height; i++) {
|
|
154
|
+
const rgb = {
|
|
155
|
+
r: image.data[i * 4],
|
|
156
|
+
g: image.data[i * 4 + 1],
|
|
157
|
+
b: image.data[i * 4 + 2]
|
|
158
|
+
};
|
|
159
|
+
indices[i] = findClosestPaletteColor(rgb, palette);
|
|
160
|
+
}
|
|
161
|
+
return { width, height, indices, palette };
|
|
162
|
+
}
|
|
163
|
+
function orderedDither(image, colorScheme) {
|
|
164
|
+
const bayerMatrix = new Uint8Array([
|
|
165
|
+
0,
|
|
166
|
+
8,
|
|
167
|
+
2,
|
|
168
|
+
10,
|
|
169
|
+
12,
|
|
170
|
+
4,
|
|
171
|
+
14,
|
|
172
|
+
6,
|
|
173
|
+
3,
|
|
174
|
+
11,
|
|
175
|
+
1,
|
|
176
|
+
9,
|
|
177
|
+
15,
|
|
178
|
+
7,
|
|
179
|
+
13,
|
|
180
|
+
5
|
|
181
|
+
]).map((v) => v * 16);
|
|
182
|
+
const { width, height } = image;
|
|
183
|
+
const palette = getPaletteColors(colorScheme);
|
|
184
|
+
const indices = new Uint8Array(width * height);
|
|
185
|
+
for (let y = 0; y < height; y++) {
|
|
186
|
+
for (let x = 0; x < width; x++) {
|
|
187
|
+
const idx = y * width + x;
|
|
188
|
+
const dataIdx = idx * 4;
|
|
189
|
+
const threshold = bayerMatrix[y % 4 * 4 + x % 4];
|
|
190
|
+
const rgb = {
|
|
191
|
+
r: Math.min(255, image.data[dataIdx] + threshold),
|
|
192
|
+
g: Math.min(255, image.data[dataIdx + 1] + threshold),
|
|
193
|
+
b: Math.min(255, image.data[dataIdx + 2] + threshold)
|
|
194
|
+
};
|
|
195
|
+
indices[idx] = findClosestPaletteColor(rgb, palette);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return { width, height, indices, palette };
|
|
199
|
+
}
|
|
200
|
+
function burkesDither(image, colorScheme) {
|
|
201
|
+
const kernel = [
|
|
202
|
+
{ dx: 1, dy: 0, weight: 32 / 200 },
|
|
203
|
+
{ dx: 2, dy: 0, weight: 12 / 200 },
|
|
204
|
+
{ dx: -2, dy: 1, weight: 5 / 200 },
|
|
205
|
+
{ dx: -1, dy: 1, weight: 12 / 200 },
|
|
206
|
+
{ dx: 0, dy: 1, weight: 26 / 200 },
|
|
207
|
+
{ dx: 1, dy: 1, weight: 12 / 200 },
|
|
208
|
+
{ dx: 2, dy: 1, weight: 5 / 200 }
|
|
209
|
+
];
|
|
210
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
211
|
+
}
|
|
212
|
+
function floydSteinbergDither(image, colorScheme) {
|
|
213
|
+
const kernel = [
|
|
214
|
+
{ dx: 1, dy: 0, weight: 7 / 16 },
|
|
215
|
+
{ dx: -1, dy: 1, weight: 3 / 16 },
|
|
216
|
+
{ dx: 0, dy: 1, weight: 5 / 16 },
|
|
217
|
+
{ dx: 1, dy: 1, weight: 1 / 16 }
|
|
218
|
+
];
|
|
219
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
220
|
+
}
|
|
221
|
+
function sierraDither(image, colorScheme) {
|
|
222
|
+
const kernel = [
|
|
223
|
+
{ dx: 1, dy: 0, weight: 5 / 32 },
|
|
224
|
+
{ dx: 2, dy: 0, weight: 3 / 32 },
|
|
225
|
+
{ dx: -2, dy: 1, weight: 2 / 32 },
|
|
226
|
+
{ dx: -1, dy: 1, weight: 4 / 32 },
|
|
227
|
+
{ dx: 0, dy: 1, weight: 5 / 32 },
|
|
228
|
+
{ dx: 1, dy: 1, weight: 4 / 32 },
|
|
229
|
+
{ dx: 2, dy: 1, weight: 2 / 32 },
|
|
230
|
+
{ dx: -1, dy: 2, weight: 2 / 32 },
|
|
231
|
+
{ dx: 0, dy: 2, weight: 3 / 32 },
|
|
232
|
+
{ dx: 1, dy: 2, weight: 2 / 32 }
|
|
233
|
+
];
|
|
234
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
235
|
+
}
|
|
236
|
+
function sierraLiteDither(image, colorScheme) {
|
|
237
|
+
const kernel = [
|
|
238
|
+
{ dx: 1, dy: 0, weight: 2 / 4 },
|
|
239
|
+
{ dx: -1, dy: 1, weight: 1 / 4 },
|
|
240
|
+
{ dx: 0, dy: 1, weight: 1 / 4 }
|
|
241
|
+
];
|
|
242
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
243
|
+
}
|
|
244
|
+
function atkinsonDither(image, colorScheme) {
|
|
245
|
+
const kernel = [
|
|
246
|
+
{ dx: 1, dy: 0, weight: 1 / 8 },
|
|
247
|
+
{ dx: 2, dy: 0, weight: 1 / 8 },
|
|
248
|
+
{ dx: -1, dy: 1, weight: 1 / 8 },
|
|
249
|
+
{ dx: 0, dy: 1, weight: 1 / 8 },
|
|
250
|
+
{ dx: 1, dy: 1, weight: 1 / 8 },
|
|
251
|
+
{ dx: 0, dy: 2, weight: 1 / 8 }
|
|
252
|
+
];
|
|
253
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
254
|
+
}
|
|
255
|
+
function stuckiDither(image, colorScheme) {
|
|
256
|
+
const kernel = [
|
|
257
|
+
{ dx: 1, dy: 0, weight: 8 / 42 },
|
|
258
|
+
{ dx: 2, dy: 0, weight: 4 / 42 },
|
|
259
|
+
{ dx: -2, dy: 1, weight: 2 / 42 },
|
|
260
|
+
{ dx: -1, dy: 1, weight: 4 / 42 },
|
|
261
|
+
{ dx: 0, dy: 1, weight: 8 / 42 },
|
|
262
|
+
{ dx: 1, dy: 1, weight: 4 / 42 },
|
|
263
|
+
{ dx: 2, dy: 1, weight: 2 / 42 },
|
|
264
|
+
{ dx: -2, dy: 2, weight: 1 / 42 },
|
|
265
|
+
{ dx: -1, dy: 2, weight: 2 / 42 },
|
|
266
|
+
{ dx: 0, dy: 2, weight: 4 / 42 },
|
|
267
|
+
{ dx: 1, dy: 2, weight: 2 / 42 },
|
|
268
|
+
{ dx: 2, dy: 2, weight: 1 / 42 }
|
|
269
|
+
];
|
|
270
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
271
|
+
}
|
|
272
|
+
function jarvisJudiceNinkeDither(image, colorScheme) {
|
|
273
|
+
const kernel = [
|
|
274
|
+
{ dx: 1, dy: 0, weight: 7 / 48 },
|
|
275
|
+
{ dx: 2, dy: 0, weight: 5 / 48 },
|
|
276
|
+
{ dx: -2, dy: 1, weight: 3 / 48 },
|
|
277
|
+
{ dx: -1, dy: 1, weight: 5 / 48 },
|
|
278
|
+
{ dx: 0, dy: 1, weight: 7 / 48 },
|
|
279
|
+
{ dx: 1, dy: 1, weight: 5 / 48 },
|
|
280
|
+
{ dx: 2, dy: 1, weight: 3 / 48 },
|
|
281
|
+
{ dx: -2, dy: 2, weight: 1 / 48 },
|
|
282
|
+
{ dx: -1, dy: 2, weight: 3 / 48 },
|
|
283
|
+
{ dx: 0, dy: 2, weight: 5 / 48 },
|
|
284
|
+
{ dx: 1, dy: 2, weight: 3 / 48 },
|
|
285
|
+
{ dx: 2, dy: 2, weight: 1 / 48 }
|
|
286
|
+
];
|
|
287
|
+
return errorDiffusionDither(image, colorScheme, kernel);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/core.ts
|
|
291
|
+
function ditherImage(image, colorScheme, mode = 1 /* BURKES */) {
|
|
292
|
+
switch (mode) {
|
|
293
|
+
case 0 /* NONE */:
|
|
294
|
+
return directPaletteMap(image, colorScheme);
|
|
295
|
+
case 2 /* ORDERED */:
|
|
296
|
+
return orderedDither(image, colorScheme);
|
|
297
|
+
case 3 /* FLOYD_STEINBERG */:
|
|
298
|
+
return floydSteinbergDither(image, colorScheme);
|
|
299
|
+
case 4 /* ATKINSON */:
|
|
300
|
+
return atkinsonDither(image, colorScheme);
|
|
301
|
+
case 5 /* STUCKI */:
|
|
302
|
+
return stuckiDither(image, colorScheme);
|
|
303
|
+
case 6 /* SIERRA */:
|
|
304
|
+
return sierraDither(image, colorScheme);
|
|
305
|
+
case 7 /* SIERRA_LITE */:
|
|
306
|
+
return sierraLiteDither(image, colorScheme);
|
|
307
|
+
case 8 /* JARVIS_JUDICE_NINKE */:
|
|
308
|
+
return jarvisJudiceNinkeDither(image, colorScheme);
|
|
309
|
+
case 1 /* BURKES */:
|
|
310
|
+
default:
|
|
311
|
+
return burkesDither(image, colorScheme);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/index.ts
|
|
316
|
+
var VERSION = "0.1.0";
|
|
317
|
+
|
|
318
|
+
export { ColorScheme, DitherMode, VERSION, ditherImage, fromValue, getColorCount, getPalette };
|
|
319
|
+
//# sourceMappingURL=index.js.map
|
|
320
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/enums.ts","../src/palettes.ts","../src/algorithms.ts","../src/core.ts","../src/index.ts"],"names":["DitherMode","ColorScheme"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Dithering algorithm modes\n * Values match firmware conventions (0-8)\n */\nexport enum DitherMode {\n NONE = 0,\n BURKES = 1,\n ORDERED = 2,\n FLOYD_STEINBERG = 3,\n ATKINSON = 4,\n STUCKI = 5,\n SIERRA = 6,\n SIERRA_LITE = 7,\n JARVIS_JUDICE_NINKE = 8,\n}","import type { RGB, ColorPalette } from './types';\n\n/**\n * E-paper display color schemes\n * Values match firmware conventions (0-5)\n */\nexport enum ColorScheme {\n MONO = 0,\n BWR = 1,\n BWY = 2,\n BWRY = 3,\n BWGBRY = 4,\n GRAYSCALE_4 = 5,\n}\n\nconst PALETTES: Record<ColorScheme, ColorPalette> = {\n [ColorScheme.MONO]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n },\n accent: 'black',\n },\n [ColorScheme.BWR]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.BWY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'yellow',\n },\n [ColorScheme.BWRY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.BWGBRY]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n white: { r: 255, g: 255, b: 255 },\n green: { r: 0, g: 255, b: 0 },\n blue: { r: 0, g: 0, b: 255 },\n red: { r: 255, g: 0, b: 0 },\n yellow: { r: 255, g: 255, b: 0 },\n },\n accent: 'red',\n },\n [ColorScheme.GRAYSCALE_4]: {\n colors: {\n black: { r: 0, g: 0, b: 0 },\n gray1: { r: 85, g: 85, b: 85 },\n gray2: { r: 170, g: 170, b: 170 },\n white: { r: 255, g: 255, b: 255 },\n },\n accent: 'black',\n },\n};\n\n/**\n * Get color palette for a color scheme\n */\nexport function getPalette(scheme: ColorScheme): ColorPalette {\n return PALETTES[scheme];\n}\n\n/**\n * Get number of colors in a color scheme\n */\nexport function getColorCount(scheme: ColorScheme): number {\n return Object.keys(PALETTES[scheme].colors).length;\n}\n\n/**\n * Create ColorScheme from firmware integer value\n */\nexport function fromValue(value: number): ColorScheme {\n if (value < 0 || value > 5) {\n throw new Error(`Invalid color scheme value: ${value}`);\n }\n return value as ColorScheme;\n}","import type { RGB, ImageBuffer, PaletteImageBuffer } from './types';\nimport { ColorScheme, getPalette } from './palettes';\n\n/**\n * Get RGB palette colors from color scheme\n */\nexport function getPaletteColors(scheme: ColorScheme): RGB[] {\n const palette = getPalette(scheme);\n return Object.values(palette.colors);\n}\n\n/**\n * Find closest palette color using Euclidean distance\n */\nexport function findClosestPaletteColor(rgb: RGB, palette: RGB[]): number {\n let minDistance = Infinity;\n let closestIdx = 0;\n\n for (let i = 0; i < palette.length; i++) {\n const pal = palette[i];\n // Euclidean distance in RGB space\n const distance =\n (rgb.r - pal.r) ** 2 + (rgb.g - pal.g) ** 2 + (rgb.b - pal.b) ** 2;\n\n if (distance < minDistance) {\n minDistance = distance;\n closestIdx = i;\n }\n }\n\n return closestIdx;\n}\n\n/**\n * Error diffusion kernel entry\n */\ninterface ErrorKernel {\n dx: number;\n dy: number;\n weight: number;\n}\n\n/**\n * Apply error diffusion dithering with specified kernel\n */\nfunction errorDiffusionDither(\n image: ImageBuffer,\n colorScheme: ColorScheme,\n kernel: ErrorKernel[]\n): PaletteImageBuffer {\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n\n // Convert RGBA to RGB float array for error accumulation\n const pixels = new Float32Array(width * height * 3);\n for (let i = 0; i < width * height; i++) {\n pixels[i * 3] = image.data[i * 4]; // R\n pixels[i * 3 + 1] = image.data[i * 4 + 1]; // G\n pixels[i * 3 + 2] = image.data[i * 4 + 2]; // B\n }\n\n const indices = new Uint8Array(width * height);\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const pixelIdx = idx * 3;\n\n // Get old pixel (clamp to 0-255)\n const oldPixel: RGB = {\n r: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx]))),\n g: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 1]))),\n b: Math.max(0, Math.min(255, Math.round(pixels[pixelIdx + 2]))),\n };\n\n // Find closest palette color\n const newIdx = findClosestPaletteColor(oldPixel, palette);\n const newPixel = palette[newIdx];\n indices[idx] = newIdx;\n\n // Calculate quantization error\n const errorR = oldPixel.r - newPixel.r;\n const errorG = oldPixel.g - newPixel.g;\n const errorB = oldPixel.b - newPixel.b;\n\n // Distribute error to neighbors using kernel\n for (const { dx, dy, weight } of kernel) {\n const nx = x + dx;\n const ny = y + dy;\n\n if (nx >= 0 && nx < width && ny >= 0 && ny < height) {\n const neighborIdx = (ny * width + nx) * 3;\n pixels[neighborIdx] += errorR * weight;\n pixels[neighborIdx + 1] += errorG * weight;\n pixels[neighborIdx + 2] += errorB * weight;\n }\n }\n }\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Direct palette mapping without dithering (NONE)\n */\nexport function directPaletteMap(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n const indices = new Uint8Array(width * height);\n\n for (let i = 0; i < width * height; i++) {\n const rgb: RGB = {\n r: image.data[i * 4],\n g: image.data[i * 4 + 1],\n b: image.data[i * 4 + 2],\n };\n indices[i] = findClosestPaletteColor(rgb, palette);\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Ordered dithering using 4x4 Bayer matrix\n */\nexport function orderedDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const bayerMatrix = new Uint8Array([\n 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5,\n ]).map((v) => v * 16);\n\n const { width, height } = image;\n const palette = getPaletteColors(colorScheme);\n const indices = new Uint8Array(width * height);\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const dataIdx = idx * 4;\n\n // Get threshold from Bayer matrix\n const threshold = bayerMatrix[(y % 4) * 4 + (x % 4)];\n\n // Add threshold noise\n const rgb: RGB = {\n r: Math.min(255, image.data[dataIdx] + threshold),\n g: Math.min(255, image.data[dataIdx + 1] + threshold),\n b: Math.min(255, image.data[dataIdx + 2] + threshold),\n };\n\n indices[idx] = findClosestPaletteColor(rgb, palette);\n }\n }\n\n return { width, height, indices, palette };\n}\n\n/**\n * Burkes dithering (divisor 200)\n * Kernel:\n * X 32 12\n * 5 12 26 12 5\n */\nexport function burkesDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 32 / 200 },\n { dx: 2, dy: 0, weight: 12 / 200 },\n { dx: -2, dy: 1, weight: 5 / 200 },\n { dx: -1, dy: 1, weight: 12 / 200 },\n { dx: 0, dy: 1, weight: 26 / 200 },\n { dx: 1, dy: 1, weight: 12 / 200 },\n { dx: 2, dy: 1, weight: 5 / 200 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Floyd-Steinberg dithering (divisor 16)\n * Most popular error diffusion algorithm\n * Kernel:\n * X 7\n * 3 5 1\n */\nexport function floydSteinbergDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 7 / 16 },\n { dx: -1, dy: 1, weight: 3 / 16 },\n { dx: 0, dy: 1, weight: 5 / 16 },\n { dx: 1, dy: 1, weight: 1 / 16 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Sierra dithering (divisor 32)\n * Kernel:\n * X 5 3\n * 2 4 5 4 2\n * 2 3 2\n */\nexport function sierraDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 5 / 32 },\n { dx: 2, dy: 0, weight: 3 / 32 },\n { dx: -2, dy: 1, weight: 2 / 32 },\n { dx: -1, dy: 1, weight: 4 / 32 },\n { dx: 0, dy: 1, weight: 5 / 32 },\n { dx: 1, dy: 1, weight: 4 / 32 },\n { dx: 2, dy: 1, weight: 2 / 32 },\n { dx: -1, dy: 2, weight: 2 / 32 },\n { dx: 0, dy: 2, weight: 3 / 32 },\n { dx: 1, dy: 2, weight: 2 / 32 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Sierra Lite dithering (divisor 4)\n * Fastest error diffusion algorithm\n * Kernel:\n * X 2\n * 1 1\n */\nexport function sierraLiteDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 2 / 4 },\n { dx: -1, dy: 1, weight: 1 / 4 },\n { dx: 0, dy: 1, weight: 1 / 4 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Atkinson dithering (divisor 8)\n * Created by Bill Atkinson for original Macintosh\n * Kernel:\n * X 1 1\n * 1 1 1\n * 1\n */\nexport function atkinsonDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 1 / 8 },\n { dx: 2, dy: 0, weight: 1 / 8 },\n { dx: -1, dy: 1, weight: 1 / 8 },\n { dx: 0, dy: 1, weight: 1 / 8 },\n { dx: 1, dy: 1, weight: 1 / 8 },\n { dx: 0, dy: 2, weight: 1 / 8 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Stucki dithering (divisor 42)\n * High quality error diffusion\n * Kernel:\n * X 8 4\n * 2 4 8 4 2\n * 1 2 4 2 1\n */\nexport function stuckiDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 8 / 42 },\n { dx: 2, dy: 0, weight: 4 / 42 },\n { dx: -2, dy: 1, weight: 2 / 42 },\n { dx: -1, dy: 1, weight: 4 / 42 },\n { dx: 0, dy: 1, weight: 8 / 42 },\n { dx: 1, dy: 1, weight: 4 / 42 },\n { dx: 2, dy: 1, weight: 2 / 42 },\n { dx: -2, dy: 2, weight: 1 / 42 },\n { dx: -1, dy: 2, weight: 2 / 42 },\n { dx: 0, dy: 2, weight: 4 / 42 },\n { dx: 1, dy: 2, weight: 2 / 42 },\n { dx: 2, dy: 2, weight: 1 / 42 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}\n\n/**\n * Jarvis-Judice-Ninke dithering (divisor 48)\n * Highest quality, slowest algorithm\n * Kernel:\n * X 7 5\n * 3 5 7 5 3\n * 1 3 5 3 1\n */\nexport function jarvisJudiceNinkeDither(\n image: ImageBuffer,\n colorScheme: ColorScheme\n): PaletteImageBuffer {\n const kernel: ErrorKernel[] = [\n { dx: 1, dy: 0, weight: 7 / 48 },\n { dx: 2, dy: 0, weight: 5 / 48 },\n { dx: -2, dy: 1, weight: 3 / 48 },\n { dx: -1, dy: 1, weight: 5 / 48 },\n { dx: 0, dy: 1, weight: 7 / 48 },\n { dx: 1, dy: 1, weight: 5 / 48 },\n { dx: 2, dy: 1, weight: 3 / 48 },\n { dx: -2, dy: 2, weight: 1 / 48 },\n { dx: -1, dy: 2, weight: 3 / 48 },\n { dx: 0, dy: 2, weight: 5 / 48 },\n { dx: 1, dy: 2, weight: 3 / 48 },\n { dx: 2, dy: 2, weight: 1 / 48 },\n ];\n\n return errorDiffusionDither(image, colorScheme, kernel);\n}","import type { ImageBuffer, PaletteImageBuffer } from './types';\nimport { DitherMode } from './enums';\nimport { ColorScheme } from './palettes';\nimport * as algorithms from './algorithms';\n\n/**\n * Apply dithering algorithm to image for e-paper display\n *\n * @param image - Input image buffer (RGBA format)\n * @param colorScheme - Target e-paper color scheme\n * @param mode - Dithering algorithm (default: BURKES)\n * @returns Palette-indexed image buffer\n *\n * @example\n * ```typescript\n * const dithered = ditherImage(imageBuffer, ColorScheme.BWR, DitherMode.FLOYD_STEINBERG);\n * ```\n */\nexport function ditherImage(\n image: ImageBuffer,\n colorScheme: ColorScheme,\n mode: DitherMode = DitherMode.BURKES\n): PaletteImageBuffer {\n switch (mode) {\n case DitherMode.NONE:\n return algorithms.directPaletteMap(image, colorScheme);\n case DitherMode.ORDERED:\n return algorithms.orderedDither(image, colorScheme);\n case DitherMode.FLOYD_STEINBERG:\n return algorithms.floydSteinbergDither(image, colorScheme);\n case DitherMode.ATKINSON:\n return algorithms.atkinsonDither(image, colorScheme);\n case DitherMode.STUCKI:\n return algorithms.stuckiDither(image, colorScheme);\n case DitherMode.SIERRA:\n return algorithms.sierraDither(image, colorScheme);\n case DitherMode.SIERRA_LITE:\n return algorithms.sierraLiteDither(image, colorScheme);\n case DitherMode.JARVIS_JUDICE_NINKE:\n return algorithms.jarvisJudiceNinkeDither(image, colorScheme);\n case DitherMode.BURKES:\n default:\n return algorithms.burkesDither(image, colorScheme);\n }\n}","export { ditherImage } from './core';\nexport { DitherMode } from './enums';\nexport {\n ColorScheme,\n getPalette,\n getColorCount,\n fromValue,\n} from './palettes';\nexport type { RGB, ImageBuffer, PaletteImageBuffer, ColorPalette } from './types';\n\nexport const VERSION = '0.1.0';"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opendisplay/epaper-dithering",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Dithering algorithms for e-paper/e-ink displays (JavaScript/TypeScript)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"test:coverage": "vitest run --coverage",
|
|
24
|
+
"type-check": "tsc --noEmit",
|
|
25
|
+
"lint": "eslint src/ tests/",
|
|
26
|
+
"lint:fix": "eslint src/ tests/ --fix",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"epaper",
|
|
31
|
+
"eink",
|
|
32
|
+
"e-paper",
|
|
33
|
+
"e-ink",
|
|
34
|
+
"dithering",
|
|
35
|
+
"image-processing",
|
|
36
|
+
"display",
|
|
37
|
+
"browser",
|
|
38
|
+
"nodejs",
|
|
39
|
+
"typescript"
|
|
40
|
+
],
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
44
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
45
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
46
|
+
"eslint": "^9.0.0",
|
|
47
|
+
"tsup": "^8.0.0",
|
|
48
|
+
"typescript": "^5.6.0",
|
|
49
|
+
"vitest": "^2.0.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/OpenDisplay-org/epaper-dithering.git",
|
|
58
|
+
"directory": "packages/javascript"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/OpenDisplay-org/epaper-dithering#readme"
|
|
61
|
+
}
|