68

I'm writing a colour picker that gets the pixel RGB values from wherever you point to on the screen. I want to also have the option of specifying that the colour I picked already has an alpha value. I'm just wondering how I can calculate the resulting colour.

For example:

The resulting pixel colour is 240,247,249 but I know that the original colour had a 10% opacity and was on top of a white (255,255,255) background. What's the calculation to work out the original RGB values?

unwind
  • 391,730
  • 64
  • 469
  • 606

3 Answers3

108

The standard blending equation is:

out = alpha * new + (1 - alpha) * old

Where out, new and old are RGB colors, and alpha is a floating point number in the range [0,1].

So, you have (for red):

240 = 0.1 * newR + 0.9 * 255

Solving for newR, we get:

newR = (240 - 0.9 * 255) / 0.1

which evaluates to 105. Repeat for the other components, and you're done.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    ? isn't your equation the same as mine? – Peter Perháč Apr 14 '09 at 11:30
  • 1
    Yes, I the equations are the same, but written slightly differently. – unwind Apr 15 '09 at 06:08
  • 2
    Wow! I've pulled my hair for 2 hours trying to reverse engineer this equation by comparing values in photoshop! Thank you for slowing my hair loss! How did you find or come up with this equation? – talentedmrjones Feb 03 '11 at 03:33
  • @talentedmrjones: It's really standard, if you've ever e.g. programmed OpenGL you will have come across it in documentation about blending modes and so on. It's fairly obvious, given the requirements. – unwind Sep 19 '11 at 16:33
  • 1
    Hello I know it is an old post but I don't seem to apply this on photoshop. I put a pixel(255,0,0) on top of a white(255,255,255) and change the opacity to 0.1. Resulting (255,229,229). With the equation newPixelRed =(255-0.9*255)/0.1, it is 255 as it is excepted. However, for green, newPixelGreen = (0-0.9*255)/0.1 resulting -2295 which should be 229. Any idea why this could happen? – Barışcan Kayaoğlu Jun 17 '15 at 15:10
  • 1
    What if the result is negative? – drct Feb 04 '19 at 12:41
  • @drct Not sure that can happen, given the expected range limits for the values used. Color components should be in [0,255] and alpha in [0,1]. – unwind Feb 06 '19 at 09:42
  • 2
    It the times of sRGB this answer is not quite accurate. The problem is gamma correction. Red, green, and blue channel values are not real light intensities. They are transformed in non-linear way. In sRGB transformation function is used. For blending purposes you can approximate it with a power law using power 2.2. So more accurate formula would be `out = (alpha * new^2.2 + (1 - alpha) * old^2.2)^(1 / 2.2)`. However if you write something like image processor you probably need to dig into sRGB transformations to do it right. – Igor Mikushkin May 05 '20 at 20:28
38

I'm just wondering how come you know its alpha and not its r,g,b... Unless the blending mode used is in every case Normal, you won't be able to calculate the original value. And even if all is ideal, you will only be able to approximate, as some rounding has probably taken place already when going from original to result. Also, you will most likely have to round when going back from result to original.

If the blending mode is Normal, then it's not too difficult :-)

opacity*original + (1-opacity)*background = resulting pixel

original R = (resulting R - ((1-opacity)*background R)) / opacity.
original G = (resulting G - ((1-opacity)*background G)) / opacity.
original B = (resulting B - ((1-opacity)*background B)) / opacity.

in which opacity is (alpha/100).

Peter Perháč
  • 20,434
  • 21
  • 120
  • 152
  • Sorry, I should have mentioned that the blending mode was normal. I was picking the colour from a web page that had an image with an opacity set, so I knew it wasn't just rgb. Thanks anyway. –  Apr 14 '09 at 11:12
8

The following pseudo-code resolves your problem:

    CalculateSolidColorFromTransparentColor(Color color, Color background)
    {
        alpha = color.A / 255;
        oneminusalpha = 1 - alpha;

        newR = ((color.R * alpha) + (oneminusalpha * background.R));
        newG = ((color.G * alpha) + (oneminusalpha * background.G));
        newB = ((color.B * alpha) + (oneminusalpha * background.B));
    }

Note that input color and backgroud has values expressed in [0..255]

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219