Coverage Report

Created: 2026-05-16 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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);