mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
f0d204d0e9
commit
f27c5b666e
5 files changed
+221
-31
No files matched your search
@@ -0,0 +1,195 @@
|
||||
// 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 (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/tdewolff/canvas"
|
||||
)
|
||||
|
||||
type shdPaint struct {
|
||||
gradient canvas.Gradient
|
||||
extend int
|
||||
mapType string
|
||||
period float64
|
||||
}
|
||||
|
||||
// newShdPaint 创建渐变画刷
|
||||
// 入参: gradient 渐变对象, extend 延伸方向, mapType 绘制方式, mapUnit 渐变区间长度
|
||||
// 返回: *shdPaint 渐变画刷
|
||||
func newShdPaint(gradient canvas.Gradient, extend, mapType string, mapUnit float64) *shdPaint {
|
||||
flags, _ := strconv.Atoi(extend)
|
||||
length := 0.0
|
||||
switch g := gradient.(type) {
|
||||
case *canvas.LinearGradient:
|
||||
length = g.End.Sub(g.Start).Length()
|
||||
case *canvas.RadialGradient:
|
||||
length = g.C1.Sub(g.C0).Length()
|
||||
}
|
||||
period := 1.0
|
||||
if mapUnit > 0 && length > 0 {
|
||||
period = mapUnit / length
|
||||
}
|
||||
return &shdPaint{gradient: gradient, extend: flags, mapType: mapType, period: period}
|
||||
}
|
||||
|
||||
// resolveShdPaint 解析渐变画刷和裁剪区域
|
||||
// 入参: ctx 画布上下文, paint 画刷
|
||||
// 返回: any 画刷, *canvas.Path 裁剪区域
|
||||
func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path) {
|
||||
s, ok := paint.(*shdPaint)
|
||||
if !ok {
|
||||
return paint, nil
|
||||
}
|
||||
switch g := s.gradient.(type) {
|
||||
case *canvas.LinearGradient:
|
||||
clip := axialShdClip(ctx, g, s.extend)
|
||||
if s.mapType != "Reflect" {
|
||||
return g, clip
|
||||
}
|
||||
d := g.End.Sub(g.Start)
|
||||
axis := canvas.Matrix{{d.X, -d.Y, g.Start.X}, {d.Y, d.X, g.Start.Y}}
|
||||
area := shdCanvasBounds(ctx).Transform(axis.Inv())
|
||||
lo, hi := s.rangeLimits(area.X0, area.X1)
|
||||
if hi <= lo {
|
||||
return g, &canvas.Path{}
|
||||
}
|
||||
gradient := reflectShdGrad(g.Grad, s.period, lo, hi)
|
||||
return gradient.ToLinear(g.Start.Add(d.Mul(lo)), g.Start.Add(d.Mul(hi))), clip
|
||||
case *canvas.RadialGradient:
|
||||
d, dr := g.C1.Sub(g.C0), g.R1-g.R0
|
||||
length := d.Length()
|
||||
if length >= math.Abs(dr) {
|
||||
return g, nil
|
||||
}
|
||||
clip := radialShdClip(ctx, g, s.extend)
|
||||
if s.mapType != "Reflect" {
|
||||
return g, clip
|
||||
}
|
||||
lo, hi := 0.0, 1.0
|
||||
if s.extend != 0 {
|
||||
distance := 0.0
|
||||
bounds := shdCanvasBounds(ctx)
|
||||
for _, point := range []canvas.Point{{X: bounds.X0, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y1}, {X: bounds.X0, Y: bounds.Y1}} {
|
||||
distance = math.Max(distance, point.Sub(g.C0).Length())
|
||||
}
|
||||
limit := (distance + g.R0) / (math.Abs(dr) - length)
|
||||
lo, hi = s.rangeLimits(-limit, limit)
|
||||
}
|
||||
if dr > 0 {
|
||||
lo = math.Max(lo, -g.R0/dr)
|
||||
} else {
|
||||
hi = math.Min(hi, -g.R0/dr)
|
||||
}
|
||||
if hi <= lo {
|
||||
return g, &canvas.Path{}
|
||||
}
|
||||
gradient := reflectShdGrad(g.Grad, s.period, lo, hi)
|
||||
return gradient.ToRadial(g.C0.Add(d.Mul(lo)), math.Max(0, g.R0+dr*lo), g.C0.Add(d.Mul(hi)), math.Max(0, g.R0+dr*hi)), clip
|
||||
}
|
||||
return s.gradient, nil
|
||||
}
|
||||
|
||||
// rangeLimits 限制渐变延伸区间
|
||||
// 入参: lo 区间起点, hi 区间终点
|
||||
// 返回: float64 起点, float64 终点
|
||||
func (s *shdPaint) rangeLimits(lo, hi float64) (float64, float64) {
|
||||
if s.extend&1 == 0 {
|
||||
lo = math.Max(lo, 0)
|
||||
}
|
||||
if s.extend&2 == 0 {
|
||||
hi = math.Min(hi, 1)
|
||||
}
|
||||
return lo, hi
|
||||
}
|
||||
|
||||
// shdCanvasBounds 获取渐变坐标系下的画布边界
|
||||
// 入参: ctx 画布上下文
|
||||
// 返回: canvas.Rect 画布边界
|
||||
func shdCanvasBounds(ctx *canvas.Context) canvas.Rect {
|
||||
origin := ctx.CoordView().Dot(canvas.Point{})
|
||||
view := ctx.CoordSystemView().Mul(ctx.View()).Translate(origin.X, origin.Y)
|
||||
if view.Det() == 0 {
|
||||
return canvas.Rect{}
|
||||
}
|
||||
width, height := ctx.Size()
|
||||
return (canvas.Rect{X1: width, Y1: height}).Transform(view.Inv())
|
||||
}
|
||||
|
||||
// radialShdClip 获取包含双圆的径向渐变裁剪区域
|
||||
// 入参: ctx 画布上下文, g 径向渐变, extend 延伸方向
|
||||
// 返回: *canvas.Path 裁剪区域
|
||||
func radialShdClip(ctx *canvas.Context, g *canvas.RadialGradient, extend int) *canvas.Path {
|
||||
inner, outer := g.C0, g.C1
|
||||
r0, r1 := g.R0, g.R1
|
||||
if r1 < r0 {
|
||||
inner, outer, r0, r1 = outer, inner, r1, r0
|
||||
extend = (extend&1)<<1 | (extend&2)>>1
|
||||
}
|
||||
if extend == 3 {
|
||||
return nil
|
||||
}
|
||||
var clip *canvas.Path
|
||||
if extend&2 == 0 {
|
||||
clip = canvas.Circle(r1).Translate(outer.X, outer.Y)
|
||||
} else {
|
||||
clip = shdCanvasBounds(ctx).ToPath()
|
||||
}
|
||||
if extend&1 == 0 && r0 > 0 {
|
||||
clip = clip.Not(canvas.Circle(r0).Translate(inner.X, inner.Y))
|
||||
}
|
||||
return clip
|
||||
}
|
||||
|
||||
// reflectShdGrad 构造镜像渐变颜色分段
|
||||
// 入参: gradient 颜色分段, period 周期长度, lo 起点, hi 终点
|
||||
// 返回: canvas.Grad 镜像颜色分段
|
||||
func reflectShdGrad(gradient canvas.Grad, period, lo, hi float64) canvas.Grad {
|
||||
base := append(canvas.Grad{{Offset: 0, Color: gradient.At(0)}}, gradient...)
|
||||
base = append(base, canvas.Stop{Offset: 1, Color: gradient.At(1)})
|
||||
reversed := make(canvas.Grad, len(base))
|
||||
for i, stop := range base {
|
||||
reversed[len(base)-1-i] = canvas.Stop{Offset: 1 - stop.Offset, Color: stop.Color}
|
||||
}
|
||||
result := canvas.Grad{{Offset: 0, Color: gradient.At(reflectShdPosition(lo / period))}}
|
||||
for i, last := math.Floor(lo/period), math.Ceil(hi/period); i < last; i++ {
|
||||
stops := base
|
||||
if math.Mod(i, 2) != 0 {
|
||||
stops = reversed
|
||||
}
|
||||
for _, stop := range stops {
|
||||
position := (i + stop.Offset) * period
|
||||
offset := (position - lo) / (hi - lo)
|
||||
if lo < position && position < hi && result[len(result)-1].Offset < offset {
|
||||
result = append(result, canvas.Stop{Offset: offset, Color: stop.Color})
|
||||
}
|
||||
}
|
||||
}
|
||||
return append(result, canvas.Stop{Offset: 1, Color: gradient.At(reflectShdPosition(hi / period))})
|
||||
}
|
||||
|
||||
// reflectShdPosition 获取镜像周期内的位置
|
||||
// 入参: position 渐变位置
|
||||
// 返回: float64 周期内的位置
|
||||
func reflectShdPosition(position float64) float64 {
|
||||
index := math.Floor(position)
|
||||
position -= index
|
||||
if math.Mod(index, 2) != 0 {
|
||||
return 1 - position
|
||||
}
|
||||
return position
|
||||
}
|
||||
Reference in new issue
Block a user