Skip to content

Commit 15109f5

Browse files
committed
add dithering to grayscale image (needs fix)
1 parent b395b77 commit 15109f5

2 files changed

Lines changed: 61 additions & 7 deletions

File tree

js/dithering.js

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,15 @@ function dithering(ctx, width, height, threshold, typeIndex) {
8585
ctx.putImageData(imageData, 0, 0);
8686
}
8787

88-
function toGrayscale(ctx, width, height) {
88+
function grayscale_dithering(ctx, width, height, threshold, typeIndex) {
89+
const type = ['binary', 'bayer', 'floydsteinberg', 'atkinson'][typeIndex];
90+
const bayerThresholdMap = [
91+
[15, 135, 45, 165],
92+
[195, 75, 225, 105],
93+
[60, 180, 30, 150],
94+
[240, 120, 210, 90],
95+
];
96+
8997
const lumR = [];
9098
const lumG = [];
9199
const lumB = [];
@@ -98,10 +106,58 @@ function toGrayscale(ctx, width, height) {
98106

99107
const imageDataLength = imageData.data.length;
100108

101-
// Greyscale luminance (sets r pixels to luminance of rgb)
109+
const w = imageData.width;
110+
let currentPixel;
111+
let newPixel;
112+
let err;
113+
114+
// Grayscale luminance (sets r pixels to luminance of rgb)
102115
for (let i = 0; i <= imageDataLength; i += 4) {
103-
const grayscale_pixel = Math.ceil(Math.round(lumR[imageData.data[i]] + lumG[imageData.data[i + 1]] + lumB[imageData.data[i + 2]]) / 16);
104-
imageData.data[i] = imageData.data[i + 1] = imageData.data[i + 2] = grayscale_pixel * 16;
116+
imageData.data[i] =
117+
Math.round(lumR[imageData.data[i]] + lumG[imageData.data[i + 1]] + lumB[imageData.data[i + 2]]);
118+
}
119+
120+
for (let currentPixelIndex = 0; currentPixelIndex <= imageDataLength; currentPixelIndex += 4) {
121+
newPixel = Math.round((imageData.data[currentPixelIndex]) / 16) * 16;
122+
123+
if (type === 'binary') {
124+
// No dithering
125+
imageData.data[currentPixelIndex] = newPixel;
126+
} else if (type === 'bayer') {
127+
// 4x4 Bayer ordered dithering algorithm
128+
// eslint-disable-next-line no-mixed-operators
129+
const x = currentPixelIndex / 4 % w;
130+
const y = Math.floor(currentPixelIndex / 4 / w);
131+
const map = Math.floor((newPixel + bayerThresholdMap[x % 4][y % 4]) / 2);
132+
//console.log(map);
133+
imageData.data[currentPixelIndex] = Math.round(map / 16) * 16;
134+
} else if (type === 'floydsteinberg') {
135+
// Floyda€"Steinberg dithering algorithm
136+
currentPixel = imageData.data[currentPixelIndex]
137+
err = Math.floor((currentPixel - newPixel) / 16);
138+
imageData.data[currentPixelIndex] = newPixel;
139+
140+
imageData.data[currentPixelIndex + 4] += err * 7;
141+
imageData.data[currentPixelIndex + 4 * w - 4] += err * 3;
142+
imageData.data[currentPixelIndex + 4 * w] += err * 5;
143+
imageData.data[currentPixelIndex + 4 * w + 4] += err * 1;
144+
} else if (type === 'atkinson') {
145+
// Bill Atkinson's dithering algorithm
146+
err = Math.floor((imageData.data[currentPixelIndex] - newPixel) / 8);
147+
imageData.data[currentPixelIndex] = newPixel;
148+
149+
imageData.data[currentPixelIndex + 4] += err;
150+
imageData.data[currentPixelIndex + 8] += err;
151+
imageData.data[currentPixelIndex + 4 * w - 4] += err;
152+
imageData.data[currentPixelIndex + 4 * w] += err;
153+
imageData.data[currentPixelIndex + 4 * w + 4] += err;
154+
imageData.data[currentPixelIndex + 8 * w] += err;
155+
} else {
156+
console.error(`unknown dithering type requested: ${type}`);
157+
}
158+
159+
// Set g and b pixels equal to r
160+
imageData.data[currentPixelIndex + 1] = imageData.data[currentPixelIndex + 2] = imageData.data[currentPixelIndex];
105161
}
106162

107163
ctx.putImageData(imageData, 0, 0);

js/script.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,12 @@ function placeImage(_image) {
435435

436436
if (settings.conversionFunction === ConversionFunctions.horizontal4bit) {
437437
// eslint-disable-next-line no-undef
438-
toGrayscale(ctx, canvas.width, canvas.height);
438+
grayscale_dithering(ctx, canvas.width, canvas.height, settings.ditheringThreshold, settings.ditheringMode);
439439
if (settings.invertColors) {
440440
invert(canvas, ctx);
441441
}
442442
}
443443

444-
toGrayscale
445-
446444
if (settings.rotation !== 0) {
447445
const clone = canvas.cloneNode(true);
448446
clone.getContext('2d').drawImage(canvas, 0, 0);

0 commit comments

Comments
 (0)