如何将位图像素复制到其他位图,保留C#中的alpha透明度?

有些人可以重写以下函数来使用任何优化机制吗? 我很确定这不是继续进行逐像素复制的方法。

我读过有关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图像具有错误像素格式的原因。

没有运气,我尝试了以下选项:

  • 创建一个新图像并使用src中的Graphics.DrawImage绘制src图像。 没有保留alpha。
  • 使用Scan0表单src创建新图像。 工作正常,但GC处理src图像时出现问题(在另一篇文章中有解释);

这个解决方案是唯一真正有效的解决方案,但我知道这不是最优的。 我需要知道如何使用WinAPI或其他最佳机制。

非常感谢你!

假设源图像确实每像素有32位,这应该是使用不安全代码和指针的足够快的实现。 使用编组可以实现同样的目的,但如果我没记错的话,性能损失约为10%-20%。

使用本机方法很可能会更快,但这应该比SetPixelSetPixel数量级。

 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 = Linkq%20Image%20Processing%20sample&referringTitle = Home

当样本在源和目标之间转换相同的图像格式时,您也可以改变周围的事物。

请注意,我已经为这个代码计时,它甚至比大型图像的不安全代码快得多,因为它使用缓存的全行预读。