如果你因为标题而来到这里,你可能应该跳过这个问题然后直接回答问题。事实证明我的代码中有一个简单的错误。
我正在尝试专门处理索引图像,因为我项目的主要部分涉及调色板交换。我尝试了以下几行代码作为更大过程的一部分:
Bitmap raw = ((Bitmap)i).Clone(region, PixelFormat.Format8bppIndexed);
byte transparent = (byte)(Array.FindIndex(raw.Palette.Entries, x => x.A < 128));
// Scan the bitmap for the first opaque pixel on each side
BitmapData bd = raw.LockBits(region, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
基本上,我想裁剪输入的指定region
,然后对图像的那一部分进行低级操作。
转换成功(我可以验证它此时是一个索引位图,但它存储在一个控件中,因此丢失了类型信息)。克隆调用似乎成功了。调试显示PixelFormat
的{{1}}确实是raw
,PixelFormat.Format8bppIndexed
的{{1}}也是(Bitmap)i
。透明调色板索引的计算工作正常。但是raw.LockBits
失败了,抱怨参数无效。
更奇怪的是,raw.LockBits
如果我删除.Clone
调用会成功(但其他代码会做错事,因为源没有被裁剪)。
Per this answer,带有参数的Clone
似乎可能会对原始数据产生某种视图,而不是实际复制它,因此{{}}的相应数据不存在1}}锁定 - 它不一定是连续的。但是如何明确复制该区域?通常的LockBits
方法不会直接起作用,因为所需的目标图像已编入索引,因此我无法获取图形。
我是否真的必须以RGB模式完成所有工作然后转换回来?肯定有某种东西更优雅?
答案 0 :(得分:2)
根据@Peter Duniho的评论和一些进一步的测试和调试,我确定实际上我只是在代码中有一个错误。结论: .Clone()方法工作得很好,这就是所希望的:
Bitmap cropped = original.Clone(region, PixelFormat.Format8bppIndexed);
或者更一般地说,
Bitmap cropped = original.Clone(region, original.PixelFormat);
region
是期望的裁剪Rectangle
。我的代码的问题在于,在后续LockBits
代码中region
不正确 - 要锁定整个裁剪图像,我想要与Rectangle
相同的宽度和高度,但是(对于X和Y,0,0)。
经过一些更多测试后,我开发了以下有关可能遇到的错误的注释:
如果您尝试使用的region
不适合Bitmap
的矩形 - 即X
或Y
为负数,或X + Width
超过Bitmap
Width
,或Y + Height
超过Bitmap
的高度 - 它会< strong> not 被剪切到原始Bitmap
的边界,而是发生异常。 负宽度和高度值也无效。
如果region
Bitmap.LockBits
的宽度为零,高度为零,或者不适合,则会引发System.ArgumentException
。消息只是&#34;参数无效。&#34; 无论哪种方式,所以您可以自行决定出现了什么问题。
如果region
Bitmap.Clone
的宽度为零或零高度,则再次获得System.ArgumentException
,这次会显示实际信息性错误消息。但是,如果它不合适,您将获得 System.OutOfMemoryException
。这有点有趣,因为0的宽度和高度值显然得到明确检查,但负值不是。
所以也许我们应该用一点功能来防范这种情况,例如:
// Create a cropped `region` of the `original` Bitmap as a new bitmap,
// preserving the original pixel format. If negative Width or Height
// are provided for the clip region and `flipNegative` is set, the result
// is flipped accordingly.
public Bitmap crop(Bitmap original, Rectangle region, bool flipNegative) {
Rectangle bounds = new Rectangle(new Point(0, 0), original.Size);
if (region.Width == 0 || region.Height == 0) { return null; }
// Normalize width and height parameters,
// and track whether we might need to flip.
bool flipHorizontal = region.Width < 0;
bool flipVertical = region.Height < 0;
if (flipHorizontal)
{
region.X += region.Width;
region.Width = -region.Width;
}
if (flipVertical)
{
region.Y += region.Height;
region.Height = -region.Height;
}
// Ensure we have a valid clipping rectangle, and make the GDI call.
if (!region.IntersectsWith(bounds)) { return null; }
region.Intersect(bounds);
Bitmap result = original.Clone(region, original.PixelFormat);
// Flip the result as appropriate.
if (flipHorizontal && flipNegative)
{
result.RotateFlip(RotateFlipType.RotateNoneFlipX);
}
if (flipVertical && flipNegative)
{
result.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
return result;
}
(我提供了flipNegative
参数,因为你可能想要两种语义。)
至于LockBits
,这里没有必要,虽然它对更低级别的操作仍然有用。