3using System.Reflection;
4using System.Threading.Tasks;
5using Microsoft.Maui.Controls;
6using Microsoft.Maui.Storage;
10using SkiaSharp.Views.Maui;
11using SkiaSharp.Views.Maui.Controls;
12using SkiaSharp.Views.Maui.Handlers;
19 public virtual Size CustomMeasuredSize(
double widthConstraint,
double heightConstraint)
24 internal class AutoHeightSKCanvasViewHandler : SKCanvasViewHandler
26 public override Size GetDesiredSize(
double widthConstraint,
double heightConstraint)
32 if (Custom == Size.Zero)
33 return base.GetDesiredSize(widthConstraint, heightConstraint);
37 return base.GetDesiredSize(widthConstraint, heightConstraint);
128 BindableProperty.Create(
133 propertyChanged: OnSourceChanged);
140 BindableProperty.Create(
145 propertyChanged: OnAspectChanged);
152 BindableProperty.Create(
157 propertyChanged: OnTintColorChanged);
196 private bool disposedValue;
205 this.EnableTouchEvents =
false;
206 PaintSurface += this.OnPaintSurface;
216 private static void OnSourceChanged(BindableObject bindable,
object oldValue,
object newValue)
229 private static void OnAspectChanged(BindableObject bindable,
object oldValue,
object newValue)
242 private static void OnTintColorChanged(BindableObject bindable,
object oldValue,
object newValue)
252 private async
void LoadSvg()
254 if (
string.IsNullOrEmpty(this.
Source))
263 using Stream Stream = await FileSystem.OpenAppPackageFileAsync(this.
Source);
264 this.svg =
new SKSvg();
265 this.svg.Load(Stream);
269 ServiceRef.
LogService.LogWarning($
"Error loading SVG asset '{this.Source}': {Ex}", this.GetClassAndMethod(MethodBase.GetCurrentMethod()));
275 this.InvalidateSurface();
277 this.InvalidateMeasure();
290 private void OnPaintSurface(
object? sender, SKPaintSurfaceEventArgs e)
292 SKCanvas Canvas = e.Surface.Canvas;
295 if (this.svg?.Picture is
null)
298 SKImageInfo Info = e.Info;
299 SKRect SvgRect = this.svg.Picture.CullRect;
302 float ScaleX = Info.Width / SvgRect.Width;
303 float ScaleY = Info.Height / SvgRect.Height;
305 float TranslateX = 0;
306 float TranslateY = 0;
312 Canvas.Scale(ScaleX, ScaleY);
313 this.DrawPictureWithOptionalTint(Canvas, this.svg.Picture);
318 Scale = Math.Max(ScaleX, ScaleY);
324 Scale = Math.Min(ScaleX, ScaleY);
329 float ScaledWidth = SvgRect.Width * Scale;
330 float ScaledHeight = SvgRect.Height * Scale;
333 TranslateX = (Info.Width - ScaledWidth) / 2;
334 TranslateY = (Info.Height - ScaledHeight) / 2;
336 Canvas.Translate(TranslateX, TranslateY);
339 this.DrawPictureWithOptionalTint(Canvas, this.svg.Picture);
347 private void DrawPictureWithOptionalTint(SKCanvas canvas, SKPicture picture)
351 canvas.DrawPicture(picture);
356 using SKPaint Paint =
new()
358 ColorFilter = SKColorFilter.CreateBlendMode(this.
TintColor.ToSKColor(), SKBlendMode.SrcIn)
360 canvas.DrawPicture(picture, Paint);
365 protected override SizeRequest OnMeasure(
double widthConstraint,
double heightConstraint)
368 if (this.svg?.Picture is
null)
370 return base.OnMeasure(widthConstraint, heightConstraint);
374 SKRect svgBounds = this.svg.Picture.CullRect;
375 double intrinsicWidth = svgBounds.Width;
376 double intrinsicHeight = svgBounds.Height;
379 bool unboundedWidth =
double.IsInfinity(widthConstraint);
380 bool unboundedHeight =
double.IsInfinity(heightConstraint);
383 if (unboundedWidth && unboundedHeight)
385 return new SizeRequest(
new Size(intrinsicWidth, intrinsicHeight));
389 else if (unboundedWidth)
391 double scale = heightConstraint / intrinsicHeight;
392 return new SizeRequest(
new Size(intrinsicWidth * scale, heightConstraint));
394 else if (unboundedHeight)
396 double scale = widthConstraint / intrinsicWidth;
397 return new SizeRequest(
new Size(widthConstraint, intrinsicHeight * scale));
404 double constraintAspect = widthConstraint / heightConstraint;
405 double intrinsicAspect = intrinsicWidth / intrinsicHeight;
407 if (intrinsicAspect > constraintAspect)
410 double scaledHeight = widthConstraint / intrinsicAspect;
411 return new SizeRequest(
new Size(widthConstraint, scaledHeight));
416 double scaledWidth = heightConstraint * intrinsicAspect;
417 return new SizeRequest(
new Size(scaledWidth, heightConstraint));
422 #region IDisposable Implementation
430 GC.SuppressFinalize(
this);
440 protected virtual void Dispose(
bool disposing)
442 if (!this.disposedValue)
447 PaintSurface -= this.OnPaintSurface;
452 this.disposedValue =
true;
462 this.
Dispose(disposing:
false);
Base class that references services in the app.
static ILogService LogService
Log service.
A custom .NET MAUI control for rendering Scalable Vector Graphics (SVG) files using SkiaSharp and Svg...
Color? TintColor
Gets or sets the tint color to override the SVG’s original colors. When set to a non-null value,...
static readonly BindableProperty SourceProperty
Identifies the Source bindable property. This property represents the SVG file name bundled as a MAUI...
static readonly BindableProperty TintColorProperty
Identifies the TintColor bindable property. When provided, the SVG will be rendered using this tint c...
SvgView()
Initializes a new instance of the SvgView class. Registers the paint surface event handler and disabl...
void Dispose()
Releases all resources used by the SvgView control.
static readonly BindableProperty AspectProperty
Identifies the Aspect bindable property. Determines how the SVG is scaled to fill the view.
Aspect Aspect
Gets or sets the scaling mode used to display the SVG. The default is Aspect.AspectFit.
virtual void Dispose(bool disposing)
Releases resources used by the control.
string Source
Gets or sets the SVG asset file name. For example: "foo.svg".