-
-
Notifications
You must be signed in to change notification settings - Fork 150
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pixel-dither): add ditherWithKernel() & various presets
- Loading branch information
1 parent
8a7ec9c
commit e2ce82a
Showing
15 changed files
with
420 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<!-- This file is generated - DO NOT EDIT! --> | ||
|
||
# ![pixel-dither](https://media.thi.ng/umbrella/banners/thing-pixel-dither.svg?cd0ecd41) | ||
|
||
[![npm version](https://img.shields.io/npm/v/@thi.ng/pixel-dither.svg)](https://www.npmjs.com/package/@thi.ng/pixel-dither) | ||
![npm downloads](https://img.shields.io/npm/dm/@thi.ng/pixel-dither.svg) | ||
[![Twitter Follow](https://img.shields.io/twitter/follow/thing_umbrella.svg?style=flat-square&label=twitter)](https://twitter.com/thing_umbrella) | ||
|
||
This project is part of the | ||
[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo. | ||
|
||
- [About](#about) | ||
- [Status](#status) | ||
- [Installation](#installation) | ||
- [Dependencies](#dependencies) | ||
- [API](#api) | ||
- [Authors](#authors) | ||
- [License](#license) | ||
|
||
## About | ||
|
||
Extensible image dithering w/ various algorithm presets. | ||
|
||
The package provides the following dithering algorithm presets (but can also be | ||
very easily extended via definition of custom kernels): | ||
|
||
- Atkinson | ||
- Bayes (ordered dithering w/ customizable sizes & levels) | ||
- Burkes | ||
- Diffusion (1D row/column, 2D) | ||
- Floyd-Steinberg | ||
- Jarvis-Judice-Ninke | ||
- Sierra 2-row | ||
- Stucki | ||
- Threshold | ||
|
||
### Status | ||
|
||
**ALPHA** - bleeding edge / work-in-progress | ||
|
||
[Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bpixel-dither%5D+in%3Atitle) | ||
|
||
## Installation | ||
|
||
```bash | ||
yarn add @thi.ng/pixel-dither | ||
``` | ||
|
||
ES module import: | ||
|
||
```html | ||
<script type="module" src="https://cdn.skypack.dev/@thi.ng/pixel-dither"></script> | ||
``` | ||
|
||
[Skypack documentation](https://docs.skypack.dev/) | ||
|
||
For NodeJS (v14.6+): | ||
|
||
```text | ||
node --experimental-specifier-resolution=node --experimental-repl-await | ||
> const pixelDither = await import("@thi.ng/pixel-dither"); | ||
``` | ||
|
||
## Dependencies | ||
|
||
- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks) | ||
- [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math) | ||
- [@thi.ng/pixel](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel) | ||
|
||
## API | ||
|
||
[Generated API docs](https://docs.thi.ng/umbrella/pixel-dither/) | ||
|
||
```ts | ||
import { packedBufferFromImage, GRAY8 } from "@thi.ng/pixel"; | ||
import { ditherWithKernel, ATKINSON } from "@thi.ng/pixel-dither"; | ||
|
||
const img = packedBufferFromImage("foo.jpg"); | ||
|
||
// apply dithering to all channels in given pixel buffer | ||
ditherWithKernel(img, ATKINSON); | ||
|
||
// first convert to 8-bit gray before dithering | ||
ditherWithKernel(img.as(GRAY8), ATKINSON); | ||
|
||
// apply dithering to select channels only | ||
// use custom threshold & error spillage/bleed factor | ||
ditherWithKernel(img, ATKINSON, { channels: [1, 2, 3], threshold: 0.66, bleed: 0.75 }); | ||
``` | ||
|
||
TODO | ||
|
||
## Authors | ||
|
||
Karsten Schmidt | ||
|
||
If this project contributes to an academic publication, please cite it as: | ||
|
||
```bibtex | ||
@misc{thing-pixel-dither, | ||
title = "@thi.ng/pixel-dither", | ||
author = "Karsten Schmidt", | ||
note = "https://thi.ng/pixel-dither", | ||
year = 2021 | ||
} | ||
``` | ||
|
||
## License | ||
|
||
© 2021 Karsten Schmidt // Apache Software License 2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { DitherKernelFactory } from "./api"; | ||
|
||
/** | ||
* (Bill) Atkinson dither kernel | ||
* | ||
* @remarks | ||
* References: | ||
* - https://beyondloom.com/blog/dither.html | ||
* - https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html | ||
*/ | ||
export const ATKINSON: DitherKernelFactory = () => ({ | ||
ox: [1, 2, -1, 0, 1, 0], | ||
oy: [0, 0, 1, 1, 1, 2], | ||
weights: [1, 1, 1, 1, 1, 1], | ||
shift: 3, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { DitherKernelFactory } from "./api"; | ||
|
||
/** | ||
* Burkes dither kernel (similar/improved version of {@link STUCKI}). | ||
* | ||
* @remarks | ||
* References: | ||
* - https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html | ||
*/ | ||
export const BURKES: DitherKernelFactory = () => ({ | ||
ox: [1, 2, -2, -1, 0, 1, 2], | ||
oy: [0, 0, 1, 1, 1, 1, 1], | ||
weights: [8, 4, 2, 4, 8, 4, 2], | ||
shift: 5, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { DitherKernelFactory } from "./api"; | ||
|
||
/** | ||
* Basic 1D (row-based) error diffusion. | ||
*/ | ||
export const DIFFUSION_ROW: DitherKernelFactory = ({ width }) => ({ | ||
ox: [1], | ||
oy: [0], | ||
weights: [1], | ||
shift: 0, | ||
x2: width - 1, | ||
}); | ||
|
||
/** | ||
* Basic 1D (column-based) error diffusion. | ||
*/ | ||
export const DIFFUSION_COLUMN: DitherKernelFactory = () => ({ | ||
ox: [0], | ||
oy: [1], | ||
weights: [1], | ||
shift: 0, | ||
}); | ||
|
||
/** | ||
* Basic 2D error diffusion | ||
*/ | ||
export const DIFFUSION_2D: DitherKernelFactory = ({ width }) => ({ | ||
ox: [1, 0], | ||
oy: [0, 1], | ||
weights: [1, 1], | ||
shift: 1, | ||
x2: width - 1, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import type { PackedBuffer } from "@thi.ng/pixel"; | ||
import { range } from "@thi.ng/pixel/range"; | ||
import type { DitherKernelFactory, DitherOpts } from "./api"; | ||
|
||
export const ditherWithKernel = ( | ||
img: PackedBuffer, | ||
kernel: DitherKernelFactory, | ||
opts?: Partial<DitherOpts> | ||
) => { | ||
const { channels, bleed, threshold } = { | ||
bleed: 1, | ||
threshold: 0.5, | ||
...opts, | ||
}; | ||
const { format, width, height } = img; | ||
for (let cid of channels || range(format.channels.length)) { | ||
const cimg = img.getChannel(cid); | ||
const chan = format.channels[cid]; | ||
const $thresh = chan.num * threshold; | ||
const $max = chan.mask0; | ||
const pixels = new Int32Array(cimg.pixels); | ||
const { x1, x2, y1, y2, ox, oy, weights, shift } = { | ||
x1: 0, | ||
x2: width, | ||
y1: 0, | ||
y2: height, | ||
...kernel(cimg), | ||
}; | ||
let p: number, err: number; | ||
for (let y = y1; y < y2; y++) { | ||
for (let x = x1, i = x + y * width; x < width; x++, i++) { | ||
p = pixels[i] < $thresh ? 0 : $max; | ||
err = (pixels[i] - p) * bleed; | ||
pixels[i] = p; | ||
if (!err) continue; | ||
for (let j = ox.length; j-- > 0; ) { | ||
const xx = x + ox[j]; | ||
const yy = y + oy[j]; | ||
if (yy >= 0 && yy < y2 && xx >= 0 && xx < x2) { | ||
pixels[yy * width + xx] += (err * weights[j]) >> shift; | ||
} | ||
} | ||
} | ||
} | ||
cimg.pixels.set(pixels); | ||
img.setChannel(cid, cimg); | ||
} | ||
return img; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { DitherKernelFactory } from "./api"; | ||
|
||
/** | ||
* Floyd-Steinberg dither kernel. | ||
* | ||
* @remarks | ||
* References: | ||
* - https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering | ||
* - https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html | ||
*/ | ||
export const FLOYD_STEINBERG: DitherKernelFactory = () => ({ | ||
ox: [1, -1, 0, 1], | ||
oy: [0, 1, 1, 1], | ||
weights: [7, 3, 5, 1], | ||
shift: 4, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,12 @@ | ||
export * from "./api"; | ||
export * from "./dither"; | ||
export * from "./ordered"; | ||
|
||
export * from "./atkinson"; | ||
export * from "./burkes"; | ||
export * from "./diffusion"; | ||
export * from "./floyd-steinberg"; | ||
export * from "./jarvis"; | ||
export * from "./sierra2"; | ||
export * from "./stucki"; | ||
export * from "./threshold"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { DitherKernelFactory } from "./api"; | ||
|
||
const A = 64 / 48; | ||
const B = 3 * A; | ||
const C = 5 * A; | ||
const D = 7 * A; | ||
|
||
/** | ||
* Jarvis-Judice-Ninke dither kernel. | ||
* | ||
* @remarks | ||
* References: | ||
* - https://en.wikipedia.org/wiki/Error_diffusion#minimized_average_error | ||
* - https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html | ||
*/ | ||
export const JARVIS_JUDICE_NINKE: DitherKernelFactory = () => ({ | ||
ox: [1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2], | ||
oy: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2], | ||
weights: [D, C, B, C, D, C, B, A, B, C, B, A], | ||
shift: 6, | ||
}); |
Oops, something went wrong.