c# – 将掩码应用于位图并在运行时生成合成图像
背景
我正在尝试使用c#4.0和WPF从头开始创建2D tile引擎.我并不是想在这个星球上构建下一个最伟大的2D游戏,而是试图学习如何使用.NET来操纵图像和图形. 目标 我正在尝试将掩码应用于位图.我的位图是彩色的,我的掩码是单色位图.在哪里出现白色,我试图用透明颜色替换相应位置的原始位图中的颜色. 我的目标是能够将一组图像存储在内存中,这些图像已经在运行时被屏蔽,这样我就可以在图层中构建它们的复合 – 即背景优先,地图上的项目,最后是根据需要,玩家头像. 到目前为止我看过的…… 我已经在这一段时间了很长一段时间,因此我在SO上发帖 – 以下详细介绍了迄今为止我所看到的内容: 我看过这篇文章Alpha masking in c# System.Drawing?,它展示了如何使用不安全的方法来使用指针算法来操作图像. 另外,这篇文章Create Image Mask展示了如何使用SetPixel / GetPixel来交换颜色. 各种MSDN页面和其他包含该主题的特别博客. 我试过的…… 我试过了不安全的指针算术方法: private static Bitmap DoApplyMask(Bitmap input,Bitmap mask) { Bitmap output = new Bitmap(input.Width,input.Height,PixelFormat.Format32bppArgb); output.MakeTransparent(); var rect = new Rectangle(0,input.Width,input.Height); var bitsMask = mask.LockBits(rect,ImageLockMode.ReadOnly,PixelFormat.Format32bppArgb); var bitsInput = input.LockBits(rect,PixelFormat.Format32bppArgb); var bitsOutput = output.LockBits(rect,ImageLockMode.WriteOnly,PixelFormat.Format32bppArgb); unsafe { for (int y = 0; y < input.Height; y++) { byte* ptrMask = (byte*)bitsMask.Scan0 + y * bitsMask.Stride; byte* ptrInput = (byte*)bitsInput.Scan0 + y * bitsInput.Stride; byte* ptrOutput = (byte*)bitsOutput.Scan0 + y * bitsOutput.Stride; for (int x = 0; x < input.Width; x++) { //I think this is right - if the blue channel is 0 than all of them are which is white? if (ptrMask[4*x] == 0) { ptrOutput[4*x] = ptrInput[4*x]; // blue ptrOutput[4*x + 1] = ptrInput[4*x + 1]; // green ptrOutput[4*x + 2] = ptrInput[4*x + 2]; // red } else ptrOutput[4 * x + 3] =255; // alpha } } } mask.UnlockBits(bitsMask); input.UnlockBits(bitsInput); output.UnlockBits(bitsOutput); return output; } 还尝试了另一种方法的变体,看起来有点像这样(对不起,我似乎已经将代码分类了 – 这是真正的代码/部分伪代码 – 只是为了了解我正在尝试的方法……) { Bitmap input... Bitmap mask... Bitmap output = new Bitmap(input.Width,PixelFormat.Format32bppArgb); output.MakeTransparent(); for (int x = 0; x < output.Width; x++) { for (int y = 0; y < output.Height; y++) { Color pixel = mask.GetPixel(x,y); //Again - not sure - my thought is if any channel has colour then it isn't white - so make it transparent. if (pixel.B > 0) output.SetPixel(x,y,Color.Transparent); else { pixel = input.GetPixel(x,y); output.SetPixel(x,Color.FromArgb(pixel.A,pixel.R,pixel.G,pixel.B)); } } } 我的实际问题…… 所以上面的方法产生黑色背景上的彩色图像或白色背景上的彩色图像(据我所知!)当我尝试使用如下代码合成图像时: public Bitmap MakeCompositeBitmap(params string[] names) { Bitmap output = new Bitmap(32,32,PixelFormat.Format32bppArgb); output.MakeTransparent(); foreach (string name in names) { //GetImage() returns an image which I have previously masked and cached. Bitmap layer = GetImage(name); for (int x = 0; x < output.Width; x++) { for (int y = 0; y < output.Height; y++) { Color pixel = layer.GetPixel(x,y); if (pixel.A > 0) output.SetPixel(x,Color.Transparent); else output.SetPixel(x,pixel.B)); } } } return output; } 我正在调用这个代码,其中包含我想要在运行时合成的各种图层的名称,首先是背景,然后是前景,以便为我提供可以在我的UI上显示的图像. 所以 – 我必须再次重新迭代,我对此完全是新的,并且知道可能会有更快的方式来做到这一点 – 但它似乎是一个如此简单的问题而我只是想深究它! 我正在使用WPF,c#4.0,VS2013,在一个Image元素中显示图像,该元素由一个listviewItem托管,托管在ListView中,托管在一个网格中,最后由一个窗口托管(如果这与任何相关性……) 总结一下我的方法.. 现在我把头发拉了出来,因为我已经有一段时间了,我只看到了前景图片!我可以看到有关该主题的大量信息,但没有必要的经验来应用该信息来创建我的问题的解决方案. 编辑:我的解决方案(感谢thumbmunkeys指出我的逻辑中的错误: 修复了DoApplyMask方法 private static Bitmap DoApplyMask(Bitmap input,input.Height); var bitsMask = mask.LockBits(rect,PixelFormat.Format32bppArgb); unsafe { for (int y = 0; y < input.Height; y++) { byte* ptrMask = (byte*)bitsMask.Scan0 + y * bitsMask.Stride; byte* ptrInput = (byte*)bitsInput.Scan0 + y * bitsInput.Stride; byte* ptrOutput = (byte*)bitsOutput.Scan0 + y * bitsOutput.Stride; for (int x = 0; x < input.Width; x++) { //I think this is right - if the blue channel is 0 than all of them are (monochrome mask) which makes the mask black if (ptrMask[4 * x] == 0) { ptrOutput[4 * x] = ptrInput[4 * x]; // blue ptrOutput[4 * x + 1] = ptrInput[4 * x + 1]; // green ptrOutput[4 * x + 2] = ptrInput[4 * x + 2]; // red //Ensure opaque ptrOutput[4*x + 3] = 255; } else { ptrOutput[4 * x] = 0; // blue ptrOutput[4 * x + 1] = 0; // green ptrOutput[4 * x + 2] = 0; // red //Ensure Transparent ptrOutput[4 * x + 3] = 0; // alpha } } } } mask.UnlockBits(bitsMask); input.UnlockBits(bitsInput); output.UnlockBits(bitsOutput); return output; } 修复了MakeCompositeBitmap()方法 public Bitmap MakeCompositeBitmap(params string[] names) { Bitmap output = new Bitmap(32,PixelFormat.Format32bppArgb); output.MakeTransparent(); foreach (string name in names) { Bitmap layer = GetImage(name); for (int x = 0; x < output.Width; x++) { for (int y = 0; y < output.Height; y++) { Color pixel = layer.GetPixel(x,y); if (pixel.A > 0) // 0 means transparent,> 0 means opaque output.SetPixel(x,pixel.B)); } } } return output; } 解决方法
第一种方法DoApplyMask有以下问题:
> if(ptrMask [4 * x] == 0)表示掩码是黑色的,在你的情况下是不透明的,所以你必须将alpha设置为0xFF(它在你的代码中是相反的方式) 对于您的混合代码,您必须执行以下操作: >从背景层开始并将其渲染为输出 >从输出中读取像素 总是最后一层的原因是你用最顶层颜色或透明颜色覆盖输出中的任何内容.您应该根据图层的Alpha值混合输出和图层颜色,而不是编写透明颜色: for (int y = 0; y < output.Height; y++) { Color pixel = layer.GetPixel(x,y); Color oPixel = output.GetPixel(x,y); byte a = pixel.A; byte ai = 0xFF - pixel.A; output.SetPixel(x,Color.FromArgb(0xFF,(pixel.R *a + oPixel.R * ai)/0xFF,(pixel.G *a + oPixel.G * ai)/0xFF,(pixel.B *a + oPixel.B * ai)/0xFF); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |