是否可以通过代码(甚至使用模板)修改
WPF中图像的颜色?
假设我有一个我需要应用于图块的图像 – 默认情况下将具有白色前景色和透明背景.类似下面的PNG(它在某处!):
我不想添加不同的图像 – 使用不同的颜色,而只是想操纵白色 – 而是将其改为黑色.
如果可以做到,有人可以给我一些关于我需要做什么/研究的指示.
解决方法
一种方法是使用BitmapDecoder类来检索原始像素数据.然后,您可以修改像素,并从修改后的像素数据构建新的WriteableBitmap:
// Copy pixel colour values from existing image. // (This loads them from an embedded resource. BitmapDecoder can work with any Stream,though.) StreamResourceInfo x = Application.GetResourceStream(new Uri(BaseUriHelper.GetBaseUri(this),"Image.png")); BitmapDecoder dec = BitmapDecoder.Create(x.Stream,BitmapCreateOptions.None,BitmapCacheOption.Default); BitmapFrame image = dec.Frames[0]; byte[] pixels = new byte[image.PixelWidth * image.PixelHeight * 4]; image.CopyPixels(pixels,image.PixelWidth*4,0); // Modify the white pixels for (int i = 0; i < pixels.Length/4; ++i) { byte b = pixels[i * 4]; byte g = pixels[i * 4 + 1]; byte r = pixels[i * 4 + 2]; byte a = pixels[i * 4 + 3]; if (r == 255 && g == 255 && b == 255 && a == 255) { // Change it to red. g = 0; b = 0; pixels[i * 4 + 1] = g; pixels[i * 4] = b; } } // Write the modified pixels into a new bitmap and use that as the source of an Image var bmp = new WriteableBitmap(image.PixelWidth,image.PixelHeight,image.DpiX,image.DpiY,PixelFormats.Pbgra32,null); bmp.WritePixels(new Int32Rect(0,image.PixelWidth,image.PixelHeight),pixels,0); img.Source = bmp;
这是一种时尚的方式,但有一个问题.如果我在深色背景上显示结果,结果如下:
如你所见,它有一种白色边框.这里发生的是你的白色十字架有抗锯齿边缘,这意味着边缘周围的像素实际上是半透明的灰色阴影.
我们可以在像素修改循环中使用稍微复杂的技术处理它:
if ((r == 255 && g == 255 && b == 255 && a == 255) || (a != 0 && a != 255 && r == g && g == b && r != 0)) { // Change it to red. g = 0; b = 0; pixels[i * 4 + 1] = g; pixels[i * 4] = b; }
以下是黑色背景上的外观:
如你所见,这看起来是正确的. (好吧,你想黑色不是红色,但基本方法对于任何目标颜色都是一样的.)
编辑2015/1/21正如ar_j在评论中指出的那样,Prgba格式需要预乘.对于我给出的示例,实际上可以安全地忽略它,但如果您以任何方式修改颜色通道而不是将它们设置为0,则需要将每个值多个(a / 255).例如,aj_j表示G通道:像素[i * 4 1] =(字节)(g * a / 255);由于g在我的代码中为零,因此没有区别,但对于非主要颜色,您需要这样做.
这是在渐变填充背景上,只是为了表明透明度正在起作用:
你也可以写出修改后的版本:
var enc = new PngBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bmp)); using (Stream pngStream = File.OpenWrite(@"c:\temp\modified.png")) { enc.Save(pngStream); }
这是结果:
您可以看到红叉,它将位于StackOverflow正在使用的任何背景颜色之上. (白色,正如我写的那样,但也许有一天他们会重新设计.)
这是否适用于你想要使用的图像更难以确定,因为它取决于你对“白色”的定义 – 取决于你的图像是如何产生的,你可能会发现事情是如此微小的灰白色(特别是在边缘附近),您可能需要进一步调整.但基本方法应该没问题.