From f2e59cfda37a3ad875f35dda5721be97abd94611 Mon Sep 17 00:00:00 2001 From: Terncode Date: Thu, 13 Feb 2025 17:24:22 +0100 Subject: [PATCH] Optimise dither --- src/UI/importMural.tsx | 20 +++----------------- src/lib/utils.ts | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/UI/importMural.tsx b/src/UI/importMural.tsx index 709896e..5a95b02 100644 --- a/src/UI/importMural.tsx +++ b/src/UI/importMural.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ImportOutput, MuralOld, RGB } from "../interfaces"; -import { canvasToImageData, convertOldMuralToNewMural, createMuralExtended, getColorScore, getExtension, +import { canvasToImageData, convertOldMuralToNewMural, createClosestIndexColor, createMuralExtended, getColorScore, getExtension, imageDataToPaletteIndices, imageToCanvas, loadImageSource, processNumberEvent, readAsArrayBuffer, readAsDataUrl, readAsString, resize, rgb } from "../lib/utils"; @@ -440,13 +440,13 @@ function quantizeImage( } export function flatQuantizeImageData(imageData: ImageData, palette: Palette) { + const indexColor = createClosestIndexColor(palette.palette); for (let i = 0; i < imageData.data.length; i += 4) { const r = imageData.data[i + 0] ?? 0; const g = imageData.data[i + 1] ?? 0; const b = imageData.data[i + 2] ?? 0; const a = imageData.data[i + 3] ?? 0; - const obj = rgb(r, g, b); - const index = findClosestIndexColor(obj, palette); + const index = indexColor(r, g, b); const color = palette.palette[index]; if (!color) throw new Error(`Unknown color index ${index}`); imageData.data[i + 0] = color.r; @@ -456,20 +456,6 @@ export function flatQuantizeImageData(imageData: ImageData, palette: Palette) { } } -export function findClosestIndexColor(rgbO: RGB, palette: Palette) { - const scores: number[] = []; - - for (const rgb of palette.palette) { - const r = getColorScore(rgbO.r, rgb.r); - const g = getColorScore(rgbO.g, rgb.g); - const b = getColorScore(rgbO.b, rgb.b); - scores.push(r + g + b); - } - const lowest = Math.min(...scores); - const index = scores.indexOf(lowest); - return index; -} - export function imageDataToCanvas(imageData: ImageData) { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d")!; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 43fe53a..ef30420 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -224,13 +224,13 @@ export function canvasToImageData(canvas: HTMLCanvasElement) { } export function flatQuantizeImageData(imageData: ImageData, palette: RGB[]) { + const indexColor = createClosestIndexColor(palette); for (let i = 0; i < imageData.data.length; i += 4) { const r = imageData.data[i + 0] ?? 0; const g = imageData.data[i + 1] ?? 0; const b = imageData.data[i + 2] ?? 0; const a = imageData.data[i + 3] ?? 0; - const obj = rgb(r, g, b); - const index = findClosestIndexColor(obj, palette); + const index = indexColor(r, g, b); const color = palette[index]; if (!color) throw new Error(`Unknown color index ${index}`); imageData.data[i + 0] = color.r; @@ -273,6 +273,7 @@ export async function getPixelStatusMural(mural: Mural, palette: RGB[]): Promise export function imageDataToPaletteIndices(imageData: ImageData, palette: RGB[]) { const { height, width } = imageData; let k = 0; + const indexColor = createClosestIndexColor(palette); const pixels = new Int8Array(height * width); for (let i = 0; i < imageData.data.length; i += 4) { const r = imageData.data[i + 0] ?? 0; @@ -282,7 +283,7 @@ export function imageDataToPaletteIndices(imageData: ImageData, palette: RGB[]) if (a < 25) { pixels[k++] = -1; } else { - const index = findClosestFormArray(rgb(r, g, b), palette); + const index = indexColor(r, g, b); pixels[k++] = index; } } @@ -381,6 +382,24 @@ export function createMuralExtended(mural: Mural, palette: string[]): MuralEx { }; } +export function createClosestIndexColor(palette: RGB[]) { + const scores = new Uint16Array(palette.length); + let lowest = Number.MAX_SAFE_INTEGER; + return (r: number, g: number, b: number) => { + lowest = Number.MAX_SAFE_INTEGER; + for (let i = 0; i < palette.length; i++) { + const rgb = palette[i]; + const rr = getColorScore(r, rgb.r); + const gg = getColorScore(g, rgb.g); + const bb = getColorScore(b, rgb.b); + const value = rr + gg + bb; + lowest = lowest < value ? lowest : value; + scores[i] = value; + } + return scores.indexOf(lowest); + }; +} + let numberFormatter: Intl.NumberFormat; try { const userLocale = navigator.language || (navigator as any).userLanguage;