/src/image/src/primitive_sealed.rs
Line | Count | Source |
1 | | //! Module for crate-private traits implemented for all primitive types. |
2 | | |
3 | | use crate::imageops::fast_blur::BlurAccumulator; |
4 | | |
5 | | /// Crate-private trait to seal the [`Primitive`](crate::Primitive) trait. |
6 | | /// |
7 | | /// This trait is `pub` but not exported, so it cannot be implemented outside |
8 | | /// this crate. |
9 | | #[allow(private_bounds)] |
10 | | pub trait PrimitiveSealed: Sized + NearestFrom<f32> + WithBlurAcc + BgraSwizzle {} |
11 | | |
12 | | impl PrimitiveSealed for usize {} |
13 | | impl PrimitiveSealed for u8 {} |
14 | | impl PrimitiveSealed for u16 {} |
15 | | impl PrimitiveSealed for u32 {} |
16 | | impl PrimitiveSealed for u64 {} |
17 | | impl PrimitiveSealed for isize {} |
18 | | impl PrimitiveSealed for i8 {} |
19 | | impl PrimitiveSealed for i16 {} |
20 | | impl PrimitiveSealed for i32 {} |
21 | | impl PrimitiveSealed for i64 {} |
22 | | impl PrimitiveSealed for f32 {} |
23 | | impl PrimitiveSealed for f64 {} |
24 | | |
25 | | /// Defines specialized methods for rgb<->bgr and rgba<->bgra swizzles |
26 | | /// |
27 | | /// By default, uses as_chunks_mut and swaps the first and third elements in the pixel slice. |
28 | | /// For u8 rgba however, benchmarks have shown that interpreting the 4 bytes as a u32 and swap+rotate |
29 | | /// ends up autovectorizing better. |
30 | | // Note: no attempts have been made to find if a similar optimization could apply to primitives beyond u8 or to bgr instead of bgra. |
31 | | pub(crate) trait BgraSwizzle: Sized { |
32 | 0 | fn swizzle_rgb_bgr(pixels: &mut [Self]) { |
33 | 0 | for pixel in pixels.as_chunks_mut::<3>().0 { |
34 | 0 | pixel.swap(0, 2); |
35 | 0 | } |
36 | 0 | } |
37 | 0 | fn swizzle_rgba_bgra(pixels: &mut [Self]) { |
38 | 0 | for pix in pixels.as_chunks_mut::<4>().0 { |
39 | 0 | pix.swap(0, 2); |
40 | 0 | } |
41 | 0 | } |
42 | | } |
43 | | |
44 | | impl BgraSwizzle for usize {} |
45 | | impl BgraSwizzle for u8 { |
46 | 0 | fn swizzle_rgba_bgra(pixels: &mut [Self]) { |
47 | 0 | for pix in pixels.as_chunks_mut::<4>().0 { |
48 | 0 | let bgra = u32::from_be_bytes(*pix); |
49 | 0 | let argb = bgra.swap_bytes(); // reverses order of pixels (bytes) |
50 | 0 | let rgba = argb.rotate_left(8); // rotate first byte to last place |
51 | 0 | *pix = rgba.to_be_bytes(); |
52 | 0 | } |
53 | 0 | } |
54 | | } |
55 | | impl BgraSwizzle for u16 {} |
56 | | impl BgraSwizzle for u32 {} |
57 | | impl BgraSwizzle for u64 {} |
58 | | impl BgraSwizzle for isize {} |
59 | | impl BgraSwizzle for i8 {} |
60 | | impl BgraSwizzle for i16 {} |
61 | | impl BgraSwizzle for i32 {} |
62 | | impl BgraSwizzle for i64 {} |
63 | | impl BgraSwizzle for f32 {} |
64 | | impl BgraSwizzle for f64 {} |
65 | | |
66 | | /// Returns the nearest value of `Self` to a given value. |
67 | | /// |
68 | | /// Properties: |
69 | | /// - For a float -> int conversion: |
70 | | /// - The float is rounded to the nearest integer. |
71 | | /// (An implementation may use a fast approximation instead of precise rounding.) |
72 | | /// - NaN is mapped to 0. |
73 | | /// - Values outside the range of the integer type are clamped to the min or max value. |
74 | | /// - For a float -> float conversion: |
75 | | /// - The float is clamped to the range `[0.0, 1.0]`. |
76 | | /// - NaN is mapped to 0.0. |
77 | | pub(crate) trait NearestFrom<T> { |
78 | | /// Returns the nearest value of `Self` to `value`. |
79 | | /// |
80 | | /// Properties: |
81 | | /// - For a float -> int conversion: |
82 | | /// - The float is rounded to the nearest integer. |
83 | | /// (An implementation may use a fast approximation instead of precise rounding.) |
84 | | /// - NaN is mapped to 0. |
85 | | /// - Values outside the range of the integer type are clamped to the min or max value. |
86 | | /// - For a float -> float conversion: |
87 | | /// - Values outside are clamped to +-inf. |
88 | | /// - NaN is kept as NaN. |
89 | | /// - Precision may be lost. |
90 | | fn nearest_from(value: T) -> Self; |
91 | | |
92 | | /// Returns the nearest value of `Self` to `value` *within* the default |
93 | | /// range of `Self`. This range is 0..=1 for floats and the full range of |
94 | | /// the integer type for integers. |
95 | | /// |
96 | | /// If `Self` is an integer type, this is the same as `nearest_from`. |
97 | | /// |
98 | | /// Properties: |
99 | | /// - For a float -> int conversion: Same as `nearest_from`. |
100 | | /// - For a float -> float conversion: |
101 | | /// - The float is clamped to the range `[0.0, 1.0]`. |
102 | | /// - NaN is mapped to 0.0. |
103 | | /// - Precision may be lost. |
104 | | fn clamp_nearest_from(value: T) -> Self; |
105 | | } |
106 | | |
107 | | impl NearestFrom<f32> for u8 { |
108 | 0 | fn nearest_from(value: f32) -> Self { |
109 | | // Approximate rounding using the well-known + 0.5 trick. |
110 | | // This does not handle certain cases correctly. E.g. `0.5_f32.nextdown()` |
111 | | // is incorrectly rounded to 1 instead of 0. However, this isn't typically |
112 | | // an issue in practice. |
113 | 0 | (value + 0.5) as u8 |
114 | 0 | } |
115 | 0 | fn clamp_nearest_from(value: f32) -> Self { |
116 | 0 | Self::nearest_from(value) |
117 | 0 | } |
118 | | } |
119 | | impl NearestFrom<f32> for u16 { |
120 | 0 | fn nearest_from(value: f32) -> Self { |
121 | 0 | (value + 0.5) as u16 |
122 | 0 | } |
123 | 0 | fn clamp_nearest_from(value: f32) -> Self { |
124 | 0 | Self::nearest_from(value) |
125 | 0 | } |
126 | | } |
127 | | impl NearestFrom<f32> for f32 { |
128 | 0 | fn nearest_from(value: f32) -> Self { |
129 | 0 | value |
130 | 0 | } |
131 | | #[allow(clippy::manual_clamp)] // to map NaN to 0.0 |
132 | 0 | fn clamp_nearest_from(value: f32) -> Self { |
133 | 0 | value.max(0.0).min(1.0) |
134 | 0 | } |
135 | | } |
136 | | impl NearestFrom<f32> for f64 { |
137 | 0 | fn nearest_from(value: f32) -> Self { |
138 | 0 | value as f64 |
139 | 0 | } |
140 | | #[allow(clippy::manual_clamp)] // to map NaN to 0.0 |
141 | 0 | fn clamp_nearest_from(value: f32) -> Self { |
142 | 0 | value.max(0.0).min(1.0) as f64 |
143 | 0 | } |
144 | | } |
145 | | |
146 | | macro_rules! impl_nearest_from_f32_for_ints { |
147 | | ($($t:ty),+) => { $( |
148 | | impl NearestFrom<f32> for $t { |
149 | 0 | fn nearest_from(value: f32) -> Self { |
150 | 0 | value.round() as $t |
151 | 0 | } Unexecuted instantiation: <u32 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <u64 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <usize as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <i8 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <i16 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <i32 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <i64 as image::primitive_sealed::NearestFrom<f32>>::nearest_from Unexecuted instantiation: <isize as image::primitive_sealed::NearestFrom<f32>>::nearest_from |
152 | 0 | fn clamp_nearest_from(value: f32) -> Self { |
153 | 0 | Self::nearest_from(value) |
154 | 0 | } Unexecuted instantiation: <u32 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <u64 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <usize as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <i8 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <i16 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <i32 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <i64 as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from Unexecuted instantiation: <isize as image::primitive_sealed::NearestFrom<f32>>::clamp_nearest_from |
155 | | } |
156 | | )+ }; |
157 | | } |
158 | | impl_nearest_from_f32_for_ints!(u32, u64, usize, i8, i16, i32, i64, isize); |
159 | | |
160 | | /// Crate-private companion to [`Primitive`] that picks the box-blur |
161 | | /// accumulator type for each primitive. |
162 | | /// |
163 | | /// `u8` uses an integer (`u32`) accumulator for speed; everything else goes |
164 | | /// through `f32`. |
165 | | pub(crate) trait WithBlurAcc: Sized { |
166 | | type BlurAcc: BlurAccumulator<Self>; |
167 | | } |
168 | | |
169 | | impl WithBlurAcc for u8 { |
170 | | type BlurAcc = u32; |
171 | | } |
172 | | |
173 | | macro_rules! impl_with_blur_acc_f32 { |
174 | | ($($t:ty),+) => { $( |
175 | | impl WithBlurAcc for $t { |
176 | | type BlurAcc = f32; |
177 | | } |
178 | | )+ }; |
179 | | } |
180 | | |
181 | | impl_with_blur_acc_f32!(u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); |