为什么BitmapSource.Create会抛出ArgumentException?

Mik*_*ras 7 c# bitmapsource stride

我试图通过使用Image和BitmapSource从WPF中显示原始数据创建的位图:

Int32[] data = new Int32[RenderHeight * RenderWidth];

for (Int32 i = 0; i < RenderHeight; i++)
{
    for (Int32 j = 0; j < RenderWidth; j++)
    {
        Int32 index = j + (i * RenderHeight);

        if (i + j % 2 == 0)
            data[index] = 0xFF0000;
        else
            data[index] = 0x00FF00;
    }
}

BitmapSource source = BitmapSource.Create(RenderWidth, RenderHeight, 96.0, 96.0, PixelFormats.Bgr32, null, data, 0);

RenderImage.Source = source;
Run Code Online (Sandbox Code Playgroud)

但是,对BitmapSource.Create的调用会抛出ArgumentException,并说"值不在预期范围内".这不是这样做的方法吗?我没有正确地打电话吗?

jas*_*son 38

你的步伐不正确.Stride是为位图的一条扫描线分配的字节数.因此,请使用以下内容:

int stride = ((RenderWidth * 32 + 31) & ~31) / 8;
Run Code Online (Sandbox Code Playgroud)

0使用stride上面定义的最后一个参数(当前)替换.

以下是对神秘步幅公式的解释:

事实:扫描线必须在32位边界上对齐(参考).

每条扫描线的字节数的天真公式为:

(width * bpp) / 8
Run Code Online (Sandbox Code Playgroud)

但这可能不会给我们在32位边界上对齐的位图,并且(width*bpp)甚至可能无法被8整除.

所以,我们要做的是强制我们的位图连续至少有32位(我们假设width > 0):

width * bpp + 31
Run Code Online (Sandbox Code Playgroud)

然后我们说我们不关心低位(位0--4),因为我们试图在32位边界上对齐:

(width * bpp + 31) & ~31
Run Code Online (Sandbox Code Playgroud)

然后除以8以返回字节:

((width * bpp + 31) & ~31) / 8
Run Code Online (Sandbox Code Playgroud)

填充可以通过计算

int padding = stride - (((width * bpp) + 7) / 8)
Run Code Online (Sandbox Code Playgroud)

天真的公式将是

stride - ((width * bpp) / 8)
Run Code Online (Sandbox Code Playgroud)

但是width * bpp可能不会在字节边界上对齐,并且当它没有时,这个公式会过度计算一个字节的填充.(想想使用1 bpp的1像素宽位图.步幅是4,天真公式会说填充是4但实际上是3.)所以我们添加一点来覆盖width * bpp不是字节的情况边界,然后我们得到上面给出的正确的公式.

  • 我花了一整天搞乱这个,你的帖子帮助我在30秒内修复了我的代码.非常感谢你!!!如果有人需要知道,`((宽*24 + 23)&〜23)/ 8;`适用于`PixelFormats.Rgb24;`. (3认同)
  • 抱歉,我应该提供详细信息。在您的情况下,您有`bpp = 32`,所以是的,公式减少到`RenderWidth * 4`。但也有一些奇怪的情况(便宜的 LCD 使用 18 bpp)以及扫描线必须在 32 位边界上对齐的事实。我在上面提供了一般公式和如何提出它的解释。希望能解惑。 (2认同)
  • @Mike Pateras:这是一个按位运算符.这意味着它会翻转整数的二进制表示中的位(因此"0"变为"1"而"1"变为"0").我说我们要忽略`width*bpp + 31`的最低五位(并隐含地保留其余部分).一种简单的方法是在这五位中加上一个"0",在其余位中加一个"1"; 这称为位掩码.如果我们用这个位掩码(`~31`)取逻辑和'width*bpp + 31`,我们就会屏蔽掉我们不关心的五个低位.如果不清楚,请告诉我. (2认同)