feat(photos): инструмент «Цвет» с WebGL-превью и пипеткой
- неразрушающие правки цвета: баланс белого, вибрация/насыщенность, цветовой микшер, колористика и поканальная коррекция - серверный рендер цвета в Go и паритетная реализация в WASM - живое превью на WebGL2 (кадр считает GPU), CPU/WASM-фолбэк - пипетка баланса белого по нейтральной точке - новые контролы: слайдеры с градиентами, цветовые колёса, двойной клик по контролу сбрасывает значение - тесты: параметры, рендер, паритет JS/WASM, e2e сохранения и сброса
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"galo/internal/albums"
|
||||
"galo/internal/photos"
|
||||
)
|
||||
|
||||
// A color edit must survive the round trip: the parameters are stored in the
|
||||
// photo metadata, the edited version is rendered, and a reset returns the
|
||||
// photo to its original look.
|
||||
func TestColorEditRoundTrip(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st, closeDB := openStack(t, filepath.Join(t.TempDir(), "galo.db"), t.TempDir())
|
||||
defer closeDB()
|
||||
|
||||
user, _, err := st.auth.Register(ctx, "color@test.local", "", "password123", "C")
|
||||
if err != nil {
|
||||
t.Fatalf("register: %v", err)
|
||||
}
|
||||
album, err := st.albums.Create(ctx, user.ID, albums.CreateInput{Title: "Color"})
|
||||
if err != nil {
|
||||
t.Fatalf("create album: %v", err)
|
||||
}
|
||||
p, err := st.photos.Upload(ctx, photos.UploadInput{
|
||||
AlbumID: album.ID, UserID: user.ID, Filename: "c.png", Data: makePNG(t),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("upload: %v", err)
|
||||
}
|
||||
|
||||
w := photos.NewWorker(st.photos, time.Second, 10, time.Minute, 2)
|
||||
w.ProcessOnce(ctx)
|
||||
|
||||
color := photos.ColorParams{Temperature: 25, Vibrance: 20}
|
||||
color.Mixer.Red.Saturation = 30
|
||||
color.Grading.Shadows = photos.ColorWheel{Hue: 220, Saturation: 40}
|
||||
edited, err := st.photos.EditPhoto(ctx, p.ID, user.ID, photos.EditParams{
|
||||
Light: photos.LightParams{Exposure: 0.3},
|
||||
Color: color,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("edit: %v", err)
|
||||
}
|
||||
if edited.EditParams == nil {
|
||||
t.Fatal("edit returned no parameters")
|
||||
}
|
||||
|
||||
w.ProcessOnce(ctx)
|
||||
got, err := st.photos.Get(ctx, p.ID, user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("get: %v", err)
|
||||
}
|
||||
if got.EditPreviewURL == "" {
|
||||
t.Fatalf("no edited preview url: %+v", got)
|
||||
}
|
||||
if _, ok := got.EditParams["color"]; !ok {
|
||||
t.Fatalf("color params not stored: %+v", got.EditParams)
|
||||
}
|
||||
|
||||
reset, err := st.photos.ResetEdit(ctx, p.ID, user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("reset: %v", err)
|
||||
}
|
||||
if len(reset.EditParams) != 0 {
|
||||
t.Fatalf("reset kept params: %+v", reset.EditParams)
|
||||
}
|
||||
if reset.EditURL != "" || reset.EditPreviewURL != "" {
|
||||
t.Fatalf("reset kept the edited version: %+v", reset)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user