Hello, again!

I recently overhauled the blending modes maths in Quick Picto Viewer. I decided to write an article here on the maths behind the common blending modes, because I was not able to easily find in one place the mathematical formulas. However, I will not be describing how they visually impact the image or their use cases.

Blending modes describe mathematically how the colours of two images are mixed when composited on top of each other. The mathematical formula is applied on each image colour channel: R, G and B, for each overlapping pixel. The formulas provided here work on normalized RGB values. To normalize the values, one has to divide by 255 each R, G and B value, given they are in the range of 0 to 255. Therefore, floating point precision is necessary for the computations.

In the following table, the base layer is B and the image on top is A, and Z is the result of the math formula which has to be applied on each colour channel. The commutative blending modes are marked with * (asterisk). For these, the layers order does not matter.

Fomula | Blend mode | Category |
---|---|---|

z = min(A, B) | *Darken | Darker |

z = A * B | *Multiply | Darker |

z = A + B - 1 | *Linear burn | Darker |

z = 1 - ((1 - B) / A) | Color burn | Darker |

z = max(A, B) | *Lighten | Brighter |

z = 1 - ( (1 - B) * (1 - A) ) | *Screen | Brighter |

z = A + B | *Linear dodge (linear add) | Brighter |

z = B / (1 - A) | Color dodge | Brighter |

if (A < 0.5) z = 2 * A * B else z = 1 - (2 * (1 - A) * (1 - B) ) |
Hard light | Contrast |

if (A < 0.5) z = (1 - 2*A) * (B^2) + 2 * B * A else z = 2 * B * (1 - A) + sqrt(B) * (2 * A - 1) |
Soft light | Contrast |

if (B < 0.5) z = 2 * A * B else z = 1 - (2 * (1 - A) * (1 - B) ) |
Overlay | Contrast |

if (A <= (1 - B)) z = 0 else z = 1 |
*Hard mix | Contrast |

z = B + (2 * A) - 1 | Linear light | Contrast |

if (A < 0.5) z = 1 - (1 - B) / (2 * A) else z = B / (2 * (1 - A)) |
Vivid light | Contrast |

z = (B + A)/2 | *Average | Contrast |

z = A + B - 2 * (A * B) | *Exclusion | Inversion |

z = abs(B - A) | *Difference | Inversion |

z = B / A | Divide | Cancelation |

z = B - A | Substract | Cancelation |

z = gray(A) + B - gray(B) | Luminosity | Component |

z = gray(B) - gray(A) + B + A/5 | Ghosting | Component contrast |

The gray() function takes the non-normalized RGB values and returns a normalized value. It is defined as:

`g = (r*0.299 + g*0.587 + b*0.114)/255.0`

I used the NTSC colour weights to convert the colour of the pixel to gray, because I get very similar results with other applications. Of course, one can try more accurate conversion algorithms.

The Ghosting blending mode is a personal concoction :-). The end results one can get with it are quite interesting.

Till next time... best regards, Marius.