1

Resize images to be used on Live Tile – Windows Phone 7

Recently I added a live tile feature to one of my WP7 applications.
The app tile would show an image of a person, retrieved from a RSS feed.

Now because of the fact that the image is of a person, it’s not squared, but portrait. Meaning that the width of the image is shorter then the height. Also the image I need to display is larger than the windows live tile.
So when you just try to display this image without any modifications, you’ll get a weird result.

The original image

To fix this, you’ll need to resize the image.

First of all, download the image and map it to a BitmapImage object, you can just use an URI that points to the image on the web for this.

Uri imageUri = new Uri(imageUrl, UriKind.Absolute);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.UriSource = imageUri;

One note, before you can start manipulating the image, you’ll need to be sure it has been downloaded completely, so register to the ImageOpened event.

bitmapImage.ImageOpened += (sender, e) =>
{
//All transformation code goes here
};

If needed you can also add code when there has ben a failure with downloading the image.

bitmapImage.ImageFailed += (sender, e) =>
{
//All code regarding image download failure goes here
};

So when you are sure the image has been downloaded you can start manipulating it. Now an extra note here, there is a great library available on Codeplex ( and NuGet ) that adds many extension methods to the WriteableBitmap that are handy when dealing with any image manipulation. It’s called WriteableBitmapEx and can be found here ( http://writeablebitmapex.codeplex.com/ ).

We need to target our image to a tile of 173 by 173 pixels, thus we need to find the aspect – ratio. To do this, we first need to determine what is the largest side of our image.

var aspect = Math.Max(bitmapImage.PixelWidth, bitmapImage.PixelHeight);

After this we can determin the destination width and height…

var ratio = (decimal)aspect / (decimal)173;
var width = decimal.ToInt32(bitmapImage.PixelWidth / ratio);
var height = decimal.ToInt32(bitmapImage.PixelHeight / ratio);

All we need to do now is performing the actual resize.

WriteableBitmap writeableBitmap = new WriteableBitmap(bitmapImage);
var resized = writeableBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.Bilinear);

Now that you have ‘constructed’ your resized image, you’ll have to store it on the phone’s isolated storage before you can use it as a live tile image!

The live tile needs an URI to this saved image to work correctly. To get this done in one go you can use following method SaveTileBitmap, it will save the image and return you the correct URI.

private Uri SaveTileBitmap(WriteableBitmap bitmap, string fileName)
{
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (!store.DirectoryExists(@"Shared\ShellContent"))
            store.CreateDirectory(@"Shared\ShellContent");
        else if (store.FileExists("Shared/ShellContent/" + fileName))
            store.DeleteFile("Shared/ShellContent/" + fileName);
        using (
        var stream = store.OpenFile(
        @"Shared\ShellContent\" + fileName,
        FileMode.OpenOrCreate))
        {
            bitmap.SaveJpeg(stream, 173, 173, 0, 100);
        }
    }
 
    return new Uri(
    "isostore:/Shared/ShellContent/" + fileName, UriKind.Absolute);
}

So the last stop is using this method to get the URI and constructing a live tile…

You’ll need to give the image a name, but any name will do.

Uri localTileUri = this.SaveTileBitmap(resized, "LiveTileImage");
ShellTile firstTile = ShellTile.ActiveTiles.First();
if (firstTile != null)
{
    var newTileData = new StandardTileData
    {
        BackgroundImage = localTileUri
    };
    firstTile.Update(newTileData);
}

BUT when you do this, you’ll notice the image is still scaled to 173 px! This is something I didn’t except, but it seems the tile will always try to stretch to 173 by 173, thus miss forming our image.

Resized image – wrong!

To compensate we need to paste our resized image on a dummy 173 by 173 before storing it in the isolated store… In other words best to store a correct sized square image with the resized image pasted inside it at the correct location.

Because we know the width was the smallest, we need to center the image on the tile! So we will be merging the resized image starting not at position 0.0 but compensating for the smaller width!

double rectX = (173 - width) / 2;
Rect rect = new Rect(0.0, 0.0, width, height);
WriteableBitmap bitmapDummy = new WriteableBitmap(173, 173);
bitmapDummy.Blit(new Rect(rectX, 0.0, width, height), resized, rect, WriteableBitmapExtensions.BlendMode.None);
 
Uri localTileUri = this.SaveTileBitmap(bitmapDummy, "LiveTileImage");
ShellTile firstTile = ShellTile.ActiveTiles.First();
if (firstTile != null)
{
    var newTileData = new StandardTileData
    {
        BackgroundImage = localTileUri
    };
 
    firstTile.Update(newTileData);
}

Resized image – correct!

Depechie

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *