Skip to content
Draft
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@
pub mod chroma;
pub mod frame;
pub mod pixel;
pub mod pixel_ext;
pub mod plane;
31 changes: 27 additions & 4 deletions src/pixel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<u16>
+ From<u8>
+ TryFrom<u16, Error: std::error::Error>
+ 'static
+ private::Sealed
{
/// Conversion from `u16` to a pixel.
///
/// If the pixel is a u8 (single byte), the conversion will be lossy.
/// Use [`From::<u8>::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
}
}
35 changes: 35 additions & 0 deletions src/pixel_ext.rs
Original file line number Diff line number Diff line change
@@ -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::<T>() == 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::<T>() == 2`).
unsafe fn assume_u16(&self) -> &u16;
}

impl<T: Pixel> PixelExt for T {
unsafe fn assume_u8(&self) -> &u8 {
unsafe { std::ptr::from_ref(self).cast::<u8>().as_ref_unchecked() }
}

unsafe fn assume_u16(&self) -> &u16 {
unsafe { std::ptr::from_ref(self).cast::<u16>().as_ref_unchecked() }
}
}
14 changes: 2 additions & 12 deletions src/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,18 +317,8 @@ impl<T: Pixel> Plane<T> {
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,
}
Expand Down
Loading