locked
Converting WriteableBitmap to an IRandomAccessStream

    Question

  • As part of an app I'm working on I've written an image crop. The image is loaded into a WriteableBitmap and each time the user crops the image a new WriteableBitmap is created of a new crop size. What I'd like to do is be able to create an IRandomAccessStream from the WriteableBitmap so that I can save the image to disk via the BitmapDecoder/BitmapEncoder class. I'll be using BitmapEncoder::CreateForTranscodingAsync to save the file, as I want to maintain the image metadata. This in turn requires a BitmapDecoder object, which is constructed from an IRandomAccessStream.

    Why the complexity to crop an image when I could be using the BitmapBounds class? Well, it's not sensible to use BitmapBounds for my scenario as each time FlushAsync is called on the BitmapEncoder the image is re-encoded. This is a bad idea for lossy formats such as JPEG. Hence I'm forced to maintain a WriteableBitmap with the cropped pixel data, and encode the WriteableBitmap on save.

    Any thoughts?

    Dave

    Thursday, May 31, 2012 1:16 PM

Answers

  • Since you already have the data decoded in your WritableBitmap you can't go through a BitmapDecoder here.

    Instead load the BitmapEncoder with SetPixelData. You can get the byte array from WriteableBitmap.Buffer's will need to swizzle the bits from BGRA to RGBA.

    If you want to save the original metadata you may be able to initialize the BitmapEncoder with CreateForTranscodingAsync from the original JPG and then overwrite the pixels with the modified ones, but I haven't tried this.

    As far as FlushAsync forcing multiple lossy re-encoding, have you tried this or are you just expecting re-encoding problems? Without having tried it myself, I'd expect it to re-encode from the original data so the losses wouldn't multiply. 

    --Rob

    • Marked as answer by David Britch Friday, June 01, 2012 9:47 AM
    Thursday, May 31, 2012 3:10 PM
    Owner

All replies

  • Since you already have the data decoded in your WritableBitmap you can't go through a BitmapDecoder here.

    Instead load the BitmapEncoder with SetPixelData. You can get the byte array from WriteableBitmap.Buffer's will need to swizzle the bits from BGRA to RGBA.

    If you want to save the original metadata you may be able to initialize the BitmapEncoder with CreateForTranscodingAsync from the original JPG and then overwrite the pixels with the modified ones, but I haven't tried this.

    As far as FlushAsync forcing multiple lossy re-encoding, have you tried this or are you just expecting re-encoding problems? Without having tried it myself, I'd expect it to re-encode from the original data so the losses wouldn't multiply. 

    --Rob

    • Marked as answer by David Britch Friday, June 01, 2012 9:47 AM
    Thursday, May 31, 2012 3:10 PM
    Owner
  • Hi Rob,

    Thanks for the response. I figured it all out for myself (including the swizzling!) after making the original post and it works a treat. The original metadata is saved by initializing BitmapEncoder::CreateForTranscodingAsync from the original image stream, and then I overwrite the pixels with the modified ones (using BitmapEncoder::SetPixelData).

    FlushAsync definitely multiplies losses for lossy formats, at least in Consumer Preview. On each re-encode the resulting JPG file gets smaller and smaller.

    Dave

    Friday, June 01, 2012 9:47 AM