In WinRT, how do I load an image and then wait onl

2019-02-19 09:34发布

问题:

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

回答1:

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));


回答2:

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



回答3:

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.