mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
This commit is contained in:
1 parent
f0d204d0e9
commit
f27c5b666e
5 files changed
+221
-31
No files matched your search
@@ -184,6 +184,8 @@ type StrokeColor struct {
|
|||||||
|
|
||||||
// AxialShd 轴向渐变
|
// AxialShd 轴向渐变
|
||||||
type AxialShd struct {
|
type AxialShd struct {
|
||||||
|
MapType string `xml:"MapType,attr"`
|
||||||
|
MapUnit float64 `xml:"MapUnit,attr"`
|
||||||
Extend string `xml:"Extend,attr"`
|
Extend string `xml:"Extend,attr"`
|
||||||
StartPoint string `xml:"StartPoint,attr"`
|
StartPoint string `xml:"StartPoint,attr"`
|
||||||
EndPoint string `xml:"EndPoint,attr"`
|
EndPoint string `xml:"EndPoint,attr"`
|
||||||
@@ -192,6 +194,8 @@ type AxialShd struct {
|
|||||||
|
|
||||||
// RadialShd 径向渐变
|
// RadialShd 径向渐变
|
||||||
type RadialShd struct {
|
type RadialShd struct {
|
||||||
|
MapType string `xml:"MapType,attr"`
|
||||||
|
MapUnit float64 `xml:"MapUnit,attr"`
|
||||||
Extend string `xml:"Extend,attr"`
|
Extend string `xml:"Extend,attr"`
|
||||||
StartPoint string `xml:"StartPoint,attr"`
|
StartPoint string `xml:"StartPoint,attr"`
|
||||||
StartRadius float64 `xml:"StartRadius,attr"`
|
StartRadius float64 `xml:"StartRadius,attr"`
|
||||||
|
|||||||
+6
-11
@@ -165,10 +165,10 @@ func parseFillPaint(fillColor *FillColor, x, y, pageH float64) any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
|
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
|
||||||
return gradient
|
return newShdPaint(gradient, fillColor.AxialShd.Extend, fillColor.AxialShd.MapType, fillColor.AxialShd.MapUnit)
|
||||||
}
|
}
|
||||||
if gradient := parseRadialShdGradient(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
|
if gradient := parseRadialShdGradient(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
|
||||||
return gradient
|
return newShdPaint(gradient, fillColor.RadialShd.Extend, fillColor.RadialShd.MapType, fillColor.RadialShd.MapUnit)
|
||||||
}
|
}
|
||||||
if fillColor.AxialShd != nil {
|
if fillColor.AxialShd != nil {
|
||||||
return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
|
return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
|
||||||
@@ -209,10 +209,10 @@ func parseStrokePaint(strokeColor *StrokeColor, x, y, pageH float64) any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
|
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
|
||||||
return gradient
|
return newShdPaint(gradient, strokeColor.AxialShd.Extend, strokeColor.AxialShd.MapType, strokeColor.AxialShd.MapUnit)
|
||||||
}
|
}
|
||||||
if gradient := parseRadialShdGradient(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
|
if gradient := parseRadialShdGradient(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
|
||||||
return gradient
|
return newShdPaint(gradient, strokeColor.RadialShd.Extend, strokeColor.RadialShd.MapType, strokeColor.RadialShd.MapUnit)
|
||||||
}
|
}
|
||||||
if strokeColor.AxialShd != nil {
|
if strokeColor.AxialShd != nil {
|
||||||
return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha)
|
return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha)
|
||||||
@@ -312,14 +312,9 @@ func parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH float64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// axialShdClip 获取轴向渐变的延伸裁剪区域
|
// axialShdClip 获取轴向渐变的延伸裁剪区域
|
||||||
// 入参: ctx 画布上下文, paint 画刷, shading 轴向渐变节点
|
// 入参: ctx 画布上下文, gradient 轴向渐变, extend 延伸方向
|
||||||
// 返回: *canvas.Path 裁剪区域
|
// 返回: *canvas.Path 裁剪区域
|
||||||
func axialShdClip(ctx *canvas.Context, paint any, shading *AxialShd) *canvas.Path {
|
func axialShdClip(ctx *canvas.Context, gradient *canvas.LinearGradient, extend int) *canvas.Path {
|
||||||
gradient, ok := paint.(*canvas.LinearGradient)
|
|
||||||
if !ok || shading == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
extend, _ := strconv.Atoi(shading.Extend)
|
|
||||||
if extend == 3 {
|
if extend == 3 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ type pathStyle struct {
|
|||||||
strokeColor color.Color
|
strokeColor color.Color
|
||||||
fillPaint any
|
fillPaint any
|
||||||
strokePaint any
|
strokePaint any
|
||||||
fillAxialShd *AxialShd
|
|
||||||
strokeAxialShd *AxialShd
|
|
||||||
fillPattern *Pattern
|
fillPattern *Pattern
|
||||||
fillPatternColor color.Color
|
fillPatternColor color.Color
|
||||||
lineWidth float64
|
lineWidth float64
|
||||||
@@ -68,7 +66,6 @@ func (s *pathStyle) applyFillColor(fill *FillColor, bx, by, pageH float64, alpha
|
|||||||
s.fillPatternColor = patternColor(fillColorNode)
|
s.fillPatternColor = patternColor(fillColorNode)
|
||||||
s.fillColor = parseFillColor(fillColorNode)
|
s.fillColor = parseFillColor(fillColorNode)
|
||||||
s.fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
s.fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
||||||
s.fillAxialShd = fillColorNode.AxialShd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyStrokeColor 应用描边颜色
|
// applyStrokeColor 应用描边颜色
|
||||||
@@ -77,7 +74,6 @@ func (s *pathStyle) applyStrokeColor(stroke *StrokeColor, bx, by, pageH float64,
|
|||||||
strokeColorNode := withStrokeAlpha(stroke, alpha)
|
strokeColorNode := withStrokeAlpha(stroke, alpha)
|
||||||
s.strokeColor = parseStrokeColor(strokeColorNode)
|
s.strokeColor = parseStrokeColor(strokeColorNode)
|
||||||
s.strokePaint = parseStrokePaint(strokeColorNode, bx, by, pageH)
|
s.strokePaint = parseStrokePaint(strokeColorNode, bx, by, pageH)
|
||||||
s.strokeAxialShd = strokeColorNode.AxialShd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathLineCap 转换线帽样式
|
// pathLineCap 转换线帽样式
|
||||||
@@ -215,7 +211,8 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
style.fillPaint = style.fillColor
|
style.fillPaint = style.fillColor
|
||||||
}
|
}
|
||||||
if shouldFill {
|
if shouldFill {
|
||||||
fillClip := intersectClipPath(clipPath, axialShdClip(ctx, style.fillPaint, style.fillAxialShd))
|
fillPaint, shadingClip := resolveShdPaint(ctx, style.fillPaint)
|
||||||
|
fillClip := intersectClipPath(clipPath, shadingClip)
|
||||||
fp := p
|
fp := p
|
||||||
fillRule := canvas.NonZero
|
fillRule := canvas.NonZero
|
||||||
if obj.Rule == "Even-Odd" {
|
if obj.Rule == "Even-Odd" {
|
||||||
@@ -233,7 +230,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
r.renderPattern(ctx, style.fillPattern, style.fillPatternColor, pageH, fp, ctm, bx, by)
|
r.renderPattern(ctx, style.fillPattern, style.fillPatternColor, pageH, fp, ctm, bx, by)
|
||||||
} else if style.fillPaint != nil {
|
} else if style.fillPaint != nil {
|
||||||
ctx.SetFillRule(fillRule)
|
ctx.SetFillRule(fillRule)
|
||||||
ctx.SetFill(style.fillPaint)
|
ctx.SetFill(fillPaint)
|
||||||
ctx.SetStrokeColor(canvas.Transparent)
|
ctx.SetStrokeColor(canvas.Transparent)
|
||||||
ctx.DrawPath(0, 0, fp)
|
ctx.DrawPath(0, 0, fp)
|
||||||
}
|
}
|
||||||
@@ -250,15 +247,16 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
if style.strokePaint == nil {
|
if style.strokePaint == nil {
|
||||||
style.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
|
style.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
|
||||||
}
|
}
|
||||||
|
strokePaint, shadingClip := resolveShdPaint(ctx, style.strokePaint)
|
||||||
ctx.SetFillColor(canvas.Transparent)
|
ctx.SetFillColor(canvas.Transparent)
|
||||||
ctx.SetStroke(style.strokePaint)
|
ctx.SetStroke(strokePaint)
|
||||||
ctx.SetStrokeWidth(style.lineWidth)
|
ctx.SetStrokeWidth(style.lineWidth)
|
||||||
ctx.SetStrokeCapper(style.lineCap)
|
ctx.SetStrokeCapper(style.lineCap)
|
||||||
ctx.SetStrokeJoiner(style.lineJoin)
|
ctx.SetStrokeJoiner(style.lineJoin)
|
||||||
if len(style.dashPattern) > 0 {
|
if len(style.dashPattern) > 0 {
|
||||||
ctx.SetDashes(style.dashOffset, style.dashPattern...)
|
ctx.SetDashes(style.dashOffset, style.dashPattern...)
|
||||||
}
|
}
|
||||||
strokeClip := intersectClipPath(clipPath, axialShdClip(ctx, style.strokePaint, style.strokeAxialShd))
|
strokeClip := intersectClipPath(clipPath, shadingClip)
|
||||||
if strokeClip != nil {
|
if strokeClip != nil {
|
||||||
sp := p.Copy()
|
sp := p.Copy()
|
||||||
if len(style.dashPattern) > 0 {
|
if len(style.dashPattern) > 0 {
|
||||||
@@ -266,7 +264,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
}
|
}
|
||||||
sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance)
|
sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance)
|
||||||
sp = applyClipPath(sp, strokeClip)
|
sp = applyClipPath(sp, strokeClip)
|
||||||
ctx.SetFill(style.strokePaint)
|
ctx.SetFill(strokePaint)
|
||||||
ctx.SetStrokeColor(canvas.Transparent)
|
ctx.SetStrokeColor(canvas.Transparent)
|
||||||
ctx.DrawPath(0, 0, sp)
|
ctx.DrawPath(0, 0, sp)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+9
-11
@@ -76,24 +76,21 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
fillColor = colorWithAlpha(canvas.Black, obj.Alpha)
|
fillColor = colorWithAlpha(canvas.Black, obj.Alpha)
|
||||||
}
|
}
|
||||||
var fillPaint any = fillColor
|
var fillPaint any = fillColor
|
||||||
var fillColorNode *FillColor
|
|
||||||
if dp != nil && dp.FillColor != nil {
|
if dp != nil && dp.FillColor != nil {
|
||||||
fillColorNode = withFillAlpha(dp.FillColor, obj.Alpha)
|
fillColorNode := withFillAlpha(dp.FillColor, obj.Alpha)
|
||||||
fillColor = parseFillColor(fillColorNode)
|
fillColor = parseFillColor(fillColorNode)
|
||||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
||||||
}
|
}
|
||||||
if obj.FillColor != nil {
|
if obj.FillColor != nil {
|
||||||
fillColorNode = withFillAlpha(obj.FillColor, obj.Alpha)
|
fillColorNode := withFillAlpha(obj.FillColor, obj.Alpha)
|
||||||
fillColor = parseFillColor(fillColorNode)
|
fillColor = parseFillColor(fillColorNode)
|
||||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
|
||||||
}
|
}
|
||||||
if fillPaint == nil {
|
if fillPaint == nil {
|
||||||
fillPaint = fillColor
|
fillPaint = fillColor
|
||||||
}
|
}
|
||||||
fillClip := clipPath
|
fillPaint, shadingClip := resolveShdPaint(ctx, fillPaint)
|
||||||
if fillColorNode != nil {
|
fillClip := intersectClipPath(clipPath, shadingClip)
|
||||||
fillClip = intersectClipPath(fillClip, axialShdClip(ctx, fillPaint, fillColorNode.AxialShd))
|
|
||||||
}
|
|
||||||
strokeStyle := newPathStyle(nil, defaultStroke, 0, obj.Alpha)
|
strokeStyle := newPathStyle(nil, defaultStroke, 0, obj.Alpha)
|
||||||
strokeClip := clipPath
|
strokeClip := clipPath
|
||||||
if shouldStroke {
|
if shouldStroke {
|
||||||
@@ -110,7 +107,8 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
strokeStyle.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
|
strokeStyle.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
|
||||||
}
|
}
|
||||||
strokeStyle.scale(ctm)
|
strokeStyle.scale(ctm)
|
||||||
strokeClip = intersectClipPath(strokeClip, axialShdClip(ctx, strokeStyle.strokePaint, strokeStyle.strokeAxialShd))
|
strokeStyle.strokePaint, shadingClip = resolveShdPaint(ctx, strokeStyle.strokePaint)
|
||||||
|
strokeClip = intersectClipPath(strokeClip, shadingClip)
|
||||||
}
|
}
|
||||||
fontStyle := canvas.FontRegular
|
fontStyle := canvas.FontRegular
|
||||||
weight := obj.Weight
|
weight := obj.Weight
|
||||||
@@ -150,7 +148,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
face := ff.Face(sizePt, fillPaint, fontStyle, canvas.FontNormal)
|
face := ff.Face(sizePt, fillPaint, fontStyle, canvas.FontNormal)
|
||||||
glyphTransforms := r.textObjectGlyphTransforms(fontID, obj)
|
glyphTransforms := r.textObjectGlyphTransforms(fontID, obj)
|
||||||
hasUnderline := strings.Contains(obj.Decoration, "Underline")
|
hasUnderline := strings.Contains(obj.Decoration, "Underline")
|
||||||
_, axialFill := fillPaint.(*canvas.LinearGradient)
|
_, shadedFill := fillPaint.(canvas.Gradient)
|
||||||
verticalAdvance := (obj.ReadDirection-obj.CharDirection)%180 != 0
|
verticalAdvance := (obj.ReadDirection-obj.CharDirection)%180 != 0
|
||||||
advanceX, advanceY := 1.0, 0.0
|
advanceX, advanceY := 1.0, 0.0
|
||||||
switch obj.ReadDirection {
|
switch obj.ReadDirection {
|
||||||
@@ -174,7 +172,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
}
|
}
|
||||||
dxs, dys := parseFloats(tc.DeltaX), parseFloats(tc.DeltaY)
|
dxs, dys := parseFloats(tc.DeltaX), parseFloats(tc.DeltaY)
|
||||||
xs, ys := parseFloats(tc.X), parseFloats(tc.Y)
|
xs, ys := parseFloats(tc.X), parseFloats(tc.Y)
|
||||||
drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || fillClip != nil || axialFill || shouldStroke
|
drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || fillClip != nil || shadedFill || shouldStroke
|
||||||
cx, cy := 0.0, 0.0
|
cx, cy := 0.0, 0.0
|
||||||
previousAdvance := 0.0
|
previousAdvance := 0.0
|
||||||
if len(xs) > 0 {
|
if len(xs) > 0 {
|
||||||
@@ -265,7 +263,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
|||||||
}
|
}
|
||||||
if fillPaint != nil {
|
if fillPaint != nil {
|
||||||
ctx.SetFill(fillPaint)
|
ctx.SetFill(fillPaint)
|
||||||
if fillClip != nil || axialFill {
|
if fillClip != nil || shadedFill {
|
||||||
scaleX := hScale
|
scaleX := hScale
|
||||||
if advanceLimit > 0 && glyphWidth*scaleX > advanceLimit {
|
if advanceLimit > 0 && glyphWidth*scaleX > advanceLimit {
|
||||||
scaleX = advanceLimit / glyphWidth
|
scaleX = advanceLimit / glyphWidth
|
||||||
|
|||||||
@@ -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