Skip to content

Instantly share code, notes, and snippets.

@VincentH-Net
Last active March 20, 2017 15:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VincentH-Net/1eb516aa1a12b00e6c0f50acb568adae to your computer and use it in GitHub Desktop.
Save VincentH-Net/1eb516aa1a12b00e6c0f50acb568adae to your computer and use it in GitHub Desktop.
Temporary fix for unsharp SVG's in FFImageLoading
using FFImageLoading;
using FFImageLoading.Config;
using FFImageLoading.DataResolvers;
using FFImageLoading.Forms;
using FFImageLoading.Svg.Platform;
using FFImageLoading.Work;
using SkiaSharp;
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace FFImageLoadingFixIssue522
{
/// <summary>
/// SVG image source with fix for unsharp SVG's
/// Usage:
/// 1) Install nuget packkages Xamarin.FFImageLoading.Svg.Forms 2.2.8, and SkiaSharp 1.56.2
/// 2) Add this code to your shared project
/// 3) Set the ScalingFactor inside iOS and Android projects after CachedImageRenderer.Init():
/// SharpSvgDataResolver.ScalingFactor = (uint)UIScreen.MainScreen.Scale;
/// SharpSvgDataResolver.ScalingFactor = (uint)Resources.DisplayMetrics.Density;
/// 4) Use SharpSvgImageSource instead of SvgImageSource
///
/// Copied from https://github.com/luberda-molinet/FFImageLoading/blob/master/source/FFImageLoading.Svg.Forms.Shared/SvgImageSource.cs
/// and applied fix for pixelated SVG's as suggested in https://github.com/luberda-molinet/FFImageLoading/issues/522
///
/// NOTE this code is a temporary fix until issue 522 is resolved properly in FFImageLoading
/// </summary>
public class SharpSvgImageSource : Xamarin.Forms.ImageSource, IVectorImageSource
{
public SharpSvgImageSource(Xamarin.Forms.ImageSource imageSource, int vectorWidth, int vectorHeight, bool useDipUnits)
{
ImageSource = imageSource;
VectorWidth = vectorWidth;
VectorHeight = vectorHeight;
UseDipUnits = useDipUnits;
}
public Xamarin.Forms.ImageSource ImageSource { get; private set; }
public int VectorWidth { get; set; }
public int VectorHeight { get; set; }
public bool UseDipUnits { get; set; }
public IVectorDataResolver GetVectorDataResolver()
{
return new SharpSvgDataResolver(VectorWidth, VectorHeight, UseDipUnits); // Using the SvgDataResolver with the suggested fix in https://github.com/luberda-molinet/FFImageLoading/issues/522 - apart from the class name, this is the only change to this class for the fix.
}
/// <summary>
/// SvgImageSource FromFile.
/// By default it uses view size as vectorWidth / vectorHeight
/// </summary>
/// <returns>The file.</returns>
/// <param name="file">File.</param>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public static SharpSvgImageSource FromFile(string file, int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
return new SharpSvgImageSource(Xamarin.Forms.ImageSource.FromFile(file), vectorWidth, vectorHeight, useDipUnits);
}
/// <summary>
/// SvgImageSource FromStream.
/// By default it uses view size as vectorWidth / vectorHeight
/// </summary>
/// <returns>The stream.</returns>
/// <param name="stream">Stream.</param>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public static SharpSvgImageSource FromStream(Func<Stream> stream, int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
return new SharpSvgImageSource(Xamarin.Forms.ImageSource.FromStream(stream), vectorWidth, vectorHeight, useDipUnits);
}
/// <summary>
/// SvgImageSource FromUri.
/// By default it uses view size as vectorWidth / vectorHeight
/// </summary>
/// <returns>The URI.</returns>
/// <param name="uri">URI.</param>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public static SharpSvgImageSource FromUri(Uri uri, int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
return new SharpSvgImageSource(Xamarin.Forms.ImageSource.FromUri(uri), vectorWidth, vectorHeight, useDipUnits);
}
/// <summary>
/// SvgImageSource FromResource.
/// By default it uses view size as vectorWidth / vectorHeight
/// </summary>
/// <returns>The resource.</returns>
/// <param name="resource">Resource.</param>
/// <param name="resolvingType">Resolving type.</param>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public static SharpSvgImageSource FromResource(string resource, Type resolvingType, int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
return FromResource(resource, resolvingType.GetTypeInfo().Assembly, vectorWidth, vectorHeight, useDipUnits);
}
/// <summary>
/// SvgImageSource FromResource.
/// By default it uses view size as vectorWidth / vectorHeight
/// </summary>
/// <returns>The resource.</returns>
/// <param name="resource">Resource.</param>
/// <param name="sourceAssembly">Source assembly.</param>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public static SharpSvgImageSource FromResource(string resource, Assembly sourceAssembly = null, int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
if (sourceAssembly == null)
{
MethodInfo callingAssemblyMethod = typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetCallingAssembly");
if (callingAssemblyMethod != null)
{
sourceAssembly = (Assembly)callingAssemblyMethod.Invoke(null, new object[0]);
}
else
{
return null;
}
}
return FromStream(() => sourceAssembly.GetManifestResourceStream(resource), vectorWidth, vectorHeight, useDipUnits);
}
}
/// <summary>
/// Svg data resolver with fix for unsharp SVG's
///
/// Copied from https://github.com/luberda-molinet/FFImageLoading/blob/master/source/FFImageLoading.Svg.Shared/SvgDataResolver.cs
/// and applied fix for pixelated SVG's as suggested in https://github.com/luberda-molinet/FFImageLoading/issues/522
///
/// NOTE this code is a temporary fix until issue 522 is resolved properly in FFImageLoading
/// </summary>
public class SharpSvgDataResolver : IVectorDataResolver
{
public static uint ScalingFactor { get; set; } = 1; // The suggested fix in https://github.com/luberda-molinet/FFImageLoading/issues/522
/// <summary>
/// Initializes a new instance of the <see cref="T:FFImageLoading.Svg.Platform.SvgDataResolver"/> class.
/// Default SVG size is read from SVG file width / height attributes
/// You can override it by specyfing vectorWidth / vectorHeight params
/// </summary>
/// <param name="vectorWidth">Vector width.</param>
/// <param name="vectorHeight">Vector height.</param>
/// <param name="useDipUnits">If set to <c>true</c> use dip units.</param>
public SharpSvgDataResolver(int vectorWidth = 0, int vectorHeight = 0, bool useDipUnits = true)
{
VectorWidth = vectorWidth;
VectorHeight = vectorHeight;
UseDipUnits = useDipUnits;
}
public Configuration Configuration { get { return ImageService.Instance.Config; } }
public bool UseDipUnits { get; private set; }
public int VectorHeight { get; private set; }
public int VectorWidth { get; private set; }
public async Task<Tuple<Stream, LoadingResult, ImageInformation>> Resolve(string identifier, TaskParameter parameters, CancellationToken token)
{
FFImageLoading.Work.ImageSource source = parameters.Source;
if (!string.IsNullOrWhiteSpace(parameters.LoadingPlaceholderPath) && parameters.LoadingPlaceholderPath == identifier)
source = parameters.LoadingPlaceholderSource;
else if (!string.IsNullOrWhiteSpace(parameters.ErrorPlaceholderPath) && parameters.ErrorPlaceholderPath == identifier)
source = parameters.ErrorPlaceholderSource;
var resolvedData = await (Configuration.DataResolverFactory ?? new DataResolverFactory())
.GetResolver(identifier, source, parameters, Configuration)
.Resolve(identifier, parameters, token).ConfigureAwait(false);
if (resolvedData?.Item1 == null)
throw new FileNotFoundException(identifier);
var svg = new SKSvg()
{
ThrowOnUnsupportedElement = false,
};
SKPicture picture;
using (var svgStream = resolvedData.Item1)
{
picture = svg.Load(resolvedData?.Item1);
}
float sizeX = 0;
float sizeY = 0;
if (VectorWidth == 0 && VectorHeight == 0)
{
if (picture.CullRect.Width > 0)
sizeX = picture.CullRect.Width;
else
sizeX = 300;
if (picture.CullRect.Height > 0)
sizeY = picture.CullRect.Height;
else
sizeY = 300;
}
else if (VectorWidth > 0 && VectorHeight > 0)
{
sizeX = VectorWidth;
sizeY = VectorHeight;
}
else if (VectorWidth > 0)
{
sizeX = VectorWidth;
sizeY = (VectorWidth / picture.CullRect.Width) * picture.CullRect.Height;
}
else
{
sizeX = (VectorHeight / picture.CullRect.Height) * picture.CullRect.Width;
sizeY = VectorHeight;
}
if (UseDipUnits) { sizeX *= ScalingFactor; sizeY *= ScalingFactor; } // The suggested fix in https://github.com/luberda-molinet/FFImageLoading/issues/522
using (var bitmap = new SKBitmap((int)sizeX, (int)sizeY))
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint())
{
canvas.Clear(SKColors.Transparent);
float scaleX = sizeX / picture.CullRect.Width;
float scaleY = sizeY / picture.CullRect.Height;
var matrix = SKMatrix.MakeScale(scaleX, scaleY);
canvas.DrawPicture(picture, ref matrix, paint);
canvas.Flush();
using (var image = SKImage.FromBitmap(bitmap))
using (var data = image.Encode(SKImageEncodeFormat.Png, 80))
{
var stream = new MemoryStream();
data.SaveTo(stream);
stream.Position = 0;
//var stream = data?.AsStream();
return new Tuple<Stream, LoadingResult, ImageInformation>(stream, resolvedData.Item2, resolvedData.Item3);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment