Search code examples
c#windows-store-appsimage-scalinglive-tile

Using a square image on a wide tile in Windows Store App


In my application I want to implement secondary tiles in order to let users access specific sections of my application directly and to show section specific information on that tile.

For tile image I want to use icon of the section that will be accessed directly, but I only have access to square-sized images which are downloaded directly from a web link (while creating the tile). As I don't have images for the wide tiles, I have tried to pad them with transparent borders in order to create a wide tile. I used WritableBitmapEx library but my code causes AccessViolationException.

StorageFile file = await ApplicationData.Current.TemporaryFolder.GetFileAsync(tempFileName);
IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);                                              

WriteableBitmap bitmap = new WriteableBitmap(150, 150);
await bitmap.SetSourceAsync(fileStream);

WriteableBitmap bitmapDummy = BitmapFactory.New(310, 150);

using (bitmapDummy.GetBitmapContext())
{
    using (bitmap.GetBitmapContext())
    {
        bitmapDummy.Blit(new Rect(80.0, 0.0, 150, 150), bitmap, new Rect(0.0, 0.0, 150, 150), WriteableBitmapExtensions.BlendMode.None);
    }
 }

the exception occurs on bitmap.GetBitmapContext() so I cannot create an image.

What I try to do here is to create an empty wide tile image and put the square tile image in the middle of it.

Also in the end I want to convert the image to transparent PNG but that is another problem as I can't work out how to create an bitmap yet.

Thanks for your helps in advance.


Solution

  • I found the solution here. It seems that the image wasn't loaded into the WriteableBitmap before I tried to access it hence AccessViolationException was thrown. So instead of,

    WriteableBitmap bitmap = new WriteableBitmap(150, 150);
    await bitmap.SetSourceAsync(fileStream);
    

    I started using,

    WriteableBitmap bitmap = await BitmapFactory.New(1, 1).FromStream(fileStream);
    

    and the problem is now solved.

    The complete code that resizes and pads the square image for those who finds this question is below.

    public static async Task PadPngImage(StorageFile inputFile, StorageFile outputFile, uint width, uint height, uint boundingBoxWidth, uint boundingBoxHeight)
    {
        // Resize the input image
        StorageFile temporaryFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("PadImageTemporaryFile.png", CreationCollisionOption.ReplaceExisting);
        await ResizeImage(inputFile, temporaryFile, width, height);            
    
        // Create a stream linked to resized image
        using (IRandomAccessStream temporaryFileStream = await temporaryFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
        {
            WriteableBitmap bitmap = await BitmapFactory.New(1, 1).FromStream(temporaryFileStream);
    
            // Create an empty image with specified bounding dimentions
            WriteableBitmap bitmapDummy = BitmapFactory.New((int)boundingBoxWidth, (int)boundingBoxHeight);
    
            using (bitmapDummy.GetBitmapContext())
            {
                using (bitmap.GetBitmapContext())
                {
                    // Calculate position of the image
                    Rect center = new Rect((boundingBoxWidth - width) / 2, (boundingBoxHeight - height) / 2, width, height);
    
                    bitmapDummy.Blit(center, bitmap, new Rect(0.0, 0.0, width, height), WriteableBitmapExtensions.BlendMode.None);                        
                }
            }
    
            using(IRandomAccessStream stream = await outputFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
            {
                await bitmapDummy.ToStream(stream, BitmapEncoder.PngEncoderId);
            }                
        }
    }
    
    public static async Task ResizeImage(StorageFile inputFile, StorageFile outputFile, uint newWidth, uint newHeight)
    {
        // Input Stream & Decoder
        using (IRandomAccessStream fileStream = await inputFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
        {
            BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
    
            // Output Stream & Encoder                
            using (IRandomAccessStream stream = await outputFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
            {
                BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(stream, decoder);
    
                // Scale the bitmap image
                encoder.BitmapTransform.ScaledWidth = newWidth;
                encoder.BitmapTransform.ScaledHeight = newHeight;
                encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
    
                await encoder.FlushAsync();
            }
        }
    }