我一直在寻找将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);
}
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
我怎样才能让2给出积极的进步?或者我如何使用负步幅的锁定位来复制数据?
这里的问题是Scan0指向第一条扫描线的开头,而不是第一个数据字节的开头.在自下而上的位图中,第一扫描线是Stride位图数据末尾的字节.
当您调用Marshal.Copy复制数据时Scan0,它会尝试(Height*Stride)从位置开始复制字节((Height-1)*Stride).显然,这将会流入杂草.
如果您只想复制位图数据,则必须使用计算起始地址Scan0 - (Height-1)*Stride.这将从位图数据的开头开始.您可以将该计算地址传递给Marshal.Copy.
如果要按顺序复制扫描线(即顶部,下一个,下一个,......底部),则必须一次复制一行:Stride从中复制字节Scan0,然后添加Stride(这是负数),复制该行Rick Brewster在那里有正确答案:https://stackoverflow.com/a/10360753/56778
我不知道为什么该FromHbitmap方法创建的 Bitmap 有一些奇怪的地方,但我知道您可以通过Bitmap bmpClone = (Bitmap)bmp.Clone();在 bmpClone 上使用和执行 LockBits来修复它。
另外,我发现如果您使用bmp.Clone(),则在完成克隆之前不能对 bmp 进行 Dispose()。
这也有效,让您尽早处理负步幅图像:
Bitmap bmp = null;
using (Bitmap bmpT = CopyToBpp(bmpO, 1))
{
bmp = new Bitmap(bmpT);
}
Run Code Online (Sandbox Code Playgroud)