Use XZing on a non-dispatcher thread

Dec 14, 2012 at 8:26 AM

Hi all,

I'm using ZXing.Net in a Windows 8 App and need to decode an image on a background thread (not the UI dispatcher thread). The method IBarcodeReader.Decode(WriteableBitmap) can't be used in this case, because WriteableBitmap only works on the UI thread.

Can I use another method like Decode(byte[], int, int, RGBLuminanceSource.BitmapFormat) to accomplish this? My input is an IRandomAccessStream with image data read from a file or captured from the webcam (it could be any of the formats BMP, PNG, JPEG). How do I have to convert the image data manually to use the raw method?

I really need to use the API from a non-UI thread (even unit tests wouldn't work with WriteableBitmap).

Thanks for your help

Coordinator
Dec 16, 2012 at 6:55 PM

You can use Decode(byte[], ....). But you have to create a byte array with the uncompressed pixel data from the IRandomAccessStream manually.
If you can partially use the dispatcher thread you can create the byte array from the PixelBuffer of a WriteableBitmap instance. If not you have to look for a way to decompress images in a background thread. Not sure how to accomplish that. It seems to me that nearly every graphics related class is derived from DispatcherObject.

The byte array should represent the data in a way how the BitmapFormat defines it. One example if you can decode the image stream to a uncompressed array with the format BGRA32 then the bytes should be in the following order: 1 byte blue, 1 byte green, 1 byte red, 1 byte alpha, 1 byte blue, 1 byte green and so on.
The size of the array should be "width * height * 4".

Dec 16, 2012 at 7:16 PM
Edited Dec 16, 2012 at 7:18 PM

Thanks, I figured out how to get from the image stream to the bitmap data on a background thread. Luckily BitmapDecoder can be used on a non-UI thread, since it doesn't derive from DispatcherObject in WinRT (interestingly, this is different from WPF).

I am using the following code:

var decoder = await BitmapDecoder.CreateAsync(inputStream);


var pixelData = await decoder.GetPixelDataAsync();

var reader = new BarcodeReader(new MultiFormatReader(), null, null, null) { PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE } };

var bytes = pixelData.DetachPixelData();

var result = reader.Decode(
                bytes,
                (int)decoder.PixelWidth,
                (int)decoder.PixelHeight,
                GetBitmapFormat(decoder.BitmapPixelFormat));

I discovered that the BarcodeReader must be initialized using above constructor in order to be able to use the raw Decode(bytes[],...) method. If the empty constructor is used, createRGBLuminanceSource isn't set to its default fallback and a NullReferenceException occurs. Is this intended?

Also for Decode(byte[],...) I am unsure what to pass for the last parameter. GetBitmapFormat currently returns BitmapFormat.Unknown for all possible values of BitmapDecoder.BitmapPixelFormat. This enum can have the following values: Bgra8, Rgba16, Rgba8 and Unknown. I wonder how to match them with the values of RGBLuminanceSource.BitmapFormat.

However, it is working well using Unknown at least for PNG and BMP images. And I can finally leverage unit tests and significantly improve the application performance: Nice!

Coordinator
Dec 16, 2012 at 8:06 PM

I only looked for the .Net 4.5 classes. Good to know that there are differences.

The NullReferenceException is a bug. I fixed it right now. Thanks for your hint.

I doesn't know the method GetBitmapFormat. If you use RGBLuminanceSource.BitmapFormat.Unknown the class RGBLuminanceSource tries to determine the right format. It's a very stupid algorithm I used. It uses the array length divided by (height * width). It can't determine the difference between ARGB32 and BGRA32.
The mapping between BitmapPixelFormat and RGBLuminanceSource.BitmapFormat is the following:
BitmapPixelFormat.Unknown -> RGBLuminanceSource.BitmapFormat.Unknown
BitmapPixelFormat.Rgba16 -> Unsupported
BitmapPixelFormat.Rgba8 -> RGBLuminanceSource.BitmapFormat.RGB32 (without alpha channel support)
BitmapPixelFormat.Bgra8-> RGBLuminanceSource.BitmapFormat.BGRA32

Dec 16, 2012 at 8:11 PM

GetBitmapFormat is just a helper method I added and until now always returned BitmapFormat.Unkown. Thanks for the mapping information, I'll change the method to use this.