8

I have 2 colors: 1 dynamically set and another that's always white 0.5 alpha. I want to calculate the resulting white color as if it was drawn on top of the dynamic color using Overlay blend mode.

I'm aware that Overlay combines Multiply and Screen blend modes.

Multiply blend mode's formula is:

Result Color = (Top Color) * (Bottom Color) /255

While Screen blend mode's is:

Result Color = 255 - [((255 - Top Color)*(255 - Bottom Color))/255]

How do I calculate the resulting color for the Overlay blend mode?

Is there a UIColor extension class out there that does this out of the box?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
samvermette
  • 40,269
  • 27
  • 112
  • 144

4 Answers4

10

There are two part of formula:

First part: If Lower Layer Value > 127.5, then do the following -

Value Unit = (255-Lower Layer Value)/127.5

Min Value = Lower Layer Value - (255-Lower Layer Value)

Overlay = (Upper Layer Value * Value Unit) + Min Value

Second part: If Lower Layer Value < 127.5, then do the following -

Value Unit=Lower Layer Value/127.5

Overlay = Upper Layer Value * Value Unit

From the formual we can see that the final result is much depend on the upper layer value. If the upper layer value is higher(lighter), then the final result is more likely to be lighter.

From here.

willi
  • 6,559
  • 4
  • 30
  • 27
  • Great. That wasn't part of my original question, but do you know any UIColor extension class out there that does this out of the box? – samvermette Apr 28 '11 at 21:42
  • Not a otb implementation, but this might help you : https://github.com/facebook/three20/blob/master/src/Three20Style/Sources/UIColorAdditions.m – Vincent Guerci Apr 28 '11 at 21:48
8

Following up on willi's answer, here's the formula ported to code:

CGFloat newComponents[4];
const CGFloat *topComponents = CGColorGetComponents([[UIColor colorWithRed:1 green:1 blue:1 alpha:1] CGColor]);
const CGFloat *components = CGColorGetComponents(self.color.CGColor);
const int n = CGColorGetNumberOfComponents(self.color.CGColor);

for(int i=0; i < n; i++) {

    if(components[i] > 0.5) {
        CGFloat value = (topComponents[i]-components[i])/0.5;
        CGFloat min = components[i]-(topComponents[i]-components[i]);
        newComponents[i] = topComponents[i]*value+min; 
    } else {
        CGFloat value = components[i]/0.5;
        newComponents[i] = topComponents[i]*value; 
    }
}

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
UIColor *resultColor = [UIColor colorWithCGColor:CGColorCreate(colorSpace, newComponents)];
CGColorSpaceRelease(colorSpace);
samvermette
  • 40,269
  • 27
  • 112
  • 144
2

I have no idea of your goal, and might be completely off-topic, but why not just using Quartz 2D?

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeOverlay);
... draw with overlay blending

CGBlendMode offers via CGContextSetBlendMode Overlay, Multiply, Screen and much more... :

enum CGBlendMode {
    /* Available in Mac OS X 10.4 & later. */
    kCGBlendModeNormal,
    kCGBlendModeMultiply,
    kCGBlendModeScreen,
    kCGBlendModeOverlay,
    kCGBlendModeDarken,
    kCGBlendModeLighten,
    kCGBlendModeColorDodge,
    kCGBlendModeColorBurn,
    kCGBlendModeSoftLight,
    kCGBlendModeHardLight,
    kCGBlendModeDifference,
    kCGBlendModeExclusion,
    kCGBlendModeHue,
    kCGBlendModeSaturation,
    kCGBlendModeColor,
    kCGBlendModeLuminosity,

    /* Available in Mac OS X 10.5 & later. R, S, and D are, respectively,
       premultiplied result, source, and destination colors with alpha; Ra,
       Sa, and Da are the alpha components of these colors.

       The Porter-Duff "source over" mode is called `kCGBlendModeNormal':
         R = S + D*(1 - Sa)

       Note that the Porter-Duff "XOR" mode is only titularly related to the
       classical bitmap XOR operation (which is unsupported by
       CoreGraphics). */

    kCGBlendModeClear,          /* R = 0 */
    kCGBlendModeCopy,           /* R = S */
    kCGBlendModeSourceIn,       /* R = S*Da */
    kCGBlendModeSourceOut,      /* R = S*(1 - Da) */
    kCGBlendModeSourceAtop,     /* R = S*Da + D*(1 - Sa) */
    kCGBlendModeDestinationOver,    /* R = S*(1 - Da) + D */
    kCGBlendModeDestinationIn,      /* R = D*Sa */
    kCGBlendModeDestinationOut,     /* R = D*(1 - Sa) */
    kCGBlendModeDestinationAtop,    /* R = S*(1 - Da) + D*Sa */
    kCGBlendModeXOR,            /* R = S*(1 - Da) + D*(1 - Sa) */
    kCGBlendModePlusDarker,     /* R = MAX(0, (1 - D) + (1 - S)) */
    kCGBlendModePlusLighter     /* R = MIN(1, S + D) */
};
typedef enum CGBlendMode CGBlendMode; /* Available in Mac OS X 10.4 & later. */
Vincent Guerci
  • 14,379
  • 4
  • 50
  • 56
  • That's what I've been doing. But I need to keep reference of that result white 0.5 alpha color drawn using Overlay blend mode. That way I can use it elsewhere and draw it using kCGBlendModeNormal and it's going to look as if I had drawn it using kCGBlendModeOverlay. – samvermette Apr 28 '11 at 21:40
  • ah I see, I guess you can combine several drawings to do this, i would not like to code this, like you do i guess, anyway, if that works... :) – Vincent Guerci Apr 28 '11 at 21:47
0

For posterity (since this is top google result), the wikipedia formula is cleaner and more straightforward.

Here is a pseudo-code version of it. You'd use 255 and 127 if you are dealing with 1 Byte colors.

// For 'a' base layer, 'b' top layer.

if (a < 0.5) {
    result = 2 * a * b;
} else {
    result = 1 - 2 * (1 - a) * (1 - b);
}

https://en.wikipedia.org/wiki/Blend_modes#Overlay

scx
  • 3,221
  • 1
  • 19
  • 37