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
@@ -1,7 +1,9 @@
package photos
import (
"bytes"
"image"
"image/png"
"testing"
"time"
)
@@ -29,6 +31,73 @@ func BenchmarkApplyLight(b *testing.B) {
b.Logf("кадров: %d", b.N)
}
// BenchmarkBoxBlur3 measures the O(n) box-blur passes on a 12 MP luminance
// plane with the radius the clarity pass uses for that frame.
func BenchmarkBoxBlur3(b *testing.B) {
w, h := 4000, 3000
src := make([]uint8, w*h)
for i := range src {
src[i] = uint8(i % 251)
}
radius := int(0.008 * float64(min(w, h)))
if radius < 2 {
radius = 2
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = boxBlur3(src, w, h, radius)
}
b.StopTimer()
b.Logf("кадров: %d, радиус: %d", b.N, radius)
}
// BenchmarkFullRenderWithEffects measures the whole edited render of a 12 MP
// photo with all effects enabled: decode, light, color, dehaze, texture,
// clarity, vignette and grain. This is the work the background worker does for
// one edit job.
func BenchmarkFullRenderWithEffects(b *testing.B) {
w, h := 4000, 3000
src := image.NewNRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
i := y*src.Stride + x*4
src.Pix[i] = uint8(x*255/w) ^ uint8(y*255/h)
src.Pix[i+1] = uint8(x * 3)
src.Pix[i+2] = uint8(y * 5)
src.Pix[i+3] = 255
}
}
var buf bytes.Buffer
encoder := png.Encoder{CompressionLevel: png.BestSpeed}
if err := encoder.Encode(&buf, src); err != nil {
b.Fatalf("encode png: %v", err)
}
data := buf.Bytes()
params := EditParams{
Light: LightParams{Exposure: 0.2, Contrast: 12, Shadows: 15},
Color: ColorParams{Vibrance: 20, Temperature: 8},
Effects: EffectsParams{
Texture: 30,
Clarity: 25,
Dehaze: 20,
Vignette: VignetteParams{
Mode: VignetteColor, Amount: 35, Color: "#ff8800", Highlights: 25,
},
Grain: GrainParams{Amount: 30, Size: 30, Roughness: 55},
},
}
seed := editSeed("benchmark-photo")
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := decodeAndAdjust(data, params, seed); err != nil {
b.Fatalf("decode and adjust: %v", err)
}
}
b.StopTimer()
b.Logf("кадров: %d, эффекты: текстура/чёткость/дымка/виньетка/зерно", b.N)
}
func TestRenderSpeed(t *testing.T) {
w, h := 4000, 3000
src := image.NewNRGBA(image.Rect(0, 0, w, h))