topfans/backend/gateway/service/compositor/finish.go
Lenticular Studio Agent 67cf3d4177 chore: 清理 laserCompositor 微服务残留
- 删除已弃用的 compositor_client.go
- 删除激光合成微服务代码
- 添加 gateway 合成控制器和测试文件
- 添加 Dify prompt 补丁脚本

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-23 22:44:03 +08:00

152 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package compositor
import (
"image"
"math"
)
// DrawMetallicSheen 绘制对角金属反光梯度screen 混合)
func DrawMetallicSheen(dst *image.NRGBA, cfg GratingConfig) {
cw := dst.Bounds().Dx()
ch := dst.Bounds().Dy()
deg := cfg.SheenBandAngle
if deg == 0 {
deg = 135
}
// 与色带垂直方向
deg += 90
rad := deg * math.Pi / 180
diag := math.Sqrt(float64(cw*cw + ch*ch))
cx := float64(cw) / 2
cy := float64(ch) / 2
x0 := cx - math.Cos(rad)*diag/2
y0 := cy - math.Sin(rad)*diag/2
x1 := cx + math.Cos(rad)*diag/2
y1 := cy + math.Sin(rad)*diag/2
alpha := 0.10 // 调低金属反光强度,让人物更清晰
for y := 0; y < ch; y++ {
for x := 0; x < cw; x++ {
// 计算投影位置 0-1
t := projectPointOnLine(float64(x), float64(y), x0, y0, x1, y1)
// 中间带高亮,两端透明
var brightness float64
if t > 0.30 && t < 0.70 {
// 高亮区域:余弦波
brightness = math.Cos((t-0.50)/0.20*math.Pi) * 0.5
if brightness < 0 {
brightness = 0
}
}
if brightness < 0.01 {
continue
}
applyScreenPixel(dst, x, y, 220.0/255.0, 230.0/255.0, 240.0/255.0, brightness*alpha)
}
}
}
func projectPointOnLine(px, py, lx0, ly0, lx1, ly1 float64) float64 {
dx := lx1 - lx0
dy := ly1 - ly0
lenSq := dx*dx + dy*dy
if lenSq < 0.001 {
return 0.5
}
t := ((px-lx0)*dx + (py-ly0)*dy) / lenSq
return clamp01(t)
}
// DrawFrostMatte 磨砂哑光层
func DrawFrostMatte(dst *image.NRGBA, variantIndex int) {
cw := dst.Bounds().Dx()
ch := dst.Bounds().Dy()
frost := 0.04 // 调低磨砂强度,真实镭射卡没有雾面效果
// 颗粒噪声
seed := variantIndex*1201 + 77
n := int(500 + frost*7500)
for k := 0; k < n; k++ {
seed = (seed*9301 + 49297) % 233280
x := seed % cw
seed = (seed*9301 + 49297) % 233280
y := seed % ch
seed = (seed*9301 + 49297) % 233280
v := 190 + seed%65
a := 0.022 + float64(seed%55)/1000.0
idx := dst.PixOffset(x, y)
// soft-light 混合简化
factor := a * (0.35 + frost*0.25)
dst.Pix[idx] = uint8(clamp01(float64(dst.Pix[idx])/255.0+float64(v)*factor/255.0) * 255)
dst.Pix[idx+1] = uint8(clamp01(float64(dst.Pix[idx+1])/255.0+float64(v)*factor/255.0) * 255)
dst.Pix[idx+2] = uint8(clamp01(float64(dst.Pix[idx+2])/255.0+float64(v+6)*factor/255.0) * 255)
}
}
// DrawFilmGrain 胶片颗粒
func DrawFilmGrain(dst *image.NRGBA, variantIndex int) {
cw := dst.Bounds().Dx()
ch := dst.Bounds().Dy()
maxDots := 8
seed := variantIndex*313 + 17
for i := 0; i < maxDots; i++ {
seed = (seed*1103515245 + 12345) & 0x7fffffff
fx := float64(seed) / float64(0x7fffffff) * float64(cw)
seed = (seed*1103515245 + 12345) & 0x7fffffff
fy := float64(seed) / float64(0x7fffffff) * float64(ch)
x := int(fx)
y := int(fy)
if x < 0 || x >= cw || y < 0 || y >= ch {
continue
}
seed = (seed*1103515245 + 12345) & 0x7fffffff
radius := float64(seed)/float64(0x7fffffff)*1.5 + 0.3
alpha := 0.08 + float64(seed%12)/100.0
drawSoftDot(dst, x, y, int(radius), alpha)
}
}
func drawSoftDot(dst *image.NRGBA, cx, cy, radius int, alpha float64) {
for dy := -radius; dy <= radius; dy++ {
for dx := -radius; dx <= radius; dx++ {
if dx*dx+dy*dy > radius*radius {
continue
}
px, py := cx+dx, cy+dy
if px < 0 || px >= dst.Bounds().Dx() || py < 0 || py >= dst.Bounds().Dy() {
continue
}
dist := math.Sqrt(float64(dx*dx + dy*dy))
fade := 1 - dist/float64(radius)
idx := dst.PixOffset(px, py)
sc := alpha * fade
dst.Pix[idx] = uint8(clamp01(float64(dst.Pix[idx])/255.0+1.0*sc) * 255)
dst.Pix[idx+1] = uint8(clamp01(float64(dst.Pix[idx+1])/255.0+1.0*sc) * 255)
dst.Pix[idx+2] = uint8(clamp01(float64(dst.Pix[idx+2])/255.0+1.0*sc) * 255)
}
}
}
// DrawGratingFinish 光栅 finish颗粒 + 磨砂
func DrawGratingFinish(dst *image.NRGBA, variantIndex int) {
DrawFilmGrain(dst, variantIndex)
DrawFrostMatte(dst, variantIndex)
}