@@ -180,9 +180,7 @@ impl Color {
180180 let [ sr, sg, sb, sa] = self . into_linear ( ) ;
181181 let [ dr, dg, db, da] = bg. into_linear ( ) ;
182182
183- let br = Self :: blend_channel ( mode, sr, dr) ;
184- let bg_ = Self :: blend_channel ( mode, sg, dg) ;
185- let bb = Self :: blend_channel ( mode, sb, db) ;
183+ let [ br, bg_, bb] = Self :: blend_channel ( mode, [ sr, sg, sb] , [ dr, dg, db] ) ;
186184
187185 // Porter–Duff combination in premultiplied form
188186 let a_out = sa + da - sa * da;
@@ -781,45 +779,63 @@ impl Color {
781779 [ out_r, out_g, out_b]
782780 }
783781
782+ /// Combine two colors with a blend function.
783+ fn blend < F > ( backdrop : & [ ColorFloat ; 3 ] , source : & [ ColorFloat ; 3 ] , mut f : F ) -> [ ColorFloat ; 3 ]
784+ where
785+ F : FnMut ( ColorFloat , ColorFloat ) -> ColorFloat ,
786+ {
787+ backdrop
788+ . iter ( )
789+ . zip ( source)
790+ . map ( |( & b, & s) | f ( b, s) )
791+ . collect :: < Vec < _ > > ( )
792+ . try_into ( )
793+ . unwrap ( )
794+ }
795+
784796 #[ inline]
785- fn blend_channel ( mode : BlendMode , b : ColorFloat , s : ColorFloat ) -> ColorFloat {
797+ fn blend_channel (
798+ mode : BlendMode ,
799+ backdrop : [ ColorFloat ; 3 ] ,
800+ source : [ ColorFloat ; 3 ] ,
801+ ) -> [ ColorFloat ; 3 ] {
786802 use BlendMode :: * ;
787803
788804 match mode {
789805 // source: https://www.w3.org/TR/compositing-1/
790806 // separable blend modes
791- Normal => s ,
792- Multiply => b * s,
793- Screen => b + s - ( b * s) ,
794- Overlay => Self :: blend_channel ( HardLight , b , s ) ,
795- Darken => b . min ( s) ,
796- Lighten => b . max ( s) ,
797- ColorDodge => {
807+ Normal => source ,
808+ Multiply => Self :: blend ( & backdrop , & source , |b , s| b * s) ,
809+ Screen => Self :: blend ( & backdrop , & source , |b , s| b + s - ( b * s) ) ,
810+ Overlay => Self :: blend_channel ( HardLight , backdrop , source ) ,
811+ Darken => Self :: blend ( & backdrop , & source , |b , s| b . min ( s) ) ,
812+ Lighten => Self :: blend ( & backdrop , & source , |b , s| b . max ( s) ) ,
813+ ColorDodge => Self :: blend ( & backdrop , & source , |b , s| {
798814 if b == 0.0 {
799815 0.0
800816 } else if s == 1.0 {
801817 1.0
802818 } else {
803819 ( 1.0 as ColorFloat ) . min ( b / ( 1.0 - s) )
804820 }
805- }
806- ColorBurn => {
821+ } ) ,
822+ ColorBurn => Self :: blend ( & backdrop , & source , |b , s| {
807823 if b == 1.0 {
808824 1.0
809825 } else if s == 0.0 {
810826 0.0
811827 } else {
812828 1.0 - ( 1.0 as ColorFloat ) . min ( ( 1.0 - b) / s)
813829 }
814- }
815- HardLight => {
830+ } ) ,
831+ HardLight => Self :: blend ( & backdrop , & source , |b , s| {
816832 if s <= 0.5 {
817- Self :: blend_channel ( Multiply , b , 2.0 * s )
833+ 2.0 * b * s
818834 } else {
819- Self :: blend_channel ( Screen , b , 2.0 * s - 1.0 )
835+ 1.0 - 2.0 * ( 1.0 - b ) * ( 1.0 - s )
820836 }
821- }
822- SoftLight => {
837+ } ) ,
838+ SoftLight => Self :: blend ( & backdrop , & source , |b , s| {
823839 if s <= 0.5 {
824840 b - ( 1.0 - 2.0 * s) * b * ( 1.0 - b)
825841 } else {
@@ -830,11 +846,20 @@ impl Color {
830846 } ;
831847 b + ( 2.0 * s - 1.0 ) * ( d - b)
832848 }
833- }
834- Difference => ( b - s) . abs ( ) ,
835- Exclusion => b + s - 2.0 * b * s,
849+ } ) ,
850+ Difference => Self :: blend ( & backdrop , & source , |b , s| ( b - s) . abs ( ) ) ,
851+ Exclusion => Self :: blend ( & backdrop , & source , |b , s| b + s - 2.0 * b * s) ,
836852 // non-separable blend modes
837- _ => s, // TODO placeholder
853+ Hue => Self :: set_lum (
854+ Self :: set_sat ( source, Self :: sat ( backdrop) ) ,
855+ Self :: lum ( backdrop) ,
856+ ) ,
857+ Saturation => Self :: set_lum (
858+ Self :: set_sat ( backdrop, Self :: sat ( source) ) ,
859+ Self :: lum ( backdrop) ,
860+ ) ,
861+ Color => Self :: set_lum ( source, Self :: lum ( backdrop) ) ,
862+ Luminosity => Self :: set_lum ( backdrop, Self :: lum ( source) ) ,
838863 }
839864 }
840865}
0 commit comments