2014-01-29 74 views
6

我有一个C#字节数组,包含一个RGBA编码图像。什么是在WPF中展示此图片的最佳方式?显示存储器中的RGBA图像

一个选择是从字节数组创建一个BitmapSource并将其附加到Image控件。但是,创建BitmapSource需要RGBA32的PixelFormat,但在Windows中似乎不可用。

byte[] buffer = new byte[] { 25, 166, 0, 255, 90, 0, 120, 255 }; 
BitmapSource.Create(2, 1, 96d, 96d, PixelFormats.Bgra32, null, buffer, 4 * 2); 

我绝对不想在我的字节数组中交换像素。

+0

'PixelFormats.Pbgra32'仍然交换红色和蓝色像素,所以这是行不通的。 – Jippe

+0

对不起,我没有仔细阅读。你读过[这个问题](http://social.msdn.microsoft.com/Forums/en-US/5c6ad447-2027-4685-9ab2-62d418695a07/why-rgba32-in-pixelformats-is-not-supported) ? – Clemens

+1

我做了,看起来好像名字为'GUID_WICPixelFormat32bppRGBA'的格式原生支持。但是,我该如何利用它呢? – Jippe

回答

7

在深入研究WIC的细节之前,您可以考虑将字节交换封装在一个简单的自定义BitmapSource中,如下所示。它需要RGBA字节数组并交换覆盖的CopyPixels方法中的像素字节以生成PBGRA缓冲区。

你可以简单地通过提供RGBA缓冲区和位图宽度一样

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

这里是实现创建这个定制的BitmapSource的一个实例:

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

正如在评论中指出,您可以通过执行不安全的复制操作来提高性能,如下所示:

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; 
      } 
     } 
    } 
} 
+0

使用不安全的代码段将像素复制到目标缓冲区会更有效率,并且已经有在线示例。 –

+0

更好的是,在更通用的版本中,您不应该硬编码通道数或假定每个子像素都是单个字节。 –

+0

@DannyVarod当然,但这会在这里创建更多的代码。我的目的不是提供一个全功能的解决方案,而是要走的方向。问题是大约32bpp RGBA。 – Clemens

2

我不明白你将如何去那里不做交换。这个例子完成了这项工作,但不是直接修改字节缓冲区,而是将每个32位字的R和B值交换到位图的后台缓冲区中。由于Windows是小端,这种位移看起来不符合直觉,因为取消引用的RGBA字读取为ABGR。但是,就整体表现而言,这可能是您最好的选择。

我只是硬编码了您在文章中使用的相同的2px示例。我没有试图扩展它。我只是想提出一种不同的方法来交换字节。我希望这有帮助。

 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; 
相关问题