3
votes

I have an image I'm trying to process into a very specific look. I found a tutorial in Photoshop and have been slowly, step by step, recreating the Photoshop effects. The latest one is a color blend mode which I was able to get working, but they add an alpha to it and it's got me stumped.

Photoshop does this by creating a filter, setting the color, selecting "Blend" and then applying the alpha. The alpha applies to the filter and not the base image.

What I'm doing is taking two colors, the base image and the overlay color to blend, and converting them to HSL. I'm figuring the Blend by taking the Hue/Saturation from the overlay and the Luminosity from the base and creating a new HSL color. I then convert that to RGB and write that pixel to the bitmap over the original base color.

What I don't know how to do is apply the alpha. It seems like I should be take the base color and the product of the blend and figure the alpha between them. I'm not sure if this is correct though, or where to find the proper calculation between the colors.

I don't see the need for code for this question but if you need it, I can post some.

Thanks

1
Whoops, my bad! I just found the equation and it all works like a charm! stackoverflow.com/questions/746899/… I'll search more thoroughly next time!Terrordoll

1 Answers

1
votes

I just solved this problem like this (background.png and colorWithAlpha.png have the same dimensions):

Bitmap bg = new Bitmap("background.png");
Bitmap overlay = new Bitmap("colorWithAlpha.png");
float m = 0;
Color c;
for (int i = 0; i < bg.Width; i++)
{
    for (int j = 0; j < bg.Height; j++)
    {
        m = overlay.GetPixel(i, j).A / 255f;
        c = HSVtoRGB(overlay.GetPixel(i,j).GetHue(), overlay.GetPixel(i,j).GetSaturation(), bg.GetPixel(i, j).GetBrightness());
        c = Color.FromArgb((int)(m * c.R + (1 - m) * bg.GetPixel(i, j).R), (int)(m * c.G + (1 - m) * bg.GetPixel(i, j).G), (int)(m * c.B + (1 - m) * bg.GetPixel(i, j).B));
        bg.SetPixel(i, j, c);
    }
}

public static Color HSVtoRGB(float hue, float saturation, float value)
{
    // HSV contains values scaled as in the color wheel:
    // that is, all from 0 to 255.

    // for ( this code to work, HSV.Hue needs
    // to be scaled from 0 to 360 (it//s the angle of the selected
    // point within the circle). HSV.Saturation and HSV.value must be
    // scaled to be between 0 and 1.

    double h;
    double s;
    double v;

    double r = 0;
    double g = 0;
    double b = 0;

    // Scale Hue to be between 0 and 360. Saturation
    // and value scale to be between 0 and 1.
    h = ((double)hue / 255 * 360) % 360;
    s = (double)saturation / 255;
    v = (double)value / 255;

    if (s == 0)
    {
        // If s is 0, all colors are the same.
        // This is some flavor of gray.
        r = v;
        g = v;
        b = v;
    }
    else
    {
        double p;
        double q;
        double t;

        double fractionalSector;
        int sectorNumber;
        double sectorPos;

        // The color wheel consists of 6 sectors.
        // Figure out which sector you//re in.
        sectorPos = h / 60;
        sectorNumber = (int)(Math.Floor(sectorPos));

        // get the fractional part of the sector.
        // That is, how many degrees into the sector
        // are you?
        fractionalSector = sectorPos - sectorNumber;

        // Calculate values for the three axes
        // of the color.
        p = v * (1 - s);
        q = v * (1 - (s * fractionalSector));
        t = v * (1 - (s * (1 - fractionalSector)));

        // Assign the fractional colors to r, g, and b
        // based on the sector the angle is in.
        switch (sectorNumber)
        {
            case 0:
                r = v;
                g = t;
                b = p;
                break;

            case 1:
                r = q;
                g = v;
                b = p;
                break;

            case 2:
                r = p;
                g = v;
                b = t;
                break;

            case 3:
                r = p;
                g = q;
                b = v;
                break;

            case 4:
                r = t;
                g = p;
                b = v;
                break;

            case 5:
                r = v;
                g = p;
                b = q;
                break;
        }
    }
    // return an RGB structure, with values scaled
    // to be between 0 and 255.
    return Color.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
}

The function HSVtoRGB is taken from https://bytes.com/topic/c-sharp/answers/236124-how-would-i-change-hue-bitmap, slightly adjusted as the original function expects all inputs as values from 0-255, so the scaling has to be removed.