Last active
March 20, 2017 15:33
-
-
Save VincentH-Net/1eb516aa1a12b00e6c0f50acb568adae to your computer and use it in GitHub Desktop.
Temporary fix for unsharp SVG's in FFImageLoading
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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