Search code examples
c#windows-runtimezxingargumentnullexception

Runtime.interop ArgumentNullException using ZXing


I am trying to parse a QR Code in a Windows Store-app using ZXing.Net, but when I try to run it using the latest version from their webpage it gives me a ArgumentNullException in BitmapLuminanceSource.Silverlight.cs on line 50

The line looks like this

var data = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(writeableBitmap.PixelBuffer, 0, (int)writeableBitmap.PixelBuffer.Length);

The WriteableBitmap is not null, so I do not what, what is null.

Can anybody help me?

It is from this method

public BitmapLuminanceSource(WriteableBitmap writeableBitmap)
   : base(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight)
{
   var height = writeableBitmap.PixelHeight;
   var width = writeableBitmap.PixelWidth;
   var stride = width * 4;
   luminances = new byte[width * height];
   Color c;

#if NETFX_CORE
   var data = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(writeableBitmap.PixelBuffer, 0, (int)writeableBitmap.PixelBuffer.Length);
   for (int y = 0; y < height; y++)
   {
      int offset = y * stride;
      for (int x = 0, xl = 0; x < stride; x += 4, xl++)
      {
         c = Color.FromArgb(
            data[x + offset], 
            data[x + offset + 1], 
            data[x + offset + 2], 
            data[x + offset + 3]);
         luminances[y * width + xl] = (byte)(0.3 * c.R + 0.59 * c.G + 0.11 * c.B + 0.01);
      }
   }
#else
   var pixels = writeableBitmap.Pixels;
   for (int y = 0; y < height; y++)
   {
      int offset = y * width;
      for (int x = 0; x < width; x++)
      {
         int srcPixel = pixels[x + offset];
         c = Color.FromArgb((byte)((srcPixel >> 0x18) & 0xff),
            (byte)((srcPixel >> 0x10) & 0xff),
            (byte)((srcPixel >> 8) & 0xff),
            (byte)(srcPixel & 0xff));
         luminances[offset + x] = (byte)(0.3 * c.R + 0.59 * c.G + 0.11 * c.B + 0.01);
      }
   }
#endif
}

UPDATE

The WriteableBitmat is created using this code

// Get the File
var File = await FilePick.PickSingleFileAsync();

// Convert the File to a Bitmap
var Stream = await File.OpenAsync(FileAccessMode.Read);
var Bmp = new BitmapImage();
Bmp.SetSource(Stream);
var WBmp = new WriteableBitmap(Bmp.PixelWidth, Bmp.PixelHeight);
WBmp.SetSource(Stream);

By using Damir Arh's answer, the error is moved a bit to the following code

c = Color.FromArgb(
    data[x + offset], 
    data[x + offset + 1], 
    data[x + offset + 2], 
    data[x + offset + 3]);

Where I get an IndexOutOfRangeException, when

x = 580
xl = 145
offset = 31104
y = 36
height = 216
width = 216
stride = 864
data = {byte[31684]}

I can of course see why it is out of range, but I can not see how to fix it

It was fixed using Damir Arh's updated answer with Stream.Seek(0)


Solution

  • I made a quick port of the Silverlight example that's available from the project homepage and it worked:

    var dlg = new FileOpenPicker();
    dlg.FileTypeFilter.Add(".png");
    var file = await dlg.PickSingleFileAsync();
    if (file != null)
    {
        currentBarcode = new WriteableBitmap(89, 89);
        using (var stream = await file.OpenReadAsync())
        {
            currentBarcode.SetSource(stream);
        }
        imgDecoderBarcode.Source = currentBarcode;
        var result = reader.Decode(currentBarcode);
        if (result != null)
        {
            txtDecoderType.Text = result.BarcodeFormat.ToString();
            txtDecoderContent.Text = result.Text;
        }
        else
        {
            txtDecoderType.Text = String.Empty;
            txtDecoderContent.Text = "No barcode found.";
        }
    }
    

    I also tried calling the offending line from the code you posted and there was no exception thrown:

    var dlg = new FileOpenPicker();
    dlg.FileTypeFilter.Add(".png");
    var file = await dlg.PickSingleFileAsync();
    if (file != null)
    {
        currentBarcode = new WriteableBitmap(89, 89);
        using (var stream = await file.OpenReadAsync())
        {
            currentBarcode.SetSource(stream);
            var data = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(currentBarcode.PixelBuffer, 0, (int)currentBarcode.PixelBuffer.Length);
        }
    }
    

    The problem in your case is how you're creating the WriteableBitmap that is being passed to this method. Because you first load a Bitmap from the same stream you're already positioned at its end when you set it as the source for WritableBitmap. You need to reposition yourself to the beginning of the stream so that the data can be loaded once more:

    // Convert the File to a Bitmap
    var Stream = await File.OpenAsync(FileAccessMode.Read);
    var Bmp = new BitmapImage();
    Bmp.SetSource(Stream);
    Stream.Seek(0);
    var WBmp = new WriteableBitmap(Bmp.PixelWidth, Bmp.PixelHeight);
    WBmp.SetSource(Stream);