Files
ds-galo/openspec/changes/photo-color-editing/design.md
T
iu 502f2766d7 docs(openspec): изменение photo-color-editing
Предложение, дельта-спека photos, дизайн и задачи для инструмента «Цвет».
2026-10-23 21:57:51 +03:00

6.6 KiB
Raw Blame History

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

Нет — все проектные решения (объём цвета, живой превью с паритетом) согласованы.