From 44af5575812f7768d50aa9efb211817306169c8c Mon Sep 17 00:00:00 2001 From: HF Date: Sat, 1 Jan 2022 01:35:18 +0100 Subject: [PATCH] update image-q and other packages --- src/components/Converter.jsx | 181 +++++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 49 deletions(-) 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) => ( + )) }