views:

366

answers:

4

I am writing an application that requires me to take a proprietary bitmap format (an MVTec Halcon HImage) and convert it into a System.Drawing.Bitmap in C#.

The only proprietary functions given to me to help me do this involve me writing to file, except for the use of a "get pointer" function.

This function is great, it gives me a pointer to the pixel data, the width, the height, and the type of the image.

My issue is that when I create my System.Drawing.Bitmap using the constructor:

new System.Drawing.Bitmap(width, height, stride, format, scan)

I need to specify a "stride" that is a multiple of 4. This may be a problem as I am unsure what size bitmap my function will be hit with. Supposing I end up with a bitmap that is 111x111 pixels, I have no way to run this function other than adding a bogus column to my image or subtracting 3 columns.

Is there a way I can sneak around this limitation?

+1  A: 

Because it's using int32 to store each pixel.

Sizeof(int32) = 4

But don't worry, when the image is saved from memory to file it will use the most efficient memory usage possible. Internally it uses 24 bits per pixel (8 bits red, 8 green and 8 blue) and leaves the last 8 bits redundant.

Dead account
A: 

As has been stated before by Jake you calculate the stride by finding the bytes per pixel (2 for 16 bit, 4 for 32 bit) and then multiplying it by the width. So if you have a width of 111 and a 32 bit image you would have 444 which is a multiple of 4.

However, let's say for a minute that you have a 24 bit image. 24 bit is equal to 3 bytes, so with a 111 pixel width you would have 333 as your stride. This is, obviously, not a multiple of 4. So you would want to round up to 336 (the next highest multiple of 4). Even though you have a bit of extra, this unused space is not significant enough to really make much of a difference in most applications.

Unfortunately, there is no way around this restriction (unless you always use 32 bit or 64 bit imagines, which are always multiples of 4.

Tim C
A: 

Remember stride is different from width. You can have an image that has 111 (8-bit) pixels per line, but each line is stored in memory 112 bytes.

This is done to make efficient use of memory and as @Ian said, it's storing the data in int32.

Matt Warren
+4  A: 

This goes back to early CPU designs. The fastest way to crunch through the bits of the bitmap is by reading them 32-bits at a time, starting at the start of a scan line. That works best when the first byte of the scan line is aligned on a 32-bit address boundary. In other words, an address that's a multiple of 4. On early CPUs, having that first byte mis-aligned would cost extra CPU cycles to read two 32-bit words from RAM and shuffle the bytes to create the 32-bit value. Ensuring each scan line starts at an aligned address (automatic if the stride is a multiple of 4) avoids that.

This isn't a real concern anymore on modern CPUs, now alignment to the cache line boundary is much more important. Nevertheless, the multiple of 4 requirement for stride stuck around for appcompat reasons.

Btw, you can easily calculate the stride from the format and width with this:

        int bitsPerPixel = ((int)format & 0xff00) >> 8;
        int bytesPerPixel = (width * bitsPerPixel + 7) / 8;
        int stride = 4 * ((bytesPerPixel + 3) / 4);
Hans Passant
This is the correct answer; plus one all day. Here's more on calculating `stride`: http://stackoverflow.com/questions/1983781/why-does-bitmapsource-create-throw-an-argumentexception/1983886#1983886.
Jason
So, I should most likely try to change my image format.Right now I'm using something that stores each pixel as a single byte.Having said that, I suppose I can't use that Bitmap constructor as there is no image type that just takes a single byte and doesn't rely on a wonky color map.
Gorchestopher H
That's correct, 8bpp requires a palette. GDI+ does not support them well, it will give you no end of hassle. You can sorta get it going by loading an existing 8bpp image to get started. You can steal its palette entries or LockBits it directly if it has the right size. Or synthesize one in a MemoryStream.
Hans Passant
Thank you all for your input.I ended up making my own palette, and for now I'm just going to force my image width to be a multiple of 4.
Gorchestopher H