I was looking for the fastest way to convert a Bitmap to 8bpp. I found 2 ways:
1.
public static System.Drawing.Image Conver
The problem here is that Scan0
points to the beginning of the first scan line, not the beginning of the first byte of data. In a bottom-up bitmap, the first scan line is Stride
bytes from the end of the bitmap data.
When you call Marshal.Copy
to copy the data from Scan0
, it tries to copy (Height*Stride)
bytes, starting from position ((Height-1)*Stride)
. Clearly, that's going to run off into the weeds.
If you just want to copy the bitmap data, you have to calculate the starting address with Scan0 - (Height-1)*Stride
. That will start you at the beginning of the bitmap data. You can pass that computed address to Marshal.Copy
.
If you want to copy the scan lines in order (i.e. top, next, next, ... bottom), then you have to copy a line at a time: copy Stride
bytes from Scan0
, then add Stride
(which is negative), copy that line, etc. Rick Brewster had the right answer there: https://stackoverflow.com/a/10360753/56778
Copy 1 row at a time, calculating the starting pointer for a row as ((byte*)scan0 + (y * stride))
. The code will be identical for either positive or negative stride.
I'm guessing the exception you're getting is due to
this.data = new byte[-length];
And then trying to copy data into a byte array of negative size (I don't see how that even compiles really...).
From the C# documentation on BitmapData: The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up
I don't know why there is something strange about the Bitmap created by the FromHbitmap
method, but I do know that you can fix it by using Bitmap bmpClone = (Bitmap)bmp.Clone();
and doing the LockBits on bmpClone.
Also, I found that if you use bmp.Clone()
, you cannot Dispose() of bmp until you have finished with the clone.
This also works and let's you dispose of the negative stride image sooner rather than later:
Bitmap bmp = null;
using (Bitmap bmpT = CopyToBpp(bmpO, 1))
{
bmp = new Bitmap(bmpT);
}