Skip to content

Instantly share code, notes, and snippets.

@taimila
Last active January 11, 2020 16:48
Show Gist options
  • Save taimila/be0ee9cd7ff2f2eeec5b0f2f44ddaa41 to your computer and use it in GitHub Desktop.
Save taimila/be0ee9cd7ff2f2eeec5b0f2f44ddaa41 to your computer and use it in GitHub Desktop.
Simple Xamarin.Forms view that allows showing SVG icons with specific color.
using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;
namespace Components
{
/// <summary>
/// Heavily inspired by https://github.com/Brainflab/XamaRed.Forms.Svg/blob/master/Sources/XamaRed.Forms.Svg/SvgView.cs
/// </summary>
public class SvgIcon : SKCanvasView
{
SkiaSharp.Extended.Svg.SKSvg svg;
static Assembly mainPclAssembly;
public static Assembly MainPclAssembly
{
get
{
if (mainPclAssembly == null)
{
if (Application.Current == null)
throw new InvalidOperationException();
mainPclAssembly = Application.Current.GetType().GetTypeInfo().Assembly;
}
return mainPclAssembly;
}
set
{
mainPclAssembly = value;
}
}
/// <summary>
/// Cache for SVG icons. Each SVG is kept in memory only once even if it's used multple places in application.
/// </summary>
static readonly IDictionary<string, SkiaSharp.Extended.Svg.SKSvg> SvgCache = new Dictionary<string, SkiaSharp.Extended.Svg.SKSvg>();
/// <summary>
/// Global prefix for resource ids. Enabled writing cleaner XAML.
/// </summary>
public static string ResourceIdsPrefix { get; set; } = string.Empty;
#region Bindable properties
public static readonly BindableProperty ResourceIdProperty = BindableProperty.Create(nameof(ResourceId), typeof(string), typeof(SvgIcon), default(string));
public string ResourceId
{
get { return (string)GetValue(ResourceIdProperty); }
set { SetValue(ResourceIdProperty, value); }
}
public static readonly BindableProperty ColorProperty = BindableProperty.Create(nameof(Color), typeof(Color), typeof(SvgIcon), Color.Black);
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
#endregion
void LoadSvgImage()
{
if (svg != null)
return;
if (!SvgCache.TryGetValue(ResourceId, out svg))
{
string fullKey = $"{ResourceIdsPrefix}{ResourceId}";
using (Stream stream = MainPclAssembly.GetManifestResourceStream(fullKey))
{
if (stream == null)
throw new FileNotFoundException($"SvgIcon : could not load SVG file {fullKey} in assembly {MainPclAssembly}. Make sure the ID is correct, the file is there and it is set to Embedded Resource build action.");
svg = new SkiaSharp.Extended.Svg.SKSvg();
svg.Load(stream);
SvgCache.Add(ResourceId, svg);
}
}
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
base.OnPaintSurface(e);
e.Surface.Canvas.Clear();
LoadSvgImage();
var matrix = CalculateScale(e.Info);
using (var paint = new SKPaint())
{
// Unlike CreateLighting(), blend mode support alpha channel of the color
paint.ColorFilter = SKColorFilter.CreateBlendMode( Color.ToSKColor(), SKBlendMode.SrcIn);
e.Surface.Canvas.DrawPicture(svg.Picture, ref matrix, paint);
}
}
SKMatrix CalculateScale(SKImageInfo info)
{
float canvasMin = Math.Min(info.Width, info.Height);
float svgMax = Math.Max(svg.Picture.CullRect.Width, svg.Picture.CullRect.Height);
float scale = canvasMin / svgMax;
return SKMatrix.MakeScale(scale, scale);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == ResourceIdProperty.PropertyName)
svg = null;
if (propertyName == ResourceIdProperty.PropertyName
|| propertyName == ColorProperty.PropertyName)
InvalidateSurface();
}
}
}
@brunoportess
Copy link

where my svg's images need to be?

@taimila
Copy link
Author

taimila commented Jan 11, 2020

You need to add it to shared project as Embedded resource. Then on properties panel you can see the resource id of the file that should be used with this class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment