Search code examples
c#win-universal-appuwprendertargetbitmap

Error using using RenderTargetBitmap in UWP


I'm trying to create a bitmap image, and have the following code:

RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(uielement);

IBuffer pixels = await renderTargetBitmap.GetPixelsAsync();

. . .

var pixelArray = pixels.ToArray();

In order to get a ToArray() extension, I came across this question. So I added:

using System.Runtime.InteropServices.WindowsRuntime; // For ToArray

To my code. However, when I run, I get the following error:

Exception thrown: 'System.ArgumentException' in System.Runtime.WindowsRuntime.dll

Additional information: The specified buffer index is not within the buffer capacity.

When I drill into the details, it says in the Stack Trace:

at >System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(IBuffer source, UInt32 sourceIndex, Int32 count) at >System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(IBuffer source)

Is this method of extracting a pixel array still applicable to the UWP? If it is, is there any way to get more detail from this error message?


Solution

  • That method of extracting a pixel array is definitely applicable to UWP. As for the error, the decompiled ToArray() goes like this:

    public static byte[] ToArray(this IBuffer source)
    {
      if (source == null)
        throw new ArgumentNullException("source");
      return WindowsRuntimeBufferExtensions.ToArray(source, 0U, checked ((int) source.Length));
    }
    

    In other words, it calls the ToArray overload that takes a start index and a length:

    public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count)
    {
      if (source == null)
        throw new ArgumentNullException("source");
      if (count < 0)
        throw new ArgumentOutOfRangeException("count");
      if (sourceIndex < 0U)
        throw new ArgumentOutOfRangeException("sourceIndex");
      if (source.Capacity <= sourceIndex)
        throw new ArgumentException(SR.GetString("Argument_BufferIndexExceedsCapacity"));
      if ((long) (source.Capacity - sourceIndex) < (long) count)
        throw new ArgumentException(SR.GetString("Argument_InsufficientSpaceInSourceBuffer"));
      byte[] destination = new byte[count];
      WindowsRuntimeBufferExtensions.CopyTo(source, sourceIndex, destination, 0, count);
      return destination;
    }
    

    The line(s) almost certainly causing your problem:

      if (source.Capacity <= sourceIndex)
        throw new ArgumentException(SR.GetString("Argument_BufferIndexExceedsCapacity"));
    

    ...and since sourceIndex is necessarily 0, that would mean that source.Capacity is also 0.

    I suggest you add some instrumentation to your code to inspect the IBuffer:

    RenderTargetBitmap rtb = new RenderTargetBitmap();
    await rtb.RenderAsync(element);
    
    IBuffer pixelBuffer = await rtb.GetPixelsAsync();
    Debug.WriteLine($"Capacity = {pixelBuffer.Capacity}, Length={pixelBuffer.Length}");
    byte[] pixels = pixelBuffer.ToArray();
    

    I think it likely that your problem occurs before the ToArray call. I'm using the exact same sequence in my own UWP app, getting debug output like so:

    Capacity = 216720, Length=216720