mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
perf: add cache for images
This commit is contained in:
@@ -168,6 +168,14 @@
|
||||
форматирование по колонкам??? для hex\bytes\rgba - пример в placeholder
|
||||
- [ ] прикрутить кастомный color picker, добавить прозрачность (range?)
|
||||
- [ ] обновить страницу kit или сделать storybook
|
||||
- [ ] **проверить `cachedDataUrl` (WeakMap-кэш превью) в реальной работе** — GC
|
||||
срабатывает недетерминированно, поэтому освобождение base64-строк (≈1.3×
|
||||
бинарного размера картинки на каждый тайл) не гарантировано своевременно.
|
||||
Проверить на длинной сессии с большими картинками (несколько ранов подряд,
|
||||
смена инструментов): растёт ли heap в DevTools → Memory после повторных
|
||||
ранов и уходит ли после принудительной сборки. Если записи копятся —
|
||||
заменить на ограниченный LRU или явную инвалидацию при
|
||||
`resetForHost`/новом ране (`web/src/lib/core/io.ts`, `cachedDataUrl`)
|
||||
- [ ] `PreviewTile`: высота плитки задана только `min-height: clamp(...)`,
|
||||
поэтому она не следует за пропорциями результата — широкий результат
|
||||
уезжает в узкую полосу, а высокий упирается в `max-height: 100vh`. Нужно,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { toDataUrl } from "$lib/core/io";
|
||||
import { cachedDataUrl } from "$lib/core/io";
|
||||
import type { ToolImageFile } from "$lib/registry";
|
||||
|
||||
interface Props {
|
||||
@@ -8,7 +8,7 @@
|
||||
let { files }: Props = $props();
|
||||
|
||||
const parts = $derived(
|
||||
files.map((file) => ({ name: file.name, url: toDataUrl(file.image) })),
|
||||
files.map((file) => ({ name: file.name, url: cachedDataUrl(file.image) })),
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { toDataUrl } from "$lib/core/io";
|
||||
import { cachedDataUrl } from "$lib/core/io";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { FileResult, ResultKind, ResultNote } from "$lib/registry";
|
||||
@@ -41,7 +41,7 @@
|
||||
}: Props = $props();
|
||||
|
||||
const shown = $derived(maskOn && mask ? mask : result);
|
||||
const resultUrl = $derived(shown ? toDataUrl(shown) : null);
|
||||
const resultUrl = $derived(shown ? cachedDataUrl(shown) : null);
|
||||
const resultDims = $derived(
|
||||
resultKind === "image" && result
|
||||
? `${result.width} × ${result.height}`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { toDataUrl } from "$lib/core/io";
|
||||
import { cachedDataUrl } from "$lib/core/io";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { InputMode, ToolImageFile } from "$lib/registry";
|
||||
@@ -33,7 +33,7 @@
|
||||
onuploadmany,
|
||||
}: Props = $props();
|
||||
|
||||
const sourceUrl = $derived(source ? toDataUrl(source) : null);
|
||||
const sourceUrl = $derived(source ? cachedDataUrl(source) : null);
|
||||
const sourceDims = $derived(
|
||||
source
|
||||
? `${source.width} × ${source.height}` +
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Star } from "@lucide/svelte";
|
||||
import { DragDropProvider } from "@dnd-kit/svelte";
|
||||
import { debounce } from "$lib/core/debounce";
|
||||
import { toDataUrl } from "$lib/core/io";
|
||||
import { cachedDataUrl } from "$lib/core/io";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import {
|
||||
chainStepTitle,
|
||||
@@ -301,7 +301,7 @@
|
||||
{#if inSet.length > 1}
|
||||
<PartsGrid files={inSet} />
|
||||
{:else if input}
|
||||
<img src={toDataUrl(input)} alt="" />
|
||||
<img src={cachedDataUrl(input)} alt="" />
|
||||
{:else}
|
||||
<span class="empty">{t("resultCard.noResult")}</span>
|
||||
{/if}
|
||||
@@ -344,7 +344,7 @@
|
||||
<PartsGrid files={outSet} />
|
||||
{:else if out}
|
||||
<img
|
||||
src={toDataUrl(
|
||||
src={cachedDataUrl(
|
||||
masks.maskOn[i] && masks.masks[i]
|
||||
? (masks.masks[i] as PixelImage)
|
||||
: out,
|
||||
|
||||
@@ -82,6 +82,21 @@ export function toBase64(img: PixelImage): string {
|
||||
return toDataUrl(img).slice("data:image/png;base64,".length);
|
||||
}
|
||||
|
||||
const dataUrlCache = new WeakMap<PixelImage, string>();
|
||||
|
||||
// Display helper: PNG-encode on the main thread is expensive and templates
|
||||
// re-render often with the same image (layout branch swaps, toggles). Results
|
||||
// are treated as immutable -- a re-run produces new objects -- so an
|
||||
// identity-keyed cache cannot go stale.
|
||||
export function cachedDataUrl(img: PixelImage): string {
|
||||
let url = dataUrlCache.get(img);
|
||||
if (url === undefined) {
|
||||
url = toDataUrl(img);
|
||||
dataUrlCache.set(img, url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export async function decodeTextImage(text: string): Promise<PixelImage> {
|
||||
const cleaned = text.trim().replace(/^data:[^,]*,/, "");
|
||||
if (cleaned.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user