4

Let's say I have a background layer (B) filled with RGB 255 115 0, and a foreground layer (F) filled with RGB 64 64 64. Both layers are in Normal blending mode, the opacity on F is 80%. The resulting color is RGB 135 78 57.

I would like to calculate the color with a script, so I searched about the formula used to interpolate between the two colors, and found this (linear interpolation)

C = F*alpha + B*(1-alpha)

So, I normalized the values to be in the range 0..1, by dividing the three color channels by 255 and the opacity by 100. Then I applied the formula above and multipied the channels of the resulting color by 255, rounding to the nearest integer, to obtain the RGB values of the resulting color C.

However, the values calculated by me are 102 74 51, which are different from those obtained by creating the color in GIMP.

Am I making wrong assumptions, or is the actual formula used by GIMP different?

UPDATE Thanks to @xenoid, I made my own experiments and I can confirm: if I switch to Legacy the values are the same as what I have computed.

Searching about linear light I stumbled upon this: Image Precision, and found some slight differences. Let's assume that F is the top layer filled with RGB 64 64 64 at 80% opacity and B is the bottom layer filled with RGB 255 115 46. C will be the color computed by the formula given above.

With Precision 8-bit Linear Light I came up with these results:

C:       102 74 60
Legacy:  102 75 61
Default: 102 75 61

With Precision 8-bit Perceptual Gamma (sRGB):

C:       102 74 60
Legacy:  102 74 60
Default: 135 78 61

It seems that working with Perceptual Gamma and using Legacy mode gives an exact match with the values computed with the formula.

2 Answers 2

6

When you use the 0..255 range you are likely in a gamma-corrected image. To compute the resulting color, Gimp converts the 0..255 value to "linear light" applies your composition formula, and reapplies the gamma correction. So the composition is really applied with darker colors.

However, since Gimp 2.10 you can work in gamma-corrected or "linear" light. This has an effect on blend modes so you have the choice between "default" and "legacy" modes (this is the small selector on the right of the blend mode selector). In practice, if you work in linear light, the legacy modes don't perform any gamma correction, and using "Normal (legacy)" will produce the output you computed:

Top: "default" mode, bottom: "legacy" mode.

enter image description here

8
  • Not true or someone here cannot read what you wrote. In linear light mode a black (RGBA=0,0,0,1) layer with opacity 50% shows underlying max bright opaque red as 73,4% bright red, not 50%
    – user82991
    Dec 14, 2019 at 15:22
  • 1
    @user287001 - actually it is true. See example. You need to choose "Legacy" blend modes in the little drop down in the layers panel, and then "Normal (legacy)" blend mode for it to work properly. The result of your little experiment gives #800000 which is indeed 50% in the red channel.
    – Billy Kerr
    Dec 14, 2019 at 16:13
  • @BillyKerr OK. Thanks for the rectification. Maybe you want to show your screenshot in an answer. I guess it's useful.
    – user82991
    Dec 14, 2019 at 16:17
  • 2
    I'll add a screeshot later..
    – xenoid
    Dec 14, 2019 at 18:38
  • 1
    @xenoid with the screenshot is even better, anyways, I made my own experiments and I can confirm: if I use legacy mode the result is correct Dec 14, 2019 at 20:41
1

I was also very curious about this, so I looked though GIMP's source code to find the formulas for non-legacy mode.

The legacy mode changes the composite space to RGB (perceptual) while the new default is RGB (linear). There might by other differences but that is the most relevant here.

Here are the formulas for the alpha and color channels

composite_alpha = source_alpha + (1 - source_alpha) * destination_alpha
    = source_alpha + destination_alpha - source_alpha * destination_alpha

source_weight = source_alpha / composite_alpha
composite_color = source_color * source_weight + destination_color * (1 - source_weight)

Where destination is the background, source is the foreground and composite is the result. All values must be in the normalized linear RGB space. GIMP uses babl for the color space conversion but I think raising the normalized rgb color components to 2.2 is common approximation.

Note that the alpha calculation is commutative.

Using your values and GIMP's color picker I get

source_alpha = 0.8
source_color = (0.051269, 0.051269, 0.051269)

destination_alpha = 1.0
destination_color = (1.0, 0.171441, 0.0)

composite_alpha = 0.8 + (1.0 - 0.8) * 1.0 = 1.0

source_weight = 0.8 / 1.0 = 0.8
composite_color = 
    (0.051269, 0.051269, 0.051269) * 0.8 
    (1.0, 0.171441, 0.0) * (1.0 - 0.8) + 
    = (0.2410152, 0.0753034, 0.0410152)

These values are very close to the ones that GIMP's color picker tells me, which are (0.241016, 0.075304, 0.041016)

You can find the code on GitLab.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.