diff --git a/nokhwa-core/src/decoder.rs b/nokhwa-core/src/decoder.rs index 8870d61..d4efe24 100644 --- a/nokhwa-core/src/decoder.rs +++ b/nokhwa-core/src/decoder.rs @@ -4,11 +4,13 @@ use crate::image::{DecodedImage, NonFloatScalarWidth}; use crate::types::{Resolution}; pub use image::{ImageBuffer, Pixel, Primitive}; use std::fmt::Debug; +use bytemuck::try_cast_slice_mut; +use crate::pixel_destination::PixelDestination; pub trait Decoder { type Config: Clone + Debug; type OutputMeta: Clone + Debug; - type DestinationFormatHint: Clone + Debug; + const SUPPORTED_DESTINATIONS: &'static [PixelDestination]; fn config(&self) -> &Self::Config; @@ -18,16 +20,29 @@ pub trait Decoder { &mut self, to_decode: FrameBuffer, buffer: impl AsMut<[u8]>, - destination_format_hint: Option, + destination_format: PixelDestination, ) -> Result; - fn decode_to_pixel_buffer( - &mut self, - to_decode: FrameBuffer, - buffer: impl AsMut<[P::Subpixel]>, - ) -> Result + fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[P::Subpixel]>) -> Result where -

::Subpixel: NonFloatScalarWidth; +

::Subpixel: NonFloatScalarWidth + { + let destination = match PixelDestination::get_by_pixel::

() { + Some(dest) => dest, + None => return Err(NokhwaError::DecoderUnknownDestinationPixelFormat(P::COLOR_MODEL, P::Subpixel::WIDTH_BYTES)) + }; + + if !Self::supports_destination(destination) { + return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination)) + } + + let buffer = buffer.as_mut(); + + let cast_slice = try_cast_slice_mut::(buffer) + .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?; + + self.decode_to_buffer(to_decode, cast_slice, destination) + } fn decode( &mut self, @@ -36,16 +51,16 @@ pub trait Decoder { where

::Subpixel: NonFloatScalarWidth; - fn output_decoder_min_size_pixel

(&self, resolution: Resolution) -> usize where + fn output_decoder_min_size_pixel

(&self, resolution: Resolution) -> Result where P: Pixel,

::Subpixel: NonFloatScalarWidth { - let channels = P::CHANNEL_COUNT as usize; - let width_bytes = <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES; - let resolution_mult = resolution.height() * resolution.width(); - (resolution_mult as usize) * (width_bytes as usize) * channels + PixelDestination::get_by_pixel::

().map(|dest| self.output_decoder_min_size(resolution, dest)).ok_or(NokhwaError::DecoderUnknownDestinationPixelFormat(P::COLOR_MODEL, P::Subpixel::WIDTH_BYTES))? + } - fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize; + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: PixelDestination) -> Result; - fn buffer_takes_destination_hint(&self) -> bool; + fn supports_destination(pixel_destination: PixelDestination) -> bool { + Self::SUPPORTED_DESTINATIONS.contains(&pixel_destination) + } } diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index b000bca..66cf753 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -16,6 +16,7 @@ use crate::frame_format::{CustomFrameFormat, FrameFormat}; use std::fmt::Debug; use thiserror::Error; +use crate::pixel_destination::PixelDestination; use crate::types::Backends; pub type NokhwaResult = Result; @@ -68,8 +69,10 @@ pub enum NokhwaError { DecoderUnsupportedFrameFormat(FrameFormat), #[error("The destination frame format from {0} to {1} is not supported.")] DecoderUnsupportedCustomFrameFormatDestination(CustomFrameFormat, FrameFormat), - #[error("Unsupported pixel configuration {0} with width {1}b.")] - DecoderUnsupportedDestinationPixelFormat(&'static str, u32), + #[error("Unknown pixel configuration {0} with width {1}b.")] + DecoderUnknownDestinationPixelFormat(&'static str, u32), + #[error("Unsupported pixel configuration {0}.")] + DecoderUnsupportedDestinationPixelFormat(PixelDestination), #[error("Bad decoder configuration: {0}")] DecoderInvalidConfiguration(String), #[error("Failed to initialize decoder: {0}")] diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index 415622d..bd62575 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -35,3 +35,4 @@ pub mod stream; pub mod traits; pub mod types; pub mod utils; +pub mod pixel_destination; diff --git a/nokhwa-core/src/pixel_destination.rs b/nokhwa-core/src/pixel_destination.rs new file mode 100644 index 0000000..c2ee930 --- /dev/null +++ b/nokhwa-core/src/pixel_destination.rs @@ -0,0 +1,67 @@ +use std::fmt::{Display, Formatter}; +use image::Pixel; +use crate::image::NonFloatScalarWidth; + +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +pub enum PixelDestination { + Rgb8, + Rgba8, + Rgb16, + Rgba16, + Bgr8, + Bgra8, + Bgr16, + Bgra16, + Luma8, + LumaA8, + Luma16, + LumaA16, +} + +impl PixelDestination { + pub fn get_by_pixel

() -> Option + where + P: Pixel, +

::Subpixel: NonFloatScalarWidth, + { + match P::COLOR_MODEL { + "RGB" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::Rgb8), + 2 => Some(PixelDestination::Rgb16), + _ => None, + }, + "RGBA" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::Rgba8), + 2 => Some(PixelDestination::Rgba16), + _ => None, + }, + "BGR" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::Bgr8), + 2 => Some(PixelDestination::Bgr16), + _ => None, + }, + "BGRA" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::Bgra8), + 2 => Some(PixelDestination::Bgra16), + _ => None, + }, + "Y" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::Luma8), + 2 => Some(PixelDestination::Luma16), + _ => None, + }, + "YA" => match P::Subpixel::WIDTH_BYTES { + 1 => Some(PixelDestination::LumaA8), + 2 => Some(PixelDestination::LumaA16), + _ => None, + } + _ => None, + } + } +} + +impl Display for PixelDestination { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index 09fed97..963244e 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -1,7 +1,6 @@ use crate::camera::Camera; use crate::error::NokhwaResult; -use crate::types::{Backends, CameraIndex, CameraInformation, QueriedCamera}; -use std::fmt::{Display, Formatter}; +use crate::types::{Backends, CameraIndex, QueriedCamera}; pub trait PlatformTrait { diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 868cc22..7e44381 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize}; use std::num::NonZeroI32; use std::ops::{Div, Rem}; use std::{ - borrow::Borrow, cmp::Ordering, fmt::{Debug, Display, Formatter}, hash::Hash, diff --git a/nokhwa-decoders/Cargo.toml b/nokhwa-decoders/Cargo.toml index 05e26a2..e301f27 100644 --- a/nokhwa-decoders/Cargo.toml +++ b/nokhwa-decoders/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" ffmpeg = ["ffmpeg-the-third"] yuyv = ["dcv-color-primitives", "yuv"] mjpeg = ["zune-jpeg", "zune-core"] -luma = ["itertools"] +luma = ["itertools", "nokhwa-iter-extensions", "itermore"] #static = ["ffmpeg-the-third/static"] #async = [] @@ -19,7 +19,7 @@ version = "0.2" path = "../nokhwa-core" [dependencies.ffmpeg-the-third] -version = "3.0" +version = "4.0.0+ffmpeg-8.0" optional = true [dependencies.yuv] @@ -31,7 +31,7 @@ version = "0.7" optional = true [dependencies.zune-jpeg] -version = "0.5.0-rc7" +version = "0.5.0-rc9" optional = true [dependencies.zune-core] @@ -39,7 +39,17 @@ version = "0.5.0-rc2" optional = true [dependencies.itertools] -version = "0.14.0" +version = "0.14" +optional = true + +[dependencies.itermore] +version = "0.8" +features = ["array_chunks"] +optional = true + +[dependencies.nokhwa-iter-extensions] +version = "0.1" +path = "../nokhwa-iter-extensions" optional = true [dev-dependencies.image] diff --git a/nokhwa-decoders/src/lib.rs b/nokhwa-decoders/src/lib.rs index bc1772d..7a9206e 100644 --- a/nokhwa-decoders/src/lib.rs +++ b/nokhwa-decoders/src/lib.rs @@ -1,5 +1,3 @@ -extern crate core; - #[cfg(feature = "ffmpeg")] pub mod ffmpeg; #[cfg(feature = "mjpeg")] diff --git a/nokhwa-decoders/src/luma.rs b/nokhwa-decoders/src/luma.rs index 38406f3..e6eb0aa 100644 --- a/nokhwa-decoders/src/luma.rs +++ b/nokhwa-decoders/src/luma.rs @@ -1,6 +1,8 @@ +use std::borrow::Cow; use std::collections::HashMap; use std::fmt::Debug; use std::mem::swap; +use bytemuck::{cast_mut, cast_slice, cast_slice_mut, try_cast_slice_mut}; use image::Pixel; use itertools::Itertools; use nokhwa_core::decoder::Decoder; @@ -9,6 +11,10 @@ use nokhwa_core::frame_buffer::FrameBuffer; use nokhwa_core::frame_format::{CustomFrameFormat, FrameFormat}; use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth}; use nokhwa_core::types::{CameraFormat, Resolution}; +use nokhwa_iter_extensions::duplicate::IterDuplicateConst; +use nokhwa_iter_extensions::interweave::IterInterweave; +use itermore::{IterArrayChunks}; +use nokhwa_core::pixel_destination::PixelDestination; #[derive(Clone, Debug, PartialEq)] pub struct LumaDecoder { @@ -18,7 +24,16 @@ pub struct LumaDecoder { impl Decoder for LumaDecoder { type Config = LumaConfig; type OutputMeta = (); - type DestinationFormatHint = LumaDestination; + const SUPPORTED_DESTINATIONS: &'static [PixelDestination] = &[ + PixelDestination::Luma8, + PixelDestination::LumaA8, + PixelDestination::Luma16, + PixelDestination::LumaA16, + PixelDestination::Rgb8, + PixelDestination::Rgba8, + PixelDestination::Rgb16, + PixelDestination::Rgba16, + ]; fn config(&self) -> &Self::Config { &self.luma_config @@ -29,8 +44,8 @@ impl Decoder for LumaDecoder { Ok(()) } - fn decode_to_buffer(&mut self, mut to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format_hint: Option) -> Result { - let destination_hint = match destination_format_hint { + fn decode_to_buffer(&mut self, mut to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format: PixelDestination) -> Result { + let destination_hint = match destination_format { Some(h) => h, None => return Err(NokhwaError::DecoderDestinationHintRequired) }; @@ -63,40 +78,180 @@ impl Decoder for LumaDecoder { let b_u16 = filter_to_u16(self.config().channel_filters.blue); let a_u16 = filter_to_u16(self.config().channel_filters.alpha); + let buffer = buffer.as_mut(); + match format { FrameFormat::Luma_8 => { match destination_hint { - LumaDestination::Luma8 => { - swap(to_decode.as_mut(), buffer.as_mut()) + PixelDestination::Luma8 => { + if to_decode.len() != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + match to_decode.consume().0 { + Cow::Borrowed(data) => { + buffer.copy_from_slice(data) + } + Cow::Owned(mut owned) => { + buffer.swap_with_slice(owned.as_mut_slice()) + } + } } - LumaDestination::LumaA8 => { + PixelDestination::LumaA8 => { let default_alpha = u8::MAX * a; - to_decode.buffer().into_iter().intersperse(default_alpha).co + if (to_decode.len() * 2) != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + to_decode.buffer().into_iter().interweave(&default_alpha, false).enumerate() + .for_each(|(len, data)| { + unsafe { + *buffer.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::Rgb8 => { + if (to_decode.len() * 3) != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + to_decode.buffer().into_iter().duplicate_const::<3>().arrays::<3>().map(|pixel| { + let px_r = &pixel[0_usize] * r; + let px_g = &pixel[1_usize] * g; + let px_b = &pixel[2_usize] * b; + [px_r, px_g, px_b] + }).flatten().enumerate().for_each(|(len, data)| { + unsafe { + *buffer.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::Rgba8 => { + if (to_decode.len() * 4) != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + to_decode.buffer().into_iter().duplicate_const::<3>().arrays::<3>().map(|pixel| { + let px_r = &pixel[0_usize] * r; + let px_g = &pixel[1_usize] * g; + let px_b = &pixel[2_usize] * b; + let px_a = 255 * b; + [px_r, px_g, px_b, px_a] + }).flatten().enumerate().for_each(|(len, data)| { + unsafe { + *buffer.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::Rgb16 => { + if (to_decode.len() * 6) != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + let temp_buffer = cast_slice_mut::(buffer); + let factor = match self.config().mode { + ConvertMode::Scaled => { + u16::MAX / (u8::MAX as u16) + } + ConvertMode::Clipped => { + 1_u16 + } + }; + + to_decode.buffer().into_iter().duplicate_const::<3>().arrays::<3>().map(|pixel| { + let px_r = (&pixel[0_usize] as u16) * r_u16 * factor; + let px_g = (&pixel[1_usize] as u16) * g_u16 * factor; + let px_b = (&pixel[2_usize] as u16) * b_u16 * factor; + [px_r, px_g, px_b] + }).flatten().enumerate().for_each(|(len, data)| { + unsafe { + *temp_buffer.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::Rgba16 => { + if (to_decode.len() * 8) != buffer.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + let temp_buffer = cast_slice_mut::(buffer); + let factor = match self.config().mode { + ConvertMode::Scaled => { + u16::MAX / (u8::MAX as u16) + } + ConvertMode::Clipped => { + 1_u16 + } + }; + + to_decode.buffer().into_iter().duplicate_const::<3>().arrays::<3>().map(|pixel| { + let px_r = (&pixel[0_usize] as u16) * r_u16 * factor; + let px_g = (&pixel[1_usize] as u16) * g_u16 * factor; + let px_b = (&pixel[2_usize] as u16) * b_u16 * factor; + let px_a = u16::MAX * a_u16; + [px_r, px_g, px_b, px_a] + }).flatten().enumerate().for_each(|(len, data)| { + unsafe { + *temp_buffer.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::Luma16 => { + let buffer_u16 = cast_slice_mut::(buffer); + + if to_decode.len() != buffer_u16.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + let factor = match self.config().mode { + ConvertMode::Scaled => 8, + ConvertMode::Clipped => 0, + }; + + to_decode.buffer().into_iter().map(|px| { + (*px as u16) << factor + }).enumerate() + .for_each(|(len, data)| { + unsafe { + *buffer_u16.get_unchecked_mut(len) = *data; + } + }); + } + PixelDestination::LumaA16 => { + let default_alpha = u16::MAX * a_u16; + let buffer_u16 = cast_slice_mut::(buffer); + + if (to_decode.len() * 2) != buffer_u16.len() { + return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!")) + } + + let factor = match self.config().mode { + ConvertMode::Scaled => 8, + ConvertMode::Clipped => 0, + }; + + to_decode.buffer().into_iter().map(|px| { + (*px as u16) << factor + }).interweave(&default_alpha, false).enumerate() + .for_each(|(len, data)| { + unsafe { + *buffer_u16.get_unchecked_mut(len) = *data; + } + }); } - LumaDestination::Rgb8 => {} - LumaDestination::Rgba8 => {} - LumaDestination::Rgb16 => {} - LumaDestination::Rgba16 => {} } } - FrameFormat::Luma_10 => {} - FrameFormat::Luma_12 => {} - FrameFormat::Luma_14 => {} - FrameFormat::Luma_16 | FrameFormat::Depth_16 => {} + FrameFormat::Luma_10 => convert_u16_type_buffers(to_decode, buffer, destination_hint, self.config().mode, self.config().channel_filters, 10), + FrameFormat::Luma_12 => convert_u16_type_buffers(to_decode, buffer, destination_hint, self.config().mode, self.config().channel_filters, 12), + FrameFormat::Luma_14 => convert_u16_type_buffers(to_decode, buffer, destination_hint, self.config().mode, self.config().channel_filters, 14), + FrameFormat::Luma_16 | FrameFormat::Depth_16 => convert_u16_type_buffers(to_decode, buffer, destination_hint, self.config().mode, self.config().channel_filters, 16), fmt => { return Err(NokhwaError::DecoderUnsupportedFrameFormat(fmt)) } } } - fn decode_to_pixel_buffer(&mut self, to_decode: FrameBuffer, buffer: impl AsMut<[P::Subpixel]>) -> Result - where -

::Subpixel: NonFloatScalarWidth - { - todo!() - } - fn decode(&mut self, to_decode: FrameBuffer) -> Result, NokhwaError> where

::Subpixel: NonFloatScalarWidth @@ -104,13 +259,10 @@ impl Decoder for LumaDecoder { todo!() } - fn output_decoder_min_size(&self, resolution: Resolution, destination_format: Self::DestinationFormatHint) -> usize { + fn output_decoder_min_size(&self, resolution: Resolution, destination_format: PixelDestination) -> usize { todo!() } - fn buffer_takes_destination_hint(&self) -> bool { - todo!() - } } fn filter_to_u8(filter: bool) -> u8 { @@ -130,12 +282,213 @@ fn filter_to_u16(filter: bool) -> u16 { } } +fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint: PixelDestination, mode: ConvertMode, channel_filters: ChannelFilters, original_bit_num: u32) -> Result<(), NokhwaError> { + let r_u16 = filter_to_u16(channel_filters.red); + let g_u16 = filter_to_u16(channel_filters.green); + let b_u16 = filter_to_u16(channel_filters.blue); + let a_u16 = filter_to_u16(channel_filters.alpha); + let r = filter_to_u8(channel_filters.red); + let g = filter_to_u8(channel_filters.green); + let b = filter_to_u8(channel_filters.blue); + let a = filter_to_u8(channel_filters.alpha); + + match hint { + PixelDestination::Luma8 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + + if to_decode_u16.len() != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().map(|px| { + (*px >> factor) as u8 + }).enumerate() + .for_each(|(len, data)| { + unsafe { + *destination.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::LumaA8 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + + if (to_decode_u16.len() * 2) != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().map(|px| { + (*px >> factor) as u8 + }).interweave(255 * a, false).enumerate() + .for_each(|(len, data)| { + unsafe { + *destination.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::Rgb8 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + + if (to_decode_u16.len() * 3) != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().duplicate_const::<3>().array_chunks::<3>().map(|px| { + let px_r = (&px[0_usize] >> factor) * r; + let px_g = (&px[1_usize] >> factor) * g; + let px_b = (&px[2_usize] >> factor)* b; + [px_r as u8, px_g as u8, px_b as u8] + }).flatten().for_each(|(len, data)| { + unsafe { + *destination.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::Rgba8 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + + if (to_decode_u16.len() * 4) != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().duplicate_const::<3>().array_chunks::<3>().map(|px| { + let px_r = (&px[0_usize] >> factor) * r_u16 ; + let px_g = (&px[1_usize] >> factor) * g_u16 ; + let px_b = (&px[2_usize] >> factor) * b_u16 ; + let px_a = u8::MAX * a; + [px_r as u8, px_g as u8, px_b as u8, px_a] + }).flatten().for_each(|(len, data)| { + unsafe { + *destination.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::Rgb16 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + let destination_buffer_u16 = cast_slice_mut::(destination); + + if (to_decode_u16.len() * 3) != destination_buffer_u16.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().duplicate_const::<3>().array_chunks::<3>().map(|px| { + let px_r = (&px[0_usize] >> factor) * r_u16; + let px_g = (&px[1_usize] >> factor) * g_u16; + let px_b = (&px[2_usize] >> factor) * b_u16; + [px_r, px_g, px_b] + }).flatten().for_each(|(len, data)| { + unsafe { + *destination_buffer_u16.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::Rgba16 => { + let to_decode_u16 = cast_slice::(to_decode.buffer()); + let destination_buffer_u16 = cast_slice_mut::(destination); + + if (to_decode_u16.len() * 3) != destination_buffer_u16.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let factor = match mode { + ConvertMode::Scaled => { + original_bit_num - 8_u32 + } + ConvertMode::Clipped => 0, + }; + + to_decode_u16.into_iter().duplicate_const::<3>().array_chunks::<3>().map(|px| { + let px_r = (&px[0_usize] >> factor) * r_u16; + let px_g = (&px[1_usize] >> factor) * g_u16; + let px_b = (&px[2_usize] >> factor) * b_u16; + let px_a = u16::MAX * a_u16; + [px_r, px_g, px_b, px_a] + }).flatten().for_each(|(len, data)| { + unsafe { + *destination_buffer_u16.get_unchecked_mut(len) = *data; + } + }); + Ok(()) + } + PixelDestination::Luma16 => { + if to_decode.len() != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + match to_decode.consume().0 { + Cow::Borrowed(data) => { + destination.copy_from_slice(data) + } + Cow::Owned(mut owned) => { + destination.swap_with_slice(owned.as_mut_slice()) + } + } + Ok(()) + } + PixelDestination::LumaA16 => { + if (to_decode.len() * 2) != destination.len() { + return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string())) + } + + let to_decode_u16 = cast_slice::(to_decode.buffer()); + let destination_buffer_u16 = cast_slice_mut::(destination); + + to_decode_u16.into_iter().interweave(&(u16::MAX * a_u16), false).enumerate().for_each(|(len, data)| { + unsafe { + *destination.get_unchecked_mut(len) = *data; + } + }); + + Ok(()) + } + _ => Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(hint)) + } + +} #[derive(Clone, Debug, PartialEq)] pub struct LumaConfig { pub mode: ConvertMode, - pub scaling_functions: ScalingFunctions, pub channel_filters: ChannelFilters, pub format: FrameFormat, pub custom_frame_format_map: Option> @@ -151,7 +504,6 @@ impl TryFrom for LumaConfig { Ok(LumaConfig { mode: ConvertMode::default(), - scaling_functions: ScalingFunctions::default(), channel_filters: ChannelFilters::default(), format: value, custom_frame_format_map: None, @@ -159,7 +511,7 @@ impl TryFrom for LumaConfig { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] pub enum ConvertMode { Scaled, Clipped @@ -171,12 +523,6 @@ impl Default for ConvertMode { } } -#[derive(Clone, Debug, Default)] -pub struct ScalingFunctions { - pub scale_up_u8_to_u16: Box u16>, - pub scale_down_u8_to_u16: Box u8>, -} - #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] pub struct ChannelFilters { pub red: bool, @@ -195,34 +541,3 @@ impl Default for ChannelFilters { } } } - -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum LumaDestination { - Luma8, - LumaA8, - Rgb8, - Rgba8, - Rgb16, - Rgba16, -} - -struct ConstIter where T: Copy + Clone + Debug + Default + Eq + Ord + PartialEq + PartialOrd { - pub val: T -} - -impl Iterator for ConstIter where T: Copy + Clone + Debug + Default + Eq + Ord + PartialEq + PartialOrd { - type Item = T; - - fn next(&mut self) -> Option { - Some(self.val) - } -} - -impl IntoIterator for ConstIter where T: Copy + Clone + Debug + Default + Eq + Ord + PartialEq + PartialOrd { - type Item = T; - type IntoIter = ConstIter; - - fn into_iter(self) -> Self::IntoIter { - self - } -} \ No newline at end of file diff --git a/nokhwa-decoders/src/mjpeg.rs b/nokhwa-decoders/src/mjpeg.rs index 92fe35c..1c6f53c 100644 --- a/nokhwa-decoders/src/mjpeg.rs +++ b/nokhwa-decoders/src/mjpeg.rs @@ -1,4 +1,3 @@ -use bytemuck::cast_slice_mut; use nokhwa_core::decoder::{Decoder, ImageBuffer, Pixel}; use nokhwa_core::error::NokhwaError; use nokhwa_core::frame_buffer::FrameBuffer; @@ -11,6 +10,7 @@ pub use zune_core::options::DecoderOptions; pub use zune_jpeg::ImageInfo; use zune_jpeg::JpegDecoder; use zune_jpeg::errors::DecodeErrors; +use nokhwa_core::pixel_destination::PixelDestination; #[derive(Clone, Debug)] pub struct MJpegDecoder { @@ -20,7 +20,14 @@ pub struct MJpegDecoder { impl Decoder for MJpegDecoder { type Config = MJpegOptions; type OutputMeta = ImageMeta; - type DestinationFormatHint = OutputColor; + const SUPPORTED_DESTINATIONS: &'static [PixelDestination] = &[ + PixelDestination::Rgb8, + PixelDestination::Rgba8, + PixelDestination::Bgr8, + PixelDestination::Bgra8, + PixelDestination::Luma8, + PixelDestination::LumaA8, + ]; fn config(&self) -> &Self::Config { &self.config @@ -35,27 +42,22 @@ impl Decoder for MJpegDecoder { &mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, - destination_format_hint: Option, + destination_format: PixelDestination, ) -> Result { let buffer = buffer.as_mut(); let cursor = ZCursor::new(to_decode.as_ref()); let mut decoder = JpegDecoder::new(cursor); - let mut config = self.config.decoder_options; - if let Some(dest_hint) = destination_format_hint { - config = self - .config - .decoder_options - .jpeg_set_out_colorspace(dest_hint.into()); - } + let colorspace = convert_destination_to_colorspace(destination_format).ok_or(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination_format))?; + + let mut config = self.config.decoder_options.jpeg_set_out_colorspace(colorspace); decoder.set_options(config); decoder.decode_into(buffer).map_err(err_to_err)?; let info = match decoder.info() { Some(i) => i, - None => { return Err(NokhwaError::Decoder( "??????? how did we get here".to_string(), @@ -66,27 +68,6 @@ impl Decoder for MJpegDecoder { Ok(info.into()) } - fn decode_to_pixel_buffer( - &mut self, - to_decode: FrameBuffer, - mut buffer: impl AsMut<[P::Subpixel]>, - ) -> Result - where -

::Subpixel: NonFloatScalarWidth, - { - let hint = match pixel_to_colorspace::

() { - Some(cs) => cs, - None => { - return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat( - P::COLOR_MODEL, - <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES, - )); - } - }; - let meta = self.decode_to_buffer(to_decode, cast_slice_mut(buffer.as_mut()), Some(hint))?; - Ok(meta) - } - fn decode( &mut self, to_decode: FrameBuffer, @@ -94,7 +75,7 @@ impl Decoder for MJpegDecoder { where

::Subpixel: NonFloatScalarWidth, { - let min_size = self.output_decoder_min_size_pixel::

(self.config.resolution); + let min_size = self.output_decoder_min_size_pixel::

(self.config.resolution)?; let mut out_buffer: Vec = vec![P::Subpixel::DEFAULT_MAX_VALUE; min_size]; let output_metadata = self.decode_to_pixel_buffer::

(to_decode, out_buffer.as_mut_slice())?; @@ -112,21 +93,18 @@ impl Decoder for MJpegDecoder { fn output_decoder_min_size( &self, resolution: Resolution, - destination_format: Self::DestinationFormatHint, - ) -> usize { + destination_format: PixelDestination, + ) -> Result { let stride = match destination_format { - OutputColor::Rgb => 3, - OutputColor::RgbA => 4, - OutputColor::Bgr => 3, - OutputColor::BgrA => 4, - OutputColor::Luma => 1, - OutputColor::LumaA => 2, + PixelDestination::Rgb8 => 3, + PixelDestination::Rgba8 => 4, + PixelDestination::Bgr8 => 3, + PixelDestination::Bgra8 => 4, + PixelDestination::Luma8 => 1, + PixelDestination::LumaA8 => 2, + fmt => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(fmt)) }; - (resolution.width() * resolution.height() * stride) as usize - } - - fn buffer_takes_destination_hint(&self) -> bool { - true + Ok((resolution.width() * resolution.height() * stride) as usize) } } @@ -159,45 +137,6 @@ pub struct MJpegOptions { pub decoder_options: DecoderOptions, } -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] -pub enum OutputColor { - Rgb, - RgbA, - Bgr, - BgrA, - Luma, - LumaA, -} - -impl From for ColorSpace { - fn from(val: OutputColor) -> Self { - match val { - OutputColor::Rgb => ColorSpace::RGB, - OutputColor::RgbA => ColorSpace::RGBA, - OutputColor::Bgr => ColorSpace::BGR, - OutputColor::BgrA => ColorSpace::BGRA, - OutputColor::Luma => ColorSpace::Luma, - OutputColor::LumaA => ColorSpace::LumaA, - } - } -} - -fn pixel_to_colorspace

() -> Option -where - P: Pixel, -

::Subpixel: NonFloatScalarWidth, -{ - match P::COLOR_MODEL { - "RGBA" => Some(OutputColor::RgbA), - "RGB" => Some(OutputColor::Rgb), - "BGR" => Some(OutputColor::Bgr), - "BGRA" => Some(OutputColor::BgrA), - "Y" => Some(OutputColor::Luma), - "YA" => Some(OutputColor::LumaA), - _ => None, - } -} - fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { match decode_errors { DecodeErrors::Format(fmt) => NokhwaError::Decoder(fmt), @@ -230,3 +169,15 @@ fn err_to_err(decode_errors: DecodeErrors) -> NokhwaError { DecodeErrors::IoErrors(io) => NokhwaError::Decoder(format!("io error: {io:?}")), } } + +fn convert_destination_to_colorspace(pixel_destination: PixelDestination) -> Option { + match pixel_destination { + PixelDestination::Rgb8 => Some(ColorSpace::RGB), + PixelDestination::Rgba8 => Some(ColorSpace::RGBA), + PixelDestination::Bgr8 => Some(ColorSpace::BGR), + PixelDestination::Bgra8 => Some(ColorSpace::BGRA), + PixelDestination::Luma8 => Some(ColorSpace::Luma), + PixelDestination::LumaA8 => Some(ColorSpace::LumaA), + _ => None, + } +} diff --git a/nokhwa-decoders/src/yuv.rs b/nokhwa-decoders/src/yuv.rs index b21cc11..c3dda88 100644 --- a/nokhwa-decoders/src/yuv.rs +++ b/nokhwa-decoders/src/yuv.rs @@ -22,6 +22,7 @@ use yuv::{ yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr, yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16, }; +use nokhwa_core::pixel_destination::PixelDestination; pub struct YUVDecoder { config: YUVConfig, @@ -36,7 +37,12 @@ impl YUVDecoder { impl Decoder for YUVDecoder { type Config = YUVConfig; type OutputMeta = (); - type DestinationFormatHint = YUVDestination; + const SUPPORTED_DESTINATIONS: &'static [PixelDestination] = &[ + PixelDestination::Rgb8, + PixelDestination::Rgba8, + PixelDestination::Rgb16, + PixelDestination::Rgba16, + ]; fn config(&self) -> &Self::Config { &self.config @@ -63,7 +69,7 @@ impl Decoder for YUVDecoder { &mut self, to_decode: FrameBuffer<'_>, mut buffer: impl AsMut<[u8]>, - destination_format: Option, + destination_format: PixelDestination, ) -> Result { let destination_format = match destination_format { Some(df) => df, @@ -103,49 +109,49 @@ impl Decoder for YUVDecoder { match yuv_format { FrameFormat::Ayuv_32 => { match destination_format { - YUVDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), - YUVDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + PixelDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), + PixelDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.premultiply_alpha)), _ => None, } } FrameFormat::Yuyv_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, stride_4px_2w, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, stride_4px_2w, self.config.range, self.config.matrix)), + PixelDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Uyvy_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Vyuy_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } FrameFormat::Yvyu_4_2_2 => { match destination_format { - YUVDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), - YUVDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)), + PixelDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), } } _ => { @@ -162,74 +168,74 @@ impl Decoder for YUVDecoder { match yuv_format { FrameFormat::NV24 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV42 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV16 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV61 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV12 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::NV21 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), _ => None, } } FrameFormat::P010 => { let a = convert_bi_planar_image_to_u16(image); match destination_format { - YUVDestination::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), - YUVDestination::Rgb16 => Some(p010_to_rgb10(&a, cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(p010_to_rgba10(&a, cast_slice_mut(buffer), stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)), + PixelDestination::Rgb16 => Some(p010_to_rgb10(&a, cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(p010_to_rgba10(&a, cast_slice_mut(buffer), stride_4px, self.config.range, self.config.matrix)), // _ => None, } } FrameFormat::P012 => { match destination_format { - YUVDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), - YUVDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_4px_2w, self.config.range, self.config.matrix)), + PixelDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)), + PixelDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_4px_2w, self.config.range, self.config.matrix)), _ => None, } } @@ -247,10 +253,10 @@ impl Decoder for YUVDecoder { match yuv_format { FrameFormat::Yuv_4_2_0 => { match destination_format { - YUVDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), - YUVDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), - YUVDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)), + PixelDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)), + PixelDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)), _ => None, } } @@ -273,31 +279,6 @@ impl Decoder for YUVDecoder { } } - fn decode_to_pixel_buffer( - &mut self, - to_decode: FrameBuffer<'_>, - mut buffer: impl AsMut<[P::Subpixel]>, - ) -> Result - where -

::Subpixel: NonFloatScalarWidth, - { - let destination = match YUVDestination::get_by_pixel::

() { - None => { - return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat( - P::COLOR_MODEL, - <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES, - )); - } - Some(d) => d, - }; - let buffer = buffer.as_mut(); - - let cast_slice = try_cast_slice_mut::(buffer) - .map_err(|why| NokhwaError::DecoderInvalidBuffer(why.to_string()))?; - - self.decode_to_buffer(to_decode, cast_slice, Some(destination)) - } - fn decode( &mut self, to_decode: FrameBuffer<'_>, @@ -324,63 +305,19 @@ impl Decoder for YUVDecoder { fn output_decoder_min_size( &self, resolution: Resolution, - destination_format: Self::DestinationFormatHint, - ) -> usize { + destination_format: PixelDestination, + ) -> Result { let px_size = match destination_format { - YUVDestination::Rgb8 | YUVDestination::Bgr8 => 3, - YUVDestination::Rgba8 | YUVDestination::Bgra8 => 4, - YUVDestination::Rgb16 => 3 * 2, - YUVDestination::Rgba16 => 4 * 2, + PixelDestination::Rgb8 | PixelDestination::Bgr8 => 3, + PixelDestination::Rgba8 | PixelDestination::Bgra8 => 4, + PixelDestination::Rgb16 => 3 * 2, + PixelDestination::Rgba16 => 4 * 2, + _ => return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination_format)) }; let reso = resolution.width() * resolution.height(); - (reso as usize) * (px_size as usize) + Ok((reso as usize) * (px_size as usize)) } - fn buffer_takes_destination_hint(&self) -> bool { - true - } -} - -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] -pub enum YUVDestination { - Rgb8, - Rgba8, - Rgb16, - Rgba16, - Bgr8, - Bgra8, -} - -impl YUVDestination { - pub fn get_by_pixel

() -> Option - where - P: Pixel, -

::Subpixel: NonFloatScalarWidth, - { - match P::COLOR_MODEL { - "RGB" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { - 1 => Some(YUVDestination::Rgb8), - 2 => Some(YUVDestination::Rgb16), - _ => None, - }, - "RGBA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { - 1 => Some(YUVDestination::Rgba8), - 2 => Some(YUVDestination::Rgba16), - _ => None, - }, - "BGR" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { - 1 => Some(YUVDestination::Bgr8), - // 2 => Some(YUVDestination::Bgr16), - _ => None, - }, - "BGRA" => match <

::Subpixel as NonFloatScalarWidth>::WIDTH_BYTES { - 1 => Some(YUVDestination::Bgra8), - // 2 => Some(YUVDestination::Bgra16), - _ => None, - }, - _ => None, - } - } } #[derive(Clone, Debug, PartialEq)] diff --git a/nokhwa-iter-extensions/Cargo.toml b/nokhwa-iter-extensions/Cargo.toml index a6a0556..52084f1 100644 --- a/nokhwa-iter-extensions/Cargo.toml +++ b/nokhwa-iter-extensions/Cargo.toml @@ -3,7 +3,7 @@ name = "nokhwa-iter-extensions" version = "0.1.0" authors = ["l1npengtul "] edition = "2024" -description = "Core type definitions for nokhwa" +description = "Iterator extensions for nokhwa: duplicate, interweave, and " keywords = ["iter", "iterator", "interweave", "duplicate"] categories = ["algorithms", "rust-patterns", "no-std", "no-std::no-alloc"] license = "MIT OR Apache-2.0" diff --git a/nokhwa-iter-extensions/src/lib.rs b/nokhwa-iter-extensions/src/lib.rs index c158f7a..c349c9a 100644 --- a/nokhwa-iter-extensions/src/lib.rs +++ b/nokhwa-iter-extensions/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] #[warn(clippy::pedantic)] pub mod interweave;