perf(photos): ускорить рендер правок в 9 раз

Расчёт правок считал math.Pow для каждого канала каждого пикселя, из-за чего
большое фото обрабатывалось больше секунды. Факторы правок теперь считаются
один раз на рендер, квадраты считаются умножением, а строки обрабатываются
параллельно.

Обработка кадра 4000×3000 сократилась с 1.23 с до 0.14 с. Задержка от
сохранения правок до появления отредактированной версии в сетке и просмотре:
около 0.15 с для небольшого фото и 0.6 с для большого (полноразмерная
версия догружается следом, через 1.4 с).
This commit is contained in:
iu
2026-10-20 23:19:03 +03:00
parent cd3d17c0f0
commit 6d3d7cbdd4
3 changed files with 121 additions and 22 deletions
+36 -5
View File
@@ -3,6 +3,8 @@ package photos
import (
"bytes"
"image"
"runtime"
"sync"
"github.com/disintegration/imaging"
"github.com/gen2brain/webp"
@@ -27,20 +29,49 @@ func applyLight(src image.Image, p LightParams) *image.NRGBA {
red := curveLUT(p.Curve.Red)
green := curveLUT(p.Curve.Green)
blue := curveLUT(p.Curve.Blue)
adj := newLightAdjuster(p)
rows := dst.Rect.Dy()
workers := runtime.GOMAXPROCS(0)
if workers > rows {
workers = rows
}
if workers <= 1 {
applyLightRows(dst, 0, rows, adj, &master, &red, &green, &blue)
return dst
}
// Рендер больших фото идёт по строкам параллельно.
chunk := (rows + workers - 1) / workers
var wg sync.WaitGroup
for from := 0; from < rows; from += chunk {
to := from + chunk
if to > rows {
to = rows
}
wg.Add(1)
go func(from, to int) {
defer wg.Done()
applyLightRows(dst, from, to, adj, &master, &red, &green, &blue)
}(from, to)
}
wg.Wait()
return dst
}
// applyLightRows applies the adjustments to the given range of rows.
func applyLightRows(dst *image.NRGBA, from, to int, adj lightAdjuster, master, red, green, blue *[256]uint8) {
width := dst.Rect.Dx()
for y := 0; y < dst.Rect.Dy(); y++ {
for y := from; y < to; y++ {
row := dst.Pix[y*dst.Stride : y*dst.Stride+width*4]
for i := 0; i+3 < len(row); i += 4 {
if row[i+3] == 0 {
continue
}
row[i] = uint8(adjustChannel(float64(row[i]), p, &master, &red))
row[i+1] = uint8(adjustChannel(float64(row[i+1]), p, &master, &green))
row[i+2] = uint8(adjustChannel(float64(row[i+2]), p, &master, &blue))
row[i] = uint8(adj.apply(float64(row[i]), master, red))
row[i+1] = uint8(adj.apply(float64(row[i+1]), master, green))
row[i+2] = uint8(adj.apply(float64(row[i+2]), master, blue))
}
}
return dst
}
// decodeAndAdjust decodes the original and applies the light adjustments to