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