Coverage Report

Created: 2025-11-05 08:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/v_frame-0.3.9/src/pixel.rs
Line
Count
Source
1
// Copyright (c) 2017-2021, The rav1e contributors. All rights reserved
2
//
3
// This source code is subject to the terms of the BSD 2 Clause License and
4
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5
// was not distributed with this source code in the LICENSE file, you can
6
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7
// Media Patent License 1.0 was not distributed with this source code in the
8
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10
#[cfg(feature = "serialize")]
11
use serde::{Deserialize, Serialize};
12
13
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
14
use wasm_bindgen::prelude::*;
15
16
use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed};
17
18
use std::fmt;
19
use std::fmt::{Debug, Display};
20
use std::mem::size_of;
21
use std::ops::AddAssign;
22
23
/// Trait for casting between primitive types.
24
pub trait CastFromPrimitive<T>: Copy + 'static {
25
    /// Casts the given value into `Self`.
26
    fn cast_from(v: T) -> Self;
27
}
28
29
macro_rules! impl_cast_from_primitive {
30
  ( $T:ty => $U:ty ) => {
31
    impl CastFromPrimitive<$U> for $T {
32
      #[inline(always)]
33
0
      fn cast_from(v: $U) -> Self { v as Self }
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<u32>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<usize>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<u32>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<usize>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<usize>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<usize>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<u64>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<i8>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<i64>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<isize>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<u64>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<i8>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<i64>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<isize>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<u32>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<u64>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<i8>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<i64>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<isize>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<u32>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<u64>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<i8>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<i64>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<isize>>::cast_from
34
    }
35
  };
36
  ( $T:ty => { $( $U:ty ),* } ) => {
37
    $( impl_cast_from_primitive!($T => $U); )*
38
  };
39
}
40
41
// casts to { u8, u16 } are implemented separately using Pixel, so that the
42
// compiler understands that CastFromPrimitive<T: Pixel> is always implemented
43
impl_cast_from_primitive!(u8 => { u32, u64, usize });
44
impl_cast_from_primitive!(u8 => { i8, i64, isize });
45
impl_cast_from_primitive!(u16 => { u32, u64, usize });
46
impl_cast_from_primitive!(u16 => { i8, i64, isize });
47
impl_cast_from_primitive!(i16 => { u32, u64, usize });
48
impl_cast_from_primitive!(i16 => { i8, i64, isize });
49
impl_cast_from_primitive!(i32 => { u32, u64, usize });
50
impl_cast_from_primitive!(i32 => { i8, i64, isize });
51
52
pub trait RegisteredPrimitive:
53
    PrimInt
54
    + AsPrimitive<u8>
55
    + AsPrimitive<i16>
56
    + AsPrimitive<u16>
57
    + AsPrimitive<i32>
58
    + AsPrimitive<u32>
59
    + AsPrimitive<usize>
60
    + CastFromPrimitive<u8>
61
    + CastFromPrimitive<i16>
62
    + CastFromPrimitive<u16>
63
    + CastFromPrimitive<i32>
64
    + CastFromPrimitive<u32>
65
    + CastFromPrimitive<usize>
66
{
67
}
68
69
impl RegisteredPrimitive for u8 {}
70
impl RegisteredPrimitive for u16 {}
71
impl RegisteredPrimitive for i16 {}
72
impl RegisteredPrimitive for i32 {}
73
74
macro_rules! impl_cast_from_pixel_to_primitive {
75
    ( $T:ty ) => {
76
        impl<T: RegisteredPrimitive> CastFromPrimitive<T> for $T {
77
            #[inline(always)]
78
0
            fn cast_from(v: T) -> Self {
79
0
                v.as_()
80
0
            }
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<u8>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<u8>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<u8>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<i16>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<u8>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<u16>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<i32>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<i16>>::cast_from
Unexecuted instantiation: <i32 as v_frame::pixel::CastFromPrimitive<_>>::cast_from
Unexecuted instantiation: <u32 as v_frame::pixel::CastFromPrimitive<_>>::cast_from
Unexecuted instantiation: <u8 as v_frame::pixel::CastFromPrimitive<_>>::cast_from
Unexecuted instantiation: <i16 as v_frame::pixel::CastFromPrimitive<_>>::cast_from
Unexecuted instantiation: <u16 as v_frame::pixel::CastFromPrimitive<_>>::cast_from
81
        }
82
    };
83
}
84
85
impl_cast_from_pixel_to_primitive!(u8);
86
impl_cast_from_pixel_to_primitive!(i16);
87
impl_cast_from_pixel_to_primitive!(u16);
88
impl_cast_from_pixel_to_primitive!(i32);
89
impl_cast_from_pixel_to_primitive!(u32);
90
91
/// Types that can be used as pixel types.
92
#[derive(PartialEq, Eq)]
93
pub enum PixelType {
94
    /// 8 bits per pixel, stored in a `u8`.
95
    U8,
96
    /// 10 or 12 bits per pixel, stored in a `u16`.
97
    U16,
98
}
99
100
/// A type that can be used as a pixel type.
101
pub trait Pixel:
102
    RegisteredPrimitive + Into<u32> + Into<i32> + Debug + Display + Send + Sync + 'static
103
{
104
    type Coeff: Coefficient;
105
106
    /// Returns a [`PixelType`] variant corresponding to this type.
107
    ///
108
    /// [`PixelType`]: enum.PixelType.html
109
    fn type_enum() -> PixelType;
110
111
    /// Converts stride in pixels to stride in bytes.
112
    #[inline]
113
0
    fn to_asm_stride(in_stride: usize) -> isize {
114
0
        (in_stride * size_of::<Self>()) as isize
115
0
    }
116
}
117
118
impl Pixel for u8 {
119
    type Coeff = i16;
120
121
    #[inline]
122
0
    fn type_enum() -> PixelType {
123
0
        PixelType::U8
124
0
    }
Unexecuted instantiation: <u8 as v_frame::pixel::Pixel>::type_enum
Unexecuted instantiation: <u8 as v_frame::pixel::Pixel>::type_enum
125
}
126
127
impl Pixel for u16 {
128
    type Coeff = i32;
129
130
    #[inline]
131
0
    fn type_enum() -> PixelType {
132
0
        PixelType::U16
133
0
    }
Unexecuted instantiation: <u16 as v_frame::pixel::Pixel>::type_enum
Unexecuted instantiation: <u16 as v_frame::pixel::Pixel>::type_enum
134
}
135
136
pub trait Coefficient:
137
    RegisteredPrimitive + Into<i32> + AddAssign + Signed + Debug + 'static
138
{
139
    type Pixel: Pixel;
140
}
141
142
impl Coefficient for i16 {
143
    type Pixel = u8;
144
}
145
impl Coefficient for i32 {
146
    type Pixel = u16;
147
}
148
149
/// Chroma subsampling format
150
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
151
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
152
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
153
#[repr(C)]
154
pub enum ChromaSampling {
155
    /// Both vertically and horizontally subsampled.
156
    #[default]
157
    Cs420,
158
    /// Horizontally subsampled.
159
    Cs422,
160
    /// Not subsampled.
161
    Cs444,
162
    /// Monochrome.
163
    Cs400,
164
}
165
166
impl FromPrimitive for ChromaSampling {
167
0
    fn from_i64(n: i64) -> Option<Self> {
168
        use ChromaSampling::*;
169
170
0
        match n {
171
0
            n if n == Cs420 as i64 => Some(Cs420),
172
0
            n if n == Cs422 as i64 => Some(Cs422),
173
0
            n if n == Cs444 as i64 => Some(Cs444),
174
0
            n if n == Cs400 as i64 => Some(Cs400),
175
0
            _ => None,
176
        }
177
0
    }
178
179
0
    fn from_u64(n: u64) -> Option<Self> {
180
0
        ChromaSampling::from_i64(n as i64)
181
0
    }
182
}
183
184
impl fmt::Display for ChromaSampling {
185
0
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
186
0
        write!(
187
0
            f,
188
0
            "{}",
189
0
            match self {
190
0
                ChromaSampling::Cs420 => "4:2:0",
191
0
                ChromaSampling::Cs422 => "4:2:2",
192
0
                ChromaSampling::Cs444 => "4:4:4",
193
0
                ChromaSampling::Cs400 => "Monochrome",
194
            }
195
        )
196
0
    }
197
}
198
199
impl ChromaSampling {
200
    /// Provides the amount to right shift the luma plane dimensions to get the
201
    ///  chroma plane dimensions.
202
    /// Only values 0 or 1 are ever returned.
203
    /// The plane dimensions must also be rounded up to accommodate odd luma plane
204
    ///  sizes.
205
    /// Cs400 returns None, as there are no chroma planes.
206
0
    pub const fn get_decimation(self) -> Option<(usize, usize)> {
207
        use self::ChromaSampling::*;
208
0
        match self {
209
0
            Cs420 => Some((1, 1)),
210
0
            Cs422 => Some((1, 0)),
211
0
            Cs444 => Some((0, 0)),
212
0
            Cs400 => None,
213
        }
214
0
    }
215
216
    /// Calculates the size of a chroma plane for this sampling type, given the luma plane dimensions.
217
0
    pub const fn get_chroma_dimensions(
218
0
        self,
219
0
        luma_width: usize,
220
0
        luma_height: usize,
221
0
    ) -> (usize, usize) {
222
0
        if let Some((ss_x, ss_y)) = self.get_decimation() {
223
0
            ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
224
        } else {
225
0
            (0, 0)
226
        }
227
0
    }
228
}
229
230
#[cfg(test)]
231
mod test {
232
    use super::*;
233
234
    #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
235
    use wasm_bindgen_test::*;
236
237
    #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
238
    wasm_bindgen_test_configure!(run_in_browser);
239
240
    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
241
    #[test]
242
    fn asm_stride() {
243
        let tests = [(7, 7, 14), (12, 12, 24), (1234, 1234, 2468)];
244
245
        for (in_stride, u8_bytes, u16_bytes) in tests {
246
            assert_eq!(u8::to_asm_stride(in_stride), u8_bytes);
247
            assert_eq!(u16::to_asm_stride(in_stride), u16_bytes);
248
        }
249
    }
250
251
    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
252
    #[test]
253
    fn type_enum() {
254
        assert!(u8::type_enum() == PixelType::U8);
255
        assert!(u16::type_enum() == PixelType::U16);
256
    }
257
258
    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
259
    #[test]
260
    fn chroma_sampling_from_int() {
261
        let expected = [
262
            (-1, None),
263
            (0, Some(ChromaSampling::Cs420)),
264
            (1, Some(ChromaSampling::Cs422)),
265
            (2, Some(ChromaSampling::Cs444)),
266
            (3, Some(ChromaSampling::Cs400)),
267
            (4, None),
268
        ];
269
270
        for (int, chroma_sampling) in expected {
271
            let converted = ChromaSampling::from_i32(int);
272
            assert_eq!(chroma_sampling, converted);
273
274
            let converted_uint = ChromaSampling::from_u32(int as u32);
275
            assert_eq!(chroma_sampling, converted_uint);
276
        }
277
    }
278
279
    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
280
    #[test]
281
    fn display_chroma_sampling() {
282
        use std::fmt::Write;
283
284
        let all_cs = [
285
            (ChromaSampling::Cs420, "4:2:0"),
286
            (ChromaSampling::Cs422, "4:2:2"),
287
            (ChromaSampling::Cs444, "4:4:4"),
288
            (ChromaSampling::Cs400, "Monochrome"),
289
        ];
290
291
        for (cs, expected) in all_cs {
292
            let mut s = String::new();
293
            write!(&mut s, "{cs}").expect("can display");
294
            assert_eq!(&s, expected);
295
        }
296
    }
297
298
    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
299
    #[test]
300
    fn chroma_sampling_dimensions() {
301
        let tests = [
302
            ((1024, 768), ChromaSampling::Cs444, (1024, 768)),
303
            ((1024, 768), ChromaSampling::Cs422, (512, 768)),
304
            ((1024, 768), ChromaSampling::Cs420, (512, 384)),
305
            ((1024, 768), ChromaSampling::Cs400, (0, 0)),
306
            // Check odd width/height
307
            ((1023, 768), ChromaSampling::Cs422, (512, 768)),
308
            ((1023, 767), ChromaSampling::Cs420, (512, 384)),
309
        ];
310
311
        for (luma, cs, expected_chroma) in tests {
312
            let chroma = cs.get_chroma_dimensions(luma.0, luma.1);
313
            assert_eq!(chroma, expected_chroma);
314
        }
315
    }
316
}