From 90578df0ce7b8252c0d4aff245057c4b421e71d6 Mon Sep 17 00:00:00 2001 From: Yegor Pelykh Date: Sun, 12 May 2024 16:08:21 +0300 Subject: [PATCH] fix: Fixed ditherImage filter --- src/filter/filter.ts | 31 ++++++------- test/filter/filter.dither-image.test.ts | 12 ++--- test/filter/filter.quantize.test.ts | 61 +++++++++++++++++++++---- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/filter/filter.ts b/src/filter/filter.ts index 5d71e1e..03ef783 100644 --- a/src/filter/filter.ts +++ b/src/filter/filter.ts @@ -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); @@ -1105,20 +1106,18 @@ 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)); @@ -1126,8 +1125,8 @@ export abstract class Filter { 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); @@ -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; } } } diff --git a/test/filter/filter.dither-image.test.ts b/test/filter/filter.dither-image.test.ts index 15aa7c5..865f22b 100644 --- a/test/filter/filter.dither-image.test.ts +++ b/test/filter/filter.dither-image.test.ts @@ -23,7 +23,7 @@ describe('Filter', () => { let id = Filter.ditherImage({ image: i0, - kernel: DitherKernel.floydSteinberg, + kernel: DitherKernel.atkinson, }); let output = encodePng({ @@ -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({ @@ -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({ @@ -62,7 +62,7 @@ describe('Filter', () => { TestUtils.writeToFile( TestFolder.output, TestSection.filter, - 'dither_atkinson.png', + 'dither_falseFloydSteinberg.png', output ); diff --git a/test/filter/filter.quantize.test.ts b/test/filter/filter.quantize.test.ts index 1f51987..cf8751a 100644 --- a/test/filter/filter.quantize.test.ts +++ b/test/filter/filter.quantize.test.ts @@ -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, @@ -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, @@ -39,13 +43,34 @@ 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, @@ -53,5 +78,23 @@ describe('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 + ); }); });