20 using System.Collections.Generic;
37 private bool disposedValue;
93 public SKPath
Path {
get;
set; }
95 internal SKPath HitTestPath {
get;
set; }
96 internal SKPath LastRenderedGlobalHitTestPath {
get;
set; }
101 public string Text {
get;
set; }
106 public SKFont
Font {
get;
set; }
146 public string Tag {
get;
private set; }
170 internal ISKRenderCanvas InternalParent {
get;
set; }
179 return (Avalonia.Controls.Canvas)InternalParent;
186 public event EventHandler<Avalonia.Input.PointerEventArgs>
PointerEnter;
191 public event EventHandler<Avalonia.Input.PointerEventArgs>
PointerLeave;
196 public event EventHandler<Avalonia.Input.PointerPressedEventArgs>
PointerPressed;
204 internal void FirePointerEnter(Avalonia.Input.PointerEventArgs e)
209 internal void FirePointerLeave(Avalonia.Input.PointerEventArgs e)
214 internal void FirePointerPressed(Avalonia.Input.PointerPressedEventArgs e)
219 internal void FirePointerReleased(Avalonia.Input.PointerReleasedEventArgs e)
239 internal void CreateHitTestPath()
243 if (this.
Path !=
null && this.
Paint !=
null)
245 this.HitTestPath?.Dispose();
246 this.HitTestPath = this.
Paint.GetFillPath(this.
Path);
253 this.HitTestPath?.Dispose();
254 this.HitTestPath =
new SKPath();
260 if (this.
Paint !=
null && this.
Font !=
null)
262 this.HitTestPath?.Dispose();
263 this.
Paint.Typeface = this.
Font.Typeface;
268 this.HitTestPath = this.
Paint.GetFillPath(pth);
284 this.InternalParent?.InvalidateDirty();
295 this.InternalParent?.InvalidateZIndex();
331 if (!
string.IsNullOrEmpty(tag))
333 act.CreateHitTestPath();
378 act.
Paint.Typeface = font.Typeface;
379 act.
Paint.TextSize = font.Size;
381 if (!
string.IsNullOrEmpty(tag))
383 act.CreateHitTestPath();
408 if (!
string.IsNullOrEmpty(tag))
410 act.CreateHitTestPath();
476 protected virtual void Dispose(
bool disposing)
482 this.
Font?.Dispose();
483 this.HitTestPath?.Dispose();
484 this.LastRenderedGlobalHitTestPath?.Dispose();
485 this.
Paint?.Dispose();
486 this.
Path?.Dispose();
489 disposedValue =
true;
497 GC.SuppressFinalize(
this);
502 internal static class SKTypefaceCache
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)
511 if (Typefaces.TryGetValue(family.
FileName, out SKTypeface tbr))
519 System.IO.MemoryStream fontStream =
new System.IO.MemoryStream((
int)family.
TrueTypeFile.
FontStream.Length);
522 fontStream.Seek(0, System.IO.SeekOrigin.Begin);
524 SKTypeface typeface = SKTypeface.FromData(SKData.Create(fontStream));
526 Typefaces[family.
FileName] = typeface;
532 SKTypeface typeface = SKTypeface.Default;
534 Typefaces[family.
FileName] = typeface;
548 internal virtual Dictionary<string, (SKBitmap, bool)> Images {
get;
set; }
549 internal virtual List<SKRenderAction> SKRenderActions {
get;
set; }
555 public Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>> TaggedActions {
get;
set; } =
new Dictionary<string, Func<SKRenderAction, IEnumerable<SKRenderAction>>>();
557 private readonly
bool removeTaggedActions =
true;
559 public string Tag {
get;
set; }
561 private readonly AvaloniaContextInterpreter.TextOptions _textOption;
563 internal override Dictionary<string, (SKBitmap, bool)> Images {
get;
set; }
565 private readonly FilterOption _filterOption;
567 public SKRenderContextImpl(
double width,
double height,
bool removeTaggedActionsAfterExecution, AvaloniaContextInterpreter.TextOptions textOption, Dictionary<
string, (SKBitmap,
bool)> images, FilterOption filterOption)
569 this.Images = images;
572 figureInitialised =
false;
574 SKRenderActions =
new List<SKRenderAction>();
575 removeTaggedActions = removeTaggedActionsAfterExecution;
580 _textOption = textOption;
581 _filterOption = filterOption;
584 internal override List<SKRenderAction> SKRenderActions {
get;
set; }
586 public double Width {
get;
private set; }
587 public double Height {
get;
private set; }
589 private void AddAction(SKRenderAction act)
591 if (!
string.IsNullOrEmpty(Tag))
593 if (TaggedActions.ContainsKey(Tag))
595 IEnumerable<SKRenderAction> actions = TaggedActions[Tag](act);
597 foreach (SKRenderAction action
in actions)
599 SKRenderActions.Add(action);
602 if (removeTaggedActions)
604 TaggedActions.Remove(Tag);
609 SKRenderActions.Add(act);
612 else if (TaggedActions.ContainsKey(
""))
614 IEnumerable<SKRenderAction> actions = TaggedActions[
""](act);
616 foreach (SKRenderAction action
in actions)
618 SKRenderActions.Add(action);
623 SKRenderActions.Add(act);
627 public void Translate(
double x,
double y)
629 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
631 SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateTranslation((
float)x, (
float)y), Tag);
635 figureInitialised =
false;
640 private void PathText(
string text,
double x,
double y)
642 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
644 GraphicsPath textPath =
new GraphicsPath().AddText(x, y, text, Font, TextBaseline);
646 for (
int j = 0; j < textPath.Segments.Count; j++)
648 switch (textPath.Segments[j].Type)
651 this.MoveTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
654 this.LineTo(textPath.Segments[j].Point.X, textPath.Segments[j].Point.Y);
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);
666 public void StrokeSimpleText(
string text,
double x,
double y)
668 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
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))
672 SKTypeface typeface = SKTypefaceCache.GetSKTypeface(Font.FontFamily);
677 Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(text);
681 if (Font.FontFamily.TrueTypeFile !=
null)
683 left -= metrics.LeftSideBearing;
689 if (Font.FontFamily.TrueTypeFile !=
null)
691 left -= metrics.LeftSideBearing;
692 top += (metrics.Top + metrics.Bottom) * 0.5;
697 if (Font.FontFamily.TrueTypeFile !=
null)
699 left -= metrics.LeftSideBearing;
704 if (Font.FontFamily.TrueTypeFile !=
null)
706 left -= metrics.LeftSideBearing;
707 top += metrics.Bottom;
711 SKPaint stroke =
new SKPaint() { IsStroke =
true, IsAntialias =
true, Style = SKPaintStyle.Stroke, StrokeWidth = (float)LineWidth, SubpixelText =
true };
713 if (this.StrokeStyle is SolidColourBrush solid)
715 stroke.Color =
new SKColor((
byte)(solid.R * 255), (
byte)(solid.G * 255), (
byte)(solid.B * 255), StrokeAlpha);
717 else if (this.StrokeStyle is LinearGradientBrush linearGradient)
719 stroke.Shader = linearGradient.ToSKShader();
721 else if (this.StrokeStyle is RadialGradientBrush radialGradient)
723 stroke.Shader = radialGradient.ToSKShader();
726 stroke.PathEffect = SKPathEffect.CreateDash(
new float[] { (float)LineDash[0], (
float)LineDash[1] }, (float)LineDash[2]);
731 stroke.StrokeCap = SKStrokeCap.Butt;
734 stroke.StrokeCap = SKStrokeCap.Round;
737 stroke.StrokeCap = SKStrokeCap.Square;
744 stroke.StrokeJoin = SKStrokeJoin.Bevel;
747 stroke.StrokeJoin = SKStrokeJoin.Round;
750 stroke.StrokeJoin = SKStrokeJoin.Miter;
754 SKRenderAction act = SKRenderAction.TextAction(text, (
float)left, (float)top,
new SKFont(typeface, (
float)Font.FontSize), stroke, Tag);
760 PathText(text, x, y);
765 public void FillText(
string text,
double x,
double y)
767 if (!Font.EnableKerning)
769 FillSimpleText(text, x, y);
773 List<(string, Point)> tSpans =
new List<(
string, Point)>();
775 System.Text.StringBuilder currentRun =
new System.Text.StringBuilder();
776 Point currentKerning =
new Point();
778 Point currentGlyphPlacementDelta =
new Point();
779 Point currentGlyphAdvanceDelta =
new Point();
780 Point nextGlyphPlacementDelta =
new Point();
781 Point nextGlyphAdvanceDelta =
new Point();
783 for (
int i = 0; i < text.Length; i++)
785 if (i < text.Length - 1)
787 currentGlyphPlacementDelta = nextGlyphPlacementDelta;
788 currentGlyphAdvanceDelta = nextGlyphAdvanceDelta;
789 nextGlyphAdvanceDelta =
new Point();
790 nextGlyphPlacementDelta =
new Point();
792 TrueTypeFile.PairKerning kerning = Font.FontFamily.TrueTypeFile.Get1000EmKerning(text[i], text[i + 1]);
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);
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);
804 if (currentGlyphPlacementDelta.X != 0 || currentGlyphPlacementDelta.Y != 0 || currentGlyphAdvanceDelta.X != 0 || currentGlyphAdvanceDelta.Y != 0)
806 if (currentRun.Length > 0)
808 tSpans.Add((currentRun.ToString(), currentKerning));
810 tSpans.Add((text[i].ToString(),
new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000, currentGlyphPlacementDelta.Y * Font.FontSize / 1000)));
813 currentKerning =
new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
817 tSpans.Add((text[i].ToString(),
new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000 + currentKerning.X, currentGlyphPlacementDelta.Y * Font.FontSize / 1000 + currentKerning.Y)));
820 currentKerning =
new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
825 currentRun.Append(text[i]);
829 if (currentRun.Length > 0)
831 tSpans.Add((currentRun.ToString(), currentKerning));
837 Font.DetailedFontMetrics fullMetrics = Font.MeasureTextAdvanced(text);
841 if (Font.FontFamily.TrueTypeFile !=
null)
843 currY += fullMetrics.Top;
848 if (Font.FontFamily.TrueTypeFile !=
null)
850 currY += (fullMetrics.Top + fullMetrics.Bottom) * 0.5;
855 if (Font.FontFamily.TrueTypeFile !=
null)
857 currY += fullMetrics.Bottom;
863 for (
int i = 0; i < tSpans.Count; i++)
865 Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(tSpans[i].Item1);
869 FillSimpleText(tSpans[i].Item1, currX + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
873 FillSimpleText(tSpans[i].Item1, currX + metrics.LeftSideBearing - fullMetrics.LeftSideBearing + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
877 currX += metrics.AdvanceWidth + tSpans[i].Item2.X;
878 currY += tSpans[i].Item2.Y;
883 public void StrokeText(
string text,
double x,
double y)
885 if (!Font.EnableKerning)
887 StrokeSimpleText(text, x, y);
891 List<(string, Point)> tSpans =
new List<(
string, Point)>();
893 System.Text.StringBuilder currentRun =
new System.Text.StringBuilder();
894 Point currentKerning =
new Point();
896 Point currentGlyphPlacementDelta =
new Point();
897 Point currentGlyphAdvanceDelta =
new Point();
898 Point nextGlyphPlacementDelta =
new Point();
899 Point nextGlyphAdvanceDelta =
new Point();
901 for (
int i = 0; i < text.Length; i++)
903 if (i < text.Length - 1)
905 currentGlyphPlacementDelta = nextGlyphPlacementDelta;
906 currentGlyphAdvanceDelta = nextGlyphAdvanceDelta;
907 nextGlyphAdvanceDelta =
new Point();
908 nextGlyphPlacementDelta =
new Point();
910 TrueTypeFile.PairKerning kerning = Font.FontFamily.TrueTypeFile.Get1000EmKerning(text[i], text[i + 1]);
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);
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);
922 if (currentGlyphPlacementDelta.X != 0 || currentGlyphPlacementDelta.Y != 0 || currentGlyphAdvanceDelta.X != 0 || currentGlyphAdvanceDelta.Y != 0)
924 if (currentRun.Length > 0)
926 tSpans.Add((currentRun.ToString(), currentKerning));
928 tSpans.Add((text[i].ToString(),
new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000, currentGlyphPlacementDelta.Y * Font.FontSize / 1000)));
931 currentKerning =
new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
935 tSpans.Add((text[i].ToString(),
new Point(currentGlyphPlacementDelta.X * Font.FontSize / 1000 + currentKerning.X, currentGlyphPlacementDelta.Y * Font.FontSize / 1000 + currentKerning.Y)));
938 currentKerning =
new Point((currentGlyphAdvanceDelta.X - currentGlyphPlacementDelta.X) * Font.FontSize / 1000, (currentGlyphAdvanceDelta.Y - currentGlyphPlacementDelta.Y) * Font.FontSize / 1000);
943 currentRun.Append(text[i]);
947 if (currentRun.Length > 0)
949 tSpans.Add((currentRun.ToString(), currentKerning));
955 Font.DetailedFontMetrics fullMetrics = Font.MeasureTextAdvanced(text);
957 for (
int i = 0; i < tSpans.Count; i++)
959 Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(tSpans[i].Item1);
963 StrokeSimpleText(tSpans[i].Item1, currX + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
967 StrokeSimpleText(tSpans[i].Item1, currX + metrics.LeftSideBearing - fullMetrics.LeftSideBearing + tSpans[i].Item2.X, currY + tSpans[i].Item2.Y);
971 currX += metrics.AdvanceWidth + tSpans[i].Item2.X;
972 currY += tSpans[i].Item2.Y;
978 public void FillSimpleText(
string text,
double x,
double y)
980 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
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))
984 SKTypeface typeface = SKTypefaceCache.GetSKTypeface(Font.FontFamily);
989 Font.DetailedFontMetrics metrics = Font.MeasureTextAdvanced(text);
993 if (Font.FontFamily.TrueTypeFile !=
null)
995 left -= metrics.LeftSideBearing;
1001 if (Font.FontFamily.TrueTypeFile !=
null)
1003 left -= metrics.LeftSideBearing;
1004 top += (metrics.Top + metrics.Bottom) * 0.5;
1009 if (Font.FontFamily.TrueTypeFile !=
null)
1011 left -= metrics.LeftSideBearing;
1016 if (Font.FontFamily.TrueTypeFile !=
null)
1018 left -= metrics.LeftSideBearing;
1019 top += metrics.Bottom;
1023 SKPaint fill =
new SKPaint() { IsStroke =
false, IsAntialias =
true, Style = SKPaintStyle.Fill, SubpixelText =
true };
1025 if (this.FillStyle is SolidColourBrush solid)
1027 fill.Color =
new SKColor((
byte)(solid.R * 255), (
byte)(solid.G * 255), (
byte)(solid.B * 255), FillAlpha);
1029 else if (this.FillStyle is LinearGradientBrush linearGradient)
1031 fill.Shader = linearGradient.ToSKShader();
1033 else if (this.FillStyle is RadialGradientBrush radialGradient)
1035 fill.Shader = radialGradient.ToSKShader();
1038 SKRenderAction act = SKRenderAction.TextAction(text, (
float)left, (
float)top,
new SKFont(typeface, (
float)Font.FontSize), fill, Tag);
1044 PathText(text, x, y);
1049 public Brush StrokeStyle {
get;
private set; } = Colour.FromRgb(0, 0, 0);
1050 private byte StrokeAlpha = 255;
1052 public Brush FillStyle {
get;
private set; } = Colour.FromRgb(0, 0, 0);
1053 private byte FillAlpha = 255;
1055 public void SetFillStyle((
int r,
int g,
int b,
double a) style)
1057 FillStyle = Colour.FromRgba(style.r, style.g, style.b, (
int)(style.a * 255));
1058 FillAlpha = (byte)(style.a * 255);
1061 public void SetFillStyle(Brush style)
1065 if (style is SolidColourBrush solid)
1067 FillAlpha = (byte)(solid.A * 255);
1075 public void SetStrokeStyle((
int r,
int g,
int b,
double a) style)
1077 StrokeStyle = Colour.FromRgba(style.r, style.g, style.b, (
int)(style.a * 255));
1078 StrokeAlpha = (byte)(style.a * 255);
1081 public void SetStrokeStyle(Brush style)
1083 StrokeStyle = style;
1085 if (style is SolidColourBrush solid)
1087 StrokeAlpha = (byte)(solid.A * 255);
1095 private double[] LineDash;
1097 public void SetLineDash(LineDash dash)
1099 LineDash =
new double[] { dash.UnitsOn, dash.UnitsOff, dash.Phase };
1102 public void Rotate(
double angle)
1104 Utils.CoerceNaNAndInfinityToZero(ref angle);
1106 SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateRotation((
float)angle), Tag);
1110 figureInitialised =
false;
1113 public void Transform(
double a,
double b,
double c,
double d,
double e,
double f)
1115 Utils.CoerceNaNAndInfinityToZero(ref a, ref b, ref c, ref d, ref e, ref f);
1117 SKRenderAction act = SKRenderAction.TransformAction(
new SKMatrix((
float)a, (
float)c, (
float)e, (
float)b, (
float)d, (
float)f, 0, 0, 1), Tag);
1121 figureInitialised =
false;
1124 public void Scale(
double x,
double y)
1126 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1128 SKRenderAction act = SKRenderAction.TransformAction(SKMatrix.CreateScale((
float)x, (
float)y), Tag);
1132 figureInitialised =
false;
1137 SKRenderAction act = SKRenderAction.SaveAction(Tag);
1141 figureInitialised =
false;
1144 public void Restore()
1146 SKRenderAction act = SKRenderAction.RestoreAction(Tag);
1150 figureInitialised =
false;
1153 public double LineWidth {
get;
set; }
1154 public LineCaps LineCap {
get;
set; }
1157 public Font Font {
get;
set; }
1159 private SKPath currentPath;
1161 private bool figureInitialised =
false;
1162 private bool disposedValue;
1164 public void MoveTo(
double x,
double y)
1166 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1168 if (currentPath ==
null)
1170 currentPath =
new SKPath() { FillType = SKPathFillType.EvenOdd };
1173 currentPath.MoveTo((
float)x, (
float)y);
1174 figureInitialised =
true;
1177 public void LineTo(
double x,
double y)
1179 Utils.CoerceNaNAndInfinityToZero(ref x, ref y);
1181 if (currentPath ==
null)
1183 currentPath =
new SKPath() { FillType = SKPathFillType.EvenOdd };
1186 if (!figureInitialised)
1188 figureInitialised =
true;
1189 currentPath.MoveTo((
float)x, (
float)y);
1193 currentPath.LineTo((
float)x, (
float)y);
1197 public void Rectangle(
double x0,
double y0,
double width,
double height)
1199 Utils.CoerceNaNAndInfinityToZero(ref x0, ref y0, ref width, ref height);
1201 if (currentPath ==
null)
1203 currentPath =
new SKPath() { FillType = SKPathFillType.EvenOdd };
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));
1211 currentPath.Close();
1212 figureInitialised =
false;
1215 public void CubicBezierTo(
double p1X,
double p1Y,
double p2X,
double p2Y,
double p3X,
double p3Y)
1217 Utils.CoerceNaNAndInfinityToZero(ref p1X, ref p1Y, ref p2X, ref p2Y, ref p3X, ref p3Y);
1219 if (currentPath ==
null)
1221 currentPath =
new SKPath() { FillType = SKPathFillType.EvenOdd };
1224 if (figureInitialised)
1226 currentPath.CubicTo((
float)p1X, (
float)p1Y, (
float)p2X, (
float)p2Y, (
float)p3X, (
float)p3Y);
1230 currentPath.MoveTo((
float)p1X, (
float)p1Y);
1231 figureInitialised =
true;
1237 currentPath.Close();
1239 figureInitialised =
false;
1242 public void Stroke()
1244 SKPaint stroke =
new SKPaint() { IsStroke =
true, IsAntialias =
true, Style = SKPaintStyle.Stroke, StrokeWidth = (float)LineWidth };
1246 if (this.StrokeStyle is SolidColourBrush solid)
1248 stroke.Color =
new SKColor((
byte)(solid.R * 255), (
byte)(solid.G * 255), (
byte)(solid.B * 255), StrokeAlpha);
1250 else if (this.StrokeStyle is LinearGradientBrush linearGradient)
1252 stroke.Shader = linearGradient.ToSKShader();
1254 else if (this.StrokeStyle is RadialGradientBrush radialGradient)
1256 stroke.Shader = radialGradient.ToSKShader();
1259 stroke.PathEffect = SKPathEffect.CreateDash(
new float[] { (float)LineDash[0], (
float)LineDash[1] }, (float)LineDash[2]);
1264 stroke.StrokeCap = SKStrokeCap.Butt;
1267 stroke.StrokeCap = SKStrokeCap.Round;
1270 stroke.StrokeCap = SKStrokeCap.Square;
1277 stroke.StrokeJoin = SKStrokeJoin.Bevel;
1280 stroke.StrokeJoin = SKStrokeJoin.Round;
1283 stroke.StrokeJoin = SKStrokeJoin.Miter;
1287 SKRenderAction act = SKRenderAction.PathAction(currentPath, stroke, Tag);
1292 figureInitialised =
false;
1297 SKPaint fill =
new SKPaint() { IsStroke =
false, IsAntialias =
true, Style = SKPaintStyle.Fill };
1299 if (this.FillStyle is SolidColourBrush solid)
1301 fill.Color =
new SKColor((
byte)(solid.R * 255), (
byte)(solid.G * 255), (
byte)(solid.B * 255), FillAlpha);
1303 else if (this.FillStyle is LinearGradientBrush linearGradient)
1305 fill.Shader = linearGradient.ToSKShader();
1307 else if (this.FillStyle is RadialGradientBrush radialGradient)
1309 fill.Shader = radialGradient.ToSKShader();
1312 SKRenderAction act = SKRenderAction.PathAction(currentPath, fill, Tag);
1317 figureInitialised =
false;
1320 public void SetClippingPath()
1322 SKRenderAction act = SKRenderAction.ClipAction(currentPath, Tag);
1327 figureInitialised =
false;
1330 public void DrawRasterImage(
int sourceX,
int sourceY,
int sourceWidth,
int sourceHeight,
double destinationX,
double destinationY,
double destinationWidth,
double destinationHeight, RasterImage image)
1332 Utils.CoerceNaNAndInfinityToZero(ref destinationX, ref destinationY, ref destinationWidth, ref destinationHeight);
1334 if (!this.Images.ContainsKey(image.Id))
1336 SKBitmap bmp = SKBitmap.Decode(image.PNGStream);
1337 this.Images.Add(image.Id, (bmp, image.Interpolate));
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);
1345 public void DrawFilteredGraphics(Graphics graphics,
IFilter filter)
1347 if (this._filterOption.Operation == FilterOption.FilterOperations.RasteriseAllWithSkia)
1349 double scale = this._filterOption.RasterisationResolution;
1351 Rectangle bounds = graphics.GetBounds();
1355 if (bounds.Size.Width > 0 && bounds.Size.Height > 0)
1357 if (!this._filterOption.RasterisationResolutionRelative)
1359 scale = scale / Math.Min(bounds.Size.Width, bounds.Size.Height);
1362 RasterImage rasterised = SKRenderContextInterpreter.Rasterise(graphics, bounds, scale,
true);
1363 RasterImage filtered =
null;
1367 filterWithRastParam.RasteriseParameter(SKRenderContextInterpreter.Rasterise, scale);
1372 filtered = locInvFilter.Filter(rasterised, scale);
1376 filtered = filterWithLoc.Filter(rasterised, bounds, scale);
1379 if (filtered !=
null)
1381 rasterised.Dispose();
1383 DrawRasterImage(0, 0, filtered.Width, filtered.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height, filtered);
1387 else if (this._filterOption.Operation == FilterOption.FilterOperations.RasteriseAllWithVectSharp)
1389 double scale = this._filterOption.RasterisationResolution;
1391 Rectangle bounds = graphics.GetBounds();
1395 if (bounds.Size.Width > 0 && bounds.Size.Height > 0)
1397 if (!this._filterOption.RasterisationResolutionRelative)
1399 scale = scale / Math.Min(bounds.Size.Width, bounds.Size.Height);
1402 if (graphics.TryRasterise(bounds, scale,
true, out RasterImage rasterised))
1404 RasterImage filtered =
null;
1408 filterWithRastParam.RasteriseParameter(SKRenderContextInterpreter.Rasterise, scale);
1413 filtered = locInvFilter.Filter(rasterised, scale);
1417 filtered = filterWithLoc.Filter(rasterised, bounds, scale);
1420 if (filtered !=
null)
1422 rasterised.Dispose();
1424 DrawRasterImage(0, 0, filtered.Width, filtered.Height, bounds.Location.X, bounds.Location.Y, bounds.Size.Width, bounds.Size.Height, filtered);
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"".");
1436 else if (this._filterOption.Operation == FilterOption.FilterOperations.IgnoreAll)
1438 graphics.CopyToIGraphicsContext(
this);
1446 protected virtual void Dispose(
bool disposing)
1452 this.currentPath?.Dispose();
1455 disposedValue =
true;
1459 public void Dispose()
1461 Dispose(disposing:
true);
1462 GC.SuppressFinalize(
this);
1479 RasteriseAllWithSkia,
1484 RasteriseAllWithVectSharp,
1536 internal static SKColor ToSKColor(
this Colour colour)
1538 return new SKColor((
byte)(255 * colour.
R), (
byte)(255 * colour.
G), (
byte)(255 * colour.
B), (
byte)(255 * colour.
A));
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);
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);
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());
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)
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());
1657 return CopyToSKRenderContext(page,
new Dictionary<
string, Func<
SKRenderAction, IEnumerable<SKRenderAction>>>(),
new Dictionary<
string, (SKBitmap,
bool)>(), textOption: textOption, filterOption: filterOption);
1672 return CopyToSKRenderContext(page, taggedActions,
new Dictionary<
string, (SKBitmap,
bool)>(), removeTaggedActionsAfterExecution, textOption, filterOption);
1689 if (filterOption ==
null)
1694 SKRenderContextImpl tbr =
new SKRenderContextImpl(page.
Width, page.
Height, removeTaggedActionsAfterExecution, textOption, images, filterOption)
1696 TaggedActions = taggedActions
1720 int width = (int)Math.Round(region.
Size.
Width * scale);
1721 int height = (int)Math.Round(region.
Size.
Height * scale);
1725 SKBitmap bitmap =
new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Unpremul);
1727 SKCanvas canvas =
new SKCanvas(bitmap);
1731 for (
int i = 0; i < ctx.SKRenderActions.Count; i++)
1735 canvas.ClipPath(ctx.SKRenderActions[i].Path, antialias:
true);
1747 SKMatrix mat = ctx.SKRenderActions[i].Transform.Value;
1748 canvas.Concat(ref mat);
1754 canvas.DrawPath(ctx.SKRenderActions[i].Path, ctx.SKRenderActions[i].Paint);
1758 canvas.DrawText(ctx.SKRenderActions[i].Text, ctx.SKRenderActions[i].TextX, ctx.SKRenderActions[i].TextY, ctx.SKRenderActions[i].Font, ctx.SKRenderActions[i].Paint);
1762 (SKBitmap image,
bool interpolateIt) = ctx.Images[ctx.SKRenderActions[i].ImageId];
1772 paint =
new SKPaint() { FilterQuality = SKFilterQuality.Medium };
1775 canvas.DrawBitmap(image, ctx.SKRenderActions[i].ImageSource.Value, ctx.SKRenderActions[i].ImageDestination.Value, paint);
1784 IntPtr pixels = bitmap.GetPixels(out IntPtr length);
1786 IntPtr tbrData = System.Runtime.InteropServices.Marshal.AllocHGlobal(length);
1787 GC.AddMemoryPressure((
long)length);
1791 Buffer.MemoryCopy((
void*)pixels, (
void*)tbrData, (
long)length, (
long)length);
1798 return new RasterImage(ref disp, width, height,
true, interpolate);