FFImageLoading, SVGCachedImage blurry when setting HeightRequest/WidthRequest from a binding

Viewed 1188

I'm using FFImageLoading to display svg icons in my Xamarin.Forms project. My understanding is that the Height and Width requests must be set explicitly in order for the SVG to be rendered properly. I'm getting a lot of pixilation whenever I try to bind the Height/Width requests to values on the ViewModel (I have this as a need because the desired size depends on data). If I set the size explicitly everything looks fine.

Does SvgCachedImage not redraw the SVG whenever the bindings for Height/Width request change?

If not, is there a way for me to explicitly force them to invalidate and redraw when the size changes?

2 Answers

The blur issue was resolved by setting Horizontal and Vertical options to fill instead of Center:

<Grid>
                                <ffimageloadingsvg:SvgCachedImage  BackgroundColor="Transparent"
                                                              Margin="{Binding HarmonicIconMargin}"
                                                              HorizontalOptions="Fill"
                                                              VerticalOptions="Fill"
                                                              WidthRequest="{Binding HarmonicIconWidth}"
                                                              HeightRequest="{Binding HarmonicIconWidth}"
                                                              Source="{Binding CurrentTestItem, Converter={StaticResource TestItemToHarmonicIconConverter}}" />
                            </Grid>

At that point it seemed be ignoring the height/width requests. I could've experimented around with that more (perhaps the request was for too much space) but I found that binding the margin to a computed property effectively enabled me to control the size of the SVG Image while not causing it to become blurred.

In order to solve svg blur issue when scale size of view,

  1. Change SvgImageSource.VectorWidth or SvgImageSource.VectorHeight
  2. Reload Image
protected override void OnSizeAllocated(double width, double height)
{
    if (0 < width && 0 < height && && Source is SvgImageSource imageSource)
    {
        imageSource.VectorWidth = (int)Math.Ceiling(width);
        imageSource.VectorHeight = (int)Math.Ceiling(height);
        svgImage.ReloadImage();

        base.OnSizeAllocated(width, height);
    }
}

According to FFImageLoading source code, SVG image size is determined by SvgImageSource.VectorWidth or SvgImageSource.VectorHeight.

double sizeX = VectorWidth;
double sizeY = VectorHeight;

if (UseDipUnits)
{
    sizeX = VectorWidth.DpToPixels();
    sizeY = VectorHeight.DpToPixels();
}

if (sizeX <= 0 && sizeY <= 0)
{
    if (picture.CullRect.Width > 0)
        sizeX = picture.CullRect.Width;
    else
        sizeX = 400;

    if (picture.CullRect.Height > 0)
        sizeY = picture.CullRect.Height;
    else
        sizeY = 400;
}
else if (sizeX > 0 && sizeY <= 0)
{
    sizeY = (int)(sizeX / picture.CullRect.Width * picture.CullRect.Height);
}
else if (sizeX <= 0 && sizeY > 0)
{
    sizeX = (int)(sizeY / picture.CullRect.Height * picture.CullRect.Width);
}

resolvedData.ImageInformation.SetType(ImageInformation.ImageType.SVG);

using (var bitmap = new SKBitmap(new SKImageInfo((int)sizeX, (int)sizeY)))
using (var canvas = new SKCanvas(bitmap))
using (var paint = new SKPaint())
{
    canvas.Clear(SKColors.Transparent);
    var scaleX = (float)sizeX / picture.CullRect.Width;
    var scaleY = (float)sizeY / picture.CullRect.Height;
    var matrix = SKMatrix.MakeScale(scaleX, scaleY);
    canvas.DrawPicture(picture, ref matrix, paint);
    canvas.Flush();

    token.ThrowIfCancellationRequested();

    return await Decode(picture, bitmap, resolvedData).ConfigureAwait(false);
}

binding cause a pixellation because view's initial width and height are used as VectorWidth and VectorHeight, which is -1 or somthing you set as default for binding property. Therefore, your svg image resolution set too low at first and then binding process scales up view without redrawing svg image.

Related