6.6 KiB
Design
Context
Неразрушающее редактирование фото уже реализовано: параметры правок хранятся в
photo.edit_params (map[string]any JSON-блоб), сервер рендерит отредактированную
копию как отдельные объекты ролей edit, edit_thumb, edit_medium,
edit_preview рядом с исходными, оригинал не меняется. На сегодня поддерживается
только блок «Свет» (LightParams в backend/internal/photos/edit.go,
зеркало на клиенте frontend/src/lib/edit.ts, живой превью через
frontend/wasm/light/ → static/light.wasm с тестом паритета
wasmMatchesReference). Хранение в JSON-блобе обратноли не требует миграции:
старые фото без ключа color рендерятся без цветовых правок.
Goals / Non-Goals
Goals:
- Добавить блок «Цвет» к неразрушающему редактированию: баланс белого (Темп, Оттенок), Вибрация, Насыщенность, Цветовой микшер по оттенкам, Точечный цвет, Цветокоррекция.
- Живой клиентский превью с паритетом трёх реализаций: сервер Go, клиент TypeScript, WASM (Rust) — как для «Света».
- Рендер цветовых правок по пикселям исходного объекта, после световых правок.
- Обратная совместимость: фото без ключа
colorвedit_paramsрендерятся как прежде.
Non-Goals:
- Синхронизация правок между устройствами или через Android-клиент.
- Изменение ролей хранения или формата объектов (
edit,edit_thumb, … остаются). - Миграция схемы базы данных.
Decisions
Цвет хранится в том же edit_params, рядом с light.
- Обоснование:
EditParamsуже расширяется полемColor ColorParams;map[string]anyна бэкенде иEditParamsна клиенте принимают новый ключ без cambios схемы. - Альтернатива: отдельная колонка/таблица для цветовых правок — отвергнута, добавила бы миграцию и не сочетается с текущей моделью хранения.
Порядок операций при рендере: свет, затем цвет.
- Обоснование: цветовые коррекции (баланс белого, насыщенность) логично применять к уже скорректированной по тональности картинке; сервер и клиент должны следовать одному порядку, иначе превью не совпадёт с результатом.
- Альтернатива: цвет до света — отвергнуто, несоответствие Lightroom-поведению и текущему клиентскому конвейеру.
Цветовая математика реализована трижды и держится в паритете: Go (сервер), TypeScript (клиент), Rust/WASM (живой превью).
- Обоснование: текущий подход со «Светом» (
applyLightв Go,applyLightToPixelsв TS,light.wasmв Rust, тестwasmMatchesReference) гарантирует, что превью в браузере совпадает с финальным рендером. Для цвета повторяем эту схему. - Альтернатива: считать цвет только на сервере и показывать превью после рендера — отвергнуто, пользователь ожидает мгновенного отклика при движении ползунков, как сейчас со светом.
Цветовые параметры применяются в пространстве RGB с промежуточным HSL.
- Обоснование: баланс белого, вибрация, насыщенность и тонирование естественно выражаются через HSL; пиксели хранятся в RGBA, поэтому пересчёт RGB→HSL→RGB на пиксельном цикле.
- Альтернатива: чистые операции над RGB-каналами — отвергнуто, менее точно соответствует цветовым инструментам Lightroom.
Risks / Trade-offs
[Расхождение реализаций Go / TS / Rust] → mitigation: тест паритета
wasmMatchesReference расширяется на цвет, изменение любой реализации требует
обновления остальных и теста.
[Сложность UI] → mitigation: цветовой микшер по 8 оттенкам и точечное тонирование
добавляют много элементов управления в PhotoEditPanel; раздел «Цвет» делаем
сворачиваемым, как «Свет».
[Производительность рендера] → mitigation: цвет добавляет один пиксельный проход
(или объединяется с существующим циклом света); параллелизация по строкам
(applyLightRows) остаётся.
[Обратная совместимость] → mitigation: отсутствие ключа color в edit_params
трактуется как нулевые цветовые правки; старые фото не затрагиваются.
Migration Plan
Миграция схемы не требуется: edit_params — JSON-блоб, новый ключ color
дополняет существующий light. Откат: удаление ключа color из edit_params
возвращает фото к состоянию без цветовых правок.
Open Questions
Нет — все проектные решения (объём цвета, живой превью с паритетом) согласованы.