Skip to main content

vello_cpu/fine/lowp/
compose.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use 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            // Have not been able to find a formula for this, so just fallback to Plus.
43            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);