[英]Merging a color into a bitmap using c# (universal windows application)
I have a .png resource that includes areas that need to be "colored" as described in the table below (these are my own definitions, if there are better terms that I should use feel free to educate me). 我有一个.png资源,其中包括需要按下表所述进行“着色”的区域(这些是我自己的定义,如果有更好的用语,我可以随时使用它来教育我)。 I need to "color" this image based on a ColorBrush (eg BGRA=#6572D8FF) such that ...
我需要基于ColorBrush(例如BGRA =#6572D8FF)为该图像“着色”,以便...
// Input Output
// Transparent BGRA=#FFFFFF00 --> #FFFFFF00
// AlphaOnly BGRA=#00000000 --> #6572D800 // Colored area
// Alpha-Shaded BGRA=#000000aa --> #6572D8aa // Colored area
// Solid colors BGRA=#bbggrrFF --> #rrggbbFF
The resultant image needs to be displayed using the following XAML ... 需要使用以下XAML显示生成的图像...
<Viewbox Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Grid Height="100" Width="100">
<Image x:Name="currentImage" />
</Grid>
</Viewbox>
Placing the control onto a red background and specifying a blue ColorBrush, I was hoping to get the bottom image (which I created by simply putting a blue ellipse behind the original image) 我希望将控件放置在红色背景上并指定蓝色ColorBrush,希望获得底部图像(我通过在原始图像后面简单地放置一个蓝色椭圆来创建该图像)
Unfortunately I get the top image. 不幸的是我得到了最高的形象。 My transparency is lost and the alpha shading I was hoping to achieve is wrong.
我的透明度丢失了,我希望达到的Alpha阴影是错误的。 Any help (including you "idiot", you should do this like this) would be greatly appreciated.
任何帮助(包括您的“白痴”,您都应该这样)将不胜感激。
public sealed class MyImage : Control {
public MyImage() {
this.DefaultStyleKey = typeof(MyImage);
Loaded += MyImage_Loaded;
}
private async void MyImage_Loaded(object sender, RoutedEventArgs e) {
((Image)this.GetTemplateChild("currentImage")).Source = await ColorImage();
}
private async Task<WriteableBitmap> ColorImage() {
// Get the image as a byte array
StorageFile fileImage = await StorageFile.GetFileFromApplicationUriAsync(new Uri(BaseImageUri, UriKind.Absolute));
ImageProperties propsImage = await fileImage.Properties.GetImagePropertiesAsync();
int height = (int)propsImage.Height;
int width = (int)propsImage.Width;
byte[] baseImagePixels = await ReadPixels(fileImage);
// Modify the mask by adding the accent color
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
byte B = baseImagePixels[4 * (y * height + x) + 0];
byte G = baseImagePixels[4 * (y * height + x) + 1];
byte R = baseImagePixels[4 * (y * height + x) + 2];
byte A = baseImagePixels[4 * (y * height + x) + 3];
if (R == 0x00 && G == 0x00 && B == 0x00 && A != 0xFF) {
baseImagePixels[4 * (y * height + x) + 0] = ColorBrush.Color.B;
baseImagePixels[4 * (y * height + x) + 1] = ColorBrush.Color.G;
baseImagePixels[4 * (y * height + x) + 2] = ColorBrush.Color.R;
}
}
}
WriteableBitmap coloredImage = new WriteableBitmap((int)propsImage.Width, (int)propsImage.Height);
using (Stream stream = coloredImage.PixelBuffer.AsStream()) {
await stream.WriteAsync(baseImagePixels, 0, baseImagePixels.Length);
}
return coloredImage;
}
private async Task<byte[]> ReadPixels(StorageFile file) {
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(await file.OpenAsync(FileAccessMode.Read));
BitmapTransform transform = new BitmapTransform();
PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
new BitmapTransform(),
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.DoNotColorManage);
return pixelData.DetachPixelData();
}
public String BaseImageUri {
get { return (String)GetValue(BaseImageUriProperty); }
set {
if (value.StartsWith("ms-appx:///")) {
SetValue(BaseImageUriProperty, value);
} else {
SetValue(BaseImageUriProperty, "ms-appx:///" + value);
}
}
}
public static readonly DependencyProperty BaseImageUriProperty =
DependencyProperty.Register("BaseImageUri", typeof(String), typeof(MyImage), new PropertyMetadata(0));
public SolidColorBrush ColorBrush {
get { return (SolidColorBrush)GetValue(ColorBrushProperty); }
set { SetValue(ColorBrushProperty, value); }
}
public static readonly DependencyProperty ColorBrushProperty =
DependencyProperty.Register("ColorBrush", typeof(SolidColorBrush), typeof(MyImage), new PropertyMetadata(0));
}
Well I found a solution. 好吧,我找到了解决方案。 I have no idea why it works though ... would love to be educated on that if somebody knows why.
我不知道为什么会这样……如果有人知道为什么会很乐意对此进行教育。 I added a second change to the for loops.
我在for循环中添加了第二个更改。
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixelIndex = 4 * (y * width + x);
byte B = baseImagePixels[pixelIndex];
byte G = baseImagePixels[pixelIndex + 1];
byte R = baseImagePixels[pixelIndex + 2];
byte A = baseImagePixels[pixelIndex + 3];
if (!(B == 0xFF && G == 0xFF && R == 0xFF && A == 0x00)) {
baseImagePixels[pixelIndex] = colorBrush.Color.B;
baseImagePixels[pixelIndex + 1] = colorBrush.Color.G;
baseImagePixels[pixelIndex + 2] = colorBrush.Color.R;
baseImagePixels[pixelIndex + 3] = colorBrush.Color.A;
} else if (B == 0xFF && G == 0xFF && R == 0xFF && A == 0x00) {
baseImagePixels[pixelIndex] = 0x00;
baseImagePixels[pixelIndex + 1] = 0x00;
baseImagePixels[pixelIndex + 2] = 0x00;
baseImagePixels[pixelIndex + 3] = 0x00;
}
}
}
This looks like a premultiplied alpha issue to me, but that's just a wild guess. 在我看来,这似乎是一个预乘的alpha问题,但这只是一个疯狂的猜测。
Some graphic libraries require that pixels with alpha < 1.0 store their RGB components as: 一些图形库要求alpha <1.0的像素将其RGB分量存储为:
R' := R x A
G' := G x A
B' := B x A
A' := A
Hence RGB → 0xFFFFFF
with zero alpha would be invalid. 因此,RGB→
0xFFFFFF
alpha值为零将无效。
Blending algorithms which expect to be fed with premultiplied alpha pixels produce unexpected results when their RGB values exceed their alpha . 当其RGB值超过其alpha值时,预期将与预乘alpha像素配合的混合算法会产生意外结果。
So fully transparent pixels are always encoded as 0x000000
with 0 alpha in a premultiplied alpha case. 因此,在预乘alpha情况下,始终将全透明像素始终编码为
0x000000
和0 alpha。
Have a look at the Wikipedia article on Alpha compositing : 看一下有关Alpha合成的Wikipedia文章:
Assuming that the pixel color is expressed using straight (non-premultiplied) RGBA tuples, a pixel value of (0.0, 0.5, 0.0, 0.5) implies a pixel that has 50% of the maximum green intensity and 50% opacity.
假设像素颜色是使用直的(未预乘)RGBA元组表示的,则像素值(0.0,0.5,0.0,0.5)表示最大绿色强度为50%,不透明度为50%的像素。 If the color were fully green, its RGBA would be (0, 1, 0, 0.5).
如果颜色是完全绿色,则其RGBA为(0,1,0,0.5)。
However, if this pixel uses premultiplied alpha, all of the RGB values (0, 1, 0) are multiplied by 0.5 and then the alpha is appended to the end to yield (0, 0.5, 0, 0.5).
但是,如果此像素使用预乘alpha,则将所有RGB值(0,1,0)乘以0.5,然后将alpha附加到末尾以产生(0,0.5,0,0.5)。 In this case, the 0.5 value for the G channel actually indicates 100% green intensity (with 50% opacity).
在这种情况下,G通道的0.5值实际上表示100%的绿色强度(不透明度为50%)。 For this reason, knowing whether a file uses premultiplied or straight alpha is essential to correctly process or composite it.
因此,知道文件是使用预乘还是直接Alpha对其正确处理或合成至关重要。
It also provides an explanation of how the various color merging operators work. 它还说明了各种颜色合并运算符的工作方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.