Skip to content

Commit

Permalink
fix: Fixed ditherImage filter
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor-pelykh committed May 12, 2024
1 parent 805104f commit 90578df
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 31 deletions.
31 changes: 15 additions & 16 deletions src/filter/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,7 @@ export abstract class Filter {
/**
* Dither an image to reduce banding patterns when reducing the number of
* colors.
* Derived from http://jsbin.com/iXofIji/2/edit
*/
public static ditherImage(opt: DitherImageOptions): MemoryImage {
const quantizer = opt.quantizer ?? new NeuralQuantizer(opt.image);
Expand All @@ -1105,29 +1106,27 @@ export abstract class Filter {
palette: palette,
});

const imageCopy = opt.image.clone();

const pIter = opt.image[Symbol.iterator]();
let itRes = pIter.next();

let index = 0;
for (let y = 0; y < height; y++) {
if (serpentine) direction *= -1;
if (serpentine) {
direction *= -1;
}

const x0 = direction === 1 ? 0 : width - 1;
const x1 = direction === 1 ? width : 0;
for (
let x = x0;
x !== x1;
x += direction, ++index, itRes = pIter.next()
) {
for (let x = x0; x !== x1; x += direction, itRes = pIter.next()) {
// Get original color
const pc = itRes.value;
const r1 = Math.trunc(pc.getChannel(0));
const g1 = Math.trunc(pc.getChannel(1));
const b1 = Math.trunc(pc.getChannel(2));

// Get converted color
let idx = quantizer.getColorIndexRgb(r1, g1, b1);
indexedImage.setPixelRgb(x, y, idx, 0, 0);
const idx = quantizer.getColorIndexRgb(r1, g1, b1);
indexedImage.setPixelIndex(x, y, idx);

const r2 = palette.get(idx, 0);
const g2 = palette.get(idx, 1);
Expand All @@ -1148,12 +1147,12 @@ export abstract class Filter {
const y1 = Math.trunc(ds[i][2]);
if (x1 + x >= 0 && x1 + x < width && y1 + y >= 0 && y1 + y < height) {
const d = ds[i][0];
idx = index + x1 + y1 * width;
idx *= 4;
const p2 = opt.image.getPixel(x1, y1);
p2.r = Math.max(0, Math.min(255, Math.trunc(p2.r + er * d)));
p2.g = Math.max(0, Math.min(255, Math.trunc(p2.g + er * d)));
p2.b = Math.max(0, Math.min(255, Math.trunc(p2.b + er * d)));
const nx = x + x1;
const ny = y + y1;
const p2 = imageCopy.getPixel(nx, ny);
p2.r += er * d;
p2.g += eg * d;
p2.b += eb * d;
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions test/filter/filter.dither-image.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Filter', () => {

let id = Filter.ditherImage({
image: i0,
kernel: DitherKernel.floydSteinberg,
kernel: DitherKernel.atkinson,
});

let output = encodePng({
Expand All @@ -32,13 +32,13 @@ describe('Filter', () => {
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'dither_floydSteinberg.png',
'dither_atkinson.png',
output
);

id = Filter.ditherImage({
image: i0,
kernel: DitherKernel.falseFloydSteinberg,
kernel: DitherKernel.floydSteinberg,
});

output = encodePng({
Expand All @@ -47,13 +47,13 @@ describe('Filter', () => {
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'dither_falseFloydSteinberg.png',
'dither_floydSteinberg.png',
output
);

id = Filter.ditherImage({
image: i0,
kernel: DitherKernel.atkinson,
kernel: DitherKernel.falseFloydSteinberg,
});

output = encodePng({
Expand All @@ -62,7 +62,7 @@ describe('Filter', () => {
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'dither_atkinson.png',
'dither_falseFloydSteinberg.png',
output
);

Expand Down
61 changes: 52 additions & 9 deletions test/filter/filter.quantize.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
/** @format */

import { decodePng, encodePng, Filter, QuantizeMethod } from '../../src';
import {
decodePng,
DitherKernel,
encodePng,
Filter,
QuantizeMethod,
} from '../../src';
import { TestFolder } from '../_utils/test-folder';
import { TestSection } from '../_utils/test-section';
import { TestUtils } from '../_utils/test-utils';

describe('Filter', () => {
test('pixelate', () => {
test('quantize', () => {
const input = TestUtils.readFromFile(
TestFolder.input,
TestSection.png,
Expand All @@ -21,16 +27,14 @@ describe('Filter', () => {
return;
}

const i1 = i0.clone();

Filter.quantize({
const q0 = Filter.quantize({
image: i0,
numberOfColors: 32,
method: QuantizeMethod.octree,
});

let output = encodePng({
image: i0,
image: q0,
});
TestUtils.writeToFile(
TestFolder.output,
Expand All @@ -39,19 +43,58 @@ describe('Filter', () => {
output
);

Filter.quantize({
image: i1,
const i0_ = decodePng({
data: input,
})!;
const q0_ = Filter.quantize({
image: i0_,
numberOfColors: 32,
method: QuantizeMethod.octree,
dither: DitherKernel.floydSteinberg,
});

output = encodePng({
image: q0_,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'quantize_octree_dither.png',
output
);

const i1 = decodePng({
data: input,
})!;
const q1 = Filter.quantize({
image: i1,
numberOfColors: 32,
});
output = encodePng({
image: q1,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'quantize_neural.png',
output
);

const i1_ = decodePng({
data: input,
})!;
const q1_ = Filter.quantize({
image: i1_,
numberOfColors: 32,
dither: DitherKernel.floydSteinberg,
});
output = encodePng({
image: q1_,
});
TestUtils.writeToFile(
TestFolder.output,
TestSection.filter,
'quantize_neural_dither.png',
output
);
});
});

0 comments on commit 90578df

Please sign in to comment.