kyleb
kyleb

Reputation: 2050

Convert grayscale partially transparent image to a single color in c#

I am trying to create a function that takes a gray scale image and a color and colors the gray scale image using that color shade but keeps the shading levels of the gray scale image. The function also should not color the transparent parts of the image. I have multiple layers (multiple png's) I will be combining later and only need to color certain layers. I have looked around and found similar things but not quite what I need. I know how to do it in HTML5 on front end for the user using Canvas but I need a way to achieve same thing on the backend using I am guessing either a manual method using unlocked bitmap memory calls or a ColorMatrix class. Can anyone help me, graphics aren't my strongest area but I am slowly learning. See the function below for what I need in C# that I did in javascript. Doing the hidden canvas stuff isn't as important because I am doing this server side for saving to PNG file...

function drawImage(imageObj, color) {
    var hidden_canvas = document.createElement("canvas");
    hidden_canvas.width = imageObj.width;
    hidden_canvas.height = imageObj.height;
    var hidden_context = hidden_canvas.getContext("2d");

    // draw the image on the hidden canvas
    hidden_context.drawImage(imageObj, 0, 0);

    if (color !== undefined) {
        var imageData = hidden_context.getImageData(0, 0, imageObj.width, imageObj.height);
        var data = imageData.data;

        for (var i = 0; i < data.length; i += 4) {
            var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];

            //red
            data[i] = brightness + color.R;
            //green
            data[i + 1] = brightness + color.G;
            //blue
            data[i + 2] = brightness + color.B;
        }

        //overwrite original image
        hidden_context.putImageData(imageData, 0, 0);
    }

    var canvas = document.getElementById('card');
    var context = canvas.getContext('2d');
    context.drawImage(hidden_canvas, 0, 0);
};

Upvotes: 0

Views: 945

Answers (1)

TaW
TaW

Reputation: 54433

This should do the job:

public static Bitmap MakeChromaChange(Bitmap bmp0, Color tCol, float gamma)
{
    Bitmap bmp1 = new Bitmap(bmp0.Width, bmp0.Height);

    using (Graphics g = Graphics.FromImage(bmp1))
    {
        float f = (tCol.R + tCol.G + tCol.B) / 765f;
        float tr = tCol.R / 255f - f;
        float tg = tCol.G / 255f - f;
        float tb = tCol.B / 255f - f;

        ColorMatrix colorMatrix = new ColorMatrix(new float[][]
        {  new float[] {1f + tr, 0, 0, 0, 0},
           new float[] {0, 1f + tg, 0, 0, 0},
           new float[] {0, 0, 1f + tb, 0, 0},
           new float[] {0, 0, 0, 1, 0},
           new float[] {0, 0, 0, 0, 1}  });

        ImageAttributes attributes = new ImageAttributes();
        attributes.SetGamma(gamma);
        attributes.SetColorMatrix(colorMatrix);

        g.DrawImage(bmp0, new Rectangle(0, 0, bmp0.Width, bmp0.Height),
            0, 0, bmp0.Width, bmp0.Height, GraphicsUnit.Pixel, attributes);
    }
    return bmp1;
}

Note that I kept a gamma parameter; if you don't need it keep the value at 1f;

Here it is at work, adding first red then more red and some blue :

enter image description hereenter image description hereenter image description here

Transparent pixels are not affected.

For more on ColorMatrix here is a really nice intro!

As a fun project I applied the known colors to a known face:

enter image description here

Upvotes: 1

Related Questions