Introducing TinySvgHelper

For a pretty long time I have had code that I have used for a couple of different apps that uses SkiaSharp to convert an svg image to a Xamarin.Forms ImageSource. I also blogged about it two years ago, https://danielhindrikes.se/index.php/2018/03/13/use-skiasharp-to-convert-svg-to-xamarin-forms-imagesource/. But I never released it as a library until now.

Get started with TinySvgHelper

The library is published to NuGet, https://www.nuget.org/packages/TinySvgHelper/

To install it, search for TinySvgHelper in the **Nuget Package Manager** or install it with the following command:

Install-Package TinySvgHelper

To use it you must add SvgHelper.Init() to your MainActivity and/or AppDelegate.

MainActivity

protected override void OnCreate(Bundle savedInstanceState)
{
    ....
 
    SvgHelper.Init();
 
    ...
}

AppDelegate

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    ...
 
    SvgHelper.Init();
 
    ...
}

Use TinySvgHelper

You can use TinySvgHelper either from your XAML- or C# code. For iOS add the svg files to the **Resources** folder and for Android add the svg files to the **Assets** folder.

XAML

First you need to import the namespace:

xmlns:svg="clr-namespace:TinySvgHelper;assembly=TinySvgHelper"

Then you will use it as a markup extension:

<Image Source="{svg:Svg FileName='logo.svg', Height=200,Width=200}" />

You can also specify a color to it:

<ShellContent Icon="{svg:Svg FileName='info.svg', Height=22,Width=22,Color=Black}" Title="Start">
    <views:StartView />
</ShellContent>
```
 
### C#
<pre lang="csharp">
using TinySvgHelper;
var image = new Image();
image.Source = SvgHelper.GetAsImageSource("info.svg", 50, 50, Color.Blue);

Read more

You can find the full documentation, the code and a sample project on GitHub, https://github.com/TinyStuff/TinySvgHelper

Use SkiaSharp to convert SVG to Xamarin.Forms ImageSource

To work with images in an app project could be a hell while you need a lot of different resolution of every image. But what if we could use vector based images to create an image in the right solution? Xamarin.Forms has no support for that out of the box. But it possible to do with SkiaSharp. SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library.

There are a Xamarin.Forms package that we can add to your project if we want to add a Skia canvas to our views. That's great but a little bit limited. We can for example not use it for icons in TabbedPage. This post will show how to create a helper that using SkisSharp to convert an SVG image to an Xamarin.Forms ImageSource.

First of all we need to install two NuGet packages:

  • SkiaSharp.View.Forms
  • SkiaSharp.Svg

The helper metod we will create takes four arguments, fileName, width, height and color.

public class SvgHelper
{
        public static ImageSource GetAsImageSource(string svgImage, float width, float height, Color color)
        {
        }
}

To get it to render correctly we need to know the scale factor for the device that the app is running on.

var scaleFactor = 0;
 
#if __IOS__
    scaleFactor = (int)UIKit.UIScreen.MainScreen.Scale;
#elif __ANDROID__
     //I have added a static Current property to my MainActivity.
     scaleFactor = MainActivity.Current.Resources.DisplayMetrics.Density
#endif

Next steg is to load the SVG image.

var svg = new SkiaSharp.Extended.Svg.SKSvg();
 
#if __IOS__
            svg.Load(svgImage);
#elif __ANDROID__
            var assetStream = MainActivity.Current.Assets.Open (svgImage);
            svg.Load(assetStream);
#endif

When we have loaded the SVG, we need to scale it to the size you passed in to the method.

var svgSize = svg.Picture.CullRect;
var svgMax = Math.Max(svgSize.Width, svgSize.Height);
 
float canvasMin = Math.Min((int)(width * scaleFactor), (int)(height * scaleFactor));
var scale = canvasMin / svgMax;
var matrix = SKMatrix.MakeScale(scale, scale);

Now it is time to draw the image, for that we need a canvas. The size of the canvas will be defined in the bitmap that we will pass to the constructor of SKCanvas. We also want to use the color that we sent to this method so we can get the color we want without having multiple images for different colors.

var bitmap = new SKBitmap((int)(width*scaleFactor), (int)(height*scaleFactor));
 
var paint = new SKPaint()
{
      ColorFilter = SKColorFilter.CreateBlendMode(color.ToSKColor(), SKBlendMode.SrcIn)
};
 
var canvas = new SKCanvas(bitmap);
canvas.DrawPicture(svg.Picture, ref matrix, paint);

Now we have a canvas, the last step is to convert the canvas to as stream so we can create a Xamarin.Forms ImageSource from it.

var image = SKImage.FromBitmap(bitmap);
var encoded = image.Encode();
var stream = encoded.AsStream();
var source = ImageSource.FromStream(() => stream);
 
return source;

Now we can use it to set the source in an image.

var image = new Image();
image.Source = SvgHelper. GetAsImageSource("my_icon.svg", 30, 30, Color.Black);

If we want to use it in XAML one solutions is to create a MarkupExtension.

 public class DrawImageExtension : IMarkupExtension
{
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            var source = SvgHelper.GetAsImageSource(FileName, Width, Height, Color);
 
            return source;
        }
 
        public string FileName { get; set; }
        public float Width { get; set; }
        public float Height { get; set; }
        public Color Color { get; set; }
}

We just need to import the namespace for the extension and then we can use it like this;

<ContentPage 
     xmlns="http://xamarin.com/schemas/2014/forms"   
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
     xmlns:ext="clr-namespace:MyApp.Extensions" x:Class="MyApp.MyPage">
     <StackLayout>
          <Image Source="{ext:DrawImage FileName=my_icon.svg, Width=25, Height=25, Color=White}" /> 
     </StackLayout>
</ContentPage>