如何从具有负步幅的位图复制像素数据?

时间:2011-07-26 18:51:46

标签: c# bitmap stride

我一直在寻找将Bitmap转换为8bpp的最快方法。 我找到了两种方法:

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

但: 1。导致质量非常低(托盘坏)

2 给我一个负步幅的位图,当我尝试锁定位并将数据复制到字节数组时,我得到一个异常:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

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

如何让 2 给出积极的进步?或者我如何使用负步幅的锁定位来复制数据?

5 个答案:

答案 0 :(得分:8)

这里的问题是Scan0指向第一条扫描线的开头,而不是第一个数据字节的开头。在自底向上的位图中,第一条扫描线是位图数据末尾的Stride字节。

当您致电Marshal.Copy以复制Scan0的数据时,它会尝试从位置(Height*Stride)开始复制((Height-1)*Stride)个字节。显然,这将会流入杂草。

如果您只想复制位图数据,则必须使用Scan0 - (Height-1)*Stride计算起始地址。这将从位图数据的开头开始。您可以将该计算地址传递给Marshal.Copy

如果要按顺序复制扫描线(即顶部,下一个,下一个,......底部),则必须一次复制一行:从{{1}复制Stride个字节},然后添加Scan0(这是否定的),复制该行等.Rick Brewster在那里有正确答案:https://stackoverflow.com/a/10360753/56778

答案 1 :(得分:6)

一次复制1行,将行的起始指针计算为((byte*)scan0 + (y * stride))。对于正面或负面的步幅,代码都是相同的。

答案 2 :(得分:4)

我不知道为什么FromHbitmap方法创建的Bitmap有些奇怪,但我知道您可以使用Bitmap bmpClone = (Bitmap)bmp.Clone();并在bmpClone上执行LockBits来修复它。 / p>

此外,我发现如果您使用bmp.Clone(),则在完成克隆之前不能对bmp进行Dispose()。

这也有效,让你尽快处理消极的步幅图像:

        Bitmap bmp = null;
        using (Bitmap bmpT = CopyToBpp(bmpO, 1))
        {
            bmp = new Bitmap(bmpT);
        }

答案 3 :(得分:1)

来自BitmapData上的C#文档:步幅是单行像素(扫描线)的宽度,四舍五入到四字节边界。如果步幅为正,则位图为自上而下。如果步幅为负,则位图为自下而上

答案 4 :(得分:-2)

我猜你得到的例外是由于

this.data = new byte[-length];

然后尝试将数据复制到负大小的字节数组中(我看不出它甚至是如何编译的......)。