feat(photos): добавить эффекты и настройки панели редактора

- текстура, чёткость, дымка, виньетка и зерно в неразрушающем редакторе
- живой WebGL2-предпросмотр эффектов и CPU-фолбэк
- секции редактора свёрнуты по умолчанию, состояние в localStorage
- вкладка «Редактирование» доступна только из просмотрщика
This commit is contained in:
iu
2026-10-24 22:57:30 +03:00
parent 4cc5c0f340
commit c953008a86
18 changed files with 3548 additions and 173 deletions
+13 -2
View File
@@ -6,6 +6,7 @@ import (
"crypto/sha256"
"encoding/hex"
"errors"
"hash/fnv"
"io"
"log/slog"
"os"
@@ -202,7 +203,7 @@ func (w *Worker) generateEdit(ctx context.Context, photoID string) {
w.failEdit(ctx, photoID, "fetch original: "+err.Error())
return
}
edited, err := decodeAndAdjust(data, params)
edited, err := decodeAndAdjust(data, params, editSeed(photoID))
if err != nil {
w.failEdit(ctx, photoID, "decode original: "+err.Error())
return
@@ -211,7 +212,8 @@ func (w *Worker) generateEdit(ctx context.Context, photoID string) {
if err != nil {
w.failEdit(ctx, photoID, "build edited variants: "+err.Error())
return
} // The edit belongs to this photo only, so the variants are not shared
}
// The edit belongs to this photo only, so the variants are not shared
// with the copies of the same original.
owner := []string{photoID}
if err := w.createVariant(ctx, owner, RoleEditThumb, thumb); err != nil {
@@ -240,6 +242,15 @@ func (w *Worker) generateEdit(ctx context.Context, photoID string) {
w.notify(ctx, photoID)
}
// editSeed is the deterministic seed of the grain render: the FNV-1a hash of the
// photo id. It depends on the photo and not on the parameters, so a repeated
// render of the same edit produces the same noise pattern.
func editSeed(photoID string) uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(photoID))
return h.Sum64()
}
func (w *Worker) failEdit(ctx context.Context, photoID, msg string) {
slog.Warn("edit job failed", "photo", photoID, "error", msg)
_ = w.svc.store.FailEditJob(ctx, photoID, msg, maxVariantAttempts)