1use crate::fine::Splat4thExt;
5use crate::peniko::{BlendMode, Compose};
6use crate::util::NormalizedMulExt;
7use vello_common::fearless_simd::*;
8use vello_common::util::Div255Ext;
9
10pub(crate) trait ComposeExt {
11 fn compose<S: Simd>(
12 &self,
13 simd: S,
14 src_c: u8x32<S>,
15 bg_c: u8x32<S>,
16 alpha_mask: Option<u8x32<S>>,
17 ) -> u8x32<S>;
18}
19
20impl ComposeExt for BlendMode {
21 fn compose<S: Simd>(
22 &self,
23 simd: S,
24 src_c: u8x32<S>,
25 bg_c: u8x32<S>,
26 alpha_mask: Option<u8x32<S>>,
27 ) -> u8x32<S> {
28 let mut res = match self.compose {
29 Compose::SrcOver => SrcOver::compose(simd, src_c, bg_c),
30 Compose::Clear => Clear::compose(simd, src_c, bg_c),
31 Compose::Copy => Copy::compose(simd, src_c, bg_c),
32 Compose::DestOver => DestOver::compose(simd, src_c, bg_c),
33 Compose::Dest => Dest::compose(simd, src_c, bg_c),
34 Compose::SrcIn => SrcIn::compose(simd, src_c, bg_c),
35 Compose::DestIn => DestIn::compose(simd, src_c, bg_c),
36 Compose::SrcOut => SrcOut::compose(simd, src_c, bg_c),
37 Compose::DestOut => DestOut::compose(simd, src_c, bg_c),
38 Compose::SrcAtop => SrcAtop::compose(simd, src_c, bg_c),
39 Compose::DestAtop => DestAtop::compose(simd, src_c, bg_c),
40 Compose::Xor => Xor::compose(simd, src_c, bg_c),
41 Compose::Plus => Plus::compose(simd, src_c, bg_c),
42 Compose::PlusLighter => Plus::compose(simd, src_c, bg_c),
44 };
45
46 if let Some(alpha_mask) = alpha_mask {
47 let alpha_mask_inv = 255 - alpha_mask;
48 let p1 = simd.widen_u8x32(alpha_mask) * simd.widen_u8x32(res);
49 let p2 = simd.widen_u8x32(alpha_mask_inv) * simd.widen_u8x32(bg_c);
50 res = simd.narrow_u16x32((p1 + p2).div_255());
51 }
52
53 res
54 }
55}
56
57macro_rules! compose {
58 ($name:ident, $fa:expr, $fb:expr, $sat:expr) => {
59 struct $name;
60
61 impl $name {
62 fn compose<S: Simd>(simd: S, src_c: u8x32<S>, bg_c: u8x32<S>) -> u8x32<S> {
63 let al_b = bg_c.splat_4th();
64 let al_s = src_c.splat_4th();
65
66 let fa = $fa(simd, al_s, al_b);
67 let fb = $fb(simd, al_s, al_b);
68
69 if $sat {
70 simd.narrow_u16x32(
71 (simd.widen_u8x32(src_c.normalized_mul(fa))
72 + simd.widen_u8x32(fb.normalized_mul(bg_c)))
73 .min(u16x32::splat(simd, 255))
74 .max(u16x32::splat(simd, 0)),
75 )
76 } else {
77 src_c.normalized_mul(fa) + fb.normalized_mul(bg_c)
78 }
79 }
80 }
81 };
82}
83
84compose!(
85 Clear,
86 |simd, _, _| u8x32::splat(simd, 0),
87 |simd, _, _| u8x32::splat(simd, 0),
88 false
89);
90compose!(
91 Copy,
92 |simd, _, _| u8x32::splat(simd, 255),
93 |simd, _, _| u8x32::splat(simd, 0),
94 false
95);
96compose!(
97 SrcOver,
98 |simd, _, _| u8x32::splat(simd, 255),
99 |_, al_s: u8x32<S>, _| 255 - al_s,
100 false
101);
102compose!(
103 DestOver,
104 |_, _, al_b: u8x32<S>| 255 - al_b,
105 |simd, _, _| u8x32::splat(simd, 255),
106 false
107);
108compose!(
109 Dest,
110 |simd, _, _| u8x32::splat(simd, 0),
111 |simd, _, _| u8x32::splat(simd, 255),
112 false
113);
114compose!(
115 Xor,
116 |_, _, al_b: u8x32<S>| 255 - al_b,
117 |_, al_s: u8x32<S>, _| 255 - al_s,
118 false
119);
120compose!(
121 SrcIn,
122 |_, _, al_b: u8x32<S>| al_b,
123 |simd, _, _| u8x32::splat(simd, 0),
124 false
125);
126compose!(
127 DestIn,
128 |simd, _, _| u8x32::splat(simd, 0),
129 |_, al_s: u8x32<S>, _| al_s,
130 false
131);
132compose!(
133 SrcOut,
134 |_, _, al_b: u8x32<S>| 255 - al_b,
135 |simd, _, _| u8x32::splat(simd, 0),
136 false
137);
138compose!(
139 DestOut,
140 |simd, _, _| u8x32::splat(simd, 0),
141 |_, al_s: u8x32<S>, _| 255 - al_s,
142 false
143);
144compose!(
145 SrcAtop,
146 |_, _, al_b: u8x32<S>| al_b,
147 |_, al_s: u8x32<S>, _| 255 - al_s,
148 false
149);
150compose!(
151 DestAtop,
152 |_, _, al_b: u8x32<S>| 255 - al_b,
153 |_, al_s: u8x32<S>, _| al_s,
154 false
155);
156compose!(
157 Plus,
158 |simd, _, _| u8x32::splat(simd, 255),
159 |simd, _, _| u8x32::splat(simd, 255),
160 true
161);