views:

369

answers:

1

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?

+2  A: 

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.

Jason
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?
Mike Pateras
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.
Jason
Thank you. One more question. What does the tilde do on an integer like that?
Mike Pateras
@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.
Jason
Perfectly clear. Thanks!
Mike Pateras