Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
115 changes: 112 additions & 3 deletions src/pixel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,87 @@
//! 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<u16>` to convert any pixel into a `u16`,
//! - `TryInto<u8>` to try to convert any pixel into a `u8` (not possible if `T` is larger
//! than one byte),
//! - `From<u8>` to convert any `u8` into a Pixel containing the given value and
//! - `TryFrom<u16>` 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<T: Pixel>(frame: &Frame<T>) -> Vec<u64> {
//! 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<T>` implementation so it is not necessary to
//! explicitly specify the wanted type (`<T as Into<u16>>::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<T: Pixel>(frame: &mut Frame<T>, 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::<T>() == 1 {
//! *pix = T::from(new_val as u8)
//! } else {
//! *pix = T::try_from(new_val).expect("T is u16");
//! }
//! }
//! }
//! ```
//!
//! Again, `From<u8>` and `TryFrom<u16>` are the only implementations for the respective traits
//! so it's not necessary to specify which one is being used.

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 {}
Expand All @@ -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:
Expand All @@ -56,7 +136,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<u8, Error: core::error::Error>
+ Into<u16>
+ From<u8>
+ TryFrom<u16, Error: core::error::Error>
// markers
+ Send
+ Sync
// misc.
+ Hash
+ 'static
+ private::Sealed
{
}

Expand Down
17 changes: 3 additions & 14 deletions src/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,20 +331,9 @@ impl<T: Pixel> Plane<T> {

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,
}
}
Expand Down