VectSharp  2.2.1
A light library for C# vector graphics
SKRenderContext.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using SkiaSharp;
19 using System;
20 using System.Collections.Generic;
21 using System.Linq;
22 using VectSharp.Filters;
23 
24 namespace VectSharp.Canvas
25 {
26 
27  /// <summary>
28  /// Represents a light-weight rendering action.
29  /// </summary>
30  public class SKRenderAction : IDisposable
31  {
32  /// <summary>
33  /// Returns a boolean value indicating whether the current instance has been disposed.
34  /// </summary>
35  public bool Disposed => disposedValue;
36 
37  private bool disposedValue;
38 
39  /// <summary>
40  /// Types of rendering actions.
41  /// </summary>
42  public enum ActionTypes
43  {
44  /// <summary>
45  /// The render action represents a path object.
46  /// </summary>
47  Path,
48 
49  /// <summary>
50  /// The render action represents a text object.
51  /// </summary>
52  Text,
53 
54  /// <summary>
55  /// The render action represents a raster image.
56  /// </summary>
58 
59  /// <summary>
60  /// The render action represents a transformation of the coordinate space.
61  /// </summary>
62  Transform,
63 
64  /// <summary>
65  /// The render action represents saving the current graphics state.
66  /// </summary>
67  Save,
68 
69  /// <summary>
70  /// The render action represents restoring the last saved graphics state.
71  /// </summary>
72  Restore,
73 
74  /// <summary>
75  /// The render action represents an update of the current clip path.
76  /// </summary>
77  Clip,
78 
79  /// <summary>
80  /// The render action represents rendering a graphics object with a filter.
81  /// </summary>
82  DrawFiltered
83  }
84 
85  /// <summary>
86  /// Type of the rendering action.
87  /// </summary>
88  public ActionTypes ActionType { get; private set; }
89 
90  /// <summary>
91  /// Path that needs to be rendered (null if the action type is not <see cref="ActionTypes.Path"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
92  /// </summary>
93  public SKPath Path { get; set; }
94 
95  internal SKPath HitTestPath { get; set; }
96  internal SKPath LastRenderedGlobalHitTestPath { get; set; }
97 
98  /// <summary>
99  /// Text that needs to be rendered (null if the action type is not <see cref="ActionTypes.Text"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
100  /// </summary>
101  public string Text { get; set; }
102 
103  /// <summary>
104  /// The font that will be used to render the text (null if the action type is not <see cref="ActionTypes.Text"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
105  /// </summary>
106  public SKFont Font { get; set; }
107 
108  /// <summary>
109  /// The X coordainate at which the text will be drawn (null if the action type is not <see cref="ActionTypes.Text"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
110  /// </summary>
111  public float TextX { get; set; }
112 
113  /// <summary>
114  /// The Y coordainate at which the text will be drawn (null if the action type is not <see cref="ActionTypes.Text"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
115  /// </summary>
116  public float TextY { get; set; }
117 
118  /// <summary>
119  /// Paint used to render the text or path (<see langword="null"/> if the action type is neither <see cref="ActionTypes.Text"/> nor <see cref="ActionTypes.Path"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
120  /// </summary>
121  public SKPaint Paint { get; set; }
122 
123  /// <summary>
124  /// Univocal identifier of the image that needs to be drawn.
125  /// </summary>
126  public string ImageId { get; set; }
127 
128  /// <summary>
129  /// The source rectangle of the image (<see langword="null"/> if the action type is not <see cref="ActionTypes.RasterImage"/>). If you change this, you probably want to call this object's <see cref="InvalidateVisual"/> method.
130  /// </summary>
131  public SKRect? ImageSource { get; set; }
132 
133  /// <summary>
134  /// The destination rectangle of the image (<see langword="null"/> if the action type is not <see cref="ActionTypes.RasterImage"/>). If you change this, you probably want to call this object's <see cref="InvalidateHitTestPath"/> method.
135  /// </summary>
136  public SKRect? ImageDestination { get; set; }
137 
138  /// <summary>
139  /// The transformation matrix that will be applied to the current coordinate system (<see langword="null"/> if the action type is not <see cref="ActionTypes.Transform"/>). If you change this, you probably want to call this object's <see cref="InvalidateVisual"/> method.
140  /// </summary>
141  public SKMatrix? Transform { get; set; } = null;
142 
143  /// <summary>
144  /// A tag to access the <see cref="SKRenderAction"/>.
145  /// </summary>
146  public string Tag { get; private set; }
147 
148  /// <summary>
149  /// The Z-index of the rendering action (an action with a higher Z-index will always appear above an action with a lower Z-index).
150  /// The more different values there are for the Z-index, the slower the rendering, so keep use of this property to a minimum.
151  /// If you change this, you probably want to call this object's <see cref="InvalidateZIndex"/> method.
152  /// </summary>
153  public uint ZIndex { get; set; } = 0;
154 
155  /// <summary>
156  /// The graphics that will be drawn with the specified <see cref="Filter"/>. If you change this, you probably want to call this object's <see cref="InvalidateVisual"/> method.
157  /// </summary>
158  public SKRenderContext Graphics { get; set; } = null;
159 
160  /// <summary>
161  /// The filter with which the <see cref="Graphics"/> is drawn. If you change this, you probably want to call this object's <see cref="InvalidateVisual"/> method.
162  /// </summary>
163  public IFilter Filter { get; set; }
164 
165  /// <summary>
166  /// An arbitrary object associated with the RenderAction.
167  /// </summary>
168  public object Payload { get; set; }
169 
170  internal ISKRenderCanvas InternalParent { get; set; }
171 
172  /// <summary>
173  /// The container of this <see cref="SKRenderAction"/>.
174  /// </summary>
175  public Avalonia.Controls.Canvas Parent
176  {
177  get
178  {
179  return (Avalonia.Controls.Canvas)InternalParent;
180  }
181  }
182 
183  /// <summary>
184  /// Raised when the pointer enters the area covered by the <see cref="SKRenderAction"/>.
185  /// </summary>
186  public event EventHandler<Avalonia.Input.PointerEventArgs> PointerEnter;
187 
188  /// <summary>
189  /// Raised when the pointer leaves the area covered by the <see cref="SKRenderAction"/>.
190  /// </summary>
191  public event EventHandler<Avalonia.Input.PointerEventArgs> PointerLeave;
192 
193  /// <summary>
194  /// Raised when the pointer is pressed while over the area covered by the <see cref="SKRenderAction"/>.
195  /// </summary>
196  public event EventHandler<Avalonia.Input.PointerPressedEventArgs> PointerPressed;
197 
198  /// <summary>
199  /// Raised when the pointer is released after a <see cref="PointerPressed"/> event.
200  /// </summary>
201  public event EventHandler<Avalonia.Input.PointerReleasedEventArgs> PointerReleased;
202 
203 
204  internal void FirePointerEnter(Avalonia.Input.PointerEventArgs e)
205  {
206  this.PointerEnter?.Invoke(this, e);
207  }
208 
209  internal void FirePointerLeave(Avalonia.Input.PointerEventArgs e)
210  {
211  this.PointerLeave?.Invoke(this, e);
212  }
213 
214  internal void FirePointerPressed(Avalonia.Input.PointerPressedEventArgs e)
215  {
216  this.PointerPressed?.Invoke(this, e);
217  }
218 
219  internal void FirePointerReleased(Avalonia.Input.PointerReleasedEventArgs e)
220  {
221  this.PointerReleased?.Invoke(this, e);
222  }
223 
224  /// <summary>
225  /// <para>Signals to this object that its shape has changed a new path needs to be computed for the purpose of hit-testing.
226  /// Also signals to the <see cref="Parent"/> that the visual properties of this object have changed and triggers a redraw.</para>
227  /// <para>This method should be called whenever the "shape" of the object represented by the <see cref="SKRenderAction"/> changes.
228  /// If only the visual properties of this object have changed (e.g. the colour), call the <see cref="InvalidateVisual"/> method instead.</para>
229  /// <para>If you make changes to more than one <see cref="SKRenderAction"/> contained in the same <see cref="Canvas"/>, you only need to invalidate the last one.</para>
230  /// <para>This method should only be called after the output has been fully initialized.</para>
231  /// </summary>
232  public void InvalidateHitTestPath()
233  {
234  CreateHitTestPath();
235 
236  this.InvalidateVisual();
237  }
238 
239  internal void CreateHitTestPath()
240  {
241  if (this.ActionType == ActionTypes.Path)
242  {
243  if (this.Path != null && this.Paint != null)
244  {
245  this.HitTestPath?.Dispose();
246  this.HitTestPath = this.Paint.GetFillPath(this.Path);
247  }
248  }
249  else if (this.ActionType == ActionTypes.RasterImage)
250  {
251  if (this.ImageDestination != null)
252  {
253  this.HitTestPath?.Dispose();
254  this.HitTestPath = new SKPath();
255  this.HitTestPath.AddRect(this.ImageDestination.Value);
256  }
257  }
258  else if (this.ActionType == ActionTypes.Text)
259  {
260  if (this.Paint != null && this.Font != null)
261  {
262  this.HitTestPath?.Dispose();
263  this.Paint.Typeface = this.Font.Typeface;
264  this.Paint.TextSize = this.Font.Size;
265 
266  using (SKPath pth = this.Paint.GetTextPath(this.Text, this.TextX, this.TextY))
267  {
268  this.HitTestPath = this.Paint.GetFillPath(pth);
269  }
270  }
271  }
272  }
273 
274  /// <summary>
275  /// <para>This methods signals to the <see cref="Parent"/> that the visual properties (e.g. the colour) of this object have changed and triggers a redraw.</para>
276  /// <para>If the "shape" of the object has changed as well, call the <see cref="InvalidateHitTestPath"/> method instead. If the Z-index of the
277  /// object has changed, call the <see cref="InvalidateZIndex"/> method instead. If both the "shape" and the Z-index of the object have changed,
278  /// call the <see cref="InvalidateAll"/> method.</para>
279  /// <para>If you make changes to more than one <see cref="SKRenderAction"/> contained in the same <see cref="Canvas"/>, you only need to invalidate the last one.</para>
280  /// <para>This method should only be called after the output has been fully initialized.</para>
281  /// </summary>
282  public void InvalidateVisual()
283  {
284  this.InternalParent?.InvalidateDirty();
285  }
286 
287  /// <summary>
288  /// <para>This methods signals to the <see cref="Parent"/> that the Z-index and visual properties (e.g. the colour) of this object have changed and triggers a redraw.</para>
289  /// <para>If the "shape" of the object has changed as well, call the <see cref="InvalidateAll"/> method instead.</para>
290  /// <para>If you make changes to more than one <see cref="SKRenderAction"/> contained in the same <see cref="Canvas"/>, you only need to invalidate the last one.</para>
291  /// <para>This method should only be called after the output has been fully initialized.</para>
292  /// </summary>
293  public void InvalidateZIndex()
294  {
295  this.InternalParent?.InvalidateZIndex();
296  }
297 
298  /// <summary>
299  /// <para>This methods signals to the <see cref="Parent"/> that the Z-index, shape and visual properties (e.g. the colour) of this object have changed and triggers a redraw.</para>
300  /// <para>If you make changes to more than one <see cref="SKRenderAction"/> contained in the same <see cref="Canvas"/>, you only need to invalidate the last one.</para>
301  /// <para>This method should only be called after the output has been fully initialized.</para>
302  /// </summary>
303  public void InvalidateAll()
304  {
305  this.InvalidateHitTestPath();
306  this.InvalidateZIndex();
307  }
308 
309  private SKRenderAction()
310  {
311 
312  }
313 
314  /// <summary>
315  /// Creates a new <see cref="SKRenderAction"/> representing a path.
316  /// </summary>
317  /// <param name="path">The geometry to be rendered.</param>
318  /// <param name="paint">The paint used to fill or stroke the path.</param>
319  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>. If this is null this <see cref="SKRenderAction"/> is not visible in the hit test.</param>
320  /// <returns>A new <see cref="SKRenderAction"/> representing a path.</returns>
321  public static SKRenderAction PathAction(SKPath path, SKPaint paint, string tag = null)
322  {
323  SKRenderAction act = new SKRenderAction()
324  {
325  ActionType = ActionTypes.Path,
326  Path = path,
327  Paint = paint,
328  Tag = tag
329  };
330 
331  if (!string.IsNullOrEmpty(tag))
332  {
333  act.CreateHitTestPath();
334  }
335 
336  return act;
337  }
338 
339  /// <summary>
340  /// Creates a new <see cref="SKRenderAction"/> representing a clipping action.
341  /// </summary>
342  /// <param name="clippingPath">The path to be used for clipping.</param>
343  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>.</param>
344  /// <returns>A new <see cref="SKRenderAction"/> representing a clipping action.</returns>
345  public static SKRenderAction ClipAction(SKPath clippingPath, string tag = null)
346  {
347  return new SKRenderAction()
348  {
349  ActionType = ActionTypes.Clip,
350  Path = clippingPath,
351  Tag = tag
352  };
353  }
354 
355  /// <summary>
356  /// Creates a new <see cref="SKRenderAction"/> representing text.
357  /// </summary>
358  /// <param name="text">The text to be rendered.</param>
359  /// <param name="x">The X coordinate at which the text will be drawn.</param>
360  /// <param name="y">The Y coordinate at which the text will be drawn.</param>
361  /// <param name="font">The font to be used to render the text.</param>
362  /// <param name="paint">The paint to be used to fill or stroke the text.</param>
363  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>. If this is null this <see cref="SKRenderAction"/> is not visible in the hit test.</param>
364  /// <returns>A new <see cref="SKRenderAction"/> representing text.</returns>
365  public static SKRenderAction TextAction(string text, float x, float y, SKFont font, SKPaint paint, string tag = null)
366  {
367  SKRenderAction act = new SKRenderAction()
368  {
369  ActionType = ActionTypes.Text,
370  Text = text,
371  TextX = x,
372  TextY = y,
373  Font = font,
374  Paint = paint,
375  Tag = tag
376  };
377 
378  act.Paint.Typeface = font.Typeface;
379  act.Paint.TextSize = font.Size;
380 
381  if (!string.IsNullOrEmpty(tag))
382  {
383  act.CreateHitTestPath();
384  }
385 
386  return act;
387  }
388 
389  /// <summary>
390  /// Creates a new <see cref="SKRenderAction"/> representing an image.
391  /// </summary>
392  /// <param name="imageId">The univocal identifier of the image to draw.</param>
393  /// <param name="sourceRect">The source rectangle of the image.</param>
394  /// <param name="destinationRect">The destination rectangle of the image.</param>
395  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>. If this is null this <see cref="SKRenderAction"/> is not visible in the hit test.</param>
396  /// <returns>A new <see cref="SKRenderAction"/> representing an image.</returns>
397  public static SKRenderAction ImageAction(string imageId, SKRect sourceRect, SKRect destinationRect, string tag = null)
398  {
399  SKRenderAction act = new SKRenderAction()
400  {
401  ActionType = ActionTypes.RasterImage,
402  ImageId = imageId,
403  ImageSource = sourceRect,
404  ImageDestination = destinationRect,
405  Tag = tag
406  };
407 
408  if (!string.IsNullOrEmpty(tag))
409  {
410  act.CreateHitTestPath();
411  }
412 
413  return act;
414  }
415 
416  /// <summary>
417  /// Creates a new <see cref="SKRenderAction"/> representing a transform.
418  /// </summary>
419  /// <param name="transform">The transform to apply.</param>
420  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>.</param>
421  /// <returns>A new <see cref="SKRenderAction"/> representing a transform.</returns>
422  public static SKRenderAction TransformAction(SKMatrix transform, string tag = null)
423  {
424  return new SKRenderAction()
425  {
426  ActionType = ActionTypes.Transform,
427  Transform = transform,
428  Tag = tag
429  };
430  }
431 
432  /// <summary>
433  /// Creates a new <see cref="SKRenderAction"/> that saves the current graphics state.
434  /// </summary>
435  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>.</param>
436  /// <returns>A new <see cref="SKRenderAction"/> that saves the current graphics state.</returns>
437  public static SKRenderAction SaveAction(string tag = null)
438  {
439  return new SKRenderAction()
440  {
441  ActionType = ActionTypes.Save,
442  Tag = tag
443  };
444  }
445 
446  /// <summary>
447  /// Creates a new <see cref="SKRenderAction"/> that saves the current graphics state.
448  /// </summary>
449  /// <param name="tag">A tag to access the <see cref="SKRenderAction"/>.</param>
450  /// <returns>A new <see cref="SKRenderAction"/> that restores the last saved graphics state.</returns>
451  public static SKRenderAction RestoreAction(string tag = null)
452  {
453  return new SKRenderAction()
454  {
455  ActionType = ActionTypes.Restore,
456  Tag = tag
457  };
458  }
459 
460  /// <summary>
461  /// Create a new <see cref="SKRenderAction"/> that draws some graphics with a filter.
462  /// </summary>
463  /// <returns>A new <see cref="SKRenderAction"/> that draws some graphics with a filter.</returns>
464  public static SKRenderAction DrawFilteredGraphicsAction(SKRenderContext graphics, IFilter filter, string tag = null)
465  {
466  return new SKRenderAction()
467  {
468  ActionType = ActionTypes.DrawFiltered,
469  Graphics = graphics,
470  Filter = filter,
471  Tag = tag
472  };
473  }
474 
475  /// <inheritdoc cref="IDisposable.Dispose"/>
476  protected virtual void Dispose(bool disposing)
477  {
478  if (!disposedValue)
479  {
480  if (disposing)
481  {
482  this.Font?.Dispose();
483  this.HitTestPath?.Dispose();
484  this.LastRenderedGlobalHitTestPath?.Dispose();
485  this.Paint?.Dispose();
486  this.Path?.Dispose();
487  }
488 
489  disposedValue = true;
490  }
491  }
492 
493  /// <inheritdoc cref="IDisposable.Dispose"/>
494  public void Dispose()
495  {
496  Dispose(disposing: true);
497  GC.SuppressFinalize(this);
498  }
499  }
500 
501 
502  internal static class SKTypefaceCache
503  {
504  private static readonly object LockObject = new object();
505  private static readonly Dictionary<string, SKTypeface> Typefaces = new Dictionary<string, SKTypeface>();
506  public static SKTypeface GetSKTypeface(FontFamily family)
507  {
508 
509  lock (LockObject)
510  {
511  if (Typefaces.TryGetValue(family.FileName, out SKTypeface tbr))
512  {
513  return tbr;
514  }
515  else
516  {
517  try
518  {
519  System.IO.MemoryStream fontStream = new System.IO.MemoryStream((int)family.TrueTypeFile.FontStream.Length);
520  family.TrueTypeFile.FontStream.Seek(0, System.IO.SeekOrigin.Begin);
521  family.TrueTypeFile.FontStream.CopyTo(fontStream);
522  fontStream.Seek(0, System.IO.SeekOrigin.Begin);
523 
524  SKTypeface typeface = SKTypeface.FromData(SKData.Create(fontStream));
525 
526  Typefaces[family.FileName] = typeface;
527 
528  return typeface;
529  }
530  catch
531  {
532  SKTypeface typeface = SKTypeface.Default;
533 
534  Typefaces[family.FileName] = typeface;
535 
536  return typeface;
537  }
538  }
539  }
540  }
541  }
542 
543  /// <summary>
544  /// Represents a page that has been prepared for fast rendering using the SkiaSharp renderer.
545  /// </summary>
546  public class SKRenderContext
547  {
548  internal virtual Dictionary<string, (SKBitmap, bool)> Images { get; set; }
549  internal virtual List<SKRenderAction> SKRenderActions { get; set; }
550  }
551 
552 
553  internal class SKRenderContextImpl : SKRenderContext, IGraphicsContext, IDisposable
554  {
555  public Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> TaggedActions { get; set; } = new Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>>();
556 
557  private readonly bool removeTaggedActions = true;
558 
559  public string Tag { get; set; }
560 
561  private readonly AvaloniaContextInterpreter.TextOptions _textOption;
562 
563  internal override Dictionary<string, (SKBitmap, bool)> Images { get; set; }
564 
565  private readonly FilterOption _filterOption;
566 
567  public SKRenderContextImpl(double width, double height, bool removeTaggedActionsAfterExecution, AvaloniaContextInterpreter.TextOptions textOption, Dictionary<string, (SKBitmap, bool)> images, FilterOption filterOption)
568  {
569  this.Images = images;
570 
571  currentPath = null;
572  figureInitialised = false;
573 
574  SKRenderActions = new List<SKRenderAction>();
575  removeTaggedActions = removeTaggedActionsAfterExecution;
576 
577  Width = width;
578  Height = height;
579 
580  _textOption = textOption;
581  _filterOption = filterOption;
582  }
583 
584  internal override List<SKRenderAction> SKRenderActions { get; set; }
585 
586  public double Width { get; private set; }
587  public double Height { get; private set; }
588 
589  private void AddAction(SKRenderAction act)
590  {
591  if (!string.IsNullOrEmpty(Tag))
592  {
593  if (TaggedActions.ContainsKey(Tag))
594  {
595  IEnumerable<SKRenderAction> actions = TaggedActions[Tag](act);
596 
597  foreach (SKRenderAction action in actions)
598  {
599  SKRenderActions.Add(action);
600  }
601 
602  if (removeTaggedActions)
603  {
604  TaggedActions.Remove(Tag);
605  }
606  }
607  else
608  {
609  SKRenderActions.Add(act);
610  }
611  }
612  else if (TaggedActions.ContainsKey(""))
613  {
614  IEnumerable<SKRenderAction> actions = TaggedActions[""](act);
615 
616  foreach (SKRenderAction action in actions)
617  {
618  SKRenderActions.Add(action);
619  }
620  }
621  else
622  {
623  SKRenderActions.Add(act);
624  }
625  }
626 
627  public void Translate(double x, double y)
628  {
629  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
630 
631  SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateTranslation((float)x, (float)y), Tag);
632  AddAction(act);
633 
634  currentPath = null;
635  figureInitialised = false;
636  }
637 
638  public TextBaselines TextBaseline { get; set; }
639 
640  private void PathText(string text, double x, double y)
641  {
642  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
643 
644  GraphicsPath textPath = new GraphicsPath().AddText(x, y, text, Font, TextBaseline);
645 
646  for (int j = 0; j < textPath.Segments.Count; j++)
647  {
648  switch (textPath.Segments[j].Type)
649  {
650  case VectSharp.SegmentType.Move:
651  this.MoveTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
652  break;
653  case VectSharp.SegmentType.Line:
654  this.LineTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
655  break;
656  case VectSharp.SegmentType.CubicBezier:
657  this.CubicBezierTo(textPath.Segments[j].Points[0].X, textPath.Segments[j].Points[0].Y, textPath.Segments[j].Points[1].X, textPath.Segments[j].Points[1].Y, textPath.Segments[j].Points[2].X, textPath.Segments[j].Points[2].Y);
658  break;
659  case VectSharp.SegmentType.Close:
660  this.Close();
661  break;
662  }
663  }
664  }
665 
666  public void StrokeSimpleText(string text, double x, double y)
667  {
668  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
669 
670  if ((_textOption == AvaloniaContextInterpreter.TextOptions.NeverConvert || (_textOption == AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary && (Font.FontFamily.IsStandardFamily || Font.FontFamily.TrueTypeFile?.FontStream != null))) && !System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux))
671  {
672  SKTypeface typeface = SKTypefaceCache.GetSKTypeface(Font.FontFamily);
673 
674  double top = y;
675  double left = x;
676 
677  Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(text);
678 
679  if (TextBaseline == TextBaselines.Top)
680  {
681  if (Font.FontFamily.TrueTypeFile != null)
682  {
683  left -= metrics.LeftSideBearing;
684  top += metrics.Top;
685  }
686  }
687  else if (TextBaseline == TextBaselines.Middle)
688  {
689  if (Font.FontFamily.TrueTypeFile != null)
690  {
691  left -= metrics.LeftSideBearing;
692  top += (metrics.Top + metrics.Bottom) * 0.5;
693  }
694  }
695  else if (TextBaseline == TextBaselines.Baseline)
696  {
697  if (Font.FontFamily.TrueTypeFile != null)
698  {
699  left -= metrics.LeftSideBearing;
700  }
701  }
702  else if (TextBaseline == TextBaselines.Bottom)
703  {
704  if (Font.FontFamily.TrueTypeFile != null)
705  {
706  left -= metrics.LeftSideBearing;
707  top += metrics.Bottom;
708  }
709  }
710 
711  SKPaint stroke = new SKPaint() { IsStroke = true, IsAntialias = true, Style = SKPaintStyle.Stroke, StrokeWidth = (float)LineWidth, SubpixelText = true };
712 
713  if (this.StrokeStyle is SolidColourBrush solid)
714  {
715  stroke.Color = new SKColor((byte)(solid.R * 255), (byte)(solid.G * 255), (byte)(solid.B * 255), StrokeAlpha);
716  }
717  else if (this.StrokeStyle is LinearGradientBrush linearGradient)
718  {
719  stroke.Shader = linearGradient.ToSKShader();
720  }
721  else if (this.StrokeStyle is RadialGradientBrush radialGradient)
722  {
723  stroke.Shader = radialGradient.ToSKShader();
724  }
725 
726  stroke.PathEffect = SKPathEffect.CreateDash(new float[] { (float)LineDash[0], (float)LineDash[1] }, (float)LineDash[2]);
727 
728  switch (LineCap)
729  {
730  case LineCaps.Butt:
731  stroke.StrokeCap = SKStrokeCap.Butt;
732  break;
733  case LineCaps.Round:
734  stroke.StrokeCap = SKStrokeCap.Round;
735  break;
736  case LineCaps.Square:
737  stroke.StrokeCap = SKStrokeCap.Square;
738  break;
739  }
740 
741  switch (LineJoin)
742  {
743  case LineJoins.Bevel:
744  stroke.StrokeJoin = SKStrokeJoin.Bevel;
745  break;
746  case LineJoins.Round:
747  stroke.StrokeJoin = SKStrokeJoin.Round;
748  break;
749  case LineJoins.Miter:
750  stroke.StrokeJoin = SKStrokeJoin.Miter;
751  break;
752  }
753 
754  SKRenderAction act = SKRenderAction.TextAction(text, (float)left, (float)top, new SKFont(typeface, (float)Font.FontSize), stroke, Tag);
755 
756  AddAction(act);
757  }
758  else
759  {
760  PathText(text, x, y);
761  Stroke();
762  }
763  }
764 
765  public void FillText(string text, double x, double y)
766  {
767  if (!Font.EnableKerning)
768  {
769  FillSimpleText(text, x, y);
770  }
771  else
772  {
773  List<(string, Point)> tSpans = new List<(string, Point)>();
774 
775  System.Text.StringBuilder currentRun = new System.Text.StringBuilder();
776  Point currentKerning = new Point();
777 
778  Point currentGlyphPlacementDelta = new Point();
779  Point currentGlyphAdvanceDelta = new Point();
780  Point nextGlyphPlacementDelta = new Point();
781  Point nextGlyphAdvanceDelta = new Point();
782 
783  for (int i = 0; i < text.Length; i++)
784  {
785  if (i < text.Length - 1)
786  {
787  currentGlyphPlacementDelta = nextGlyphPlacementDelta;
788  currentGlyphAdvanceDelta = nextGlyphAdvanceDelta;
789  nextGlyphAdvanceDelta = new Point();
790  nextGlyphPlacementDelta = new Point();
791 
792  TrueTypeFile.PairKerning kerning = Font.FontFamily.TrueTypeFile.Get1000EmKerning(text[i], text[i + 1]);
793 
794  if (kerning != null)
795  {
796  currentGlyphPlacementDelta = new Point(currentGlyphPlacementDelta.X + kerning.Glyph1Placement.X, currentGlyphPlacementDelta.Y + kerning.Glyph1Placement.Y);
797  currentGlyphAdvanceDelta = new Point(currentGlyphAdvanceDelta.X + kerning.Glyph1Advance.X, currentGlyphAdvanceDelta.Y + kerning.Glyph1Advance.Y);
798 
799  nextGlyphPlacementDelta = new Point(nextGlyphPlacementDelta.X + kerning.Glyph2Placement.X, nextGlyphPlacementDelta.Y + kerning.Glyph2Placement.Y);
800  nextGlyphAdvanceDelta = new Point(nextGlyphAdvanceDelta.X + kerning.Glyph2Advance.X, nextGlyphAdvanceDelta.Y + kerning.Glyph2Advance.Y);
801  }
802  }
803 
804  if (currentGlyphPlacementDelta.X != 0 || currentGlyphPlacementDelta.Y != 0 || currentGlyphAdvanceDelta.X != 0 || currentGlyphAdvanceDelta.Y != 0)
805  {
806  if (currentRun.Length > 0)
807  {
808  tSpans.Add((currentRun.ToString(), currentKerning));
809 
810  tSpans.Add((text[i].ToString(), new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000, currentGlyphPlacementDelta.Y * Font.FontSize / 1000)));
811 
812  currentRun.Clear();
813  currentKerning = new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
814  }
815  else
816  {
817  tSpans.Add((text[i].ToString(), new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000 + currentKerning.X, currentGlyphPlacementDelta.Y * Font.FontSize / 1000 + currentKerning.Y)));
818 
819  currentRun.Clear();
820  currentKerning = new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
821  }
822  }
823  else
824  {
825  currentRun.Append(text[i]);
826  }
827  }
828 
829  if (currentRun.Length > 0)
830  {
831  tSpans.Add((currentRun.ToString(), currentKerning));
832  }
833 
834  double currX = x;
835  double currY = y;
836 
837  Font.DetailedFontMetrics fullMetrics = Font.MeasureTextAdvanced(text);
838 
839  if (TextBaseline == TextBaselines.Top)
840  {
841  if (Font.FontFamily.TrueTypeFile != null)
842  {
843  currY += fullMetrics.Top;
844  }
845  }
846  else if (TextBaseline == TextBaselines.Middle)
847  {
848  if (Font.FontFamily.TrueTypeFile != null)
849  {
850  currY += (fullMetrics.Top + fullMetrics.Bottom) * 0.5;
851  }
852  }
853  else if (TextBaseline == TextBaselines.Bottom)
854  {
855  if (Font.FontFamily.TrueTypeFile != null)
856  {
857  currY += fullMetrics.Bottom;
858  }
859  }
860 
861  TextBaseline = TextBaselines.Baseline;
862 
863  for (int i = 0; i < tSpans.Count; i++)
864  {
865  Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(tSpans[i].Item1);
866 
867  if (i == 0)
868  {
869  FillSimpleText(tSpans[i].Item1, currX + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
870  }
871  else
872  {
873  FillSimpleText(tSpans[i].Item1, currX + metrics.LeftSideBearing - fullMetrics.LeftSideBearing + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
874  }
875 
876 
877  currX += metrics.AdvanceWidth + tSpans[i].Item2.X;
878  currY += tSpans[i].Item2.Y;
879  }
880  }
881  }
882 
883  public void StrokeText(string text, double x, double y)
884  {
885  if (!Font.EnableKerning)
886  {
887  StrokeSimpleText(text, x, y);
888  }
889  else
890  {
891  List<(string, Point)> tSpans = new List<(string, Point)>();
892 
893  System.Text.StringBuilder currentRun = new System.Text.StringBuilder();
894  Point currentKerning = new Point();
895 
896  Point currentGlyphPlacementDelta = new Point();
897  Point currentGlyphAdvanceDelta = new Point();
898  Point nextGlyphPlacementDelta = new Point();
899  Point nextGlyphAdvanceDelta = new Point();
900 
901  for (int i = 0; i < text.Length; i++)
902  {
903  if (i < text.Length - 1)
904  {
905  currentGlyphPlacementDelta = nextGlyphPlacementDelta;
906  currentGlyphAdvanceDelta = nextGlyphAdvanceDelta;
907  nextGlyphAdvanceDelta = new Point();
908  nextGlyphPlacementDelta = new Point();
909 
910  TrueTypeFile.PairKerning kerning = Font.FontFamily.TrueTypeFile.Get1000EmKerning(text[i], text[i + 1]);
911 
912  if (kerning != null)
913  {
914  currentGlyphPlacementDelta = new Point(currentGlyphPlacementDelta.X + kerning.Glyph1Placement.X, currentGlyphPlacementDelta.Y + kerning.Glyph1Placement.Y);
915  currentGlyphAdvanceDelta = new Point(currentGlyphAdvanceDelta.X + kerning.Glyph1Advance.X, currentGlyphAdvanceDelta.Y + kerning.Glyph1Advance.Y);
916 
917  nextGlyphPlacementDelta = new Point(nextGlyphPlacementDelta.X + kerning.Glyph2Placement.X, nextGlyphPlacementDelta.Y + kerning.Glyph2Placement.Y);
918  nextGlyphAdvanceDelta = new Point(nextGlyphAdvanceDelta.X + kerning.Glyph2Advance.X, nextGlyphAdvanceDelta.Y + kerning.Glyph2Advance.Y);
919  }
920  }
921 
922  if (currentGlyphPlacementDelta.X != 0 || currentGlyphPlacementDelta.Y != 0 || currentGlyphAdvanceDelta.X != 0 || currentGlyphAdvanceDelta.Y != 0)
923  {
924  if (currentRun.Length > 0)
925  {
926  tSpans.Add((currentRun.ToString(), currentKerning));
927 
928  tSpans.Add((text[i].ToString(), new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000, currentGlyphPlacementDelta.Y * Font.FontSize / 1000)));
929 
930  currentRun.Clear();
931  currentKerning = new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
932  }
933  else
934  {
935  tSpans.Add((text[i].ToString(), new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000 + currentKerning.X, currentGlyphPlacementDelta.Y * Font.FontSize / 1000 + currentKerning.Y)));
936 
937  currentRun.Clear();
938  currentKerning = new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
939  }
940  }
941  else
942  {
943  currentRun.Append(text[i]);
944  }
945  }
946 
947  if (currentRun.Length > 0)
948  {
949  tSpans.Add((currentRun.ToString(), currentKerning));
950  }
951 
952  double currX = x;
953  double currY = y;
954 
955  Font.DetailedFontMetrics fullMetrics = Font.MeasureTextAdvanced(text);
956 
957  for (int i = 0; i < tSpans.Count; i++)
958  {
959  Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(tSpans[i].Item1);
960 
961  if (i == 0)
962  {
963  StrokeSimpleText(tSpans[i].Item1, currX + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
964  }
965  else
966  {
967  StrokeSimpleText(tSpans[i].Item1, currX + metrics.LeftSideBearing - fullMetrics.LeftSideBearing + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
968  }
969 
970 
971  currX += metrics.AdvanceWidth + tSpans[i].Item2.X;
972  currY += tSpans[i].Item2.Y;
973  }
974  }
975  }
976 
977 
978  public void FillSimpleText(string text, double x, double y)
979  {
980  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
981 
982  if ((_textOption == AvaloniaContextInterpreter.TextOptions.NeverConvert || (_textOption == AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary && (Font.FontFamily.IsStandardFamily || Font.FontFamily.TrueTypeFile?.FontStream != null))) && !System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux))
983  {
984  SKTypeface typeface = SKTypefaceCache.GetSKTypeface(Font.FontFamily);
985 
986  double top = y;
987  double left = x;
988 
989  Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(text);
990 
991  if (TextBaseline == TextBaselines.Top)
992  {
993  if (Font.FontFamily.TrueTypeFile != null)
994  {
995  left -= metrics.LeftSideBearing;
996  top += metrics.Top;
997  }
998  }
999  else if (TextBaseline == TextBaselines.Middle)
1000  {
1001  if (Font.FontFamily.TrueTypeFile != null)
1002  {
1003  left -= metrics.LeftSideBearing;
1004  top += (metrics.Top + metrics.Bottom) * 0.5;
1005  }
1006  }
1007  else if (TextBaseline == TextBaselines.Baseline)
1008  {
1009  if (Font.FontFamily.TrueTypeFile != null)
1010  {
1011  left -= metrics.LeftSideBearing;
1012  }
1013  }
1014  else if (TextBaseline == TextBaselines.Bottom)
1015  {
1016  if (Font.FontFamily.TrueTypeFile != null)
1017  {
1018  left -= metrics.LeftSideBearing;
1019  top += metrics.Bottom;
1020  }
1021  }
1022 
1023  SKPaint fill = new SKPaint() { IsStroke = false, IsAntialias = true, Style = SKPaintStyle.Fill, SubpixelText = true };
1024 
1025  if (this.FillStyle is SolidColourBrush solid)
1026  {
1027  fill.Color = new SKColor((byte)(solid.R * 255), (byte)(solid.G * 255), (byte)(solid.B * 255), FillAlpha);
1028  }
1029  else if (this.FillStyle is LinearGradientBrush linearGradient)
1030  {
1031  fill.Shader = linearGradient.ToSKShader();
1032  }
1033  else if (this.FillStyle is RadialGradientBrush radialGradient)
1034  {
1035  fill.Shader = radialGradient.ToSKShader();
1036  }
1037 
1038  SKRenderAction act = SKRenderAction.TextAction(text, (float)left, (float)top, new SKFont(typeface, (float)Font.FontSize), fill, Tag);
1039 
1040  AddAction(act);
1041  }
1042  else
1043  {
1044  PathText(text, x, y);
1045  Fill();
1046  }
1047  }
1048 
1049  public Brush StrokeStyle { get; private set; } = Colour.FromRgb(0, 0, 0);
1050  private byte StrokeAlpha = 255;
1051 
1052  public Brush FillStyle { get; private set; } = Colour.FromRgb(0, 0, 0);
1053  private byte FillAlpha = 255;
1054 
1055  public void SetFillStyle((int r, int g, int b, double a) style)
1056  {
1057  FillStyle = Colour.FromRgba(style.r, style.g, style.b, (int)(style.a * 255));
1058  FillAlpha = (byte)(style.a * 255);
1059  }
1060 
1061  public void SetFillStyle(Brush style)
1062  {
1063  FillStyle = style;
1064 
1065  if (style is SolidColourBrush solid)
1066  {
1067  FillAlpha = (byte)(solid.A * 255);
1068  }
1069  else
1070  {
1071  FillAlpha = 255;
1072  }
1073  }
1074 
1075  public void SetStrokeStyle((int r, int g, int b, double a) style)
1076  {
1077  StrokeStyle = Colour.FromRgba(style.r, style.g, style.b, (int)(style.a * 255));
1078  StrokeAlpha = (byte)(style.a * 255);
1079  }
1080 
1081  public void SetStrokeStyle(Brush style)
1082  {
1083  StrokeStyle = style;
1084 
1085  if (style is SolidColourBrush solid)
1086  {
1087  StrokeAlpha = (byte)(solid.A * 255);
1088  }
1089  else
1090  {
1091  StrokeAlpha = 255;
1092  }
1093  }
1094 
1095  private double[] LineDash;
1096 
1097  public void SetLineDash(LineDash dash)
1098  {
1099  LineDash = new double[] { dash.UnitsOn, dash.UnitsOff, dash.Phase };
1100  }
1101 
1102  public void Rotate(double angle)
1103  {
1104  Utils.CoerceNaNAndInfinityToZero(ref angle);
1105 
1106  SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateRotation((float)angle), Tag);
1107  AddAction(act);
1108 
1109  currentPath = null;
1110  figureInitialised = false;
1111  }
1112 
1113  public void Transform(double a, double b, double c, double d, double e, double f)
1114  {
1115  Utils.CoerceNaNAndInfinityToZero(ref a, ref b, ref c, ref d, ref e, ref f);
1116 
1117  SKRenderAction act = SKRenderAction.TransformAction(new SKMatrix((float)a, (float)c, (float)e, (float)b, (float)d, (float)f, 0, 0, 1), Tag);
1118  AddAction(act);
1119 
1120  currentPath = null;
1121  figureInitialised = false;
1122  }
1123 
1124  public void Scale(double x, double y)
1125  {
1126  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1127 
1128  SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateScale((float)x, (float)y), Tag);
1129  AddAction(act);
1130 
1131  currentPath = null;
1132  figureInitialised = false;
1133  }
1134 
1135  public void Save()
1136  {
1137  SKRenderAction act = SKRenderAction.SaveAction(Tag);
1138  AddAction(act);
1139 
1140  currentPath = null;
1141  figureInitialised = false;
1142  }
1143 
1144  public void Restore()
1145  {
1146  SKRenderAction act = SKRenderAction.RestoreAction(Tag);
1147  AddAction(act);
1148 
1149  currentPath = null;
1150  figureInitialised = false;
1151  }
1152 
1153  public double LineWidth { get; set; }
1154  public LineCaps LineCap { get; set; }
1155  public LineJoins LineJoin { get; set; }
1156 
1157  public Font Font { get; set; }
1158 
1159  private SKPath currentPath;
1160 
1161  private bool figureInitialised = false;
1162  private bool disposedValue;
1163 
1164  public void MoveTo(double x, double y)
1165  {
1166  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1167 
1168  if (currentPath == null)
1169  {
1170  currentPath = new SKPath() { FillType = SKPathFillType.EvenOdd };
1171  }
1172 
1173  currentPath.MoveTo((float)x, (float)y);
1174  figureInitialised = true;
1175  }
1176 
1177  public void LineTo(double x, double y)
1178  {
1179  Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1180 
1181  if (currentPath == null)
1182  {
1183  currentPath = new SKPath() { FillType = SKPathFillType.EvenOdd };
1184  }
1185 
1186  if (!figureInitialised)
1187  {
1188  figureInitialised = true;
1189  currentPath.MoveTo((float)x, (float)y);
1190  }
1191  else
1192  {
1193  currentPath.LineTo((float)x, (float)y);
1194  }
1195  }
1196 
1197  public void Rectangle(double x0, double y0, double width, double height)
1198  {
1199  Utils.CoerceNaNAndInfinityToZero(ref x0, ref y0, ref width, ref height);
1200 
1201  if (currentPath == null)
1202  {
1203  currentPath = new SKPath() { FillType = SKPathFillType.EvenOdd };
1204  }
1205 
1206  currentPath.MoveTo((float)x0, (float)y0);
1207  currentPath.LineTo((float)(x0 + width), (float)y0);
1208  currentPath.LineTo((float)(x0 + width), (float)(y0 + height));
1209  currentPath.LineTo((float)x0, (float)(y0 + height));
1210 
1211  currentPath.Close();
1212  figureInitialised = false;
1213  }
1214 
1215  public void CubicBezierTo(double p1X, double p1Y, double p2X, double p2Y, double p3X, double p3Y)
1216  {
1217  Utils.CoerceNaNAndInfinityToZero(ref p1X, ref p1Y, ref p2X, ref p2Y, ref p3X, ref p3Y);
1218 
1219  if (currentPath == null)
1220  {
1221  currentPath = new SKPath() { FillType = SKPathFillType.EvenOdd };
1222  }
1223 
1224  if (figureInitialised)
1225  {
1226  currentPath.CubicTo((float)p1X, (float)p1Y, (float)p2X, (float)p2Y, (float)p3X, (float)p3Y);
1227  }
1228  else
1229  {
1230  currentPath.MoveTo((float)p1X, (float)p1Y);
1231  figureInitialised = true;
1232  }
1233  }
1234 
1235  public void Close()
1236  {
1237  currentPath.Close();
1238 
1239  figureInitialised = false;
1240  }
1241 
1242  public void Stroke()
1243  {
1244  SKPaint stroke = new SKPaint() { IsStroke = true, IsAntialias = true, Style = SKPaintStyle.Stroke, StrokeWidth = (float)LineWidth };
1245 
1246  if (this.StrokeStyle is SolidColourBrush solid)
1247  {
1248  stroke.Color = new SKColor((byte)(solid.R * 255), (byte)(solid.G * 255), (byte)(solid.B * 255), StrokeAlpha);
1249  }
1250  else if (this.StrokeStyle is LinearGradientBrush linearGradient)
1251  {
1252  stroke.Shader = linearGradient.ToSKShader();
1253  }
1254  else if (this.StrokeStyle is RadialGradientBrush radialGradient)
1255  {
1256  stroke.Shader = radialGradient.ToSKShader();
1257  }
1258 
1259  stroke.PathEffect = SKPathEffect.CreateDash(new float[] { (float)LineDash[0], (float)LineDash[1] }, (float)LineDash[2]);
1260 
1261  switch (LineCap)
1262  {
1263  case LineCaps.Butt:
1264  stroke.StrokeCap = SKStrokeCap.Butt;
1265  break;
1266  case LineCaps.Round:
1267  stroke.StrokeCap = SKStrokeCap.Round;
1268  break;
1269  case LineCaps.Square:
1270  stroke.StrokeCap = SKStrokeCap.Square;
1271  break;
1272  }
1273 
1274  switch (LineJoin)
1275  {
1276  case LineJoins.Bevel:
1277  stroke.StrokeJoin = SKStrokeJoin.Bevel;
1278  break;
1279  case LineJoins.Round:
1280  stroke.StrokeJoin = SKStrokeJoin.Round;
1281  break;
1282  case LineJoins.Miter:
1283  stroke.StrokeJoin = SKStrokeJoin.Miter;
1284  break;
1285  }
1286 
1287  SKRenderAction act = SKRenderAction.PathAction(currentPath, stroke, Tag);
1288 
1289  AddAction(act);
1290 
1291  currentPath = null;
1292  figureInitialised = false;
1293  }
1294 
1295  public void Fill()
1296  {
1297  SKPaint fill = new SKPaint() { IsStroke = false, IsAntialias = true, Style = SKPaintStyle.Fill };
1298 
1299  if (this.FillStyle is SolidColourBrush solid)
1300  {
1301  fill.Color = new SKColor((byte)(solid.R * 255), (byte)(solid.G * 255), (byte)(solid.B * 255), FillAlpha);
1302  }
1303  else if (this.FillStyle is LinearGradientBrush linearGradient)
1304  {
1305  fill.Shader = linearGradient.ToSKShader();
1306  }
1307  else if (this.FillStyle is RadialGradientBrush radialGradient)
1308  {
1309  fill.Shader = radialGradient.ToSKShader();
1310  }
1311 
1312  SKRenderAction act = SKRenderAction.PathAction(currentPath, fill, Tag);
1313 
1314  AddAction(act);
1315 
1316  currentPath = null;
1317  figureInitialised = false;
1318  }
1319 
1320  public void SetClippingPath()
1321  {
1322  SKRenderAction act = SKRenderAction.ClipAction(currentPath, Tag);
1323 
1324  AddAction(act);
1325 
1326  currentPath = null;
1327  figureInitialised = false;
1328  }
1329 
1330  public void DrawRasterImage(int sourceX, int sourceY, int sourceWidth, int sourceHeight, double destinationX, double destinationY, double destinationWidth, double destinationHeight, RasterImage image)
1331  {
1332  Utils.CoerceNaNAndInfinityToZero(ref destinationX, ref destinationY, ref destinationWidth, ref destinationHeight);
1333 
1334  if (!this.Images.ContainsKey(image.Id))
1335  {
1336  SKBitmap bmp = SKBitmap.Decode(image.PNGStream);
1337  this.Images.Add(image.Id, (bmp, image.Interpolate));
1338  }
1339 
1340  SKRenderAction act = SKRenderAction.ImageAction(image.Id, new SKRect(sourceX, sourceY, sourceX + sourceWidth, sourceY + sourceHeight), new SKRect((float)destinationX, (float)destinationY, (float)(destinationX + destinationWidth), (float)(destinationY + destinationHeight)), Tag);
1341 
1342  AddAction(act);
1343  }
1344 
1345  public void DrawFilteredGraphics(Graphics graphics, IFilter filter)
1346  {
1347  if (this._filterOption.Operation == FilterOption.FilterOperations.RasteriseAllWithSkia)
1348  {
1349  double scale = this._filterOption.RasterisationResolution;
1350 
1351  Rectangle bounds = graphics.GetBounds();
1352 
1353  bounds = new Rectangle(bounds.Location.X - filter.TopLeftMargin.X, bounds.Location.Y - filter.TopLeftMargin.Y, bounds.Size.Width + filter.TopLeftMargin.X + filter.BottomRightMargin.X, bounds.Size.Height + filter.TopLeftMargin.Y + filter.BottomRightMargin.Y);
1354 
1355  if (bounds.Size.Width > 0 && bounds.Size.Height > 0)
1356  {
1357  if (!this._filterOption.RasterisationResolutionRelative)
1358  {
1359  scale = scale / Math.Min(bounds.Size.Width, bounds.Size.Height);
1360  }
1361 
1362  RasterImage rasterised = SKRenderContextInterpreter.Rasterise(graphics, bounds, scale, true);
1363  RasterImage filtered = null;
1364 
1365  if (filter is IFilterWithRasterisableParameter filterWithRastParam)
1366  {
1367  filterWithRastParam.RasteriseParameter(SKRenderContextInterpreter.Rasterise, scale);
1368  }
1369 
1370  if (filter is ILocationInvariantFilter locInvFilter)
1371  {
1372  filtered = locInvFilter.Filter(rasterised, scale);
1373  }
1374  else if (filter is IFilterWithLocation filterWithLoc)
1375  {
1376  filtered = filterWithLoc.Filter(rasterised, bounds, scale);
1377  }
1378 
1379  if (filtered != null)
1380  {
1381  rasterised.Dispose();
1382 
1383  DrawRasterImage(0, 0, filtered.Width, filtered.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height, filtered);
1384  }
1385  }
1386  }
1387  else if (this._filterOption.Operation == FilterOption.FilterOperations.RasteriseAllWithVectSharp)
1388  {
1389  double scale = this._filterOption.RasterisationResolution;
1390 
1391  Rectangle bounds = graphics.GetBounds();
1392 
1393  bounds = new Rectangle(bounds.Location.X - filter.TopLeftMargin.X, bounds.Location.Y - filter.TopLeftMargin.Y, bounds.Size.Width + filter.TopLeftMargin.X + filter.BottomRightMargin.X, bounds.Size.Height + filter.TopLeftMargin.Y + filter.BottomRightMargin.Y);
1394 
1395  if (bounds.Size.Width > 0 && bounds.Size.Height > 0)
1396  {
1397  if (!this._filterOption.RasterisationResolutionRelative)
1398  {
1399  scale = scale / Math.Min(bounds.Size.Width, bounds.Size.Height);
1400  }
1401 
1402  if (graphics.TryRasterise(bounds, scale, true, out RasterImage rasterised))
1403  {
1404  RasterImage filtered = null;
1405 
1406  if (filter is IFilterWithRasterisableParameter filterWithRastParam)
1407  {
1408  filterWithRastParam.RasteriseParameter(SKRenderContextInterpreter.Rasterise, scale);
1409  }
1410 
1411  if (filter is ILocationInvariantFilter locInvFilter)
1412  {
1413  filtered = locInvFilter.Filter(rasterised, scale);
1414  }
1415  else if (filter is IFilterWithLocation filterWithLoc)
1416  {
1417  filtered = filterWithLoc.Filter(rasterised, bounds, scale);
1418  }
1419 
1420  if (filtered != null)
1421  {
1422  rasterised.Dispose();
1423 
1424  DrawRasterImage(0, 0, filtered.Width, filtered.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height, filtered);
1425  }
1426  }
1427  else
1428  {
1429  throw new NotImplementedException(@"The filter could not be rasterised! You can avoid this error by doing one of the following:
1430  • Add a reference to VectSharp.Raster or VectSharp.Raster.ImageSharp (you may also need to add a using directive somewhere to force the assembly to be loaded).
1431  • Provide your own implementation of Graphics.RasterisationMethod.
1432  • Set the FilterOption.Operation to ""RasteriseAllWithSkia"", ""IgnoreAll"" or ""SkipAll"".");
1433  }
1434  }
1435  }
1436  else if (this._filterOption.Operation == FilterOption.FilterOperations.IgnoreAll)
1437  {
1438  graphics.CopyToIGraphicsContext(this);
1439  }
1440  else
1441  {
1442 
1443  }
1444  }
1445 
1446  protected virtual void Dispose(bool disposing)
1447  {
1448  if (!disposedValue)
1449  {
1450  if (disposing)
1451  {
1452  this.currentPath?.Dispose();
1453  }
1454 
1455  disposedValue = true;
1456  }
1457  }
1458 
1459  public void Dispose()
1460  {
1461  Dispose(disposing: true);
1462  GC.SuppressFinalize(this);
1463  }
1464  }
1465 
1466  /// <summary>
1467  /// Determines how and whether image filters are rasterised.
1468  /// </summary>
1469  public class FilterOption
1470  {
1471  /// <summary>
1472  /// Defines whether image filters should be rasterised or not.
1473  /// </summary>
1474  public enum FilterOperations
1475  {
1476  /// <summary>
1477  /// Image filters will always be rasterised using the SkiaSharp backend.
1478  /// </summary>
1479  RasteriseAllWithSkia,
1480 
1481  /// <summary>
1482  /// Image filters will always be rasterised using the VectSharp.Raster or VectSharp.Raster.ImageSharp. This option requires a reference to VectSharp.Raster or to VectSharp.Raster.ImageSharp to be added.
1483  /// </summary>
1484  RasteriseAllWithVectSharp,
1485 
1486  /// <summary>
1487  /// All image filters will be ignored.
1488  /// </summary>
1489  IgnoreAll,
1490 
1491  /// <summary>
1492  /// All the images that should be drawn with a filter will be ignored.
1493  /// </summary>
1494  SkipAll
1495  }
1496 
1497  /// <summary>
1498  /// Defines whether image filters should be rasterised or not.
1499  /// </summary>
1500  public FilterOperations Operation { get; } = FilterOperations.RasteriseAllWithSkia;
1501 
1502  /// <summary>
1503  /// The resolution that will be used to rasterise image filters. Depending on the value of <see cref="RasterisationResolutionRelative"/>, this can either be an absolute resolution (i.e. a size in pixel), or a scale factor that is applied to the image size in graphics units.
1504  /// </summary>
1505  public double RasterisationResolution { get; } = 1;
1506 
1507  /// <summary>
1508  /// Determines whether the value of <see cref="RasterisationResolution"/> is absolute (i.e. a size in pixel), or relative (i.e. a scale factor that is applied to the image size in graphics units).
1509  /// </summary>
1510  public bool RasterisationResolutionRelative { get; } = true;
1511 
1512  /// <summary>
1513  /// The default options for image filter rasterisation.
1514  /// </summary>
1515  public static FilterOption Default = new FilterOption(FilterOperations.RasteriseAllWithSkia, 1, true);
1516 
1517  /// <summary>
1518  /// Create a new <see cref="FilterOption"/> object.
1519  /// </summary>
1520  /// <param name="operation">Defines whether image filters should be rasterised or not.</param>
1521  /// <param name="rasterisationResolution">The resolution that will be used to rasterise image filters. Depending on the value of <see cref="RasterisationResolutionRelative"/>, this can either be an absolute resolution (i.e. a size in pixel), or a scale factor that is applied to the image size in graphics units.</param>
1522  /// <param name="rasterisationResolutionRelative">Determines whether the value of <see cref="RasterisationResolution"/> is absolute (i.e. a size in pixel), or relative (i.e. a scale factor that is applied to the image size in graphics units).</param>
1523  public FilterOption(FilterOperations operation, double rasterisationResolution, bool rasterisationResolutionRelative)
1524  {
1525  this.Operation = operation;
1526  this.RasterisationResolution = rasterisationResolution;
1527  this.RasterisationResolutionRelative = rasterisationResolutionRelative;
1528  }
1529  }
1530 
1531  /// <summary>
1532  /// Contains methods to render a <see cref="Page"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharp renderer.
1533  /// </summary>
1534  public static class SKRenderContextInterpreter
1535  {
1536  internal static SKColor ToSKColor(this Colour colour)
1537  {
1538  return new SKColor((byte)(255 * colour.R), (byte)(255 * colour.G), (byte)(255 * colour.B), (byte)(255 * colour.A));
1539  }
1540 
1541  internal static SKShader ToSKShader(this LinearGradientBrush brush)
1542  {
1543  return SKShader.CreateLinearGradient(new SKPoint((float)brush.StartPoint.X, (float)brush.StartPoint.Y), new SKPoint((float)brush.EndPoint.X, (float)brush.EndPoint.Y), (from el in brush.GradientStops select el.Colour.ToSKColor()).ToArray(), (from el in brush.GradientStops select (float)el.Offset).ToArray(), SKShaderTileMode.Clamp);
1544  }
1545 
1546  internal static SKShader ToSKShader(this RadialGradientBrush brush)
1547  {
1548  return SKShader.CreateTwoPointConicalGradient(new SKPoint((float)brush.FocalPoint.X, (float)brush.FocalPoint.Y), 0, new SKPoint((float)brush.Centre.X, (float)brush.Centre.Y), (float)brush.Radius, (from el in brush.GradientStops select el.Colour.ToSKColor()).ToArray(), (from el in brush.GradientStops select (float)el.Offset).ToArray(), SKShaderTileMode.Clamp);
1549  }
1550 
1551  /// <summary>
1552  /// Render a <see cref="Document"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharp renderer. Each page corresponds to a layer in the image.
1553  /// </summary>
1554  /// <param name="document">The <see cref="Document"/> to render.</param>
1555  /// <param name="width">The width of the document. If this is <see langword="null" />, the width of the largest page is used.</param>
1556  /// <param name="height">The height of the document. If this is <see langword="null" />, the height of the largest page is used.</param>
1557  /// <param name="background">The background colour of the document. If this is <see langword="null" />, a transparent background is used.</param>
1558  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1559  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1560  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1561  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, double? width = null, double? height = null, Colour? background = null, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1562  {
1563  return new SKMultiLayerRenderCanvas((from el in document.Pages select el.CopyToSKRenderContext(textOption, filterOption)).ToList(), (from el in document.Pages select SKRenderAction.TransformAction(SKMatrix.Identity)).ToList(), background ?? Colour.FromRgba(0, 0, 0, 0), width ?? (from el in document.Pages select el.Width).Max(), height ?? (from el in document.Pages select el.Height).Max());
1564  }
1565 
1566  /// <summary>
1567  /// Render a <see cref="Document"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharp renderer. Each page corresponds to a layer in the image.
1568  /// </summary>
1569  /// <param name="document">The <see cref="Document"/> to render.</param>
1570  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1571  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1572  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1573  /// <param name="width">The width of the document. If this is <see langword="null" />, the width of the largest page is used.</param>
1574  /// <param name="height">The height of the document. If this is <see langword="null" />, the height of the largest page is used.</param>
1575  /// <param name="background">The background colour of the document. If this is <see langword="null" />, a transparent background is used.</param>
1576  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1577  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1578  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1579  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, bool removeTaggedActionsAfterExecution = true, double? width = null, double? height = null, Colour? background = null, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1580  {
1581  return new SKMultiLayerRenderCanvas((from el in document.Pages select el.CopyToSKRenderContext(taggedActions, removeTaggedActionsAfterExecution, textOption, filterOption)).ToList(), (from el in document.Pages select SKRenderAction.TransformAction(SKMatrix.Identity)).ToList(), background ?? Colour.FromRgba(0, 0, 0, 0), width ?? (from el in document.Pages select el.Width).Max(), height ?? (from el in document.Pages select el.Height).Max());
1582  }
1583 
1584  /// <summary>
1585  /// Render a <see cref="Document"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharp renderer. Each page corresponds to a layer in the image.
1586  /// </summary>
1587  /// <param name="document">The <see cref="Document"/> to render.</param>
1588  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1589  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1590  /// <param name="images">A dictionary that associates to each raster image path (or data URL) the image rendered as a <see cref="SKBitmap"/> and a boolean value indicating whether it should be drawn as "pixelated" or not. This will be populated automatically as the page is rendered.
1591  /// If you are rendering multiple <see cref="Page"/>s (or you are rendering the same page multiple times), it will be beneficial to keep a reference to this dictionary and pass it again on further rendering requests; otherwise, you can just pass an empty dictionary.</param>
1592  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1593  /// <param name="width">The width of the document. If this is <see langword="null" />, the width of the largest page is used.</param>
1594  /// <param name="height">The height of the document. If this is <see langword="null" />, the height of the largest page is used.</param>
1595  /// <param name="background">The background colour of the document. If this is <see langword="null" />, a transparent background is used.</param>
1596  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1597  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1598  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1599  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, Dictionary<string, (SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution = true, double? width = null, double? height = null, Colour? background = null, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1600  {
1601  return new SKMultiLayerRenderCanvas((from el in document.Pages select el.CopyToSKRenderContext(taggedActions, images, removeTaggedActionsAfterExecution, textOption, filterOption)).ToList(), (from el in document.Pages select SKRenderAction.TransformAction(SKMatrix.Identity)).ToList(), background ?? Colour.FromRgba(0, 0, 0, 0), width ?? (from el in document.Pages select el.Width).Max(), height ?? (from el in document.Pages select el.Height).Max());
1602  }
1603 
1604  /// <summary>
1605  /// Render a <see cref="Page"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharp renderer.
1606  /// </summary>
1607  /// <param name="page">The <see cref="Page"/> to render.</param>
1608  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1609  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1610  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1611  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1612  {
1613  return new SKMultiLayerRenderCanvas(new List<SKRenderContext>() { page.CopyToSKRenderContext(textOption, filterOption) }, new List<SKRenderAction>() { SKRenderAction.TransformAction(SKMatrix.Identity) }, page.Background, page.Width, page.Height);
1614  }
1615 
1616  /// <summary>
1617  /// Render a <see cref="Page"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharpRenderer.
1618  /// </summary>
1619  /// <param name="page">The <see cref="Page"/> to render.</param>
1620  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1621  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1622  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1623  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1624  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1625  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1626  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, bool removeTaggedActionsAfterExecution = true, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1627  {
1628  return new SKMultiLayerRenderCanvas(new List<SKRenderContext>() { page.CopyToSKRenderContext(taggedActions, removeTaggedActionsAfterExecution, textOption, filterOption) }, new List<SKRenderAction>() { SKRenderAction.TransformAction(SKMatrix.Identity) }, page.Background, page.Width, page.Height);
1629  }
1630 
1631  /// <summary>
1632  /// Render a <see cref="Page"/> to an <see cref="Avalonia.Controls.Canvas"/> using the SkiaSharpRenderer.
1633  /// </summary>
1634  /// <param name="page">The <see cref="Page"/> to render.</param>
1635  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1636  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1637  /// <param name="images">A dictionary that associates to each raster image path (or data URL) the image rendered as a <see cref="SKBitmap"/> and a boolean value indicating whether it should be drawn as "pixelated" or not. This will be populated automatically as the page is rendered.
1638  /// If you are rendering multiple <see cref="Page"/>s (or you are rendering the same page multiple times), it will be beneficial to keep a reference to this dictionary and pass it again on further rendering requests; otherwise, you can just pass an empty dictionary.</param>
1639  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1640  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1641  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1642  /// <returns>An <see cref="Avalonia.Controls.Canvas"/> containing the rendered graphics objects.</returns>
1643  public static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, Dictionary<string, (SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution = true, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1644  {
1645  return new SKMultiLayerRenderCanvas(new List<SKRenderContext>() { page.CopyToSKRenderContext(taggedActions, images, removeTaggedActionsAfterExecution, textOption, filterOption) }, new List<SKRenderAction>() { SKRenderAction.TransformAction(SKMatrix.Identity) }, page.Background, page.Width, page.Height);
1646  }
1647 
1648  /// <summary>
1649  /// Render a <see cref="Page"/> to a <see cref="SKRenderContext"/>. This can be drawn using the SkiaSharpRenderer by adding it to a <see cref="SKMultiLayerRenderCanvas"/>.
1650  /// </summary>
1651  /// <param name="page">The <see cref="Page"/> to render.</param>
1652  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1653  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1654  /// <returns>A <see cref="SKRenderContext"/> containing the rendered graphics objects.</returns>
1655  public static SKRenderContext CopyToSKRenderContext(this Page page, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1656  {
1657  return CopyToSKRenderContext(page, new Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>>(), new Dictionary<string, (SKBitmap, bool)>(), textOption: textOption, filterOption: filterOption);
1658  }
1659 
1660  /// <summary>
1661  /// Render a <see cref="Page"/> to a <see cref="SKRenderContext"/>. This can be drawn using the SkiaSharpRenderer by adding it to a <see cref="SKMultiLayerRenderCanvas"/>.
1662  /// </summary>
1663  /// <param name="page">The <see cref="Page"/> to render.</param>
1664  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1665  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1666  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1667  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1668  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1669  /// <returns>A <see cref="SKRenderContext"/> containing the rendered graphics objects.</returns>
1670  public static SKRenderContext CopyToSKRenderContext(this Page page, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, bool removeTaggedActionsAfterExecution = true, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1671  {
1672  return CopyToSKRenderContext(page, taggedActions, new Dictionary<string, (SKBitmap, bool)>(), removeTaggedActionsAfterExecution, textOption, filterOption);
1673  }
1674 
1675  /// <summary>
1676  /// Render a <see cref="Page"/> to a <see cref="SKRenderContext"/>. This can be drawn using the SkiaSharpRenderer by adding it to a <see cref="SKMultiLayerRenderCanvas"/>.
1677  /// </summary>
1678  /// <param name="page">The <see cref="Page"/> to render.</param>
1679  /// <param name="taggedActions">A Dictionary containing the actions that will be performed on items with the corresponding tag.
1680  /// These should be functions that accept one parameter of type <see cref="SKRenderAction"/> and return an <see cref="IEnumerable{SKRenderAction}"/> of the render actions that will actually be added to the plot.</param>
1681  /// <param name="images">A dictionary that associates to each raster image path (or data URL) the image rendered as a <see cref="SKBitmap"/> and a boolean value indicating whether it should be drawn as "pixelated" or not. This will be populated automatically as the page is rendered.
1682  /// If you are rendering multiple <see cref="Page"/>s (or you are rendering the same page multiple times), it will be beneficial to keep a reference to this dictionary and pass it again on further rendering requests; otherwise, you can just pass an empty dictionary.</param>
1683  /// <param name="removeTaggedActionsAfterExecution">Whether the actions should be removed from <paramref name="taggedActions"/> after their execution. Set to false if the same action should be performed on multiple items with the same tag.</param>
1684  /// <param name="textOption">Defines whether text items should be converted into paths when drawing.</param>
1685  /// <param name="filterOption">Defines how and whether image filters should be rasterised when rendering the image.</param>
1686  /// <returns>A <see cref="SKRenderContext"/> containing the rendered graphics objects.</returns>
1687  public static SKRenderContext CopyToSKRenderContext(this Page page, Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> taggedActions, Dictionary<string, (SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution = true, AvaloniaContextInterpreter.TextOptions textOption = AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption = default)
1688  {
1689  if (filterOption == null)
1690  {
1691  filterOption = FilterOption.Default;
1692  }
1693 
1694  SKRenderContextImpl tbr = new SKRenderContextImpl(page.Width, page.Height, removeTaggedActionsAfterExecution, textOption, images, filterOption)
1695  {
1696  TaggedActions = taggedActions
1697  };
1698  page.Graphics.CopyToIGraphicsContext(tbr);
1699 
1700  return tbr;
1701  }
1702 
1703 
1704  /// <summary>
1705  /// Rasterise a region of a <see cref="Graphics"/> object.
1706  /// </summary>
1707  /// <param name="graphics">The <see cref="Graphics"/> object that will be rasterised.</param>
1708  /// <param name="region">The region of the <paramref name="graphics"/> that will be rasterised.</param>
1709  /// <param name="scale">The scale at which the image will be rendered.</param>
1710  /// <param name="interpolate">Whether the resulting image should be interpolated or not when it is drawn on another <see cref="Graphics"/> surface.</param>
1711  /// <returns>A <see cref="RasterImage"/> containing the rasterised graphics.</returns>
1712  public static RasterImage Rasterise(this Graphics graphics, Rectangle region, double scale, bool interpolate)
1713  {
1714  Page pag = new Page(1, 1);
1715  pag.Graphics.DrawGraphics(0, 0, graphics);
1716  pag.Crop(region.Location, region.Size);
1717 
1718  //pag.PaintToSKCanvas().RenderAtResolution((int)Math.Round(region.Size.Width * scale), (int)Math.Round(region.Size.Height * scale), null);
1719 
1720  int width = (int)Math.Round(region.Size.Width * scale);
1721  int height = (int)Math.Round(region.Size.Height * scale);
1722 
1723  SKRenderContext ctx = pag.CopyToSKRenderContext();
1724 
1725  SKBitmap bitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Unpremul);
1726 
1727  SKCanvas canvas = new SKCanvas(bitmap);
1728 
1729  canvas.Save();
1730 
1731  for (int i = 0; i < ctx.SKRenderActions.Count; i++)
1732  {
1733  if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Clip)
1734  {
1735  canvas.ClipPath(ctx.SKRenderActions[i].Path, antialias: true);
1736  }
1737  else if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Restore)
1738  {
1739  canvas.Restore();
1740  }
1741  else if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Save)
1742  {
1743  canvas.Save();
1744  }
1745  else if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Transform)
1746  {
1747  SKMatrix mat = ctx.SKRenderActions[i].Transform.Value;
1748  canvas.Concat(ref mat);
1749  }
1750  else
1751  {
1752  if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Path && ctx.SKRenderActions[i].Path != null)
1753  {
1754  canvas.DrawPath(ctx.SKRenderActions[i].Path, ctx.SKRenderActions[i].Paint);
1755  }
1756  else if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.Text)
1757  {
1758  canvas.DrawText(ctx.SKRenderActions[i].Text, ctx.SKRenderActions[i].TextX, ctx.SKRenderActions[i].TextY, ctx.SKRenderActions[i].Font, ctx.SKRenderActions[i].Paint);
1759  }
1760  else if (ctx.SKRenderActions[i].ActionType == SKRenderAction.ActionTypes.RasterImage)
1761  {
1762  (SKBitmap image, bool interpolateIt) = ctx.Images[ctx.SKRenderActions[i].ImageId];
1763 
1764  SKPaint paint;
1765 
1766  if (!interpolateIt)
1767  {
1768  paint = null;
1769  }
1770  else
1771  {
1772  paint = new SKPaint() { FilterQuality = SKFilterQuality.Medium };
1773  }
1774 
1775  canvas.DrawBitmap(image, ctx.SKRenderActions[i].ImageSource.Value, ctx.SKRenderActions[i].ImageDestination.Value, paint);
1776 
1777  paint?.Dispose();
1778  }
1779  }
1780  }
1781 
1782  canvas.Restore();
1783 
1784  IntPtr pixels = bitmap.GetPixels(out IntPtr length);
1785 
1786  IntPtr tbrData = System.Runtime.InteropServices.Marshal.AllocHGlobal(length);
1787  GC.AddMemoryPressure((long)length);
1788 
1789  unsafe
1790  {
1791  Buffer.MemoryCopy((void*)pixels, (void*)tbrData, (long)length, (long)length);
1792  }
1793 
1794  canvas.Dispose();
1795  bitmap.Dispose();
1796 
1797  DisposableIntPtr disp = new DisposableIntPtr(tbrData);
1798  return new RasterImage(ref disp, width, height, true, interpolate);
1799  }
1800  }
1801 }
VectSharp.Canvas.SKRenderContextInterpreter.Rasterise
static RasterImage Rasterise(this Graphics graphics, Rectangle region, double scale, bool interpolate)
Rasterise a region of a Graphics object.
Definition: SKRenderContext.cs:1712
VectSharp.Rectangle
Represents a rectangle.
Definition: Point.cs:173
VectSharp.Canvas.SKRenderAction.ActionTypes
ActionTypes
Types of rendering actions.
Definition: SKRenderContext.cs:43
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to an Avalonia.Controls.Canvas using the SkiaSharp renderer.
Definition: SKRenderContext.cs:1611
VectSharp.Canvas.FilterOption.RasterisationResolutionRelative
bool RasterisationResolutionRelative
Determines whether the value of RasterisationResolution is absolute (i.e. a size in pixel),...
Definition: SKRenderContext.cs:1510
VectSharp.Canvas
Definition: AvaloniaContext.cs:29
VectSharp.Canvas.SKRenderAction.TextX
float TextX
The X coordainate at which the text will be drawn (null if the action type is not ActionTypes....
Definition: SKRenderContext.cs:111
VectSharp.Canvas.FilterOption.FilterOperations
FilterOperations
Defines whether image filters should be rasterised or not.
Definition: SKRenderContext.cs:1475
VectSharp.Filters.IFilter.TopLeftMargin
Point TopLeftMargin
Determines how much the area of the filter's subject should be expanded on the top-left to accommodat...
Definition: Filters.cs:30
VectSharp.Graphics.DrawGraphics
void DrawGraphics(Point origin, Graphics graphics)
Draws a Graphics object on the current Graphics object.
Definition: Graphics.cs:802
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, Dictionary< string,(SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution=true, double? width=null, double? height=null, Colour? background=null, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Document to an Avalonia.Controls.Canvas using the SkiaSharp renderer. Each page corresponds ...
Definition: SKRenderContext.cs:1599
VectSharp.Canvas.SKRenderAction.Filter
IFilter Filter
The filter with which the Graphics is drawn. If you change this, you probably want to call this objec...
Definition: SKRenderContext.cs:163
VectSharp.Canvas.SKRenderContext
Represents a page that has been prepared for fast rendering using the SkiaSharp renderer.
Definition: SKRenderContext.cs:547
VectSharp.Canvas.AvaloniaContextInterpreter
Contains methods to render a Page to an Avalonia.Controls.Canvas.
Definition: AvaloniaContext.cs:2493
VectSharp.Canvas.FilterOption.FilterOption
FilterOption(FilterOperations operation, double rasterisationResolution, bool rasterisationResolutionRelative)
Create a new FilterOption object.
Definition: SKRenderContext.cs:1523
VectSharp.Canvas.SKRenderAction.InvalidateVisual
void InvalidateVisual()
Definition: SKRenderContext.cs:282
VectSharp.Colour
Represents an RGB colour.
Definition: Colour.cs:26
VectSharp.Canvas.SKRenderAction.Payload
object Payload
An arbitrary object associated with the RenderAction.
Definition: SKRenderContext.cs:168
VectSharp.Canvas.FilterOption.Operation
FilterOperations Operation
Defines whether image filters should be rasterised or not.
Definition: SKRenderContext.cs:1500
VectSharp.Canvas.SKRenderAction.Tag
string Tag
A tag to access the SKRenderAction.
Definition: SKRenderContext.cs:146
VectSharp.Page.Height
double Height
Height of the page.
Definition: Document.cs:57
VectSharp.RasterImage
Represents a raster image, created from raw pixel data. Consider using the derived classes included i...
Definition: RasterImage.cs:99
VectSharp.Colour.R
double R
Red component of the colour. Range: [0, 1].
Definition: Colour.cs:30
VectSharp.Canvas.FilterOption.Default
static FilterOption Default
The default options for image filter rasterisation.
Definition: SKRenderContext.cs:1515
VectSharp.Canvas.FilterOption
Determines how and whether image filters are rasterised.
Definition: SKRenderContext.cs:1470
VectSharp.DisposableIntPtr
An IDisposable wrapper around an IntPtr that frees the allocated memory when it is disposed.
Definition: RasterImage.cs:54
VectSharp.Page.Background
Colour Background
Background colour of the page.
Definition: Document.cs:67
VectSharp.Canvas.SKRenderAction.Font
SKFont Font
The font that will be used to render the text (null if the action type is not ActionTypes....
Definition: SKRenderContext.cs:106
VectSharp
Definition: Brush.cs:26
VectSharp.Filters.IFilter
Represents a filter. Do not implement this interface directly; instead, implement ILocationInvariantF...
Definition: Filters.cs:26
VectSharp.Graphics.CopyToIGraphicsContext
void CopyToIGraphicsContext(IGraphicsContext destinationContext)
Copy the current graphics to an instance of a class implementing IGraphicsContext.
Definition: Graphics.cs:599
VectSharp.Canvas.SKRenderAction.ZIndex
uint ZIndex
The Z-index of the rendering action (an action with a higher Z-index will always appear above an acti...
Definition: SKRenderContext.cs:153
VectSharp.Document
Represents a collection of pages.
Definition: Document.cs:28
VectSharp.Page.Width
double Width
Width of the page.
Definition: Document.cs:52
VectSharp.Page
Represents a Graphics object with a width and height.
Definition: Document.cs:48
VectSharp.RadialGradientBrush
Represents a brush painting with a radial gradient.
Definition: Brush.cs:368
VectSharp.GradientBrush.GradientStops
GradientStops GradientStops
The colour stops in the gradient.
Definition: Brush.cs:236
VectSharp.Colour.A
double A
Alpha component of the colour. Range: [0, 1].
Definition: Colour.cs:45
VectSharp.Size.Height
double Height
Height of the object.
Definition: Point.cs:155
VectSharp.Canvas.SKMultiLayerRenderCanvas
Represents a multi-threaded, triple-buffered canvas on which the image is drawn using SkiaSharp.
Definition: SKMultiLayerRenderCanvas.cs:44
VectSharp.SegmentType.Close
@ Close
The segment represents the closing segment of a figure.
VectSharp.LineCaps
LineCaps
Represents line caps.
Definition: Enums.cs:71
VectSharp.Canvas.SKRenderAction.PathAction
static SKRenderAction PathAction(SKPath path, SKPaint paint, string tag=null)
Creates a new SKRenderAction representing a path.
Definition: SKRenderContext.cs:321
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, bool removeTaggedActionsAfterExecution=true, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to an Avalonia.Controls.Canvas using the SkiaSharpRenderer.
Definition: SKRenderContext.cs:1626
VectSharp.Canvas.SKRenderAction
Represents a light-weight rendering action.
Definition: SKRenderContext.cs:31
VectSharp.Font
Represents a typeface with a specific size.
Definition: Font.cs:29
VectSharp.TextBaselines
TextBaselines
Represent text baselines.
Definition: Enums.cs:24
VectSharp.Canvas.SKRenderContextInterpreter.CopyToSKRenderContext
static SKRenderContext CopyToSKRenderContext(this Page page, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, bool removeTaggedActionsAfterExecution=true, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to a SKRenderContext. This can be drawn using the SkiaSharpRenderer by adding it to a S...
Definition: SKRenderContext.cs:1670
VectSharp.Canvas.SKRenderAction.ImageSource
SKRect? ImageSource
The source rectangle of the image (null if the action type is not ActionTypes.RasterImage)....
Definition: SKRenderContext.cs:131
VectSharp.Canvas.SKRenderAction.InvalidateAll
void InvalidateAll()
Definition: SKRenderContext.cs:303
VectSharp.Graphics
Represents an abstract drawing surface.
Definition: Graphics.cs:262
VectSharp.Canvas.SKRenderAction.ImageId
string ImageId
Univocal identifier of the image that needs to be drawn.
Definition: SKRenderContext.cs:126
VectSharp.Rectangle.Size
Size Size
The size of the rectangle.
Definition: Point.cs:187
VectSharp.Canvas.SKRenderAction.ActionType
ActionTypes ActionType
Type of the rendering action.
Definition: SKRenderContext.cs:88
VectSharp.Canvas.SKRenderAction.TextAction
static SKRenderAction TextAction(string text, float x, float y, SKFont font, SKPaint paint, string tag=null)
Creates a new SKRenderAction representing text.
Definition: SKRenderContext.cs:365
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, bool removeTaggedActionsAfterExecution=true, double? width=null, double? height=null, Colour? background=null, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Document to an Avalonia.Controls.Canvas using the SkiaSharp renderer. Each page corresponds ...
Definition: SKRenderContext.cs:1579
VectSharp.LineJoins
LineJoins
Represents line joining options.
Definition: Enums.cs:92
VectSharp.Canvas.SKRenderContextInterpreter.CopyToSKRenderContext
static SKRenderContext CopyToSKRenderContext(this Page page, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to a SKRenderContext. This can be drawn using the SkiaSharpRenderer by adding it to a S...
Definition: SKRenderContext.cs:1655
VectSharp.Canvas.SKRenderContextInterpreter
Contains methods to render a Page to an Avalonia.Controls.Canvas using the SkiaSharp renderer.
Definition: SKRenderContext.cs:1535
VectSharp.FontFamily
Represents a typeface.
Definition: Font.cs:421
VectSharp.Canvas.SKRenderAction.PointerPressed
EventHandler< Avalonia.Input.PointerPressedEventArgs > PointerPressed
Raised when the pointer is pressed while over the area covered by the SKRenderAction.
Definition: SKRenderContext.cs:196
VectSharp.Canvas.SKRenderAction.PointerEnter
EventHandler< Avalonia.Input.PointerEventArgs > PointerEnter
Raised when the pointer enters the area covered by the SKRenderAction.
Definition: SKRenderContext.cs:186
VectSharp.LinearGradientBrush.EndPoint
Point EndPoint
The end point of the gradient. Note that this is relative to the current coordinate system when the g...
Definition: Brush.cs:254
VectSharp.Canvas.SKRenderAction.Parent
Avalonia.Controls.Canvas Parent
The container of this SKRenderAction.
Definition: SKRenderContext.cs:176
VectSharp.RadialGradientBrush.Radius
double Radius
The radius of the gradient.
Definition: Brush.cs:382
VectSharp.Canvas.SKRenderAction.Disposed
bool Disposed
Returns a boolean value indicating whether the current instance has been disposed.
Definition: SKRenderContext.cs:35
VectSharp.Page.Graphics
Graphics Graphics
Graphics surface of the page.
Definition: Document.cs:62
VectSharp.Canvas.SKRenderAction.InvalidateHitTestPath
void InvalidateHitTestPath()
Definition: SKRenderContext.cs:232
VectSharp.Canvas.SKRenderAction.TextY
float TextY
The Y coordainate at which the text will be drawn (null if the action type is not ActionTypes....
Definition: SKRenderContext.cs:116
VectSharp.Filters.IFilterWithRasterisableParameter
Represents a filter with a parameter that needs to be rasterised at the same resolution as the subjec...
Definition: Filters.cs:72
VectSharp.LinearGradientBrush.StartPoint
Point StartPoint
The starting point of the gradient. Note that this is relative to the current coordinate system when ...
Definition: Brush.cs:249
VectSharp.Colour.B
double B
Blue component of the colour. Range: [0, 1].
Definition: Colour.cs:40
VectSharp.Point.X
double X
Horizontal (x) coordinate, measured to the right of the origin.
Definition: Point.cs:32
VectSharp.Canvas.SKRenderAction.SaveAction
static SKRenderAction SaveAction(string tag=null)
Creates a new SKRenderAction that saves the current graphics state.
Definition: SKRenderContext.cs:437
VectSharp.Canvas.SKRenderAction.Paint
SKPaint Paint
Paint used to render the text or path (null if the action type is neither ActionTypes....
Definition: SKRenderContext.cs:121
VectSharp.Canvas.SKRenderAction.Path
SKPath Path
Path that needs to be rendered (null if the action type is not ActionTypes.Path). If you change this,...
Definition: SKRenderContext.cs:93
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Page page, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, Dictionary< string,(SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution=true, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to an Avalonia.Controls.Canvas using the SkiaSharpRenderer.
Definition: SKRenderContext.cs:1643
VectSharp.Canvas.SKRenderAction.DrawFilteredGraphicsAction
static SKRenderAction DrawFilteredGraphicsAction(SKRenderContext graphics, IFilter filter, string tag=null)
Create a new SKRenderAction that draws some graphics with a filter.
Definition: SKRenderContext.cs:464
VectSharp.Document.Pages
List< Page > Pages
The pages in the document.
Definition: Document.cs:32
VectSharp.TrueTypeFile.FontStream
Stream FontStream
A stream pointing to the TrueType file source (either on disk or in memory). Never dispose this strea...
Definition: TrueType.cs:47
VectSharp.LinearGradientBrush
Represents a brush painting with a linear gradient.
Definition: Brush.cs:245
VectSharp.Canvas.SKRenderAction.Dispose
void Dispose()
Definition: SKRenderContext.cs:494
VectSharp.Canvas.SKRenderAction.ImageDestination
SKRect? ImageDestination
The destination rectangle of the image (null if the action type is not ActionTypes....
Definition: SKRenderContext.cs:136
VectSharp.Filters
Definition: BoxBlurFilter.cs:22
VectSharp.Canvas.AvaloniaContextInterpreter.TextOptions
TextOptions
Defines whether text items should be converted into paths when drawing.
Definition: AvaloniaContext.cs:2498
VectSharp.Canvas.SKRenderContextInterpreter.CopyToSKRenderContext
static SKRenderContext CopyToSKRenderContext(this Page page, Dictionary< string, Func< SKRenderAction, IEnumerable< SKRenderAction >>> taggedActions, Dictionary< string,(SKBitmap, bool)> images, bool removeTaggedActionsAfterExecution=true, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Page to a SKRenderContext. This can be drawn using the SkiaSharpRenderer by adding it to a S...
Definition: SKRenderContext.cs:1687
VectSharp.Rectangle.Location
Point Location
The top-left corner of the rectangle.
Definition: Point.cs:182
VectSharp.Size.Width
double Width
Width of the object.
Definition: Point.cs:150
VectSharp.Filters.IFilter.BottomRightMargin
Point BottomRightMargin
Determines how much the area of the filter's subject should be expanded on the bottom-right to accomm...
Definition: Filters.cs:35
VectSharp.RadialGradientBrush.Centre
Point Centre
Represents the centre of the gradient.
Definition: Brush.cs:377
VectSharp.Filters.IFilterWithLocation
Represents a filter whose results depend on the position of the subject image on the graphics surface...
Definition: Filters.cs:56
VectSharp.Filters.ILocationInvariantFilter
Represents a filter that can be applied to an image regardless of its location on the graphics surfac...
Definition: Filters.cs:42
VectSharp.Canvas.SKRenderAction.Text
string Text
Text that needs to be rendered (null if the action type is not ActionTypes.Text). If you change this,...
Definition: SKRenderContext.cs:101
VectSharp.Canvas.SKRenderAction.Transform
SKMatrix? Transform
The transformation matrix that will be applied to the current coordinate system (null if the action t...
Definition: SKRenderContext.cs:141
VectSharp.Canvas.SKRenderAction.ClipAction
static SKRenderAction ClipAction(SKPath clippingPath, string tag=null)
Creates a new SKRenderAction representing a clipping action.
Definition: SKRenderContext.cs:345
VectSharp.IGraphicsContext
This interface should be implemented by classes intended to provide graphics output capability to a G...
Definition: Graphics.cs:36
VectSharp.Canvas.FilterOption.RasterisationResolution
double RasterisationResolution
The resolution that will be used to rasterise image filters. Depending on the value of RasterisationR...
Definition: SKRenderContext.cs:1505
VectSharp.Canvas.SKRenderAction.InvalidateZIndex
void InvalidateZIndex()
Definition: SKRenderContext.cs:293
VectSharp.Canvas.SKRenderAction.PointerLeave
EventHandler< Avalonia.Input.PointerEventArgs > PointerLeave
Raised when the pointer leaves the area covered by the SKRenderAction.
Definition: SKRenderContext.cs:191
VectSharp.Canvas.SKRenderContextInterpreter.PaintToSKCanvas
static SKMultiLayerRenderCanvas PaintToSKCanvas(this Document document, double? width=null, double? height=null, Colour? background=null, AvaloniaContextInterpreter.TextOptions textOption=AvaloniaContextInterpreter.TextOptions.ConvertIfNecessary, FilterOption filterOption=default)
Render a Document to an Avalonia.Controls.Canvas using the SkiaSharp renderer. Each page corresponds ...
Definition: SKRenderContext.cs:1561
VectSharp.Page.Crop
void Crop(Point topLeft, Size size)
Translate and resize the Page so that it displays the rectangle defined by topLeft and size .
Definition: Document.cs:88
VectSharp.Canvas.SKRenderAction.PointerReleased
EventHandler< Avalonia.Input.PointerReleasedEventArgs > PointerReleased
Raised when the pointer is released after a PointerPressed event.
Definition: SKRenderContext.cs:201
VectSharp.Canvas.SKRenderAction.TransformAction
static SKRenderAction TransformAction(SKMatrix transform, string tag=null)
Creates a new SKRenderAction representing a transform.
Definition: SKRenderContext.cs:422
VectSharp.RadialGradientBrush.FocalPoint
Point FocalPoint
The focal point of the gradient (i.e. the point within the circle where the gradient starts).
Definition: Brush.cs:372
VectSharp.Canvas.SKRenderAction.ImageAction
static SKRenderAction ImageAction(string imageId, SKRect sourceRect, SKRect destinationRect, string tag=null)
Creates a new SKRenderAction representing an image.
Definition: SKRenderContext.cs:397
VectSharp.Colour.G
double G
Green component of the colour. Range: [0, 1].
Definition: Colour.cs:35
VectSharp.Canvas.SKRenderAction.RestoreAction
static SKRenderAction RestoreAction(string tag=null)
Creates a new SKRenderAction that saves the current graphics state.
Definition: SKRenderContext.cs:451
VectSharp.Point.Y
double Y
Vertical (y) coordinate, measured to the bottom of the origin.
Definition: Point.cs:37
VectSharp.FontFamily.TrueTypeFile
TrueTypeFile TrueTypeFile
Parsed TrueType font file for this font family. See also: VectSharp.TrueTypeFile.
Definition: Font.cs:586
VectSharp.FontFamily.FileName
string FileName
Full path to the TrueType font file for this font family (or, if this is a standard font family,...
Definition: Font.cs:575
VectSharp.Colour.FromRgba
static Colour FromRgba(double r, double g, double b, double a)
Create a new colour from RGBA (red, green, blue and alpha) values.
Definition: Colour.cs:99