-
Notifications
You must be signed in to change notification settings - Fork 29
/
svg.fs
212 lines (208 loc) · 9.17 KB
/
svg.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
module FVim.svg
open common
open ShimSkiaSharp
open Avalonia.Svg.Commands
open System.Collections.Generic
open System
open Avalonia.Media.Immutable
open Avalonia
open Avalonia.Media
open Avalonia.Svg
let private s_assetLoader = new AvaloniaAssetLoader() :> Svg.Model.IAssetLoader
let private s_factory = AvaloniaLocator.Current.GetService<Platform.IPlatformRenderInterface>()
// ported from: https://github.com/wieslawsoltes/Svg.Skia/blob/master/src/Avalonia.Svg/AvaloniaPicture.cs
type SvgPicture(data: string) =
let svg = Svg.SvgDocument.FromSvg(data)
let picture: SKPicture =
match Svg.Model.SvgExtensions.ToModel(svg, s_assetLoader, ignoreAttributes=Unchecked.defaultof<_>) with
| pic,_,_ -> pic
let _commands = ResizeArray<DrawCommand>()
let mutable m_brush: SolidColorBrush = null
let mutable m_fg = Colors.Black
let mutable m_bg = Colors.White
let b (brush: IBrush) =
if isNull m_brush then brush else
let color =
match brush with
| :? SolidColorBrush as brush -> brush.Color
| :? ImmutableSolidColorBrush as brush -> brush.Color
| _ -> Colors.Blue
if color = Colors.White then
m_brush.Color <- m_bg
m_brush :> IBrush
elif color = Colors.Black then
m_brush.Color <- m_fg
m_brush :> IBrush
else brush
let p (pen: IPen) =
match pen with
| :? Pen as pen ->
pen.Brush <- b pen.Brush
| _ -> ()
pen
do
if isNull picture.Commands then () else
for cmd in picture.Commands do
match cmd with
| :? ClipPathCanvasCommand as clipPathCanvasCommand ->
let path = clipPathCanvasCommand.ClipPath.ToGeometry(false)
// TODO: clipPathCanvasCommand.Operation
// TODO: clipPathCanvasCommand.Antialias
if notNull path then
_commands.Add(new GeometryClipDrawCommand(path))
| :? ClipRectCanvasCommand as clipRectCanvasCommand ->
let rect = clipRectCanvasCommand.Rect.ToRect()
// TODO: clipRectCanvasCommand.Operation
// TODO: clipRectCanvasCommand.Antialias
_commands.Add(new ClipDrawCommand(rect))
| :? SaveCanvasCommand ->
// TODO: SaveCanvasCommand
_commands.Add(new SaveDrawCommand())
| :? RestoreCanvasCommand ->
// TODO: RestoreCanvasCommand
_commands.Add(new RestoreDrawCommand())
| :? SetMatrixCanvasCommand as setMatrixCanvasCommand ->
let matrix = setMatrixCanvasCommand.Matrix.ToMatrix()
_commands.Add(new SetTransformDrawCommand(matrix))
| :? SaveLayerCanvasCommand as saveLayerCanvasCommand ->
// TODO: SaveLayerCanvasCommand
_commands.Add(new SaveLayerDrawCommand())
| :? DrawImageCanvasCommand as drawImageCanvasCommand ->
if isNull drawImageCanvasCommand.Image then () else
let image = drawImageCanvasCommand.Image.ToBitmap()
if image = null then () else
let source = drawImageCanvasCommand.Source.ToRect()
let dest = drawImageCanvasCommand.Dest.ToRect()
let paint = drawImageCanvasCommand.Paint
let bitmapInterpolationMode =
if paint <> null then paint.FilterQuality.ToBitmapInterpolationMode()
else Visuals.Media.Imaging.BitmapInterpolationMode.Default
_commands.Add(new ImageDrawCommand(image, source, dest, bitmapInterpolationMode))
| :? DrawPathCanvasCommand as drawPathCanvasCommand ->
if (isNull drawPathCanvasCommand.Path) || (isNull drawPathCanvasCommand.Paint) then () else
let struct(brush, pen) = drawPathCanvasCommand.Paint.ToBrushAndPen()
match drawPathCanvasCommand.Path.Commands ?-> (fun x -> x.Count) with
| ValueSome 1 ->
let pathCommand = drawPathCanvasCommand.Path.Commands.[0]
match pathCommand with
| :? AddRectPathCommand as addRectPathCommand ->
let rect = addRectPathCommand.Rect.ToRect()
_commands.Add(new RectangleDrawCommand(brush, pen, rect, 0.0, 0.0))
true
| :? AddRoundRectPathCommand as addRoundRectPathCommand ->
let rect = addRoundRectPathCommand.Rect.ToRect()
let rx = float addRoundRectPathCommand.Rx
let ry = float addRoundRectPathCommand.Ry
_commands.Add(new RectangleDrawCommand(brush, pen, rect, rx, ry))
true
| :? AddOvalPathCommand as addOvalPathCommand ->
let rect = addOvalPathCommand.Rect.ToRect()
let ellipseGeometry = s_factory.CreateEllipseGeometry(rect)
_commands.Add(new GeometryDrawCommand(brush, pen, ellipseGeometry))
true
| :? AddCirclePathCommand as addCirclePathCommand ->
let x = float addCirclePathCommand.X
let y = float addCirclePathCommand.Y
let radius = float addCirclePathCommand.Radius
let rect = new Rect(x - radius, y - radius, radius + radius, radius + radius)
let ellipseGeometry = s_factory.CreateEllipseGeometry(rect)
_commands.Add(new GeometryDrawCommand(brush, pen, ellipseGeometry))
true
| :? AddPolyPathCommand as addPolyPathCommand ->
if isNull addPolyPathCommand.Points then false else
let close = addPolyPathCommand.Close
let polylineGeometry = addPolyPathCommand.Points.ToGeometry(close)
_commands.Add(new GeometryDrawCommand(brush, pen, polylineGeometry))
true
| _ -> false
| ValueSome 2 ->
match drawPathCanvasCommand.Path.Commands.[0], drawPathCanvasCommand.Path.Commands.[1] with
| :? MoveToPathCommand as moveTo, (:? LineToPathCommand as lineTo) ->
let p1 = new Point(float moveTo.X, float moveTo.Y)
let p2 = new Point(float lineTo.X, float lineTo.Y)
_commands.Add(new LineDrawCommand(pen, p1, p2))
true
| _ -> false
| _ -> false
|> function
| false ->
let geometry = drawPathCanvasCommand.Path.ToGeometry(notNull brush)
if notNull geometry then
_commands.Add(new GeometryDrawCommand(brush, pen, geometry))
| _ -> ()
| :? DrawTextBlobCanvasCommand as drawPositionedTextCanvasCommand ->
// TODO: DrawTextBlobCanvasCommand
()
| :? DrawTextCanvasCommand as drawTextCanvasCommand ->
if isNull drawTextCanvasCommand.Paint then () else
let struct(brush, _) = drawTextCanvasCommand.Paint.ToBrushAndPen()
let text = drawTextCanvasCommand.Paint.ToFormattedText(drawTextCanvasCommand.Text)
let x = float drawTextCanvasCommand.X
let y = float drawTextCanvasCommand.Y
let origin = new Point(x, y - float drawTextCanvasCommand.Paint.TextSize)
_commands.Add(new TextDrawCommand(brush, origin, text))
| :? DrawTextOnPathCanvasCommand as drawTextOnPathCanvasCommand ->
// TODO: DrawTextOnPathCanvasCommand
()
| _ -> ()
member __.Draw(context: Avalonia.Media.DrawingContext) =
let pushedStates = new Stack<Stack<IDisposable>>()
use _transformContainerState = context.PushTransformContainer()
for cmd in _commands do
match cmd with
| :? GeometryClipDrawCommand as geometryClipDrawCommand ->
let geometryPushedState = context.PushGeometryClip(geometryClipDrawCommand.Clip)
let currentPushedStates = pushedStates.Peek()
currentPushedStates.Push(geometryPushedState)
| :? ClipDrawCommand as clipDrawCommand ->
let clipPushedState = context.PushClip(clipDrawCommand.Clip)
let currentPushedStates = pushedStates.Peek()
currentPushedStates.Push(clipPushedState)
| :? SaveDrawCommand ->
pushedStates.Push(new Stack<IDisposable>())
| :? RestoreDrawCommand ->
let currentPushedStates = pushedStates.Pop()
while currentPushedStates.Count > 0 do
let pushedState = currentPushedStates.Pop()
pushedState.Dispose()
| :? SetTransformDrawCommand as setTransformDrawCommand ->
let transformPreTransform = context.PushSetTransform(setTransformDrawCommand.Matrix)
let currentPushedStates = pushedStates.Peek()
currentPushedStates.Push(transformPreTransform)
| :? SaveLayerDrawCommand as saveLayerDrawCommand ->
pushedStates.Push(new Stack<IDisposable>())
| :? ImageDrawCommand as imageDrawCommand ->
context.DrawImage(
imageDrawCommand.Source,
imageDrawCommand.SourceRect,
imageDrawCommand.DestRect,
imageDrawCommand.BitmapInterpolationMode)
| :? GeometryDrawCommand as geometryDrawCommand ->
context.DrawGeometry(
b geometryDrawCommand.Brush,
p geometryDrawCommand.Pen,
geometryDrawCommand.Geometry)
| :? LineDrawCommand as lineDrawCommand ->
context.DrawLine(
p lineDrawCommand.Pen,
lineDrawCommand.P1,
lineDrawCommand.P2)
| :? RectangleDrawCommand as rectangleDrawCommand ->
context.DrawRectangle(
b rectangleDrawCommand.Brush,
p rectangleDrawCommand.Pen,
rectangleDrawCommand.Rect,
rectangleDrawCommand.RadiusX,
rectangleDrawCommand.RadiusY)
| :? TextDrawCommand as textDrawCommand ->
context.DrawText(
b textDrawCommand.Brush,
textDrawCommand.Origin,
textDrawCommand.FormattedText)
| _ -> ()
member __.Width = float picture.CullRect.Width
member __.Height = float picture.CullRect.Height
member __.SetTheme(brush, fg, bg) =
m_brush <- brush
m_fg <- fg
m_bg <- bg