diff --git a/Cargo.toml b/Cargo.toml index 60dc90c..cca2af2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,6 @@ edition = "2024" repository = "https://github.com/rust-av/v_frame" include = ["Cargo.toml", "README.md", "LICENSE", "src"] -[dependencies] -num-traits = "0.2.19" - [features] padding_api = [] diff --git a/src/lib.rs b/src/lib.rs index 5e26476..8ae738e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,4 +36,5 @@ pub mod chroma; pub mod frame; pub mod pixel; +pub mod pixel_ext; pub mod plane; diff --git a/src/pixel.rs b/src/pixel.rs index f8a1c0e..51700ce 100644 --- a/src/pixel.rs +++ b/src/pixel.rs @@ -22,7 +22,6 @@ //! - 8-bit frames must use `u8` //! - 9-16 bit frames must use `u16` -use num_traits::PrimInt; use std::fmt::Debug; mod private { @@ -56,14 +55,38 @@ mod private { /// i.e. using [`std::mem::zeroed`] must __not__ cause undefined behavior for /// implementing types. pub unsafe trait Pixel: - Debug + Copy + Clone + Default + Send + Sync + PrimInt + 'static + private::Sealed + Sized + + Debug + + Copy + + Clone + + Default + + Send + + Sync + + Into + + From + + TryFrom + + 'static + + private::Sealed { + /// Conversion from `u16` to a pixel. + /// + /// If the pixel is a u8 (single byte), the conversion will be lossy. + /// Use [`From::::from`][From::from] instead if you want to create a pixel from an u8. + fn from_u16(value: u16) -> Self; } /// Pixel implementation for 8-bit video data. // SAFETY: u8 is valid if represented by a zeroed byte. -unsafe impl Pixel for u8 {} +unsafe impl Pixel for u8 { + fn from_u16(value: u16) -> Self { + value as Self + } +} /// Pixel implementation for high bit-depth (9-16 bit) video data. // SAFETY: u16 is valid if represented by zeroed bytes. -unsafe impl Pixel for u16 {} +unsafe impl Pixel for u16 { + fn from_u16(value: u16) -> Self { + value + } +} diff --git a/src/pixel_ext.rs b/src/pixel_ext.rs new file mode 100644 index 0000000..386144e --- /dev/null +++ b/src/pixel_ext.rs @@ -0,0 +1,35 @@ +//! Extensions for the [`Pixel`] trait. +//! +//! Currently only the [`PixelExt`] trait is provided, but more might be +//! added in the future. + +use crate::pixel::Pixel; + +/// Implements additional traits on `Pixel` types for performance-sensitive applications. +pub trait PixelExt { + /// Assume the reference is a reference to a `u8`. + /// + /// # Safety + /// + /// Callers must ensure that the implementing type is a `u8`, for example by + /// checking the size of the type (via `size_of::() == 1`). + unsafe fn assume_u8(&self) -> &u8; + + /// Assume the reference is a reference to a `u16`. + /// + /// # Safety + /// + /// Callers must ensure that the implementing type is a `u16`, for example by + /// checking the size of the type (via `size_of::() == 2`). + unsafe fn assume_u16(&self) -> &u16; +} + +impl PixelExt for T { + unsafe fn assume_u8(&self) -> &u8 { + unsafe { std::ptr::from_ref(self).cast::().as_ref_unchecked() } + } + + unsafe fn assume_u16(&self) -> &u16 { + unsafe { std::ptr::from_ref(self).cast::().as_ref_unchecked() } + } +} diff --git a/src/plane.rs b/src/plane.rs index bf701ee..b5eeb23 100644 --- a/src/plane.rs +++ b/src/plane.rs @@ -317,18 +317,8 @@ impl Plane { let total = self.width().get() * self.height().get() * byte_width; ExactSizeWrapper { iter: self.pixels().flat_map(move |pix| { - let bytes: [u8; 2] = if byte_width == 1 { - [ - pix.to_u8() - .expect("Pixel::byte_data only supports u8 and u16 pixels"), - 0, - ] - } else { - pix.to_u16() - .expect("Pixel::byte_data only supports u8 and u16 pixels") - .to_le_bytes() - }; - bytes.into_iter().take(byte_width) + let pix: u16 = pix.into(); + pix.to_le_bytes().into_iter().take(byte_width) }), len: total, }