How can I copy the pixel data from a Bitmap with negative stride?

大憨熊 提交于 2020-01-19 14:16:20

问题


I was looking for the fastest way to convert a Bitmap to 8bpp. I found 2 ways:

1.

        public static System.Drawing.Image ConvertTo8bpp(Bitmap oldbmp)
    {
        using (var ms = new MemoryStream())
        {
            oldbmp.Save(ms, ImageFormat.Gif);
            ms.Position = 0;
            return System.Drawing.Image.FromStream(ms);
        }
    }

2. http://www.wischik.com/lu/programmer/1bpp.html

But: 1. Results in a very low quality result (bad pallet)

and 2 gives me a Bitmap with negative stride, when I try to lockbits and copy the data to a byte array I get an exception: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

        this.stride = bmpData.Stride;
        this.bytesPerPixel = GetBytesPerPixel(bmp.PixelFormat);
        int length = bmpData.Stride * bmp.Height;
        if (this.stride < 0)
            this.data = new byte[-length];
        else
            this.data = new byte[length];
        Marshal.Copy(bmpData.Scan0, data, 0, length);

        //Unlock the bitmap
        bmp.UnlockBits(bmpData);

How can I make 2 gives a positive stride? Or how can I copy data using lockbits of a negative stride??


回答1:


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.




回答2:


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




回答3:


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);
        }



回答4:


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




回答5:


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...).



来源:https://stackoverflow.com/questions/6835006/how-can-i-copy-the-pixel-data-from-a-bitmap-with-negative-stride

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!