【问题标题】:Show RGBA image from memory显示内存中的 RGBA 图像
【发布时间】:2014-02-21 02:09:55
【问题描述】:

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

一种选择是从字节数组创建 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);

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

【问题讨论】:

  • PixelFormats.Pbgra32 仍然交换红色和蓝色像素,所以这不起作用。
  • 对不起,我没有仔细阅读。你读过this question吗?
  • 我做到了,似乎原生支持友好名称GUID_WICPixelFormat32bppRGBA 的格式。但是,我该如何利用它呢?
  • WIC 具有这种格式,它只是没有被 WPF 公开。从技术上讲,您可以通过使用反射编写结构的私有字段来初始化 PixelFormat。然而,这样做非常痛苦,它不是一个非常简单的结构。 guid 的 SecurityCriticalDataForSet 包装器是一个皮塔饼。
  • @Jippe,有人要进行位交换,无论是你、WIC 还是你的显卡都没有太大区别

标签: c# wpf image-processing


【解决方案1】:

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

您可以通过提供 RGBA 缓冲区和位图宽度来简单地创建此自定义 BitmapSource 的实例

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

这里是实现:

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

【讨论】:

  • 使用不安全的代码段将像素复制到目标缓冲区会更有效,网上已经有这样的例子。
  • 更好的是,在更通用的版本中,您不应该硬编码通道数或假设每个子像素是单个字节。
  • @DannyVarod 当然,但这会在这里创建更多代码。我的目的不是提供功能齐全的解决方案,而只是提供前进的方向。而问题正是关于 32bpp RGBA。
  • @DannyVarod 你不明白。该示例是关于如何将整个事物封装在专门的 BitmapSource 中。请停止抱怨其他人的回答。改进你自己的。
  • @Clemens +1 评论“请继续,州长”(:
【解决方案2】:

您可以:

一个。分配一个新的缓冲区并将子像素复制到其中,同时交换通道 (using unsafe code) 并使用该缓冲区创建一个 BitmapSource。 (Using an array as a sourceUsing an IntPtr as a source。)

b.按原样显示图像(使用错误的通道顺序)并在渲染期间使用HLSL shader 交换子像素。

【讨论】:

    【解决方案3】:

    如果不进行交换,我看不出你将如何到达那里。这个例子完成了这项工作,但不是直接更改字节缓冲区,它只是在将每个 32 位字的 R 和 B 值读入位图的后台缓冲区时交换它们。由于 Windows 是 little-endian,因此位移看起来不直观,因为取消引用的 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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-04
      • 2017-05-14
      相关资源
      最近更新 更多