diff --git a/src/components/Converter.jsx b/src/components/Converter.jsx
index c47fb3f..41317f8 100644
--- a/src/components/Converter.jsx
+++ b/src/components/Converter.jsx
@@ -6,7 +6,7 @@
import React, { useState, useEffect } from 'react';
import { useSelector, shallowEqual } from 'react-redux';
import fileDownload from 'js-file-download';
-import { utils, applyPalette } from 'image-q';
+import iq from 'image-q';
import { jt, t } from 'ttag';
import printGIMPPalette from '../core/exportGPL';
@@ -43,6 +43,113 @@ function readFile(
fr.readAsDataURL(file);
}
+const ColorDistanceCalculators = [
+ 'Euclidean',
+ 'Manhattan',
+ 'CIEDE2000',
+ 'CIE94Textiles',
+ 'CIE94GraphicArts',
+ 'EuclideanBT709NoAlpha',
+ 'EuclideanBT709',
+ 'ManhattanBT709',
+ 'CMetric',
+ 'PNGQuant',
+ 'ManhattanNommyde',
+];
+
+function quantize(
+ pointContainer,
+ colors,
+ colorDist,
+ strategy,
+ onProgress,
+) {
+ return new Promise((resolve, reject) => {
+ // read colors into palette
+ const palette = new iq.utils.Palette();
+ palette.add(iq.utils.Point.createByRGBA(0, 0, 0, 0));
+ colors.forEach((clr) => {
+ const [r, g, b] = clr;
+ const point = iq.utils.Point.createByRGBA(r, g, b, 255);
+ palette.add(point);
+ });
+ // construct color distance calculator
+ let distance;
+ switch (colorDist) {
+ case 'Euclidean':
+ distance = new iq.distance.Euclidean();
+ break;
+ case 'Manhattan':
+ distance = new iq.distance.Manhattan();
+ break;
+ case 'CIEDE2000':
+ distance = new iq.distance.CIEDE2000();
+ break;
+ case 'CIE94Textiles':
+ distance = new iq.distance.CIE94Textiles();
+ break;
+ case 'CIE94GraphicArts':
+ distance = new iq.distance.CIE94GraphicArts();
+ break;
+ case 'EuclideanBT709NoAlpha':
+ distance = new iq.distance.EuclideanBT709NoAlpha();
+ break;
+ case 'EuclideanBT709':
+ distance = new iq.distance.EuclideanBT709();
+ break;
+ case 'ManhattanBT709':
+ distance = new iq.distance.ManhattanBT709();
+ break;
+ case 'CMetric':
+ distance = new iq.distance.CMetric();
+ break;
+ case 'PNGQuant':
+ distance = new iq.distance.PNGQuant();
+ break;
+ case 'ManhattanNommyde':
+ distance = new iq.distance.ManhattanNommyde();
+ break;
+ default:
+ distance = new iq.distance.Euclidean();
+ }
+ // construct image quantizer
+ let imageQuantizer;
+ if (strategy === 'Nearest') {
+ imageQuantizer = new iq.image.NearestColor(distance);
+ } else if (strategy === 'Riemersma') {
+ imageQuantizer = new iq.image.ErrorDiffusionRiemersma(distance);
+ } else {
+ imageQuantizer = new iq.image.ErrorDiffusionArray(
+ distance,
+ iq.image.ErrorDiffusionArrayKernel[strategy],
+ true,
+ 0,
+ false,
+ );
+ }
+ // quantize
+ let outPointContainer;
+ const iterator = imageQuantizer.quantize(pointContainer, palette);
+ const next = () => {
+ try {
+ const result = iterator.next();
+ if (result.done) {
+ resolve(outPointContainer);
+ } else {
+ if (result.value.pointContainer) {
+ outPointContainer = result.value.pointContainer;
+ }
+ if (onProgress) onProgress(result.value.progress);
+ setTimeout(next, 10);
+ }
+ } catch (error) {
+ reject(error);
+ }
+ };
+ setTimeout(next, 10);
+ });
+}
+
function drawPixels(idxi8, width, height) {
const can = document.createElement('canvas');
can.width = width;
@@ -155,28 +262,23 @@ async function renderOutputImage(opts) {
height,
aa,
);
- pointContainer = utils.PointContainer.fromHTMLCanvasElement(image);
+ pointContainer = iq.utils.PointContainer.fromHTMLCanvasElement(image);
} else {
- pointContainer = utils.PointContainer.fromHTMLImageElement(image);
+ pointContainer = iq.utils.PointContainer.fromHTMLImageElement(image);
}
// dither
const { colors, strategy, colorDist } = dither;
- const palette = new utils.Palette();
- palette.add(utils.Point.createByRGBA(0, 0, 0, 0));
- colors.forEach((clr) => {
- const [r, g, b] = clr;
- const point = utils.Point.createByRGBA(r, g, b, 255);
- palette.add(point);
- });
const progEl = document.getElementById('qprog');
// eslint-disable-next-line no-await-in-loop
- pointContainer = await applyPalette(pointContainer, palette, {
- colorDistanceFormula: colorDist,
- imageQuantization: strategy,
- onProgress: (progress) => {
+ pointContainer = await quantize(
+ pointContainer,
+ colors,
+ colorDist,
+ strategy,
+ (progress) => {
progEl.innerHTML = `Loading... ${Math.round(progress)} %`;
},
- });
+ );
progEl.innerHTML = 'Done';
image = drawPixels(
pointContainer.toUint8Array(),
@@ -220,8 +322,8 @@ function Converter() {
const [selectedCanvas, selectCanvas] = useState(canvasId);
const [selectedFile, selectFile] = useState(null);
- const [selectedStrategy, selectStrategy] = useState('nearest');
- const [selectedColorDist, selectColorDist] = useState('euclidean');
+ const [selectedStrategy, selectStrategy] = useState('Nearest');
+ const [selectedColorDist, selectColorDist] = useState('Euclidean');
const [selectedScaleKeepRatio, selectScaleKeepRatio] = useState(true);
const [scaleData, setScaleData] = useState({
enabled: false,
@@ -236,8 +338,6 @@ function Converter() {
offsetY: 0,
});
- const input = document.createElement('canvas');
-
useEffect(() => {
if (selectedFile) {
const canvas = canvases[selectedCanvas];
@@ -346,21 +446,14 @@ function Converter() {
}}
>
{
- ['nearest',
- 'riemersma',
- 'floyd-steinberg',
- 'false-floyd-steinberg',
- 'stucki',
- 'atkinson',
- 'jarvis',
- 'burkes',
- 'sierra',
- 'two-sierra',
- 'sierra-lite'].map((strat) => (
-
+ ['Nearest',
+ 'Riemersma',
+ ...Object.keys(iq.image.ErrorDiffusionArrayKernel),
+ ].map((strat) => (
+
))
}
@@ -373,21 +466,11 @@ function Converter() {
}}
>
{
- ['cie94-textiles',
- 'cie94-graphic-arts',
- 'ciede2000',
- 'color-metric',
- 'euclidean',
- 'euclidean-bt709-noalpha',
- 'euclidean-bt709',
- 'manhattan',
- 'manhattan-bt709',
- 'manhattan-nommyde',
- 'pngquant'].map((strat) => (
-
+ ColorDistanceCalculators.map((strat) => (
+
))
}