8

How can I convert a BitmapImage object to byte array in UWP ? in .Net it is easy to achieve, even in previous WinRT versions, looked all over the internet but without success, one of the solutions was to use a WriteableBitmap like mentioned in this answer, but in the current version of UWP, constructing a WriteableBitmap out of a BitmapImage isn't possible, any work around ?

Community
  • 1
  • 1
AymenDaoudi
  • 7,811
  • 9
  • 52
  • 84
  • How do you get that BitmapImage? From an Url or what? – fillobotto Mar 19 '16 at 22:50
  • 1
    @fillobotto : From a Url sir, a web url. – AymenDaoudi Mar 19 '16 at 22:52
  • I just elaborated an answer, working for me in a blank UWP project – fillobotto Mar 19 '16 at 23:07
  • Possible duplicate of [Convert BitmapImage to byte\[\] array in Windows Phone 8.1 Runtime](http://stackoverflow.com/questions/34184855/convert-bitmapimage-to-byte-array-in-windows-phone-8-1-runtime) – Rob Caplan - MSFT Mar 20 '16 at 03:05
  • This is exactly the same as in previous WinRT versions. The answer you linked which created a WritableBitmap from a BitmapImage was for a Windows Phone Silverlight app, not a Windows Phone Runtime app. – Rob Caplan - MSFT Mar 20 '16 at 03:07
  • Your question isn't clear. Do you want to encode a bitmap (e.g. as PNG) or just access the raw pixel data? Anyway, one possible approach in your situation (where the image source is a a web url), is to do the web request manually to get the encoded bitmap buffer (from the web response), then create a BitmapImage from the reponse buffer and keep the buffer for later use. – Clemens Mar 20 '16 at 08:51

2 Answers2

12

Since you start from image url, the only way I can figure out is to get the stream of the image. To do this, RandomAccessStreamReference.CreateFromUri() method is really useful.

Windows.Storage.Streams.IRandomAccessStream random = await Windows.Storage.Streams.RandomAccessStreamReference.CreateFromUri(new Uri("http://...")).OpenReadAsync();

Then we have to decode the stream in order to be able to read all pixels for later usage.

Windows.Graphics.Imaging.BitmapDecoder decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(random);
Windows.Graphics.Imaging.PixelDataProvider pixelData = await decoder.GetPixelDataAsync();

Finally you can access pixel buffer in such a way.

byte[] bytes = pixelData.DetachPixelData();
fillobotto
  • 3,698
  • 5
  • 34
  • 58
  • Any way to convert the actual BitmapImage object itself without passing by the url ? the scenario is hard to explain ... – AymenDaoudi Mar 19 '16 at 23:49
  • 2
    @AymenDaoudi while googling I found a post from MSDN forums saying this was not possible. I'll post a link if I find it again https://social.msdn.microsoft.com/Forums/en-US/8974f4a4-f116-499b-ad00-e0c8a00a6494/convert-bitmapimage-to-byte?forum=winappswithcsharp – fillobotto Mar 19 '16 at 23:59
4

Yeah, to a byte[] is not too complex, I think you can get it. I wish it were simpler, but it's not.

http://codepaste.net/ijx28i

This code actually goes a step further and converts the byte[] into a Base64 string (for serializing) but you can ignore that extra step, right?

// using System.Runtime.InteropServices.WindowsRuntime;

private async Task<string> ToBase64(Image control)
{
    var bitmap = new RenderTargetBitmap();
    await bitmap.RenderAsync(control);
    return await ToBase64(bitmap);
}

private async Task<string> ToBase64(WriteableBitmap bitmap)
{
    var bytes = bitmap.PixelBuffer.ToArray();
    return await ToBase64(bytes, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight);
}

private async Task<string> ToBase64(StorageFile bitmap)
{
    var stream = await bitmap.OpenAsync(Windows.Storage.FileAccessMode.Read);
    var decoder = await BitmapDecoder.CreateAsync(stream);
    var pixels = await decoder.GetPixelDataAsync();
    var bytes = pixels.DetachPixelData();
    return await ToBase64(bytes, (uint)decoder.PixelWidth, (uint)decoder.PixelHeight, decoder.DpiX, decoder.DpiY);
}

private async Task<string> ToBase64(RenderTargetBitmap bitmap)
{
    var bytes = (await bitmap.GetPixelsAsync()).ToArray();
    return await ToBase64(bytes, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight);
}

private async Task<string> ToBase64(byte[] image, uint height, uint width, double dpiX = 96, double dpiY = 96)
{
    // encode image
    var encoded = new InMemoryRandomAccessStream();
    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, encoded);
    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, height, width, dpiX, dpiY, image);
    await encoder.FlushAsync();
    encoded.Seek(0);

    // read bytes
    var bytes = new byte[encoded.Size];
    await encoded.AsStream().ReadAsync(bytes, 0, bytes.Length);

    // create base64
    return Convert.ToBase64String(bytes);
}

private async Task<ImageSource> FromBase64(string base64)
{
    // read stream
    var bytes = Convert.FromBase64String(base64);
    var image = bytes.AsBuffer().AsStream().AsRandomAccessStream();

    // decode image
    var decoder = await BitmapDecoder.CreateAsync(image);
    image.Seek(0);

    // create bitmap
    var output = new WriteableBitmap((int)decoder.PixelHeight, (int)decoder.PixelWidth);
    await output.SetSourceAsync(image);
    return output;
}

This was the important part:

var bytes = (await bitmap.GetPixelsAsync()).ToArray();

Thought you might enjoy seeing it context.

Best of luck.

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • Been searching and experimenting for hours and this is the only helpful thing I've found. Needed to convert Image.Source to a CanvasBitmap for a Win2D animation when neither the StorageFile nor uri is available. Crazy how there's no way to do this. RenderTargetBitmap is the least awful workaround by far. – Sean O'Neil Aug 23 '18 at 04:19