Photoshop Blend Mode Math
Been working on programmatically blending bitmaps. Below are the macros that I’ve come up, borrowing the some of the formulas from other websites (see sources at the bottom of this post).
Channel blending
#define ChannelBlend_Normal(B,L) ((uint8)(B)) #define ChannelBlend_Lighten(B,L) ((uint8)((L > B) ? L:B)) #define ChannelBlend_Darken(B,L) ((uint8)((L > B) ? B:L)) #define ChannelBlend_Multiply(B,L) ((uint8)((B * L) / 255)) #define ChannelBlend_Average(B,L) ((uint8)((B + L) / 2)) #define ChannelBlend_Add(B,L) ((uint8)(min(255, (B + L)))) #define ChannelBlend_Subtract(B,L) ((uint8)((B + L < 255) ? 0:(B + L - 255))) #define ChannelBlend_Difference(B,L) ((uint8)(abs(B - L))) #define ChannelBlend_Negation(B,L) ((uint8)(255 - abs(255 - B - L))) #define ChannelBlend_Screen(B,L) ((uint8)(255 - (((255 - B) * (255 - L)) >> 8))) #define ChannelBlend_Exclusion(B,L) ((uint8)(B + L - 2 * B * L / 255)) #define ChannelBlend_Overlay(B,L) ((uint8)((L < 128) ? (2 * B * L / 255):(255 - 2 * (255 - B) * (255 - L) / 255))) #define ChannelBlend_SoftLight(B,L) ((uint8)((L < 128)?(2*((B>>1)+64))*((float)L/255):(255-(2*(255-((B>>1)+64))*(float)(255-L)/255)))) #define ChannelBlend_HardLight(B,L) (ChannelBlend_Overlay(L,B)) #define ChannelBlend_ColorDodge(B,L) ((uint8)((L == 255) ? L:min(255, ((B << 8 ) / (255 - L))))) #define ChannelBlend_ColorBurn(B,L) ((uint8)((L == 0) ? L:max(0, (255 - ((255 - B) << 8 ) / L)))) #define ChannelBlend_LinearDodge(B,L)(ChannelBlend_Add(B,L)) #define ChannelBlend_LinearBurn(B,L) (ChannelBlend_Subtract(B,L)) #define ChannelBlend_LinearLight(B,L)((uint8)(L < 128)?ChannelBlend_LinearBurn(B,(2 * L)):ChannelBlend_LinearDodge(B,(2 * (L - 128)))) #define ChannelBlend_VividLight(B,L) ((uint8)(L < 128)?ChannelBlend_ColorBurn(B,(2 * L)):ChannelBlend_ColorDodge(B,(2 * (L - 128)))) #define ChannelBlend_PinLight(B,L) ((uint8)(L < 128)?ChannelBlend_Darken(B,(2 * L)):ChannelBlend_Lighten(B,(2 * (L - 128)))) #define ChannelBlend_HardMix(B,L) ((uint8)((ChannelBlend_VividLight(B,L) < 128) ? 0:255)) #define ChannelBlend_Reflect(B,L) ((uint8)((L == 255) ? L:min(255, (B * B / (255 - L))))) #define ChannelBlend_Glow(B,L) (ChannelBlend_Reflect(L,B)) #define ChannelBlend_Phoenix(B,L) ((uint8)(min(B,L) - max(B,L) + 255)) #define ChannelBlend_Alpha(B,L,O) ((uint8)(O * B + (1 - O) * L)) #define ChannelBlend_AlphaF(B,L,F,O) (ChannelBlend_Alpha(F(B,L),B,O))
B is the base channel and L is the blend channel. For example you to blend using Glow you simply call:
Target[i] = ChannelBlend_Glow(Base[i], Blend[i]);
The great thing about these macros is that you can apply the blending effect simply by passing in the channel value, without regard to which channel is red, green, or blue.
To use the blending along with opacity you can use the following.
Target[i] = ChannelBlend_AlphaF(Base[i], Blend[i], Blend_Subtract, 0.5F)
Color blending
To add certain blend modes that utilize hue, luminosity, and saturation we have to construct a per-color interface instead of per-channel interface. For these macros we assume that A and B are buffer pointers and they point to bytes with channels red, green, and blue in that order.
#define ColorBlend_Buffer(T,B,L,M) (T)[0] = ChannelBlend_##M((B)[0], (L)[0]), \
(T)[1] = ChannelBlend_##M((B)[1], (L)[1]), \
(T)[2] = ChannelBlend_##M((B)[2], (L)[2])
#define ColorBlend_Normal(T,B,L) (ColorBlend_Buffer(T,B,L,Normal))
#define ColorBlend_Lighten(T,B,L) (ColorBlend_Buffer(T,B,L,Lighten))
#define ColorBlend_Darken(T,B,L) (ColorBlend_Buffer(T,B,L,Darken))
#define ColorBlend_Multiply(T,B,L) (ColorBlend_Buffer(T,B,L,Multiply))
#define ColorBlend_Average(T,B,L) (ColorBlend_Buffer(T,B,L,Average))
#define ColorBlend_Add(T,B,L) (ColorBlend_Buffer(T,B,L,Add))
#define ColorBlend_Subtract(T,B,L) (ColorBlend_Buffer(T,B,L,Subtract))
#define ColorBlend_Difference(T,B,L) (ColorBlend_Buffer(T,B,L,Difference))
#define ColorBlend_Negation(T,B,L) (ColorBlend_Buffer(T,B,L,Negation))
#define ColorBlend_Screen(T,B,L) (ColorBlend_Buffer(T,B,L,Screen))
#define ColorBlend_Exclusion(T,B,L) (ColorBlend_Buffer(T,B,L,Exclusion))
#define ColorBlend_Overlay(T,B,L) (ColorBlend_Buffer(T,B,L,Overlay))
#define ColorBlend_SoftLight(T,B,L) (ColorBlend_Buffer(T,B,L,SoftLight))
#define ColorBlend_HardLight(T,B,L) (ColorBlend_Buffer(T,B,L,HardLight))
#define ColorBlend_ColorDodge(T,B,L) (ColorBlend_Buffer(T,B,L,ColorDodge))
#define ColorBlend_ColorBurn(T,B,L) (ColorBlend_Buffer(T,B,L,ColorBurn))
#define ColorBlend_LinearDodge(T,B,L) (ColorBlend_Buffer(T,B,L,LinearDodge))
#define ColorBlend_LinearBurn(T,B,L) (ColorBlend_Buffer(T,B,L,LinearBurn))
#define ColorBlend_LinearLight(T,B,L) (ColorBlend_Buffer(T,B,L,LinearLight))
#define ColorBlend_VividLight(T,B,L) (ColorBlend_Buffer(T,B,L,VividLight))
#define ColorBlend_PinLight(T,B,L) (ColorBlend_Buffer(T,B,L,PinLight))
#define ColorBlend_HardMix(T,B,L) (ColorBlend_Buffer(T,B,L,HardMix))
#define ColorBlend_Reflect(T,B,L) (ColorBlend_Buffer(T,B,L,Reflect))
#define ColorBlend_Glow(T,B,L) (ColorBlend_Buffer(T,B,L,Glow))
#define ColorBlend_Phoenix(T,B,L) (ColorBlend_Buffer(T,B,L,Phoenix))
#define ColorBlend_Hue(T,B,L) ColorBlend_Hls(T,B,L,HueL,LuminationB,SaturationB)
#define ColorBlend_Saturation(T,B,L) ColorBlend_Hls(T,B,L,HueB,LuminationB,SaturationL)
#define ColorBlend_Color(T,B,L) ColorBlend_Hls(T,B,L,HueL,LuminationB,SaturationL)
#define ColorBlend_Luminosity(T,B,L) ColorBlend_Hls(T,B,L,HueB,LuminationL,SaturationB)
#define ColorBlend_Hls(T,B,L,O1,O2,O3) { \
float64 HueB, LuminationB, SaturationB; \
float64 HueL, LuminationL, SaturationL; \
Color_RgbToHls((B)[2],(B)[1],(B)[0], &HueB, &LuminationB, &SaturationB); \
Color_RgbToHls((L)[2],(L)[1],(L)[0], &HueL, &LuminationL, &SaturationL); \
Color_HlsToRgb(O1,O2,O3,&(T)[2],&(T)[1],&(T)[0]); \
}
int32 Color_HueToRgb(float64 M1, float64 M2, float64 Hue, float64 *Channel)
{
if (Hue < 0.0)
Hue += 1.0;
else if (Hue > 1.0)
Hue -= 1.0;
if ((6.0 * Hue) < 1.0)
*Channel = (M1 + (M2 - M1) * Hue * 6.0);
else if ((2.0 * Hue) < 1.0)
*Channel = (M2);
else if ((3.0 * Hue) < 2.0)
*Channel = (M1 + (M2 - M1) * ((2.0F / 3.0F) - Hue) * 6.0);
else
*Channel = (M1);
return TRUE;
}
int32 Color_RgbToHls(uint8 Red, uint8 Green, uint8 Blue, float64 *Hue, float64 *Lumination, float64 *Saturation)
{
float64 Delta;
float64 Max, Min;
float64 Redf, Greenf, Bluef;
Redf = ((float64)Red / 255.0F);
Greenf = ((float64)Green / 255.0F);
Bluef = ((float64)Blue / 255.0F);
Max = max(max(Redf, Greenf), Bluef);
Min = min(min(Redf, Greenf), Bluef);
*Hue = 0;
*Lumination = (Max + Min) / 2.0F;
*Saturation = 0;
if (Max == Min)
return TRUE;
Delta = (Max - Min);
if (*Lumination < 0.5)
*Saturation = Delta / (Max + Min);
else
*Saturation = Delta / (2.0 - Max - Min);
if (Redf == Max)
*Hue = (Greenf - Bluef) / Delta;
else if (Greenf == Max)
*Hue = 2.0 + (Bluef - Redf) / Delta;
else
*Hue = 4.0 + (Redf - Greenf) / Delta;
*Hue /= 6.0;
if (*Hue < 0.0)
*Hue += 1.0;
return TRUE;
}
int32 Color_HlsToRgb(float64 Hue, float64 Lumination, float64 Saturation, uint8 *Red, uint8 *Green, uint8 *Blue)
{
float64 M1, M2;
float64 Redf, Greenf, Bluef;
if (Saturation == 0)
{
Redf = Lumination;
Greenf = Lumination;
Bluef = Lumination;
}
else
{
if (Lumination <= 0.5)
M2 = Lumination * (1.0 + Saturation);
else
M2 = Lumination + Saturation - Lumination * Saturation;
M1 = (2.0 * Lumination - M2);
Color_HueToRgb(M1, M2, Hue + (1.0F / 3.0F), &Redf);
Color_HueToRgb(M1, M2, Hue, &Greenf);
Color_HueToRgb(M1, M2, Hue - (1.0F / 3.0F), &Bluef);
}
*Red = (uint8)(Redf * 255);
*Blue = (uint8)(Bluef * 255);
*Green = (uint8)(Greenf * 255);
return TRUE;
}
So now we can use the hue, saturation, color, and luminosity blend modes along with the rest of the blend modes. To use the ColorBlend macros we just position our bitmap’s pointers to the next RGB iteration and call,
ColorBlend_Glow(Target + iTarget, Base + iBase, Blend + iBlend);
Color macros
Here are some color macros for combining and extracting channels colors out of an integer. One macro that I find a lot of use for is HexToRgb. With it I can copy and paste a hex color value out of the Photoshop color dialog and use the macro to do all the conversion for me to an rgb value.
#define COLOR_OPAQUE (0)
#define COLOR_TRANSPARENT (127)
#define RGB_SIZE (3)
#define RGB_BPP (24)
#define RGB_MAXRED (255)
#define RGB_MAXGREEN (255)
#define RGB_MAXBLUE (255)
#define ARGB_SIZE (4)
#define ARGB_BPP (32)
#define ARGB_MAXALPHA (127)
#define ARGB_MAXRED (RGB_MAXRED)
#define ARGB_MAXGREEN (RGB_MAXGREEN)
#define ARGB_MAXBLUE (RGB_MAXBLUE)
/*********************************************************************/
#define Color_GetChannel(c,shift) ((uint8)((c) >> (shift)))
#define Color_Reverse(c,bpp) ((((uint8)(c) << 24) | ((uint8)((c) >> 8 ) << 16) | ((uint8)((c) >> 16) << 8 ) | \
((uint8)((c) >> 24))) >> (32 - (bpp)))
#define Rgb_ByteWidth(width) ((width) * RGB_SIZE)
#define Rgb_PixelWidth(width) ((width) / RGB_SIZE)
#define Rgb_GetRed(rgb) (Color_GetChannel(rgb, 0))
#define Rgb_GetGreen(rgb) (Color_GetChannel(rgb, 8))
#define Rgb_GetBlue(rgb) (Color_GetChannel(rgb, 16))
#define Rgba_GetRed(rgba) (Color_GetChannel(rgba, 24))
#define Rgba_GetGreen(rgba) (Color_GetChannel(rgba, 16))
#define Rgba_GetBlue(rgba) (Color_GetChannel(rgba, 8))
#define Rgba_GetAlpha(rgba) (Color_GetChannel(rgba, 0))
#define Argb_GetAlpha(argb) (Color_GetChannel(argb, 24))
#define Argb_GetRed(argb) (Color_GetChannel(argb, 16))
#define Argb_GetGreen(argb) (Color_GetChannel(argb, 8))
#define Argb_GetBlue(argb) (Color_GetChannel(argb, 0))
#define MakeRgb(r,g,b) (((uint32)(uint8)(b) << 16) | ((uint16)(uint8)(g) << 8 ) | (uint8)(r))
#define MakeRgba(r,g,b,a) (((uint32)(uint8)(r) << 24) | ((uint16)(uint8)(g) << 16) | ((uint16)(uint8)(b) << 8 ) | (uint8)(a))
#define MakeArgb(a,r,g,b) (((uint32)(uint8)(a) << 24) | ((uint32)(uint8)(r) << 16) | ((uint16)(uint8)(g) << 8 ) | (uint8)(b))
#define HexToRgb(hex) (MakeRgb(((hex & 0xFF0000) >> 16), ((hex & 0x00FF00) >> 8 ), (hex & 0xFF)))
Image blending
To show you what each effect does I’ve blended the following two photos.
![]() Base Layer Source |
![]() Blend Layer Source |
![]() Base Layer |
![]() Blend Layer |
More blending resources
PegTop blend modes
Forensic Photoshop
Insight into Photoshop 7.0 Blending Modes
SF – Basics – Blending Modes
finish the blend() modes
Romz blog
ReactOS RGB/HLS conversion fucntions




































































Photoshop Blend Modes
January 24th, 2008 at 7:43 pm
February 26th, 2008 at 3:04 pm
Nice use of the math for some interesting effects. Glad to be of help.
Jim Hoerricks
Author – Forensic Photoshop blog
Math Art | Heart Broken
April 13th, 2008 at 3:34 pm
CrossProcessing shader « Romz blog
June 11th, 2008 at 3:17 am
June 16th, 2008 at 5:52 pm
Nathan, this is a really great resource, thanks so much for publishing it
June 17th, 2008 at 1:09 am
No problemo. I couldn’t find any site out there that had EVERY photoshop blend mode. So I worked on getting the macros right and then put them all up here for everyone to use.
September 16th, 2008 at 11:18 pm
nice
September 23rd, 2008 at 5:29 am
thx for the links
September 26th, 2008 at 1:09 am
Thankyou very much, Nathan, this exactly answers what I’ve been wondering about these blend-modes… Great
PatchBox r284 | Quadratura
September 30th, 2008 at 7:07 am
impress your friends: perform an isaac-edit at parties! « isaac bowen
January 1st, 2009 at 11:23 am
January 2nd, 2009 at 12:17 pm
It seems you made a small error in the definition of Blend_Add(A,B): “max” should be “min”, so it reads “((uint8)(min(255, (A + B))))”. Great list by the way!
January 2nd, 2009 at 5:53 pm
Thanks I fixed that. I made changes to it so it used min and max where needed and I guess that one got screwed up.
Photoshop math with GLSL shaders « Romz blog
January 5th, 2009 at 3:41 pm
January 5th, 2009 at 8:42 pm
If anybody else has fixes or other blending code they would like to contribute, just add a comment with the code or fix.
January 9th, 2009 at 3:08 am
Hey Nathan, you can check on my blog: http://blog.mouaif.org, I’ve posted GLSL and now also HLSL code of all the Photoshop blending modes based on your code + other blending modes (like hue, saturation, luminosity, color) + other stuff. Thanks!
January 9th, 2009 at 9:41 am
@Romz: Cool! Nice blog. I noticed in your code that you said you fixed HardMix, what was wrong with it? I updated my definition of HardMix so that it is the same as yours. I’ve also updated the my blog post with the per-color code and copied your blend definitions for hue, luminosity, saturation, and color. I like how you used vectors in your code. Very nice.
January 11th, 2009 at 3:59 pm
Thank you for the link
Concerning hard mix I don’t remember where I saw the exact definition but after a quick search I found the same description (with less details):
Hard Mix: This blend mode is a combination of the Vivid Light mode and a posterization effect (i.e., where the image appears more pixelated). It will posterize the bottom layer pixels through the blend layer and recolor the image using the specifications of the Vivid Light mode. A higher Fill Opacity on the top layer will increase the posterization effect on the image. http://www.uwec.edu/help/PhotoshopCS2/blendmodes.htm
Photoshop Blend Mode Math « François Tarlier’s Blog
March 19th, 2009 at 4:27 pm
June 13th, 2009 at 2:25 pm
Here’s a good use for hard mix -
http://www.digitalartform.com/archives/2009/06/custom_halftone.html
Cory Plotts’ Blog » Blend Modes, Part I
June 16th, 2009 at 5:55 pm
Cory Plotts’ Blog » Blend Modes, Part II
June 16th, 2009 at 9:25 pm
June 21st, 2009 at 12:40 pm
Today I updated a few of the blend modes and added gradient images.
June 21st, 2009 at 12:45 pm
If anybody finds any problems with the blend modes or has a more accurate representation of the photoshop blend modes please let me know so I can update this post.
June 29th, 2009 at 4:12 pm
I think each blend mode is best demonstrated by at least one ‘killer app.’ I’m trying to collect them here:
http://www.digitalartform.com/archives/2009/06/photoshop_blend.html
June 29th, 2009 at 4:30 pm
Cool!
Colormath Basics | Laurens Corijn
August 6th, 2009 at 2:56 pm
September 5th, 2009 at 11:26 pm
Heres how I deal with RGB’s.
template
struct rgb
{
static const int alpha = (hex & 0xFF000000) >> 24;
static const int red = (hex & 0xFF0000) >> 16;
static const int green = (hex & 0x00FF00) >> 8;
static const int blue = hex & 0x0000FF;
};
typedef rgb black_t;
int red = black_t::red;
int green = black_t::green;
int blue = black_t::blue;
Photoshop ブレンドモードを使いこなしてワンランク上の画像合成 | WEB制作.Hack
October 2nd, 2009 at 5:46 am
October 10th, 2009 at 2:29 pm
In photoshop there are some additional bleding mode that missing here
1) Darker Color:
Get the color of color with min luminosity
float bgLuminosity = 0.3 * bg.Red + 0.59 * bg.Green + 0.11 * bg.Blue;
float fgLuminosity = 0.3 * fg.Red + 0.59 * fg.Green + 0.11 * fg.Blue;
if (bgLuminosity fgLuminosity)
result = bg;
else
result = fg;
October 10th, 2009 at 2:30 pm
2) The second mode is Lighter color:
Get the color of color with max luminosity
float bgLuminosity = 0.3 * bg.Red + 0.59 * bg.Green + 0.11 * bg.Blue;
float fgLuminosity = 0.3 * fg.Red + 0.59 * fg.Green + 0.11 * fg.Blue;
if (bgLuminosity > fgLuminosity)
result = bg;
else
result = fg;
October 21st, 2009 at 9:08 am
The overlay blend mode should be (B < 128) instead of (L < 128). If you look at my sample on http://karljensen.com/BitmapTest.html the bottom left is sampling each pixel and redrawing using a similar equation. The right one is what happens when you select "overlay" blend mode within Flash. I think all that has happened is that you have mixed Overlay and Hard Light.
November 6th, 2009 at 2:35 pm
It seems to me that the overlay, soft light, and hard light blend modes (at least the gradient squares and images above) have the layers reversed.
That is, either the math is backwards or Base Layer should be Blend Layer and vice versa.
The gradient squares do not match what comes out of Photoshop.
December 8th, 2009 at 8:19 am
Excellent! I would be rally interested in learning more about the math behind digital picture processing: RAW conversion, demosaicing, exposure, brightness, contrast, sharpening, etc. all the way to blend modes. Suggestions for a good book?
December 20th, 2009 at 6:03 pm
The Soft Light blend code is not close to giving photoshop results. After much checking,
“If b <.5 THEN 2ab + a^2 (1-2b) ELSE sqrt(a) (2b – 1) + 2 a(1 – b)" gives correct results where b is from the blend layer, (Units here are in percentages of 255).
February 23rd, 2010 at 11:15 am
Seems that Hue, Color, Saturation, Luminance blend modes in Photoshop use a color model different from HLS – Luminance is defined differently, using an approximation of visual brightness. Probably something like as 0.3 * Red + 0.59 * Green + 0.11 * Blue or the like, maybe even taking color profile information into account.
March 6th, 2010 at 2:55 pm
Awesome! Kudos for posting this, I was looking all over for these formulas and you’re about the only one who has it all in one simple, concise location. Congratulations: you’re a required resource!
March 8th, 2010 at 4:55 pm
These are awesome, man! Thanks! Can you give me an idea what effects I need in order to programmatically “bruise” an image? For example, I want to darken colors on a photo to make a person appear to be injured. I’m not quite sure how to blend the colors. I’ve tried just subtracting RGB values to darken it. I’ve tried to make the color go towards a shade of brown. They don’t look quite that realistic.
Any idea? What’s the C code for that? Thanks again!
gonzobrains
May 10th, 2010 at 7:37 pm
@gonzobrains
I don’t know how to get the image to appear bruised by just using blending tools, but I have two suggestions.
A. You could simple find another image that has bruises and mask the two images together, or
B. You could look for bruise brushes available for download (deviantart is usually a good source for that).
Hope that helps. (:
May 11th, 2010 at 8:04 pm
hello, the the normal mode seems to be:
#define ChannelBlend_Normal(B,L) ((uint8)(L))
the L is the source color (top layer, contrast to the backdrop color).
June 21st, 2010 at 12:40 pm
Thank You for this resource. This is very helpful.
I just graduated with a masters working in GPGPU programming. I’ve been wanting these algorithms for years.
June 21st, 2010 at 12:42 pm
@Burkhard: Photoshop may be converting colorspace to L.a.b. space and then desaturating. Once desaturated, the colors are probably converted back to RGB (or CMYK depending on preference).