Branchless Photoshop Blend Modes

by Pol Jeremias on April 3, 2016

No Comments

If you have used Adobe Photoshop you have probably seen that it allows users to draw (or create content) in different layers. These layers can after be combined in many different ways, the blend modes. https://en.wikipedia.org/wiki/Blend_modes.

The blog post you are reading right now builds on top of the idea of having a branchless RGB to HSL conversion (http://www.poljeremias.com/branchless-rgb-to-hsl), and it proposes branchless solutions to the most common color blending operations.

If you want to just play around with it, visit the following Shadertoy, there you can try live the different branchless blending operations : https://www.shadertoy.com/view/Md3GzX

This post contains implementations for the following blending operations :


    • Source
    • Destination
    • Multiply
    • Screen
    • Exclusion
    • Overlay
    • Hard Light
    • Soft Light
    • Color Dodge

  • Color Burn
  • Linear Dodge
  • Linear Burn
  • Vivid Light
  • Linear Light
  • Pin Light
  • Hard Mix
  • Substract
  • Divide

  • Addition
  • Difference
  • Darken
  • Lighten
  • Invert
  • Invert RGB
  • Hue
  • Saturation
  • Color
  • Luminosity



Source

vec3 source(in vec3 src, in vec3 dst)
{
	return src;
}

source
Destination

vec3 dest(in vec3 src, in vec3 dst)
{
	return dst;
}

Dest
Multiply

vec3 multiply(in vec3 src, in vec3 dst)
{
	return src * dst;
}

Multiply
Screen

vec3 screen(in vec3 src, in vec3 dst)
{
    return src + dst - src * dst;
}

Screen
Exclusion

vec3 exclusion(in vec3 src, in vec3 dst)
{
    return dst + src - (2.0*dst*src);
}

Exclusion

Overlay

vec3 overlay(in vec3 src, in vec3 dst)
{
    return mix(2.0 * src * dst, 1.0 - 2.0 * (1.0 - src) * (1.0-dst), step(0.5, dst));
}

Overlay

Hard Light

vec3 hardlight(in vec3 src, in vec3 dst)
{
    return mix(2.0 * src * dst,  1.0 - 2.0 * (1.0 - src) * (1.0-dst), step(0.5, src));
}

Hardlight

 

Soft Light

vec3 softlight(in vec3 src, in vec3 dst)
{
    return mix(dst - (1.0 - 2.0 * src) * dst * (1.0 - dst), 
               mix(dst + ( 2.0 * src - 1.0 ) * (sqrt(dst) - dst),
                   dst + (2.0 * src - 1.0) * dst * ((16.0 * dst - 12.0) * dst + 3.0),
                   step(0.5, src) * (1.0 - step(0.25, dst))),
               step(0.5, src));
}

Softlight

Color Dodge

vec3 colorDodge(in vec3 src, in vec3 dst)
{
    return step(0.0, dst) * mix(min(vec3(1.0), dst/ (1.0 - src)), vec3(1.0), step(1.0, src)); 
}

Colordodge

Color Burn

vec3 colorBurn(in vec3 src, in vec3 dst)
{
    return mix(step(0.0, src) * (1.0 - min(vec3(1.0), (1.0 - dst) / src)),
        vec3(1.0), step(1.0, dst));
}

Colorburn

Linear Dodge

vec3 linearDodge(in vec3 src, in vec3 dst)
{
    return clamp(src.xyz + dst.xyz, 0.0, 1.0);
}

Lineardodge

Linear Burn

vec3 linearBurn(in vec3 src, in vec3 dst)
{
    return clamp(src.xyz + dst.xyz - 1.0, 0.0, 1.0);
}

Linearburn

Vivid Light

vec3 vividLight(in vec3 src, in vec3 dst)
{
    return mix(max(vec3(0.0), 1.0 - min(vec3(1.0), (1.0 - dst) / (2.0 * src))),
               min(vec3(1.0), dst / (2.0 * (1.0 - src))),
               step(0.5, src));
}

Vividlight

Linear Light

vec3 linearLight(in vec3 src, in vec3 dst)
{
    return clamp(2.0 * src + dst - 1.0, 0.0, 1.0);;
}

Linearlight

Pin Light

vec3 pinLight(in vec3 src, in vec3 dst)
{
    return mix(mix(2.0 * src, dst, step(0.5 * dst, src)),
        max(vec3(0.0), 2.0 * src - 1.0), 
        step(dst, (2.0 * src - 1.0))
    );
}

Pinlight

Hard Mix

vec3 hardMix(in vec3 src, in vec3 dst)
{
    return step(1.0, src + dst);
}

Hardmix

Subtract

vec3 subtract(in vec3 src, in vec3 dst)
{
    return dst - src;
}

Substract

Divide

vec3 divide(in vec3 src, in vec3 dst)
{
    return dst / src;
}

Divide

Addition

vec3 addition(vec3 src, vec3 dst)
{
    return src + dst;
}

Add

Difference

vec3 difference(in vec3 src, in vec3 dst )
{
    return abs(dst - src);   
}

Diff

Darken

vec3 darken(in vec3 src, in vec3 dst)
{
    return min(src, dst);
}

Darken

Lighten

vec3 lighten(in vec3 src, in vec3 dst)
{
    return max(src, dst);
}

Lighten

Invert

vec3 invert(in vec3 src, in vec3 dst)
{
    return 1.0 - dst;
}

Invert

Invert RGB

vec3 invertRGB(in vec3 src, in vec3 dst)
{
    return src * (1.0 - dst);
}

InvertRGB

 

Hue

vec3 hue(in vec3 src, in vec3 dst)
{
    vec3 dstHSL = rgb2hsl(dst);
    vec3 srcHSL = rgb2hsl(src);
    return hsl2rgb(vec3(srcHSL.r, dstHSL.gb));
}

Hue
Saturation

vec3 saturation(in vec3 src, in vec3 dst)
{
    vec3 dstHSL = rgb2hsl(dst);
    vec3 srcHSL = rgb2hsl(src);
    return hsl2rgb(vec3(dstHSL.r, srcHSL.g, dstHSL.b));
}

Saturation
Color

vec3 color(in vec3 src, in vec3 dst)
{
    vec3 dstHSL = rgb2hsl(dst);
    vec3 srcHSL = rgb2hsl(src);
    return hsl2rgb(vec3(srcHSL.rg, dstHSL.b));
}

Color
Luminosity

vec3 luminosity(in vec3 src, in vec3 dst)
{
    vec3 dstHSL = rgb2hsl(dst);
    vec3 srcHSL = rgb2hsl(src);
    return hsl2rgb(vec3(dstHSL.rg, srcHSL.b));
}

Luminosity

References

– Shadertoy implementation : https://www.shadertoy.com/view/Md3GzX
– RGB To HSL blog post : http://www.poljeremias.com/branchless-rgb-to-hsl
– RGB to HSL code : https://www.shadertoy.com/view/MsKGRW
– Hsl to RGB code thanks to IQ : https://www.shadertoy.com/view/lsS3Wc

Leave a Reply