1use crate::peniko::{BlendMode, Compose, ImageQuality, Mix};
5use vello_common::encode::EncodedImage;
6use vello_common::fearless_simd::{Simd, SimdBase, f32x4, u8x32, u16x16, u16x32};
7use vello_common::math::FloatExt;
8
9#[allow(
10 dead_code,
11 reason = "this is not used because the division by 255 is now done with SIMD, but\
12we still keep it around to document its properties."
13)]
14pub(crate) mod scalar {
15 #[inline(always)]
36 pub(crate) const fn div_255(val: u16) -> u16 {
37 debug_assert!(
38 val < 65280,
39 "the properties of `div_255` do not hold for values of `65280` or greater"
40 );
41 (val + 255) >> 8
42 }
43
44 #[cfg(test)]
45 mod tests {
46 use crate::util::scalar::div_255;
47
48 #[test]
49 fn div_255_properties() {
50 for i in 0_u16..256 * 255 {
51 let expected = i / 255;
52 let actual = div_255(i);
53
54 assert!(
55 expected <= actual,
56 "In case of a discrepancy, the division should yield a value higher than the original."
57 );
58
59 let diff = expected.abs_diff(actual);
60 assert!(diff <= 1, "Rounding error shouldn't be higher than 1.");
61
62 if i % 255 == 0 {
63 assert_eq!(diff, 0, "Division should be accurate for multiples of 255.");
64 }
65 }
66 }
67 }
68}
69
70pub(crate) trait NormalizedMulExt {
71 fn normalized_mul(self, other: Self) -> Self;
72}
73
74impl<S: Simd> NormalizedMulExt for u8x32<S> {
75 #[inline(always)]
76 fn normalized_mul(self, other: Self) -> Self {
77 let divided = (self.simd.widen_u8x32(self) * other.simd.widen_u8x32(other)).div_255();
78 self.simd.narrow_u16x32(divided)
79 }
80}
81
82pub(crate) trait Div255Ext {
83 fn div_255(self) -> Self;
84}
85
86impl<S: Simd> Div255Ext for u16x32<S> {
87 #[inline(always)]
88 fn div_255(self) -> Self {
89 let p1 = Self::splat(self.simd, 255);
90 let p2 = self + p1;
91 p2.shr(8)
92 }
93}
94
95impl<S: Simd> Div255Ext for u16x16<S> {
96 #[inline(always)]
97 fn div_255(self) -> Self {
98 let p1 = Self::splat(self.simd, 255);
99 let p2 = self + p1;
100 p2.shr(8)
101 }
102}
103
104#[inline(always)]
105pub(crate) fn normalized_mul<S: Simd>(a: u8x32<S>, b: u8x32<S>) -> u16x32<S> {
106 (S::widen_u8x32(a.simd, a) * S::widen_u8x32(b.simd, b)).div_255()
107}
108
109pub(crate) trait BlendModeExt {
110 fn is_default(&self) -> bool;
111}
112
113impl BlendModeExt for BlendMode {
114 fn is_default(&self) -> bool {
116 matches!(self.mix, Mix::Normal | Mix::Clip) && self.compose == Compose::SrcOver
117 }
118}
119
120pub(crate) trait EncodedImageExt {
121 fn has_skew(&self) -> bool;
122 fn nearest_neighbor(&self) -> bool;
123}
124
125impl EncodedImageExt for EncodedImage {
126 fn has_skew(&self) -> bool {
127 !(self.x_advance.y as f32).is_nearly_zero() || !(self.y_advance.x as f32).is_nearly_zero()
128 }
129
130 fn nearest_neighbor(&self) -> bool {
131 self.quality == ImageQuality::Low
132 }
133}
134
135pub(crate) trait Premultiply {
136 fn premultiply(self, alphas: Self) -> Self;
137 fn unpremultiply(self, alphas: Self) -> Self;
138}
139
140impl<S: Simd> Premultiply for f32x4<S> {
141 #[inline(always)]
142 fn premultiply(self, alphas: Self) -> Self {
143 self * alphas
144 }
145
146 #[inline(always)]
147 fn unpremultiply(self, alphas: Self) -> Self {
148 let zero = Self::splat(alphas.simd, 0.0);
149 let divided = self / alphas;
150
151 self.simd
152 .select_f32x4(self.simd.simd_eq_f32x4(alphas, zero), zero, divided)
153 }
154}