From 48ed0bb775259e86fc3946ba1cbccacc0bce9f89 Mon Sep 17 00:00:00 2001 From: Iu Date: Wed, 21 Oct 2026 01:32:14 +0300 Subject: [PATCH] =?UTF-8?q?fix(web):=20=D0=B3=D0=B8=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=87=D0=B8=D1=82=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=B8=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Гистограмма считалась по базовому превью один раз и не менялась при движении ползунков и кривой. Теперь по уменьшенной копии фото накапливается сэмпл пикселей, а сама гистограмма пересчитывается с применением текущих правок, так что график реагирует на манипуляции сразу. --- .../src/lib/components/PhotoEditPanel.svelte | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/PhotoEditPanel.svelte b/frontend/src/lib/components/PhotoEditPanel.svelte index 13370cc..df98d2c 100644 --- a/frontend/src/lib/components/PhotoEditPanel.svelte +++ b/frontend/src/lib/components/PhotoEditPanel.svelte @@ -2,6 +2,7 @@ import { photosApi } from '$lib/api'; import Icon from './Icon.svelte'; import { edit } from '$lib/stores/edit.svelte'; + import { applyLight } from '$lib/light-wasm'; import { CHANNEL_COLORS, CHANNEL_LABELS, @@ -53,21 +54,22 @@ }; }); - // Гистограмма каналов считается по базовому превью фото. + // Гистограмма каналов считается по базовому превью фото и пересчитывается + // с учётом текущих правок, поэтому реагирует на движение ползунков и кривой. type Histogram = { red: number[]; green: number[]; blue: number[] }; - let histogram = $state(null); + let sample = $state(null); $effect(() => { const url = photo.preview_url; if (!url) { - histogram = null; + sample = null; return; } let cancelled = false; const img = new Image(); img.onload = () => { if (cancelled) return; - histogram = computeHistogram(img); + sample = sampleImage(img); }; img.src = url; return () => { @@ -75,8 +77,8 @@ }; }); - /** Считает гистограмму каналов по уменьшенной копии фото. */ - function computeHistogram(img: HTMLImageElement): Histogram | null { + /** Уменьшенная копия фото: пиксели для расчёта гистограммы. */ + function sampleImage(img: HTMLImageElement): Uint8ClampedArray | null { const width = 256; const height = Math.max(1, Math.round((img.naturalHeight / img.naturalWidth) * width)); const canvas = document.createElement('canvas'); @@ -85,7 +87,15 @@ const ctx = canvas.getContext('2d'); if (!ctx) return null; ctx.drawImage(img, 0, 0, width, height); - const data = ctx.getImageData(0, 0, width, height).data; + return new Uint8ClampedArray(ctx.getImageData(0, 0, width, height).data); + } + + /** Гистограмма с учётом текущих правок. */ + const histogram = $derived.by(() => { + const pixels = sample; + if (!pixels) return null; + const data = new Uint8ClampedArray(pixels); + applyLight(data, params.light); const r = new Array(256).fill(0); const g = new Array(256).fill(0); const b = new Array(256).fill(0); @@ -101,7 +111,7 @@ if (!max) return null; const norm = (values: number[]) => values.map((v) => v / max); return { red: norm(r), green: norm(g), blue: norm(b) }; - } + }); /** Каналы, которые показываем на графике: у мастер-канала — все три. */ const visibleChannels = $derived(