是否可以重写以下函数以使用任何优化机制?我很确定这不是逐像素复制的方法。
我已经阅读过有关 AlphaBlend 或 BitBlt 的内容,但我不习惯本机代码。
public static Bitmap GetAlphaBitmap(Bitmap srcBitmap)
{
Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb);
Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
try
{
for (int y = 0; y <= srcData.Height - 1; y++)
{
for (int x = 0; x <= srcData.Width - 1; x++)
{
Color pixelColor = Color.FromArgb(
Marshal.ReadInt32(srcData.Scan0, (srcData.Stride * y) + (4 * x)));
result.SetPixel(x, y, pixelColor);
}
}
}
finally
{
srcBitmap.UnlockBits(srcData);
}
return result;
}
重要提示:源图像的像素格式错误(Format32bppRgb),所以我需要调整Alpha通道。这是唯一对我有用的机制。
src 图像像素格式错误的原因解释为here。
我尝试了以下选项,但没有运气:
这个解决方案是唯一真正有效的解决方案,但我知道这不是最佳方案。我需要知道如何使用 WinAPI 或其他最佳机制来做到这一点。
非常感谢!
假设源图像实际上每像素有 32 位,这应该是使用不安全代码和指针的足够快的实现。使用编组可以实现相同的效果,但如果我没记错的话,性能损失约为 10%-20%。
使用本机方法很可能会更快,但这应该已经比
SetPixel
快几个数量级了。
public unsafe static Bitmap Clone32BPPBitmap(Bitmap srcBitmap)
{
Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb);
Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
BitmapData resData = result.LockBits(bmpBounds, ImageLockMode.WriteOnly, result.PixelFormat);
int* srcScan0 = (int*)srcData.Scan0;
int* resScan0 = (int*)resData.Scan0;
int numPixels = srcData.Stride / 4 * srcData.Height;
try
{
for (int p = 0; p < numPixels; p++)
{
resScan0[p] = srcScan0[p];
}
}
finally
{
srcBitmap.UnlockBits(srcData);
result.UnlockBits(resData);
}
return result;
}
这是使用编组的此方法的安全版本:
public static Bitmap Copy32BPPBitmapSafe(Bitmap srcBitmap)
{
Bitmap result = new Bitmap(srcBitmap.Width, srcBitmap.Height, PixelFormat.Format32bppArgb);
Rectangle bmpBounds = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(bmpBounds, ImageLockMode.ReadOnly, srcBitmap.PixelFormat);
BitmapData resData = result.LockBits(bmpBounds, ImageLockMode.WriteOnly, result.PixelFormat);
Int64 srcScan0 = srcData.Scan0.ToInt64();
Int64 resScan0 = resData.Scan0.ToInt64();
int srcStride = srcData.Stride;
int resStride = resData.Stride;
int rowLength = Math.Abs(srcData.Stride);
try
{
byte[] buffer = new byte[rowLength];
for (int y = 0; y < srcData.Height; y++)
{
Marshal.Copy(new IntPtr(srcScan0 + y * srcStride), buffer, 0, rowLength);
Marshal.Copy(buffer, 0, new IntPtr(resScan0 + y * resStride), rowLength);
}
}
finally
{
srcBitmap.UnlockBits(srcData);
result.UnlockBits(resData);
}
return result;
}
编辑:您的源图像具有负步幅,这意味着扫描线在内存中颠倒存储(仅在 y 轴上,行仍然从左到右)。这实际上意味着
.Scan0
返回位图最后一行的第一个像素。
因此我修改了代码以一次复制一行。
注意:我只修改了安全代码。不安全的代码仍然假设两个图像都取得了积极的进展!
尝试位图克隆方法。
我的 Codeblocks 库中的实用程序类 http://codeblocks.codeplex.com 允许您使用 LINQ 将源图像转换为任何其他图像。
请在此处查看此示例:http://codeblocks.codeplex.com/wikipage?title=Linq%20Image%20Processing%20sample&referringTitle=Home
虽然示例在源和目标之间转换相同的图像格式,但您也可以更改周围的内容。
请注意,我已经对这段代码进行了计时,它比大图像的不安全代码要快得多,因为它使用缓存的全行预读。