diff --git a/nokhwa-core/src/buffer.rs b/nokhwa-core/src/buffer.rs index fdc8486..689d696 100644 --- a/nokhwa-core/src/buffer.rs +++ b/nokhwa-core/src/buffer.rs @@ -16,7 +16,7 @@ use crate::{ error::NokhwaError, - pixel_format::FormatDecoder, + frame_format::FrameFormat, types::{FrameFormat, Resolution}, }; use bytes::Bytes; @@ -69,87 +69,87 @@ impl Buffer { self.source_frame_format } - /// Decodes a image with allocation using the provided [`FormatDecoder`]. - /// # Errors - /// Will error when the decoding fails. - #[inline] - pub fn decode_image( - &self, - ) -> Result>, NokhwaError> { - let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?; - let image = - ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data) - .ok_or(NokhwaError::ProcessFrameError { - src: self.source_frame_format, - destination: stringify!(F).to_string(), - error: "Failed to create buffer".to_string(), - })?; - Ok(image) - } - - /// Decodes a image with allocation using the provided [`FormatDecoder`] into a `buffer`. - /// # Errors - /// Will error when the decoding fails, or the provided buffer is too small. - #[inline] - pub fn decode_image_to_buffer( - &self, - buffer: &mut [u8], - ) -> Result<(), NokhwaError> { - F::write_output_buffer( - self.source_frame_format, - self.resolution, - &self.buffer, - buffer, - ) - } - /// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). - /// - /// Note that this does a clone when creating the buffer, to decouple the lifetime of the internal data to the temporary Buffer. If you want to avoid this, please see [`decode_opencv_mat`](Self::decode_opencv_mat). - /// # Errors - /// Will error when the decoding fails, or `OpenCV` failed to create/copy the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). - /// # Safety - /// This function uses `unsafe` in order to create the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). Please see [`Mat::new_rows_cols_with_data`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html#method.new_rows_cols_with_data) for more. - /// - /// Most notably, the `data` **must** stay in scope for the duration of the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html) or bad, ***bad*** things happen. - #[cfg(feature = "opencv-mat")] - #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))] - #[allow(clippy::cast_possible_wrap)] - pub fn decode_opencv_mat( - &mut self, - ) -> Result { - use image::Pixel; - use opencv::core::{Mat, Mat_AUTO_STEP, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4}; - - let array_type = match F::Output::CHANNEL_COUNT { - 1 => CV_8UC1, - 2 => CV_8UC2, - 3 => CV_8UC3, - 4 => CV_8UC4, - _ => { - return Err(NokhwaError::ProcessFrameError { - src: FrameFormat::RAWRGB, - destination: "OpenCV Mat".to_string(), - error: "Invalid Decoder FormatDecoder Channel Count".to_string(), - }) - } - }; - - unsafe { - // TODO: Look into removing this unnecessary copy. - let mat1 = Mat::new_rows_cols_with_data( - self.resolution.height_y as i32, - self.resolution.width_x as i32, - array_type, - self.buffer.as_ref().as_ptr().cast_mut().cast(), - Mat_AUTO_STEP, - ) - .map_err(|why| NokhwaError::ProcessFrameError { - src: FrameFormat::RAWRGB, - destination: "OpenCV Mat".to_string(), - error: why.to_string(), - })?; - - Ok(mat1) - } - } + // /// Decodes a image with allocation using the provided [`FormatDecoder`]. + // /// # Errors + // /// Will error when the decoding fails. + // #[inline] + // pub fn decode_image( + // &self, + // ) -> Result>, NokhwaError> { + // let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?; + // let image = + // ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data) + // .ok_or(NokhwaError::ProcessFrameError { + // src: self.source_frame_format, + // destination: stringify!(F).to_string(), + // error: "Failed to create buffer".to_string(), + // })?; + // Ok(image) + // } + // + // /// Decodes a image with allocation using the provided [`FormatDecoder`] into a `buffer`. + // /// # Errors + // /// Will error when the decoding fails, or the provided buffer is too small. + // #[inline] + // pub fn decode_image_to_buffer( + // &self, + // buffer: &mut [u8], + // ) -> Result<(), NokhwaError> { + // F::write_output_buffer( + // self.source_frame_format, + // self.resolution, + // &self.buffer, + // buffer, + // ) + // } + // /// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). + // /// + // /// Note that this does a clone when creating the buffer, to decouple the lifetime of the internal data to the temporary Buffer. If you want to avoid this, please see [`decode_opencv_mat`](Self::decode_opencv_mat). + // /// # Errors + // /// Will error when the decoding fails, or `OpenCV` failed to create/copy the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). + // /// # Safety + // /// This function uses `unsafe` in order to create the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). Please see [`Mat::new_rows_cols_with_data`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html#method.new_rows_cols_with_data) for more. + // /// + // /// Most notably, the `data` **must** stay in scope for the duration of the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html) or bad, ***bad*** things happen. + // #[cfg(feature = "opencv-mat")] + // #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))] + // #[allow(clippy::cast_possible_wrap)] + // pub fn decode_opencv_mat( + // &mut self, + // ) -> Result { + // use image::Pixel; + // use opencv::core::{Mat, Mat_AUTO_STEP, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4}; + // + // let array_type = match F::Output::CHANNEL_COUNT { + // 1 => CV_8UC1, + // 2 => CV_8UC2, + // 3 => CV_8UC3, + // 4 => CV_8UC4, + // _ => { + // return Err(NokhwaError::ProcessFrameError { + // src: FrameFormat::RAWRGB, + // destination: "OpenCV Mat".to_string(), + // error: "Invalid Decoder FormatDecoder Channel Count".to_string(), + // }) + // } + // }; + // + // unsafe { + // // TODO: Look into removing this unnecessary copy. + // let mat1 = Mat::new_rows_cols_with_data( + // self.resolution.height_y as i32, + // self.resolution.width_x as i32, + // array_type, + // self.buffer.as_ref().as_ptr().cast_mut().cast(), + // Mat_AUTO_STEP, + // ) + // .map_err(|why| NokhwaError::ProcessFrameError { + // src: FrameFormat::RAWRGB, + // destination: "OpenCV Mat".to_string(), + // error: why.to_string(), + // })?; + // + // Ok(mat1) + // } + // } } diff --git a/nokhwa-core/src/frame_format.rs b/nokhwa-core/src/frame_format.rs index 6b5a27d..66baca4 100644 --- a/nokhwa-core/src/frame_format.rs +++ b/nokhwa-core/src/frame_format.rs @@ -3,11 +3,6 @@ use std::fmt::{Display, Formatter}; use std::str::FromStr; /// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`. -/// - YUYV is a mathematical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr) -/// - NV12 is same as above. Note that a partial compression (e.g. [16, 235] may be coerced to [0, 255]. -/// - MJPEG is a motion-jpeg compressed frame, it allows for high frame rates. -/// - GRAY is a grayscale image format, usually for specialized cameras such as IR Cameras. -/// - RAWRGB is a Raw RGB888 format. #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum FrameFormat { @@ -15,11 +10,11 @@ pub enum FrameFormat { H265, H264, H263, - AVC1, - MPEG1, - MPEG2, - MPEG4, - MJPEG, + Avc1, + Mpeg1, + Mpeg2, + Mpeg4, + MJpeg, XVid, VP8, VP9, @@ -37,84 +32,73 @@ pub enum FrameFormat { Imc2, Imc4, - // UV - UV8, - // Grayscale Formats Luma8, - Luma8I, - Luma10, - Luma10B, - Luma12, - Luma12I, - Luma16, - - // Depth Formats - Z16, // RGB Formats Rgb8, - // Bayer + RgbA8, + + // Custom + Custom(u128), +} + +impl FrameFormat { + pub const ALL: &'static [FrameFormat] = &[ + FrameFormat::H263, + FrameFormat::H264, + FrameFormat::H265, + FrameFormat::Avc1, + FrameFormat::Mpeg1, + FrameFormat::Mpeg2, + FrameFormat::Mpeg4, + FrameFormat::MJpeg, + FrameFormat::XVid, + FrameFormat::VP8, + FrameFormat::VP9, + FrameFormat::Yuv422, + FrameFormat::Uyv422, + FrameFormat::Nv12, + FrameFormat::Nv21, + FrameFormat::Yv12, + FrameFormat::Imc2, + FrameFormat::Imc4, + FrameFormat::Luma8, + FrameFormat::Rgb8, + FrameFormat::RgbA8, + ]; + + pub const COMPRESSED: &'static [FrameFormat] = &[ + FrameFormat::H263, + FrameFormat::H264, + FrameFormat::H265, + FrameFormat::Avc1, + FrameFormat::Mpeg1, + FrameFormat::Mpeg2, + FrameFormat::Mpeg4, + FrameFormat::MJpeg, + FrameFormat::XVid, + FrameFormat::VP8, + FrameFormat::VP9, + ]; + + pub const CHROMA: &'static [FrameFormat] = &[ + FrameFormat::Yuv422, + FrameFormat::Uyv422, + FrameFormat::Nv12, + FrameFormat::Nv21, + FrameFormat::Yv12, + FrameFormat::Imc2, + FrameFormat::Imc4, + ]; + + pub const LUMA: &'static [FrameFormat] = &[FrameFormat::Luma8]; + + pub const RGB: &'static [FrameFormat] = &[FrameFormat::Rgb8, FrameFormat::RgbA8]; } impl Display for FrameFormat { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FrameFormat::MJPEG => { - write!(f, "MJPEG") - } - FrameFormat::YUYV => { - write!(f, "YUYV") - } - FrameFormat::GRAY => { - write!(f, "GRAY") - } - FrameFormat::RAWRGB => { - write!(f, "RAWRGB") - } - FrameFormat::NV12 => { - write!(f, "NV12") - } - } + write!(f, "{self:?}") } } -impl FromStr for FrameFormat { - type Err = NokhwaError; - - fn from_str(s: &str) -> Result { - match s { - "MJPEG" => Ok(FrameFormat::MJPEG), - "YUYV" => Ok(FrameFormat::YUYV), - "GRAY" => Ok(FrameFormat::GRAY), - "RAWRGB" => Ok(FrameFormat::RAWRGB), - "NV12" => Ok(FrameFormat::NV12), - _ => Err(NokhwaError::StructureError { - structure: "FrameFormat".to_string(), - error: format!("No match for {s}"), - }), - } - } -} - -/// Returns all the frame formats -#[must_use] -pub const fn frame_formats() -> &'static [FrameFormat] { - &[ - FrameFormat::MJPEG, - FrameFormat::YUYV, - FrameFormat::NV12, - FrameFormat::GRAY, - FrameFormat::RAWRGB, - ] -} - -/// Returns all the color frame formats -#[must_use] -pub const fn color_frame_formats() -> &'static [FrameFormat] { - &[ - FrameFormat::MJPEG, - FrameFormat::YUYV, - FrameFormat::NV12, - FrameFormat::RAWRGB, - ] -} diff --git a/nokhwa-core/src/pixel_format.rs b/nokhwa-core/src/pixel_format.rs index 05e5ff3..2b46a59 100644 --- a/nokhwa-core/src/pixel_format.rs +++ b/nokhwa-core/src/pixel_format.rs @@ -13,389 +13,48 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +use crate::buffer::Buffer; use crate::error::NokhwaError; +use crate::frame_format::FrameFormat; use crate::types::{ - buf_mjpeg_to_rgb, buf_nv12_to_rgb, buf_yuyv422_to_rgb, color_frame_formats, frame_formats, - mjpeg_to_rgb, nv12_to_rgb, yuyv422_to_rgb, FrameFormat, Resolution, + buf_mjpeg_to_rgb, buf_nv12_to_rgb, buf_yuyv422_to_rgb, mjpeg_to_rgb, nv12_to_rgb, + yuyv422_to_rgb, Resolution, }; -use image::{Luma, LumaA, Pixel, Rgb, Rgba}; +use image::{ImageBuffer, Luma, LumaA, Pixel, Primitive, Rgb, Rgba}; use std::fmt::Debug; +use std::ops::Deref; -/// Trait that has methods to convert raw data from the webcam to a proper raw image. -pub trait FormatDecoder: Clone + Sized + Send + Sync { - type Output: Pixel; - const FORMATS: &'static [FrameFormat]; +pub trait FormatDecoders: Send + Sync { + const NAME: &'static str; - /// Allocates and returns a `Vec` - /// # Errors - /// If the data is malformed, or the source [`FrameFormat`] is incompatible, this will error. - fn write_output( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - ) -> Result, NokhwaError>; + const PRIMARY: FrameFormat; - /// Writes to a user provided buffer. - /// # Errors - /// If the data is malformed, the source [`FrameFormat`] is incompatible, or the user-alloted buffer is not large enough, this will error. - fn write_output_buffer( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - dest: &mut [u8], - ) -> Result<(), NokhwaError>; + const ACCEPTABLE: &'static [FrameFormat]; + + const PLATFORM_ACCEPTABLE: &'static [(&'static str, &'static [u128])]; + + type Primitive: Primitive; + + type Container: Deref; } -/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGB888 in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image) -/// -/// ```.ignore -/// use image::{ImageBuffer, Rgb}; -/// let image: ImageBuffer, Vec> = buffer.to_image::(); -/// ``` -#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct RgbFormat; - -impl FormatDecoder for RgbFormat { - type Output = Rgb; - const FORMATS: &'static [FrameFormat] = color_frame_formats(); - - #[inline] - fn write_output( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - ) -> Result, NokhwaError> { - match fcc { - FrameFormat::MJPEG => mjpeg_to_rgb(data, false), - FrameFormat::YUYV => yuyv422_to_rgb(data, false), - FrameFormat::GRAY => Ok(data - .iter() - .flat_map(|x| { - let pxv = *x; - [pxv, pxv, pxv] - }) - .collect()), - FrameFormat::RAWRGB => Ok(data.to_vec()), - FrameFormat::NV12 => nv12_to_rgb(resolution, data, false), - } - } - - #[inline] - fn write_output_buffer( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - dest: &mut [u8], - ) -> Result<(), NokhwaError> { - match fcc { - FrameFormat::MJPEG => buf_mjpeg_to_rgb(data, dest, false), - FrameFormat::YUYV => buf_yuyv422_to_rgb(data, dest, false), - FrameFormat::GRAY => { - if dest.len() != data.len() * 3 { - return Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "Luma => RGB".to_string(), - error: "Bad buffer length".to_string(), - }); - } - - data.iter().enumerate().for_each(|(idx, pixel_value)| { - let index = idx * 3; - dest[index] = *pixel_value; - dest[index + 1] = *pixel_value; - dest[index + 2] = *pixel_value; - }); - Ok(()) - } - FrameFormat::RAWRGB => { - dest.copy_from_slice(data); - Ok(()) - } - FrameFormat::NV12 => buf_nv12_to_rgb(resolution, data, dest, false), - } - } +pub trait RgbDecoder: FormatDecoders { + fn decode_rgb(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; } -/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGBA8888 in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image) -/// -/// ```.ignore -/// use image::{ImageBuffer, Rgba}; -/// let image: ImageBuffer, Vec> = buffer.to_image::(); -/// ``` -#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct RgbAFormat; - -impl FormatDecoder for RgbAFormat { - type Output = Rgba; - - const FORMATS: &'static [FrameFormat] = color_frame_formats(); - - #[inline] - fn write_output( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - ) -> Result, NokhwaError> { - match fcc { - FrameFormat::MJPEG => mjpeg_to_rgb(data, true), - FrameFormat::YUYV => yuyv422_to_rgb(data, true), - FrameFormat::GRAY => Ok(data - .iter() - .flat_map(|x| { - let pxv = *x; - [pxv, pxv, pxv, 255] - }) - .collect()), - FrameFormat::RAWRGB => Ok(data - .chunks_exact(3) - .flat_map(|x| [x[0], x[1], x[2], 255]) - .collect()), - FrameFormat::NV12 => nv12_to_rgb(resolution, data, true), - } - } - - #[inline] - fn write_output_buffer( - fcc: FrameFormat, - resolution: Resolution, - - data: &[u8], - dest: &mut [u8], - ) -> Result<(), NokhwaError> { - match fcc { - FrameFormat::MJPEG => buf_mjpeg_to_rgb(data, dest, true), - FrameFormat::YUYV => buf_yuyv422_to_rgb(data, dest, true), - FrameFormat::GRAY => { - if dest.len() != data.len() * 4 { - return Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "Luma => RGBA".to_string(), - error: "Bad buffer length".to_string(), - }); - } - - data.iter().enumerate().for_each(|(idx, pixel_value)| { - let index = idx * 4; - dest[index] = *pixel_value; - dest[index + 1] = *pixel_value; - dest[index + 2] = *pixel_value; - dest[index + 3] = 255; - }); - Ok(()) - } - FrameFormat::RAWRGB => { - data.chunks_exact(3).enumerate().for_each(|(idx, px)| { - let index = idx * 4; - dest[index] = px[0]; - dest[index + 1] = px[1]; - dest[index + 2] = px[2]; - dest[index + 3] = 255; - }); - Ok(()) - } - FrameFormat::NV12 => buf_nv12_to_rgb(resolution, data, dest, true), - } - } +pub trait RgbADecoder: FormatDecoders { + fn decode_rgba(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; } -/// A Zero-Size-Type that contains the definition to convert a given image stream to an Luma8(Grayscale 8-bit) in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image) -/// -/// ```.ignore -/// use image::{ImageBuffer, Luma}; -/// let image: ImageBuffer, Vec> = buffer.to_image::(); -/// ``` -#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct LumaFormat; - -impl FormatDecoder for LumaFormat { - type Output = Luma; - - const FORMATS: &'static [FrameFormat] = frame_formats(); - - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] - #[inline] - fn write_output( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - ) -> Result, NokhwaError> { - match fcc { - FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)? - .as_slice() - .chunks_exact(3) - .map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - (avg / 3) as u8 - }) - .collect()), - FrameFormat::YUYV => Ok(yuyv422_to_rgb(data, false)? - .as_slice() - .chunks_exact(3) - .map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - (avg / 3) as u8 - }) - .collect()), - FrameFormat::NV12 => Ok(nv12_to_rgb(resolution, data, false)? - .as_slice() - .chunks_exact(3) - .map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - (avg / 3) as u8 - }) - .collect()), - FrameFormat::GRAY => Ok(data.to_vec()), - FrameFormat::RAWRGB => Ok(data - .chunks(3) - .map(|px| ((i32::from(px[0]) + i32::from(px[1]) + i32::from(px[2])) / 3) as u8) - .collect()), - } - } - - #[inline] - fn write_output_buffer( - fcc: FrameFormat, - _resolution: Resolution, - data: &[u8], - dest: &mut [u8], - ) -> Result<(), NokhwaError> { - match fcc { - // TODO: implement! - FrameFormat::MJPEG | FrameFormat::YUYV | FrameFormat::NV12 => { - Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "Luma => RGB".to_string(), - error: "Conversion Error".to_string(), - }) - } - - FrameFormat::GRAY => { - data.iter().zip(dest.iter_mut()).for_each(|(pxv, d)| { - *d = *pxv; - }); - Ok(()) - } - FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "RGB => RGB".to_string(), - error: "Conversion Error".to_string(), - }), - } - } +pub trait LumaDecoder: FormatDecoders { + fn decode_luma(&self, buffer: &Buffer) -> ImageBuffer, Self::Container>; } -/// A Zero-Size-Type that contains the definition to convert a given image stream to an LumaA8(Grayscale 8-bit with 8-bit alpha) in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image) -/// -/// ```.ignore -/// use image::{ImageBuffer, LumaA}; -/// let image: ImageBuffer, Vec> = buffer.to_image::(); -/// ``` -#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct LumaAFormat; - -impl FormatDecoder for LumaAFormat { - type Output = LumaA; - - const FORMATS: &'static [FrameFormat] = frame_formats(); - - #[allow(clippy::cast_possible_truncation)] - #[inline] - fn write_output( - fcc: FrameFormat, - resolution: Resolution, - data: &[u8], - ) -> Result, NokhwaError> { - match fcc { - FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)? - .as_slice() - .chunks_exact(3) - .flat_map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - [(avg / 3) as u8, 255] - }) - .collect()), - FrameFormat::YUYV => Ok(yuyv422_to_rgb(data, false)? - .as_slice() - .chunks_exact(3) - .flat_map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - [(avg / 3) as u8, 255] - }) - .collect()), - FrameFormat::NV12 => Ok(nv12_to_rgb(resolution, data, false)? - .as_slice() - .chunks_exact(3) - .flat_map(|x| { - let mut avg = 0; - x.iter().for_each(|v| avg += u16::from(*v)); - [(avg / 3) as u8, 255] - }) - .collect()), - FrameFormat::GRAY => Ok(data.iter().flat_map(|x| [*x, 255]).collect()), - FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "RGB => RGB".to_string(), - error: "Conversion Error".to_string(), - }), - } - } - - #[inline] - fn write_output_buffer( - fcc: FrameFormat, - _resolution: Resolution, - data: &[u8], - dest: &mut [u8], - ) -> Result<(), NokhwaError> { - match fcc { - FrameFormat::MJPEG => { - // FIXME: implement! - Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "MJPEG => LumaA".to_string(), - error: "Conversion Error".to_string(), - }) - } - FrameFormat::YUYV => Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "YUYV => LumaA".to_string(), - error: "Conversion Error".to_string(), - }), - FrameFormat::NV12 => Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "NV12 => LumaA".to_string(), - error: "Conversion Error".to_string(), - }), - FrameFormat::GRAY => { - if dest.len() != data.len() * 2 { - return Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "GRAY8 => LumaA".to_string(), - error: "Conversion Error".to_string(), - }); - } - - data.iter() - .zip(dest.chunks_exact_mut(2)) - .enumerate() - .for_each(|(idx, (pxv, d))| { - let index = idx * 2; - d[index] = *pxv; - d[index + 1] = 255; - }); - Ok(()) - } - FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError { - src: fcc, - destination: "RGB => RGB".to_string(), - error: "Conversion Error".to_string(), - }), - } - } +pub trait LumaADecoder: FormatDecoders { + fn decode_luma_a( + &self, + buffer: &Buffer, + ) -> ImageBuffer, Self::Container>; } + +// TODO: Wgpu Decoder