Why does BitmapSource.Create throw an ArgumentException?
Asked Answered
V

1

8

I'm trying to get an bitmap created from raw data to show in WPF, by using an Image and a BitmapSource:

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;

However the call to BitmapSource.Create throws an ArgumentException, saying "Value does not fall within the expected range". Is this not the way to do this? Am I not making that call properly?

Valor answered 31/12, 2009 at 3:19 Comment(0)
M
39

Your stride is incorrect. Stride is the number of bytes allocated for one scanline of the bitmap. Thus, use the following:

int stride = ((RenderWidth * 32 + 31) & ~31) / 8;

and replace the last parameter (currently 0) with stride as defined above.

Here is an explanation for the mysterious stride formula:

Fact: Scanlines must be aligned on 32-bit boundaries (reference).

The naive formula for the number of bytes per scanline would be:

(width * bpp) / 8

But this might not give us a bitmap aligned on a 32-bit boundary and (width * bpp) might not even have been divisible by 8.

So, what we do is we force our bitmap to have at least 32 bits in a row (we assume that width > 0):

width * bpp + 31

and then we say that we don't care about the low-order bits (bits 0--4) because we are trying to align on 32-bit boundaries:

(width * bpp + 31) & ~31

and then divide by 8 to get back to bytes:

((width * bpp + 31) & ~31) / 8

The padding can be computed by

int padding = stride - (((width * bpp) + 7) / 8)

The naive formula would be

stride - ((width * bpp) / 8)

But width * bpp might not align on a byte boundary and when it doesn't this formula would over count the padding by a byte. (Think of a 1 pixel wide bitmap using 1 bpp. The stride is 4 and the naive formula would say that the padding is 4 but in reality it is 3.) So we add a little bit to cover the case that width * bpp is not a byte boundary and then we get the correct formula given above.

Musket answered 31/12, 2009 at 4:10 Comment(7)
Thank you, but how on earth did you come up with that expression? Why isn't it simply RenderWidth * 4? Isn't that the number of bytes for one line?Valor
Sorry, I should have provided details. In your case you have bpp = 32 so yes the formula reduces to RenderWidth * 4. But there are odd cases (cheap LCDs use 18 bpp) and the fact that scanlines have to be aligned on 32-bit boundaries. I provided the general formula and an explanation of how to come up with it above. Hope it's elucidating.Musket
Thank you. One more question. What does the tilde do on an integer like that?Valor
@Mike Pateras: It is the bitwise not operator. That means that it flips the bits in the binary representation of the integer (so 0 becomes 1 and 1 becomes 0). I said that we want to ignore the lowest five bits of width * bpp + 31 (and implicitly keep the rest). An easy way to do that is to make a number that has 0 in those five bits and a 1 in the rest of the bits; this is called a bitmask. If we take the logical and width * bpp + 31 with this bitmask (~31) we have masked away the five low-order bits that we don't care about. Let me know if that isn't clear.Musket
I spent all day messing with this and your post helped me fix my code in 30 seconds. Thank you VERY much!!! and if anyone needs to know, ((width * 24 + 23) & ~23) / 8; works with PixelFormats.Rgb24;.Exalt
@Jason could you please tell me how to calculate stride for PixelFormats.Rgb24 i tried these three methods, but none seems to working good //int stride = 4 * ((width * bytesPerPixel + 3) / 4);//one way //int stride = ((width * 32 + 31) & ~31) / 8;// another way //int stride = ((width * 24 + 23) & ~23) / 8;// yet another wayPhalangeal
@Phalangeal Minimum stride to contain data of a certain bpp is ((bpp * width) + 7) / 8;, and with that as GetMinStride function, the full 4-byte-aligned stride would be ((GetMinStride(width, bpp) + 3) / 4) * 4;Soniasonic

© 2022 - 2024 — McMap. All rights reserved.