Files
ofdgo/ofdgo_renderer_gradient_gg.go
T
2026-09-23 21:05:47 +08:00

117 lines
3.9 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"fmt"
"image/color"
"math"
"github.com/gogpu/gg"
)
// ggGradientBrush 仅在颜色插值及延伸等价时使用原生渐变,否则保留OFD采样
// 入参: gradient 局部渐变, matrix 局部到像素变换
// 返回: gg.Brush 画刷, error 类型或变换错误
func ggGradientBrush(gradient *RasterGradient, matrix RasterMatrix) (gg.Brush, error) {
if gradient.Kind != RasterLinear && gradient.Kind != RasterRadial {
return nil, fmt.Errorf("unsupported gradient %d", gradient.Kind)
}
inverse, err := matrix.Inverse()
if err != nil {
return nil, err
}
if brush := ggNativeGradient(gradient, matrix, inverse); brush != nil {
return brush, nil
}
return gg.NewCustomBrush(func(x, y float64) gg.RGBA {
p := inverse.Apply(RasterPoint{X: x, Y: y})
return ggRGBA(gradient.At(p.X, p.Y))
}), nil
}
// ggNativeGradient 将恒定非预乘色的透明度渐变映射为GG原生画刷
// GG使用线性光颜色插值,不接受改变色相、重复节点或单侧透明延伸的OFD渐变
// 入参: g 局部渐变, matrix 正变换, inverse 逆变换
// 返回: gg.Brush 等价原生画刷,不匹配时为空
func ggNativeGradient(g *RasterGradient, matrix, inverse RasterMatrix) gg.Brush {
if len(g.Stops) < 2 {
return nil
}
var reference color.RGBA
for i, stop := range g.Stops {
if !finite(stop.Offset) || stop.Offset < 0 || stop.Offset > 1 || i > 0 && stop.Offset <= g.Stops[i-1].Offset {
return nil
}
if stop.Color.A > reference.A {
reference = stop.Color
}
}
if reference.A == 0 {
return nil
}
for _, stop := range g.Stops {
c := stop.Color
if uint32(c.R)*uint32(reference.A) != uint32(reference.R)*uint32(c.A) ||
uint32(c.G)*uint32(reference.A) != uint32(reference.G)*uint32(c.A) ||
uint32(c.B)*uint32(reference.A) != uint32(reference.B)*uint32(c.A) {
return nil
}
}
extend, period := gg.ExtendPad, 1.0
if spread := g.Spread; spread != nil {
if spread.Extend != 3 {
return nil
}
switch spread.MapType {
case "Repeat":
extend = gg.ExtendRepeat
case "Reflect":
extend = gg.ExtendReflect
}
if extend != gg.ExtendPad && spread.Period > 0 {
period = spread.Period
}
}
stops := make([]gg.ColorStop, len(g.Stops))
for i, stop := range g.Stops {
c := ggRGBA(reference)
c.A = float64(stop.Color.A) / 255
stops[i] = gg.ColorStop{Offset: stop.Offset, Color: c}
}
start := matrix.Apply(g.Start)
if g.Kind == RasterLinear {
dx, dy := g.End.X-g.Start.X, g.End.Y-g.Start.Y
length := dx*dx + dy*dy
if !rasterPositive(length) {
return nil
}
// 渐变参数为协向量,非等比缩放或错切不能直接变换两个端点
x, y := (inverse[0]*dx+inverse[1]*dy)/length, (inverse[2]*dx+inverse[3]*dy)/length
factor := period / (x*x + y*y)
brush := gg.NewLinearGradientBrush(start.X, start.Y, start.X+x*factor, start.Y+y*factor)
brush.Stops, brush.Extend = stops, extend
return brush
}
xscale, yscale := math.Hypot(matrix[0], matrix[1]), math.Hypot(matrix[2], matrix[3])
if g.Start != g.End || g.R0 != 0 || !rasterPositive(g.R1) ||
math.Abs(xscale-yscale) > 1e-12*xscale || math.Abs(matrix[0]*matrix[2]+matrix[1]*matrix[3]) > 1e-12*xscale*yscale {
return nil
}
brush := gg.NewRadialGradientBrush(start.X, start.Y, 0, g.R1*xscale*period)
brush.Stops, brush.Extend = stops, extend
return brush
}