Skip to content

Commit 102d018

Browse files
committed
Separable Blend Modes
Added several new BlendMethods to support separable blend modes: * ColorDodge * ColorBurn * HardLight * SoftLight * Difference * Exclusion
1 parent 9c0bb32 commit 102d018

File tree

1 file changed

+52
-10
lines changed

1 file changed

+52
-10
lines changed

src/color/model.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ pub enum BlendMode {
2222
Overlay,
2323
Darken,
2424
Lighten,
25-
// TODO: Add SoftLight, HardLight, ColorDodge, ColorBurn, etc.
25+
ColorDodge,
26+
ColorBurn,
27+
HardLight,
28+
SoftLight,
29+
Difference,
30+
Exclusion,
2631
}
2732

2833
// stores sRGB under the hood, with lots of conversion funcs
@@ -637,21 +642,58 @@ impl Color {
637642
}
638643

639644
#[inline]
640-
fn blend_channel(mode: BlendMode, s: ColorFloat, d: ColorFloat) -> ColorFloat {
645+
fn blend_channel(mode: BlendMode, b: ColorFloat, s: ColorFloat) -> ColorFloat {
641646
use BlendMode::*;
647+
642648
match mode {
649+
// source: https://www.w3.org/TR/compositing-1/
650+
// separable blend modes
643651
Normal => s,
644-
Multiply => s * d,
645-
Screen => 1.0 - (1.0 - s) * (1.0 - d),
646-
Overlay => {
647-
if d <= 0.5 {
648-
2.0 * s * d
652+
Multiply => b * s,
653+
Screen => b + s - (b * s),
654+
Overlay => Self::blend_channel(HardLight, b, s),
655+
Darken => b.min(s),
656+
Lighten => b.max(s),
657+
ColorDodge => {
658+
if b == 0.0 {
659+
0.0
660+
} else if s == 1.0 {
661+
1.0
662+
} else {
663+
(1.0 as ColorFloat).min(b / (1.0 - s))
664+
}
665+
}
666+
ColorBurn => {
667+
if b == 1.0 {
668+
1.0
669+
} else if s == 0.0 {
670+
0.0
671+
} else {
672+
1.0 - (1.0 as ColorFloat).min((1.0 - b) / s)
673+
}
674+
}
675+
HardLight => {
676+
if s <= 0.5 {
677+
Self::blend_channel(Multiply, b, 2.0 * s)
678+
} else {
679+
Self::blend_channel(Screen, b, 2.0 * s - 1.0)
680+
}
681+
}
682+
SoftLight => {
683+
if s <= 0.5 {
684+
b - (1.0 - 2.0 * s) * b * (1.0 - b)
649685
} else {
650-
1.0 - 2.0 * (1.0 - s) * (1.0 - d)
686+
let d = if b <= 0.25 {
687+
((16.0 * b - 12.0) * b + 4.0) * b
688+
} else {
689+
b.sqrt()
690+
};
691+
b + (2.0 * s - 1.0) * (d - b)
651692
}
652693
}
653-
Darken => s.min(d),
654-
Lighten => s.max(d),
694+
Difference => (b - s).abs(),
695+
Exclusion => b + s - 2.0 * b * s,
696+
// non-separable blend modes
655697
}
656698
}
657699
}

0 commit comments

Comments
 (0)