WPF - changing image colors on the fly (C #)

Is it possible to change image colors in WPF using code (or even using templates)?

Suppose I have an image that I need to apply to a Tile, which by default will have a white foreground color and a transparent background. Something like the following PNG (it's here somewhere!):

Add New PNG

Instead of adding different images - with different colors, I just want to manipulate white - and I will say that I changed it to black.

If this can be done, can someone give me some pointers on what I need to do / have a look at.

+6
source share
1 answer

One way to do this is to use the BitmapDecoder class to get raw pixel data. Then you can change the pixels and build a new WriteableBitmap from the modified pixel data:

 // 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, 0, image.PixelWidth, image.PixelHeight), pixels, image.PixelWidth*4, 0); img.Source = bmp; 

It works after the mod, but there is a problem. Here's what the result looks like if I show it on a dark background:

red cross on black background

As you can see, he had some kind of white border. What happened here is that your white cross had smoothed edges, which means that the pixels around the edges are actually a translucent shade of gray.

We can handle this using a slightly more sophisticated technique in the pixel modification loop:

 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; } 

Here's how it looks on a black background:

red on black without white border

As you can see, this looks right. (OK, you need black not red, but the basic approach will be the same for any target color.)

EDIT 2015/1/21 . As noted in the comments on ar_j, the Prgba format requires premultiplication. In the example I gave, it's actually safe to ignore it, but if you changed the color channels in any way other than setting them to 0, you would need to multiply each value by (a/255) . For example, since aj_j shows for the channel G: pixels[i * 4 + 1] = (byte)(g * a / 255); Since g is zero in my code, this does not matter, but for non-primary colors you will need to do it this way.

Here, on a gradient fill background, it is simply shown that transparency works:

red cross on gradient background

You can also write the modified version:

 var enc = new PngBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bmp)); using (Stream pngStream = File.OpenWrite(@"c:\temp\modified.png")) { enc.Save(pngStream); } 

Here is the result:

modified PNG

You can see the red cross, and it will be on top of any background color used by StackOverflow. (White, as I write this, but maybe they will redesign one day.)

How much this will work for the images you want to use is harder to know for sure, because it depends on what your definition of β€œwhite” is - depending on how your images were created, you may find that everything is so small (especially around the edges), and you may need additional setup. But the basic approach should be in order.

+7
source

All Articles