I'm using WriteableBitmapEx in a WinRT project. I load an image into a WriteableBitmap from the users Picture library. However, I cannot then immediately write to that image, if I do, it will be overwritten with the image itself (it seems like it's Async loading the image and then it's overwritting my drawing on top of it). I don't know how to stop it from doing that (I tried using Await on the SetSource but that's not an Async method).
I've used an "Await Task.Delay(1000)" and that works, but it seems hacky because 1000ms may or may not be enough time.. I would like it to wait until the bitmap is loaded before proceeding.
Can anyone seem off the top what I'm doing wrong or suggest how I can ensure the WriteableBitmap is loaded from the pictures library before doing any processing? Here's the example code snippet I created:
Dim file = Await picker.PickSingleFileAsync
If file Is Nothing Then
Exit Sub
End If
Dim wbm As New WriteableBitmap(1, 1)
wbm.SetSource(Await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
' If I don't have this, the DrawLine will not show up, if I do, it will.
Await Task.Delay(1000)
wbm.DrawLine(1, 1, 255, 255, Colors.Green)
wbm.Invalidate()
ImageMain.Source = wbm
The OpenAsync returning just means the stream is available, not that the data is actually read from it. It seems like you would therefore want to both open + read first and then you would be fine.
Since Filip pointed out that ReadAsync would require you to create and pass in a buffer first, I've updated the below snippet to use DataReader to actually load the stream into the byte array after doing OpenReadAsync to get the IRandomAccessStream.
var randomAccessStream = await file.OpenReadAsync();
var dataReader = new DataReader(randomAccessStream);
await dataReader.LoadAsync(randomAccessStream.Size);
byte[] imageBytes;
dataReader.ReadBytes(out imageBytes);
wbm.SetSource(new MemoryStream(imageBytes));
This method loads an image from the app's content, decodes it and passes back an ready-to-use WriteableBitmap.
Taken from the WriteableBitmapEx library:
/// <summary>
/// Loads an image from the applications content and fills this WriteableBitmap with it.
/// </summary>
/// <param name="bmp">The WriteableBitmap.</param>
/// <param name="uri">The URI to the content file.</param>
/// <returns>The WriteableBitmap that was passed as parameter.</returns>
public static async Task<WriteableBitmap> FromContent(this WriteableBitmap bmp, Uri uri)
{
// Decode pixel data
var file = await StorageFile.GetFileFromApplicationUriAsync(uri);
var decoder = await BitmapDecoder.CreateAsync(await file.OpenAsync(FileAccessMode.Read));
var transform = new global::Windows.Graphics.Imaging.BitmapTransform();
var pixelData = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
var pixels = pixelData.DetachPixelData();
// Copy to WriteableBitmap
bmp = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
using (var bmpStream = bmp.PixelBuffer.AsStream())
{
bmpStream.Seek(0, SeekOrigin.Begin);
bmpStream.Write(pixels, 0, (int)bmpStream.Length);
return bmp;
}
}
BTW, WinRT is now officially supported by WriteableBitmapEx. ;)
http://kodierer.blogspot.de/2012/05/one-bitmap-to-rule-them-all.html
I don't think WB has the events that BitmapImage does to wait for it to open. I would try using BitmapDecoder/CreateAsync/GetPixelDataAsync/DetachPixelData and copy resulting byte array to the WB's pixel buffer instead of calling wb.SetSource(). Then maybe call wb.Invalidate(). Or maybe just replace the Delay call with an Invalidate one.