在我们谈论性能之前,让我们检查您的代码:
var originalColor = scrBitmap.GetPixel(i, j);
if (originalColor = Color.Black)
newBitmap.SetPixel(i, j, Color.Red);
这里有两个错误:
- 你不比较,
Color.Black
但你分配 Color.Black
给originalColor
。
- 你不处理透明度。
要检查透明度,您不应该比较Color
对象,而是比较 R、G、B 值,让我们更改为:
var originalColor = scrBitmap.GetPixel(i, j);
if (originalColor.R == 0 && originalColor.G == 0 && originalColor.B == 0)
newBitmap.SetPixel(i, j, Color.FromArgb(originalColor.A, Color.Red));
现在您会看到它可以工作,但是处理每个图像需要很长时间:GetPixel
并且SetPixel
速度很慢(主要是因为它们会检查和计算每次调用的所有内容)。直接处理位图数据要好得多。如果您事先知道图像格式(并且每个图像都是固定的),那么您可以使用更多代码更快地完成它:
static unsafe Bitmap ReplaceColor(Bitmap source,
Color toReplace,
Color replacement)
{
const int pixelSize = 4; // 32 bits per pixel
Bitmap target = new Bitmap(
source.Width,
source.Height,
PixelFormat.Format32bppArgb);
BitmapData sourceData = null, targetData = null;
try
{
sourceData = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
targetData = target.LockBits(
new Rectangle(0, 0, target.Width, target.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
for (int y = 0; y < source.Height; ++y)
{
byte* sourceRow = (byte*)sourceData.Scan0 + (y * sourceData.Stride);
byte* targetRow = (byte*)targetData.Scan0 + (y * targetData.Stride);
for (int x = 0; x < source.Width; ++x)
{
byte b = sourceRow[x * pixelSize + 0];
byte g = sourceRow[x * pixelSize + 1];
byte r = sourceRow[x * pixelSize + 2];
byte a = sourceRow[x * pixelSize + 3];
if (toReplace.R == r && toReplace.G == g && toReplace.B == b)
{
r = replacement.R;
g = replacement.G;
b = replacement.B;
}
targetRow[x * pixelSize + 0] = b;
targetRow[x * pixelSize + 1] = g;
targetRow[x * pixelSize + 2] = r;
targetRow[x * pixelSize + 3] = a;
}
}
}
finally
{
if (sourceData != null)
source.UnlockBits(sourceData);
if (targetData != null)
target.UnlockBits(targetData);
}
return target;
}
当然,这可以进一步优化,您可能需要处理不同的格式(请参阅此像素格式列表和有关其布局的这篇文章),但将其视为使用位图的起点。
为了完整起见,这是没有直接访问位图数据的等效颜色。请注意,这应该很少使用,因为它非常慢。
static Bitmap ReplaceColor(Bitmap source,
Color toReplace,
Color replacement)
{
var target = new Bitmap(source.Width, source.Height);
for (int x = 0; x < source.Width; ++x)
{
for (int y = 0; y < source.Height; ++y)
{
var color = source.GetPixel(x, y);
target.SetPixel(x, y, color == toReplace ? replacement : color);
}
}
return target;
}
另请注意,这考虑了 Alpha 通道(例如,50% 透明绿色与 30% 透明绿色的颜色不同)。要忽略 alpha,您可以使用以下内容:
if (color.R == toReplace.R && color.G == toReplace.G && color.B == toReplace.B)
最后,如果您知道要替换的像素很少,您可以创建原始图像的原始副本(Graphics.FromImage
用于创建上下文并在其中绘制source
位图),这样您SetPixel()
只有在有替换时才会调用。IMO 此处的任何优化都毫无用处:如果您需要性能,请使用第一个解决方案...