Resizing Images in Windows Phone 7

I got an email recently asking about resizing images in Silverlight on WP7. Specifically, the need to reduce the size of an image prior to uploading (presumably full camera captured resolution isn’t required – reduction saves both upload time and cost).

I’d assumed that WriteableBitmap has the facility and sure enough I came across a few useful pointers and articles (including this one on the Coding4Fun blog). In fact there are a couple of mechanisms for doing this with WriteableBitmap.

SaveJpeg

The Microsoft.Phone assembly includes a couple of extension methods for the WriteableBitmap class, LoadJpeg() and SaveJpeg(). No prizes for guessing what they do. The SaveJpeg signature looks like this:

 public static void SaveJpeg(
  this WriteableBitmap bitmap, 
  Stream targetStream, 
  int targetWidth, 
  int targetHeight, 
  int orientation, 
  int quality
  );

So you’re able to specify both the target dimensions and also the desired quality – both can result in a significant reduction in image “size”. Along the way you may want to push the work of re-sizing the image onto a background thread. You’ll get a cross-thread access issue if you try and create the WriteableBitmap on the background thread so I ended up with this:

 void task_Completed(object sender, PhotoResult e)
{
  if (e.Error == null)
  {
    BitmapImage bi = new BitmapImage();
    bi.SetSource(e.ChosenPhoto);
    WriteableBitmap wb = new WriteableBitmap(bi);

    ThreadPool.QueueUserWorkItem(callback =>
    {
      MemoryStream ms = new MemoryStream();
      wb.SaveJpeg(ms, 80, 60, 0, 80);
      using (MediaLibrary lib = new MediaLibrary())
        lib.SavePicture("Test", ms.ToArray());
    });

  }
}

Test (3)This is the handler for a CameraCatureTask / PhotoChooserTask. It takes the resulting image and converts it to a small JPEG image (80x60 pixels) with an image quality of 80 which is then saved to the users “Saved Pictures” library. On my test image, this reduces the size from 300kB to 9kB.

Applying a ScaleTransform

If you don’t need to save the image, and perhaps you already have it rendered in the visual tree, then an alternative technique is to use an overload of the WriteableBitmap constructor to apply a scale transform to the image. The relevant constructor takes the form:

 WriteableBitmap(UIElement, Transform)

Thus I can create a WriteableBitmap from a UIElement transformed in some arbitrary fashion (in our case a scale transform will do the job nicely).

 void task_Completed(object sender, PhotoResult e)
{
  if (e.Error == null)
  {
    BitmapImage bi = new BitmapImage();
    bi.SetSource(e.ChosenPhoto);
    Image image1 = new Image()
    {
      Width = 800,
      Height = 600,
      Visibility = System.Windows.Visibility.Collapsed,
      Source = bi
    };

    ScaleTransform st = new ScaleTransform()
    {
      ScaleX = 0.1, 
      ScaleY = 0.1
    };

    WriteableBitmap wb = new WriteableBitmap(image1, st);

    ThreadPool.QueueUserWorkItem(callback =>
    {
      MemoryStream ms = new MemoryStream();
      wb.SaveJpeg(ms, 80, 60, 0, 80);
      using (MediaLibrary lib = new MediaLibrary())
        lib.SavePicture("Test", ms.ToArray());
    });
  }
}

Again I’ve created a handler (note this is a contrived example as I have to create the UIElement and I also save the scaled down image to ensure the code’s working). The essence of this technique is the “middle section”of the code – the ScaleTransform and the construction of the WriteableBitmap instance. Note also, there is a potential issue here as I don’t check whether the image has been rendered before I create the WriteableBitmap (eg imagine this Image was created in XAML with a source set to a remote URL).

Summary

That’s just a couple of ways to handle image resizing in Silverlight on Windows Phone 7. I’m sure there are others I’ve not thought about (and I’m sure the above could be improved / optimised).