7

What is the best way to set the RGB components of every pixel in a System.Drawing.Bitmap to a single, solid color? If possible, I'd like to avoid manually looping through each pixel to do this.

Note: I want to keep the same alpha component from the original bitmap. I only want to change the RGB values.

I looked into using a ColorMatrix or ColorMap, but I couldn't find any way to set all pixels to a specific given color with either approach.

AakashM
  • 62,551
  • 17
  • 151
  • 186
Charles
  • 6,199
  • 6
  • 50
  • 66

3 Answers3

15

Yes, use a ColorMatrix. It ought to look like this:

  0  0  0  0  0
  0  0  0  0  0
  0  0  0  0  0 
  0  0  0  1  0 
  R  G  B  0  1

Where R, G and B are the scaled color values of the replacement color (divide by 255.0f)

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • This doesn't set the color of each pixel to a specific color, does it? I'm pretty sure that would increment each color channel by R, G & B. I want the whole image to be a solid color, while retaining just the transparency/alpha of each pixel. – Charles Mar 24 '10 at 19:32
  • 1
    The zeros in the diagonal produce black, the bottom numbers get added. – Hans Passant Mar 24 '10 at 19:38
  • 2
    Thanks Hans. I had missed the division by 255 and it was driving me nuts! I have put together a downloadable walkthrough and example if anyone is interested: http://blog.benpowell.co.uk/2011/01/change-color-of-transparent-png-image.html – Rebecca Jan 13 '11 at 17:24
  • I have tried this. Created `ColorMatrix`, assigned it to `ImageAttributes` and used it while drawing with `DrawImage`. Works perfect on Win7 and latter, however XP and Vista causes problems, do you have any ideas, how to solve this? – ST3 Aug 21 '14 at 09:50
7

I know this is already answered, but based on Hans Passant's answer the resulting code looks something like this:

public class Recolor
{
    public static Bitmap Tint(string filePath, Color c)
    {
        // load from file
        Image original = Image.FromFile(filePath);
        original = new Bitmap(original);

        //get a graphics object from the new image
        Graphics g = Graphics.FromImage(original);

        //create the ColorMatrix
        ColorMatrix colorMatrix = new ColorMatrix(
            new float[][]{
                    new float[] {0, 0, 0, 0, 0},
                    new float[] {0, 0, 0, 0, 0},
                    new float[] {0, 0, 0, 0, 0},
                    new float[] {0, 0, 0, 1, 0},
                    new float[] {c.R / 255.0f,
                                 c.G / 255.0f,
                                 c.B / 255.0f,
                                 0, 1}
                });

        //create some image attributes
        ImageAttributes attributes = new ImageAttributes();

        //set the color matrix attribute
        attributes.SetColorMatrix(colorMatrix);

        //draw the original image on the new image
        //using the color matrix
        g.DrawImage(original, 
            new Rectangle(0, 0, original.Width, original.Height),
            0, 0, original.Width, original.Height,
            GraphicsUnit.Pixel, attributes);

        //dispose the Graphics object
        g.Dispose();

        //return a bitmap
        return (Bitmap)original;
    }
}

Download a working demo here: http://benpowell.org/change-the-color-of-a-transparent-png-image-icon-on-the-fly-using-asp-net-mvc/

Rebecca
  • 13,914
  • 10
  • 95
  • 136
3

The best (in terms of perf, at least) option is to use Bitmap.LockBits, and loop through the pixel data in the scan line, setting the RGB values.

Since you don't want to change the Alpha, you are going to have to loop through each pixel - there is no single memory assignment that will preserve alpha and replace RGB, since they're interleaved together.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373