fix(web): гистограмма пересчитывается при правках
Гистограмма считалась по базовому превью один раз и не менялась при движении ползунков и кривой. Теперь по уменьшенной копии фото накапливается сэмпл пикселей, а сама гистограмма пересчитывается с применением текущих правок, так что график реагирует на манипуляции сразу.
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import { photosApi } from '$lib/api';
|
import { photosApi } from '$lib/api';
|
||||||
import Icon from './Icon.svelte';
|
import Icon from './Icon.svelte';
|
||||||
import { edit } from '$lib/stores/edit.svelte';
|
import { edit } from '$lib/stores/edit.svelte';
|
||||||
|
import { applyLight } from '$lib/light-wasm';
|
||||||
import {
|
import {
|
||||||
CHANNEL_COLORS,
|
CHANNEL_COLORS,
|
||||||
CHANNEL_LABELS,
|
CHANNEL_LABELS,
|
||||||
@@ -53,21 +54,22 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Гистограмма каналов считается по базовому превью фото.
|
// Гистограмма каналов считается по базовому превью фото и пересчитывается
|
||||||
|
// с учётом текущих правок, поэтому реагирует на движение ползунков и кривой.
|
||||||
type Histogram = { red: number[]; green: number[]; blue: number[] };
|
type Histogram = { red: number[]; green: number[]; blue: number[] };
|
||||||
let histogram = $state<Histogram | null>(null);
|
let sample = $state<Uint8ClampedArray | null>(null);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const url = photo.preview_url;
|
const url = photo.preview_url;
|
||||||
if (!url) {
|
if (!url) {
|
||||||
histogram = null;
|
sample = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
histogram = computeHistogram(img);
|
sample = sampleImage(img);
|
||||||
};
|
};
|
||||||
img.src = url;
|
img.src = url;
|
||||||
return () => {
|
return () => {
|
||||||
@@ -75,8 +77,8 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Считает гистограмму каналов по уменьшенной копии фото. */
|
/** Уменьшенная копия фото: пиксели для расчёта гистограммы. */
|
||||||
function computeHistogram(img: HTMLImageElement): Histogram | null {
|
function sampleImage(img: HTMLImageElement): Uint8ClampedArray | null {
|
||||||
const width = 256;
|
const width = 256;
|
||||||
const height = Math.max(1, Math.round((img.naturalHeight / img.naturalWidth) * width));
|
const height = Math.max(1, Math.round((img.naturalHeight / img.naturalWidth) * width));
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
@@ -85,7 +87,15 @@
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) return null;
|
if (!ctx) return null;
|
||||||
ctx.drawImage(img, 0, 0, width, height);
|
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<Histogram | null>(() => {
|
||||||
|
const pixels = sample;
|
||||||
|
if (!pixels) return null;
|
||||||
|
const data = new Uint8ClampedArray(pixels);
|
||||||
|
applyLight(data, params.light);
|
||||||
const r = new Array<number>(256).fill(0);
|
const r = new Array<number>(256).fill(0);
|
||||||
const g = new Array<number>(256).fill(0);
|
const g = new Array<number>(256).fill(0);
|
||||||
const b = new Array<number>(256).fill(0);
|
const b = new Array<number>(256).fill(0);
|
||||||
@@ -101,7 +111,7 @@
|
|||||||
if (!max) return null;
|
if (!max) return null;
|
||||||
const norm = (values: number[]) => values.map((v) => v / max);
|
const norm = (values: number[]) => values.map((v) => v / max);
|
||||||
return { red: norm(r), green: norm(g), blue: norm(b) };
|
return { red: norm(r), green: norm(g), blue: norm(b) };
|
||||||
}
|
});
|
||||||
|
|
||||||
/** Каналы, которые показываем на графике: у мастер-канала — все три. */
|
/** Каналы, которые показываем на графике: у мастер-канала — все три. */
|
||||||
const visibleChannels = $derived<CurveChannel[]>(
|
const visibleChannels = $derived<CurveChannel[]>(
|
||||||
|
|||||||
Reference in New Issue
Block a user