XAML 101: Image Resizing with DecodePixelWidth and DecodePixelHeight

FacebookTwitterGoogle+Share

Source code for this sample on GitHub: https://github.com/billreiss/xamlnative/tree/master/Xaml101/009_DecodeImageWidthHeight

UPDATED: I missed an important detail in this sample, like I mention below I am learning about this topic myself. Hugo Vermaak pointed out that the image after using this technique lost some definition, and so I did a little more research and so added a section to the bottom of this post about DecodePixelType.

One of the reasons I love sharing knowledge with others is that it’s also a great learning experience. One of the best ways to learn about a technology is to decide to do a presentation on it, and then panic and cram and regret agreeing to doing it, and then finally pulling it all together and learning how it works in depth (well at least enough depth to be a convincing “expert”). Doing this blog series on XAML, I will learn a lot of new things along the way, and this is one of the first. After my previous post about Image and ImageBrush, Tim Heuer of Microsoft tweeted me:

So here we go, something new for me but it looks pretty cool. In the last sample, the original image was larger than the avatar we actually wanted to display. The original image was 300×300 but we were displaying it as 70×70. Here is the XAML we were using:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Ellipse Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top" Width="70" Height="70">
        <Ellipse.Fill>
            <ImageBrush ImageSource="Images/healy.jpg" />
        </Ellipse.Fill>
    </Ellipse>
</Grid>

And the resulting app:

image

Looking at the above XAML, the image is loaded at its original resolution and then the image is scaled down by the Universal Windows Platform framework to the desired size. This can cause performance issues and also may not look great. There is a better way. If you know what size you want an image to be when you display it, and if it’s a JPEG or PNG file (the two most popular image formats on the web) you can have the image scale itself as it’s being loaded. The XAML is a little more complex but not too bad:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Ellipse Margin="10" HorizontalAlignment="Left" VerticalAlignment="Top" Width="70" Height="70">
        <Ellipse.Fill>
            <ImageBrush>
                <ImageBrush.ImageSource>
                    <BitmapImage UriSource="Images/healy.jpg" DecodePixelHeight="70" DecodePixelWidth="70" DecodePixelType="Logical"/>
                </ImageBrush.ImageSource>
            </ImageBrush>
        </Ellipse.Fill>
    </Ellipse>
</Grid>

Instead of specifying a path on the ImageBrush.ImageSource property, we tell the ImageBrush to use a BitmapImage as its ImageSource, and the BitmapImage has the DecodePixelHeight and DecodePixelWidth properties to tell the image decoder what size you want the image to be.

UPDATED: Notice the DecodePixelType property. This defaults to “Physical”, meaning that the image would be decoded to exactly 70×70 pixels. XAML uses device independent pixels (DIPs) which may or may not correspond to a single physical pixel. This depends on screen size and resolution in order to scale your app to look good on all devices. By setting DecodePixelType to Logical, the Windows 10 runtime will take that 70 pixels value and figure out how many actual pixels this is on the particular device, and decode at that size instead. It’s the best of both worlds, you get to decode to the exact size you actually need without doing all the calculations yourself.

Here is the app now:

image

You may not be able to tell a difference, I think it looks a little less “jaggy”. You can use the same technique on the Image’s Source property. You actually don’t need to specify DecodePixelHeight if you want to preserve the original aspect ratio, you can just give a value for DecodePixelWidth and the DecodePixelHeight will be calculated for you.

  • hugovermaak

    I have to be honest, I see a decrease in quality on my image, I am debugging it on my Lumia 930, I would have thought that the DecodePixelWidth and Height properties would take into consideration a devices’ PPI. Maybe it is because I don’t know how all the assets gets packaged up for deployment.

    • Bill Reiss

      Can you try setting DecodePixelType to Logical and see if it helps? I’ll try it tonight if you don’t get to it by then.

      • hugovermaak

        Yep. That seems to have sorted it out. Nice and crispy now. Thank you.

        • Bill Reiss

          Ok great thanks for testing it out. I will update the post tonight to reflect this.