Daniel Hindrikes
Developer and architect with focus on mobile- and cloud solutions!
Developer and architect with focus on mobile- and cloud solutions!
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:
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;