From e6dc50381f234663f1fd5989942806e7bd08854b Mon Sep 17 00:00:00 2001 From: CrazyboyQCD Date: Mon, 1 Sep 2025 11:32:03 +0800 Subject: [PATCH 1/4] perf: remove unnecssary `Math.sqrt` for comparision --- src/ui/ccModal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/ccModal.ts b/src/ui/ccModal.ts index fb3f1ec..0c03645 100644 --- a/src/ui/ccModal.ts +++ b/src/ui/ccModal.ts @@ -243,7 +243,7 @@ function weightedNearest(r: number, g: number, b: number, palette: number[][]) { const x = (512 + rmean) * rdiff * rdiff >> 8; const y = 4 * gdiff * gdiff; const z = (767 - rmean) * bdiff * bdiff >> 8; - const dist = Math.sqrt(x + y + z); + const dist = x + y + z; if (dist < bestDist) { bestDist = dist; best = [pr, pg, pb]; } } return best || [0,0,0]; From b8769ff619721c3ca60b4243e21d649fa8f49320 Mon Sep 17 00:00:00 2001 From: CrazyboyQCD Date: Mon, 1 Sep 2025 11:35:39 +0800 Subject: [PATCH 2/4] perf: cache color calculation in `processImage` --- src/ui/ccModal.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ui/ccModal.ts b/src/ui/ccModal.ts index 0c03645..bf7ba3b 100644 --- a/src/ui/ccModal.ts +++ b/src/ui/ccModal.ts @@ -233,7 +233,7 @@ function closeCCModal() { } function weightedNearest(r: number, g: number, b: number, palette: number[][]) { - let best: number[] | null = null, bestDist = Infinity; + let best: [number, number, number] | null = null, bestDist = Infinity; for (let i = 0; i < palette.length; i++) { const [pr, pg, pb] = palette[i]; const rmean = (pr + r) / 2; @@ -266,11 +266,18 @@ function processImage() { const palette = getActivePalette(); const counts: Record = {}; + const colorCache: Map = new Map(); for (let i = 0; i < src.length; i += 4) { const r = src[i], g = src[i+1], b = src[i+2], a = src[i+3]; if (a === 0) { out[i]=0; out[i+1]=0; out[i+2]=0; out[i+3]=0; continue; } - const [nr, ng, nb] = palette.length ? weightedNearest(r,g,b,palette) : [r,g,b]; + const color = (r<<24)|(g<<16)|(b<<8)|a; + let cached = colorCache.get(color); + if (!cached) { + cached = palette.length ? weightedNearest(r,g,b,palette) : [r,g,b]; + colorCache.set(color, cached); + } + const [nr, ng, nb] = cached; out[i]=nr; out[i+1]=ng; out[i+2]=nb; out[i+3]=255; const key = `${nr},${ng},${nb}`; counts[key] = (counts[key] || 0) + 1; From c4c7649c82a7a5d0ac332a55e7217701bd04d7d3 Mon Sep 17 00:00:00 2001 From: CrazyboyQCD Date: Mon, 1 Sep 2025 11:52:52 +0800 Subject: [PATCH 3/4] perf: remove unnecssary `Math.sqrt` for comparision in `findClosestColorIndex` --- src/core/overlay.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/overlay.ts b/src/core/overlay.ts index 7fd740a..c63a0c0 100644 --- a/src/core/overlay.ts +++ b/src/core/overlay.ts @@ -42,11 +42,10 @@ function findClosestColorIndex(r: number, g: number, b: number) { let index = 0; for (let i = 0; i < ALL_COLORS.length; i++) { const color = ALL_COLORS[i]; - const distance = Math.sqrt( + const distance = Math.pow(r - color[0], 2) + Math.pow(g - color[1], 2) + - Math.pow(b - color[2], 2) - ); + Math.pow(b - color[2], 2); if (distance < minDistance) { minDistance = distance; index = i; From c431779d257d3e696572efc439cb3f682995b5d9 Mon Sep 17 00:00:00 2001 From: CrazyboyQCD Date: Tue, 2 Sep 2025 11:11:49 +0800 Subject: [PATCH 4/4] perf: use `LRUCache` for color cache --- src/core/cache.ts | 2 ++ src/ui/ccModal.ts | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/core/cache.ts b/src/core/cache.ts index 0379711..81d81f1 100644 --- a/src/core/cache.ts +++ b/src/core/cache.ts @@ -24,6 +24,7 @@ export const overlayCache = new LRUCache(500); export const imageDecodeCache = new LRUCache(64); export const paletteDetectionCache = new LRUCache(200); export const baseMinifyCache = new LRUCache(100); +export const colorCaches = new LRUCache>(500); export const tooLargeOverlays = new Set(); export function clearOverlayCache() { @@ -31,5 +32,6 @@ export function clearOverlayCache() { imageDecodeCache.clear(); paletteDetectionCache.clear(); baseMinifyCache.clear(); + colorCaches.clear(); tooLargeOverlays.clear(); } \ No newline at end of file diff --git a/src/ui/ccModal.ts b/src/ui/ccModal.ts index bf7ba3b..1b23d63 100644 --- a/src/ui/ccModal.ts +++ b/src/ui/ccModal.ts @@ -1,7 +1,8 @@ /// import { WPLACE_FREE, WPLACE_PAID, WPLACE_NAMES, DEFAULT_FREE_KEYS } from '../core/palette'; import { createCanvas } from '../core/canvas'; -import { config, saveConfig } from '../core/store'; +import { colorCaches } from '../core/cache'; +import { config, saveConfig, type OverlayItem } from '../core/store'; import { MAX_OVERLAY_DIM } from '../core/constants'; import { ensureHook } from '../core/hook'; import { clearOverlayCache, paletteDetectionCache } from '../core/cache'; @@ -38,7 +39,7 @@ type CCState = { selectedPaid: Set; realtime: boolean; - overlay: any | null; + overlay: OverlayItem | null; lastColorCounts: Record; isStale: boolean; }; @@ -191,7 +192,7 @@ export function buildCCModal() { renderPaletteGrid(); } -export function openCCModal(overlay: any) { +export function openCCModal(overlay: OverlayItem) { if (!cc) return; cc.overlay = overlay; @@ -266,7 +267,12 @@ function processImage() { const palette = getActivePalette(); const counts: Record = {}; - const colorCache: Map = new Map(); + const id = cc.overlay.id; + let colorCache = colorCaches.get(id); + if (!colorCache) { + colorCache = new Map(); + colorCaches.set(id, colorCache); + } for (let i = 0; i < src.length; i += 4) { const r = src[i], g = src[i+1], b = src[i+2], a = src[i+3];