Show RGBA image from memory Show RGBA image from memory wpf wpf

Show RGBA image from memory


Before you delve into the details of WIC, you may consider to encapsulate byte swapping in a simple custom BitmapSource like shown below. It takes an RGBA byte array and swaps the pixel bytes in the overridden CopyPixels method to produce a PBGRA buffer.

You can simply create an instance of this custom BitmapSource by providing the RGBA buffer and the bitmap width like

var buffer = new byte[] { 25, 166, 0, 255, 90, 0, 120, 255 };var bitmapSource = new RgbaBitmapSource(buffer, 2);

Here is the implementation:

public class RgbaBitmapSource : BitmapSource{    private byte[] rgbaBuffer;    private int pixelWidth;    private int pixelHeight;    public RgbaBitmapSource(byte[] rgbaBuffer, int pixelWidth)    {        this.rgbaBuffer = rgbaBuffer;        this.pixelWidth = pixelWidth;        this.pixelHeight = rgbaBuffer.Length / (4 * pixelWidth);    }    public override void CopyPixels(        Int32Rect sourceRect, Array pixels, int stride, int offset)    {        for (int y = sourceRect.Y; y < sourceRect.Y + sourceRect.Height; y++)        {            for (int x = sourceRect.X; x < sourceRect.X + sourceRect.Width; x++)            {                int i = stride * y + 4 * x;                byte a = rgbaBuffer[i + 3];                byte r = (byte)(rgbaBuffer[i] * a / 256); // pre-multiplied R                byte g = (byte)(rgbaBuffer[i + 1] * a / 256); // pre-multiplied G                byte b = (byte)(rgbaBuffer[i + 2] * a / 256); // pre-multiplied B                pixels.SetValue(b, i + offset);                pixels.SetValue(g, i + offset + 1);                pixels.SetValue(r, i + offset + 2);                pixels.SetValue(a, i + offset + 3);            }        }    }    protected override Freezable CreateInstanceCore()    {        return new RgbaBitmapSource(rgbaBuffer, pixelWidth);    }    public override event EventHandler<DownloadProgressEventArgs> DownloadProgress;    public override event EventHandler DownloadCompleted;    public override event EventHandler<ExceptionEventArgs> DownloadFailed;    public override event EventHandler<ExceptionEventArgs> DecodeFailed;    public override double DpiX    {        get { return 96; }    }    public override double DpiY    {        get { return 96; }    }    public override PixelFormat Format    {        get { return PixelFormats.Pbgra32; }    }    public override int PixelWidth    {        get { return pixelWidth; }    }    public override int PixelHeight    {        get { return pixelHeight; }    }    public override double Width    {        get { return pixelWidth; }    }    public override double Height    {        get { return pixelHeight; }    }}

As pointed out in a comment, you might improve performance by implementing an unsafe copy operation, which could look like this:

unsafe public override void CopyPixels(    Int32Rect sourceRect, Array pixels, int stride, int offset){    fixed (byte* source = rgbaBuffer, destination = (byte[])pixels)    {        byte* dstPtr = destination + offset;        for (int y = sourceRect.Y; y < sourceRect.Y + sourceRect.Height; y++)        {            for (int x = sourceRect.X; x < sourceRect.X + sourceRect.Width; x++)            {                byte* srcPtr = source + stride * y + 4 * x;                byte a = *(srcPtr + 3);                *(dstPtr++) = (byte)(*(srcPtr + 2) * a / 256); // pre-multiplied B                *(dstPtr++) = (byte)(*(srcPtr + 1) * a / 256); // pre-multiplied G                *(dstPtr++) = (byte)(*srcPtr * a / 256); // pre-multiplied R                *(dstPtr++) = a;            }        }    }}


I can't see how you're going to get there without doing the swap. This example does the job but instead of altering the byte buffer directly, it just swaps the R and B values for each 32-bit word as it reads them into the bitmap's backbuffer. Since Windows is little-endian, the bit-shifting looks counter intuitive because a dereferenced RGBA word reads as ABGR. But, this is probably your best bet in terms of overall performance.

I just hard-coded the same 2px example you used in your post. I haven't tried to scale it. I just wanted to present a different approach to swapping the bytes. I hope this helps.

        byte[] buffer = new byte[] { 25, 166, 0, 255, 90, 0, 120, 255 };        WriteableBitmap bitmap = new WriteableBitmap(2, 1, 96, 96, PixelFormats.Bgra32, null);        int numPixels = buffer.Length / sizeof(uint);        bitmap.Lock();        unsafe {            fixed (byte* pSrcData = &buffer[0]) {                uint* pCurrent = (uint*)pSrcData;                uint* pBitmapData = (uint*)bitmap.BackBuffer;                for (int n = 0; n < numPixels; n++) {                    uint x = *(pCurrent++);                    // Swap R and B. Source is in format: 0xAABBGGRR                    *(pBitmapData + n) =                        (x & 0xFF000000) |                        (x & 0x00FF0000) >> 16 |                        (x & 0x0000FF00) |                        (x & 0x000000FF) << 16;                }            }        }        bitmap.AddDirtyRect(new Int32Rect(0, 0, 2, 1));        bitmap.Unlock();        image1.Source = bitmap;


You can either:

a. allocate a new buffer and copy the sub-pixels into it while swapping the channels (using unsafe code) and create a BitmapSource using the buffer. (Using an array as a source or Using an IntPtr as a source.)

or

b. Display the image as is (with the wrong channel order) and use an HLSL shader to swap the sub-pixels during rendering.