From 45654259b5cb05aaf9b4af79976b715aaae298da Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Sat, 9 May 2026 12:51:14 +0200 Subject: [PATCH 1/2] refactor!: update Pixel subtraits - decouple from num_traits - remove arithmetics over Pixel - add all std formatting - add minimal conversions --- Cargo.toml | 3 --- src/pixel.rs | 35 ++++++++++++++++++++++++++++++++--- src/plane.rs | 17 +++-------------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c2195db..c04bb20 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/pixel.rs b/src/pixel.rs index f8a1c0e..b90b441 100644 --- a/src/pixel.rs +++ b/src/pixel.rs @@ -22,8 +22,8 @@ //! - 8-bit frames must use `u8` //! - 9-16 bit frames must use `u16` -use num_traits::PrimInt; -use std::fmt::Debug; +use core::fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; +use core::hash::Hash; mod private { pub trait Sealed {} @@ -56,7 +56,36 @@ 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 + + Copy + + Clone + // formatting + + Display + + Debug + + Octal + + LowerHex + + UpperHex + + LowerExp + + UpperExp + + Binary + + Default + // comparisons + + PartialEq + + Eq + + PartialOrd + + Ord + // conversions + + TryInto + + Into + + From + + TryFrom + // markers + + Send + + Sync + // misc. + + Hash + + 'static + + private::Sealed { } diff --git a/src/plane.rs b/src/plane.rs index 0fcae4b..4ee7f04 100644 --- a/src/plane.rs +++ b/src/plane.rs @@ -331,20 +331,9 @@ impl Plane { let total = self.width() * self.height() * 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) - }), + iter: self + .pixels() + .flat_map(move |pix| pix.into().to_le_bytes().into_iter().take(byte_width)), len: total, } } From f961865cbcc91a639eec307dbf6c669ef38dc758 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Sat, 9 May 2026 14:26:05 +0200 Subject: [PATCH 2/2] doc: add explanation of how to use Pixel now --- src/pixel.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/pixel.rs b/src/pixel.rs index b90b441..fc00bec 100644 --- a/src/pixel.rs +++ b/src/pixel.rs @@ -21,6 +21,84 @@ //! The type used must match the bit depth specified when creating frames: //! - 8-bit frames must use `u8` //! - 9-16 bit frames must use `u16` +//! +//! # Working with `T: Pixel` +//! +//! It is often necessary to convert between an abstract pixel and their concrete +//! integer representation in order to work with them, for example to implement an +//! algorithm operating on video frames. +//! +//! For this purpose, `Pixel` is a supertrait of some conversions from the standard +//! library, namely: +//! - `Into` to convert any pixel into a `u16`, +//! - `TryInto` to try to convert any pixel into a `u8` (not possible if `T` is larger +//! than one byte), +//! - `From` to convert any `u8` into a Pixel containing the given value and +//! - `TryFrom` to try to convert any `u16` into a `T` (not possible if `T` is smaller +//! than two bytes). +//! +//! For example, to sum all pixels in a row: +//! +//! ``` +//! use v_frame::frame::Frame; +//! use v_frame::pixel::Pixel; +//! +//! pub fn summed_rows(frame: &Frame) -> Vec { +//! frame.y_plane +//! .rows() +//! // row is &[T] here +//! .map(|row| { +//! row.iter() +//! .map(|&pix| pix.into()) // convert T -> u16 +//! .map(|pix| u64::from(pix)) // widen so sum doesn't overflow +//! .sum() +//! }) +//! .collect() +//! } +//! ``` +//! +//! Note that Pixel only provides one `Into` implementation so it is not necessary to +//! explicitly specify the wanted type (`>::into(pix)`). +//! +//! Here's a somewhat contrived example of how to convert integers into pixels: +//! +//! ``` +//! use v_frame::frame::Frame; +//! use v_frame::pixel::Pixel; +//! +//! fn random_value() -> i32 { +//! // chosen by a fair dice roll +//! 4 +//! } +//! +//! fn clamp_to_range(value: i32, bit_depth: u8) -> u16 { +//! let bit_depth_max = (1u16 << bit_depth) - 1; +//! +//! if value < 0 { +//! 0 +//! } else if value > i32::from(bit_depth_max) { +//! bit_depth_max +//! } else { +//! value as u16 +//! } +//! } +//! +//! pub fn change_some_rows(frame: &mut Frame, bit_depth: u8) { +//! for pix in frame.y_plane.pixels_mut() { +//! let old_val = i32::from((*pix).into()); +//! +//! let new_val = clamp_to_range(old_val + random_value(), bit_depth); +//! if size_of::() == 1 { +//! *pix = T::from(new_val as u8) +//! } else { +//! *pix = T::try_from(new_val).expect("T is u16"); +//! } +//! } +//! } +//! ``` +//! +//! Again, `From` and `TryFrom` are the only implementations for the respective traits +//! so it's not necessary to specify which one is being used. use core::fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; use core::hash::Hash; @@ -41,6 +119,8 @@ mod private { /// data structures and algorithms to work with both standard and high bit-depth /// video content. /// +/// See the [module documentation][crate::pixel] for more details. +/// /// # Type Safety /// /// The library enforces correct type usage through validation: