mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
feat(图形绘制): 支持更多的图形绘制
This commit is contained in:
@@ -58,6 +58,7 @@ type GraphicObject struct {
|
|||||||
|
|
||||||
// Clips 裁剪区域集合
|
// Clips 裁剪区域集合
|
||||||
type Clips struct {
|
type Clips struct {
|
||||||
|
TransFlag bool `xml:"TransFlag,attr"`
|
||||||
Clip []Clip `xml:"Clip"`
|
Clip []Clip `xml:"Clip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ type Clip struct {
|
|||||||
|
|
||||||
// ClipArea 裁剪区域
|
// ClipArea 裁剪区域
|
||||||
type ClipArea struct {
|
type ClipArea struct {
|
||||||
|
CTM string `xml:"CTM,attr"`
|
||||||
Path []PathObject `xml:"Path"`
|
Path []PathObject `xml:"Path"`
|
||||||
Text []TextObject `xml:"Text"`
|
Text []TextObject `xml:"Text"`
|
||||||
}
|
}
|
||||||
@@ -100,6 +102,7 @@ type FillColor struct {
|
|||||||
Value string `xml:"Value,attr"`
|
Value string `xml:"Value,attr"`
|
||||||
Alpha *int `xml:"Alpha,attr"`
|
Alpha *int `xml:"Alpha,attr"`
|
||||||
AxialShd *AxialShd `xml:"AxialShd"`
|
AxialShd *AxialShd `xml:"AxialShd"`
|
||||||
|
RadialShd *RadialShd `xml:"RadialShd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextCode 文本内容节点
|
// TextCode 文本内容节点
|
||||||
@@ -145,6 +148,7 @@ type StrokeColor struct {
|
|||||||
Value string `xml:"Value,attr"`
|
Value string `xml:"Value,attr"`
|
||||||
Alpha *int `xml:"Alpha,attr"`
|
Alpha *int `xml:"Alpha,attr"`
|
||||||
AxialShd *AxialShd `xml:"AxialShd"`
|
AxialShd *AxialShd `xml:"AxialShd"`
|
||||||
|
RadialShd *RadialShd `xml:"RadialShd"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AxialShd 轴向渐变
|
// AxialShd 轴向渐变
|
||||||
@@ -155,6 +159,16 @@ type AxialShd struct {
|
|||||||
Segment []ShdSegment `xml:"Segment"`
|
Segment []ShdSegment `xml:"Segment"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RadialShd 径向渐变
|
||||||
|
type RadialShd struct {
|
||||||
|
Extend string `xml:"Extend,attr"`
|
||||||
|
StartPoint string `xml:"StartPoint,attr"`
|
||||||
|
StartRadius float64 `xml:"StartRadius,attr"`
|
||||||
|
EndPoint string `xml:"EndPoint,attr"`
|
||||||
|
EndRadius float64 `xml:"EndRadius,attr"`
|
||||||
|
Segment []ShdSegment `xml:"Segment"`
|
||||||
|
}
|
||||||
|
|
||||||
// ShdSegment 渐变分段
|
// ShdSegment 渐变分段
|
||||||
type ShdSegment struct {
|
type ShdSegment struct {
|
||||||
Position float64 `xml:"Position,attr"`
|
Position float64 `xml:"Position,attr"`
|
||||||
|
|||||||
+58
-1
@@ -557,6 +557,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
if rectPath := r.buildTinyFillRectPath(obj, pageH, ctm, bx, by); rectPath != nil {
|
if rectPath := r.buildTinyFillRectPath(obj, pageH, ctm, bx, by); rectPath != nil {
|
||||||
p = rectPath
|
p = rectPath
|
||||||
}
|
}
|
||||||
|
clipPath := r.buildClipPath(obj.Clips, pageH, bx, by, ctm)
|
||||||
shouldFill := true
|
shouldFill := true
|
||||||
if obj.Fill != nil {
|
if obj.Fill != nil {
|
||||||
shouldFill = *obj.Fill
|
shouldFill = *obj.Fill
|
||||||
@@ -567,7 +568,13 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
if shouldFill && fillPaint != nil {
|
if shouldFill && fillPaint != nil {
|
||||||
ctx.SetFill(fillPaint)
|
ctx.SetFill(fillPaint)
|
||||||
ctx.SetStrokeColor(canvas.Transparent)
|
ctx.SetStrokeColor(canvas.Transparent)
|
||||||
ctx.DrawPath(0, 0, p)
|
fp := p
|
||||||
|
if clipPath != nil {
|
||||||
|
fp = p.Copy()
|
||||||
|
fp.Close()
|
||||||
|
fp = fp.And(clipPath)
|
||||||
|
}
|
||||||
|
ctx.DrawPath(0, 0, fp)
|
||||||
}
|
}
|
||||||
shouldStroke := true
|
shouldStroke := true
|
||||||
if obj.Stroke != nil {
|
if obj.Stroke != nil {
|
||||||
@@ -588,8 +595,20 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
|||||||
if len(dashPattern) > 0 {
|
if len(dashPattern) > 0 {
|
||||||
ctx.SetDashes(dashOffset, dashPattern...)
|
ctx.SetDashes(dashOffset, dashPattern...)
|
||||||
}
|
}
|
||||||
|
if clipPath != nil {
|
||||||
|
sp := p.Copy()
|
||||||
|
if len(dashPattern) > 0 {
|
||||||
|
sp = sp.Dash(dashOffset, dashPattern...)
|
||||||
|
}
|
||||||
|
sp = sp.Stroke(lineWidth, lineCap, lineJoin, canvas.Tolerance)
|
||||||
|
sp = sp.And(clipPath)
|
||||||
|
ctx.SetFill(strokePaint)
|
||||||
|
ctx.SetStrokeColor(canvas.Transparent)
|
||||||
|
ctx.DrawPath(0, 0, sp)
|
||||||
|
} else {
|
||||||
ctx.DrawPath(0, 0, p)
|
ctx.DrawPath(0, 0, p)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ctx.Pop()
|
ctx.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,6 +1177,44 @@ func (r *Renderer) buildPath(obj PathObject, pageH float64, ctm Matrix) *canvas.
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildClipPath 构建裁剪路径
|
||||||
|
// 入参: clips 裁剪对象, pageH 页面高度, bx 边界X坐标, by 边界Y坐标, objectCTM 对象CTM
|
||||||
|
// 返回: *canvas.Path 路径对象
|
||||||
|
func (r *Renderer) buildClipPath(clips *Clips, pageH float64, bx, by float64, objectCTM Matrix) *canvas.Path {
|
||||||
|
if clips == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var p *canvas.Path
|
||||||
|
for _, clip := range clips.Clip {
|
||||||
|
var clipPath *canvas.Path
|
||||||
|
for _, area := range clip.Area {
|
||||||
|
areaCTM := NewMatrix(area.CTM)
|
||||||
|
if clips.TransFlag {
|
||||||
|
areaCTM = objectCTM.Multiply(areaCTM)
|
||||||
|
}
|
||||||
|
for _, pathObj := range area.Path {
|
||||||
|
ctm := areaCTM.Multiply(NewMatrix(pathObj.CTM))
|
||||||
|
cp := r.buildPath(pathObj, pageH, ctm)
|
||||||
|
cp.Translate(bx, -by)
|
||||||
|
cp.Close()
|
||||||
|
if clipPath == nil {
|
||||||
|
clipPath = cp
|
||||||
|
} else {
|
||||||
|
clipPath = clipPath.Or(cp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if clipPath != nil {
|
||||||
|
if p == nil {
|
||||||
|
p = clipPath
|
||||||
|
} else {
|
||||||
|
p = p.And(clipPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// buildTinyFillRectPath 构建微小填充矩形路径
|
// buildTinyFillRectPath 构建微小填充矩形路径
|
||||||
// 入参: obj 路径对象, pageH 页面高度, ctm 变换矩阵, bx 边界X坐标, by 边界Y坐标
|
// 入参: obj 路径对象, pageH 页面高度, ctm 变换矩阵, bx 边界X坐标, by 边界Y坐标
|
||||||
// 返回: *canvas.Path 路径对象
|
// 返回: *canvas.Path 路径对象
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ func parseFillColor(fillColor *FillColor) color.Color {
|
|||||||
if strings.TrimSpace(fillColor.Value) != "" {
|
if strings.TrimSpace(fillColor.Value) != "" {
|
||||||
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
|
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
|
||||||
}
|
}
|
||||||
|
if fillColor.AxialShd != nil {
|
||||||
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
||||||
|
}
|
||||||
|
return parseRadialShdColor(fillColor.RadialShd, fillColor.Alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFillPaint 解析填充画刷
|
// parseFillPaint 解析填充画刷
|
||||||
@@ -73,7 +76,13 @@ func parseFillPaint(fillColor *FillColor, x, y, pageH, originX, originY float64)
|
|||||||
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
||||||
return gradient
|
return gradient
|
||||||
}
|
}
|
||||||
|
if gradient := parseRadialShdGradient(fillColor.RadialShd, fillColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
||||||
|
return gradient
|
||||||
|
}
|
||||||
|
if fillColor.AxialShd != nil {
|
||||||
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
return parseAxialShdColor(fillColor.AxialShd, fillColor.Alpha)
|
||||||
|
}
|
||||||
|
return parseRadialShdColor(fillColor.RadialShd, fillColor.Alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseStrokeColor 解析勾边颜色
|
// parseStrokeColor 解析勾边颜色
|
||||||
@@ -86,7 +95,10 @@ func parseStrokeColor(strokeColor *StrokeColor) color.Color {
|
|||||||
if strings.TrimSpace(strokeColor.Value) != "" {
|
if strings.TrimSpace(strokeColor.Value) != "" {
|
||||||
return parseColorWithAlpha(strokeColor.Value, strokeColor.Alpha)
|
return parseColorWithAlpha(strokeColor.Value, strokeColor.Alpha)
|
||||||
}
|
}
|
||||||
|
if strokeColor.AxialShd != nil {
|
||||||
return parseAxialShdColor(strokeColor.AxialShd, strokeColor.Alpha)
|
return parseAxialShdColor(strokeColor.AxialShd, strokeColor.Alpha)
|
||||||
|
}
|
||||||
|
return parseRadialShdColor(strokeColor.RadialShd, strokeColor.Alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseStrokePaint 解析勾边画刷
|
// parseStrokePaint 解析勾边画刷
|
||||||
@@ -102,7 +114,13 @@ func parseStrokePaint(strokeColor *StrokeColor, x, y, pageH, originX, originY fl
|
|||||||
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
||||||
return gradient
|
return gradient
|
||||||
}
|
}
|
||||||
|
if gradient := parseRadialShdGradient(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH, originX, originY); gradient != nil {
|
||||||
|
return gradient
|
||||||
|
}
|
||||||
|
if strokeColor.AxialShd != nil {
|
||||||
return parseAxialShdColor(strokeColor.AxialShd, strokeColor.Alpha)
|
return parseAxialShdColor(strokeColor.AxialShd, strokeColor.Alpha)
|
||||||
|
}
|
||||||
|
return parseRadialShdColor(strokeColor.RadialShd, strokeColor.Alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAxialShdColor 解析轴向渐变颜色
|
// parseAxialShdColor 解析轴向渐变颜色
|
||||||
@@ -157,6 +175,55 @@ func parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH, originX,
|
|||||||
return grad.ToLinear(startPoint, endPoint)
|
return grad.ToLinear(startPoint, endPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseRadialShdColor 解析径向渐变颜色
|
||||||
|
// 入参: radialShd 径向渐变节点, alpha 透明度
|
||||||
|
// 返回: color.Color 颜色对象
|
||||||
|
func parseRadialShdColor(radialShd *RadialShd, alpha *int) color.Color {
|
||||||
|
if radialShd != nil {
|
||||||
|
for _, segment := range radialShd.Segment {
|
||||||
|
if strings.TrimSpace(segment.Color.Value) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if alpha == nil {
|
||||||
|
alpha = segment.Color.Alpha
|
||||||
|
}
|
||||||
|
return parseColorWithAlpha(segment.Color.Value, alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRadialShdGradient 解析径向渐变
|
||||||
|
// 入参: radialShd 径向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度, originX 原点X坐标, originY 原点Y坐标
|
||||||
|
// 返回: canvas.Gradient 渐变对象
|
||||||
|
func parseRadialShdGradient(radialShd *RadialShd, alpha *int, x, y, pageH, originX, originY float64) canvas.Gradient {
|
||||||
|
if radialShd == nil || radialShd.EndRadius <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
start := parseFloats(radialShd.StartPoint)
|
||||||
|
end := parseFloats(radialShd.EndPoint)
|
||||||
|
if len(start) < 2 || len(end) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
grad := canvas.NewGradient()
|
||||||
|
for _, segment := range radialShd.Segment {
|
||||||
|
if strings.TrimSpace(segment.Color.Value) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
segmentAlpha := alpha
|
||||||
|
if segmentAlpha == nil {
|
||||||
|
segmentAlpha = segment.Color.Alpha
|
||||||
|
}
|
||||||
|
grad.Add(segment.Position, colorToRGBA(parseColorWithAlpha(segment.Color.Value, segmentAlpha)))
|
||||||
|
}
|
||||||
|
if len(grad) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
startPoint := canvas.Point{X: x + start[0] - originX, Y: pageH - (y + start[1]) - originY}
|
||||||
|
endPoint := canvas.Point{X: x + end[0] - originX, Y: pageH - (y + end[1]) - originY}
|
||||||
|
return grad.ToRadial(startPoint, radialShd.StartRadius, endPoint, radialShd.EndRadius)
|
||||||
|
}
|
||||||
|
|
||||||
// colorToRGBA 转换颜色对象
|
// colorToRGBA 转换颜色对象
|
||||||
// 入参: c 颜色对象
|
// 入参: c 颜色对象
|
||||||
// 返回: color.RGBA RGBA颜色
|
// 返回: color.RGBA RGBA颜色
|
||||||
|
|||||||
Reference in New Issue
Block a user