4

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?

Sean
  • 868
  • 9
  • 22
  • Have you tried setting DownSampleToViewSize to true? and if not, can you tell me the behaviour? – Ricardo Dias Morais Mar 03 '20 at 23:23
  • It made the image smaller but still blurry. I also tried listening to changes to the properties on the viewmodel and then calling ReloadImage(). This didn't do anything. It looks like there'd be a way to invalidate it and have it redraw. – Sean Mar 03 '20 at 23:37
  • Tell me the SVG image Width and Height and the values that you are binding it to, if the values doesn't have the same aspect ratio, it maybe aking the image pixelated, you need to keep the same scale – Ricardo Dias Morais Mar 03 '20 at 23:39
  • Ah... Yes, I'm binding both the height and the width to the same underlying value so the icon is square. Bear in mind too, I've tested by hard coding the values. For example 80 and 80 or 100 and 100 for width and height. When hardcoded, the image looks correct. When I bind to the VM, the icons are *very* pixilated. These hardcoded values are within the range of the values generated by the ViewModel. – Sean Mar 04 '20 at 03:55
  • Do you load the SVG image by url or local file? You can try to load the SVG image by local file, if you can got the same result. – Leon Mar 04 '20 at 07:52
  • It is by local file. I seem to have solved the issue by setting HorizontalOptions and VerticalOption from Center, which they both were before, to "Fill".. I don't know why this worked but they are displaying crisp and clear now. – Sean Mar 05 '20 at 15:12
  • If you set the center `HorizontalOptions`, `VerticalOption` and `width` `height` at the same time, it will affect each other. Please post your solution to answer, it will help others who have similar issue. – Leon Mar 06 '20 at 07:50
  • Thanks.. Solution below – Sean Mar 06 '20 at 19:10

2 Answers2

2

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.

Sean
  • 868
  • 9
  • 22
0

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.

ibocon
  • 1,352
  • 3
  • 17
  • 40