diff --git a/examples/jpeg-decode.rs b/examples/jpeg-decode.rs index 9fd921a..36235eb 100644 --- a/examples/jpeg-decode.rs +++ b/examples/jpeg-decode.rs @@ -2,11 +2,15 @@ use std::{num::NonZeroU32, rc::Rc, time::Instant}; use anyhow::bail; use fev::{ + buffer::{Buffer, BufferType}, + config::Config, + context::Context, display::Display, image::{Image, ImageFormat}, jpeg::{JpegDecodeSession, JpegInfo}, - surface::ExportSurfaceFlags, - PixelFormat, + surface::{ExportSurfaceFlags, Surface}, + vpp::{ColorProperties, ColorStandardType, ProcPipelineParameterBuffer, SourceRange}, + Entrypoint, PixelFormat, Profile, }; use winit::{ dpi::PhysicalSize, @@ -53,24 +57,31 @@ fn main() -> anyhow::Result<()> { let graphics_context = softbuffer::Context::new(win.clone()).unwrap(); let mut surface = softbuffer::Surface::new(&graphics_context, win.clone()).unwrap(); - let PhysicalSize { width, height } = win.inner_size(); - log::info!("window size: {width}x{height}"); + let s = win.inner_size(); + log::info!("window size: {}x{}", s.width, s.height); + + let jpeg_info = JpegInfo::new(&jpeg)?; + let (width, height) = (jpeg_info.width(), jpeg_info.height()); surface .resize( - NonZeroU32::new(info.width.into()).unwrap(), - NonZeroU32::new(info.height.into()).unwrap(), + NonZeroU32::new(width.into()).unwrap(), + NonZeroU32::new(height.into()).unwrap(), ) .unwrap(); let display = Display::new(win.clone())?; - let jpeg_info = JpegInfo::new(&jpeg)?; - let mut context = JpegDecodeSession::new(&display, jpeg_info.width(), jpeg_info.height())?; + let mut context = JpegDecodeSession::new(&display, width, height)?; let prime = context .surface() .export_prime(ExportSurfaceFlags::SEPARATE_LAYERS | ExportSurfaceFlags::READ)?; log::debug!("PRIME export: {prime:#?}"); + let config = Config::new(&display, Profile::None, Entrypoint::VideoProc)?; + let mut vpp_context = Context::new(&config, width.into(), height.into())?; + let mut vpp_surface = + Surface::with_pixel_format(&display, width.into(), height.into(), PixelFormat::RGBA)?; + let mut image = Image::new( &display, ImageFormat::new(PixelFormat::RGBA), @@ -80,9 +91,32 @@ fn main() -> anyhow::Result<()> { log::debug!(""); let start = Instant::now(); - let surf = context.decode_and_convert(&jpeg)?; + let surf = context.decode(&jpeg)?; log::debug!(" took {:?}", start.elapsed()); - surf.copy_to_image(&mut image)?; + + // Use VPP to convert the surface to RGBA. + let mut pppbuf = ProcPipelineParameterBuffer::new(surf); + + // The input color space is the JPEG color space + let input_props = ColorProperties::new().with_color_range(SourceRange::FULL); + pppbuf.set_input_color_properties(input_props); + pppbuf.set_input_color_standard(ColorStandardType::BT601); + // The output color space is 8-bit non-linear sRGB + let output_props = ColorProperties::new().with_color_range(SourceRange::FULL); + pppbuf.set_output_color_properties(output_props); + pppbuf.set_output_color_standard(ColorStandardType::SRGB); + // NB: not all implementations support converting color standards (eg. Mesa). + // such implementations will typically output an image that is brighter than the reference data. + + let mut pppbuf = Buffer::new_param(&vpp_context, BufferType::ProcPipelineParameter, pppbuf)?; + + let mut picture = vpp_context.begin_picture(&mut vpp_surface)?; + picture.render_picture(&mut pppbuf)?; + unsafe { picture.end_picture()? } + + drop(pppbuf); + + vpp_surface.copy_to_image(&mut image)?; let mapping = image.map()?; log::debug!("{} byte output", mapping.len()); diff --git a/src/jpeg.rs b/src/jpeg.rs index 748ce9c..9a4116f 100644 --- a/src/jpeg.rs +++ b/src/jpeg.rs @@ -17,8 +17,7 @@ use crate::{ error::Error, raw::{Rectangle, VA_PADDING_LOW, VA_PADDING_MEDIUM}, surface::{RTFormat, Surface}, - vpp::{ColorProperties, ColorStandardType, ProcPipelineParameterBuffer, SourceRange}, - Entrypoint, PixelFormat, Profile, Result, Rotation, SliceParameterBufferBase, + Entrypoint, Profile, Result, Rotation, SliceParameterBufferBase, }; use self::parser::{JpegParser, SegmentKind, SofMarker}; @@ -420,12 +419,8 @@ impl JpegInfo { pub struct JpegDecodeSession { width: u32, height: u32, - jpeg_surface: Surface, - vpp_surface: Surface, - jpeg_context: Context, - vpp_context: Context, } impl JpegDecodeSession { @@ -444,19 +439,13 @@ impl JpegDecodeSession { let config = Config::new(&display, Profile::JPEGBaseline, Entrypoint::VLD)?; let jpeg_context = Context::new(&config, width, height)?; - let config = Config::new(&display, Profile::None, Entrypoint::VideoProc)?; - let vpp_context = Context::new(&config, width, height)?; - let jpeg_surface = Surface::new(&display, width, height, RTFormat::YUV420)?; - let vpp_surface = Surface::with_pixel_format(&display, width, height, PixelFormat::RGBA)?; Ok(Self { width, height, jpeg_surface, - vpp_surface, jpeg_context, - vpp_context, }) } @@ -614,29 +603,4 @@ impl JpegDecodeSession { Ok(&mut self.jpeg_surface) } - - pub fn decode_and_convert(&mut self, jpeg: &[u8]) -> Result<&mut Surface> { - self.decode(jpeg)?; - - let mut pppbuf = ProcPipelineParameterBuffer::new(&self.jpeg_surface); - // The input color space is the JPEG color space - let input_props = ColorProperties::new().with_color_range(SourceRange::FULL); - pppbuf.set_input_color_properties(input_props); - pppbuf.set_input_color_standard(ColorStandardType::BT601); - // The output color space is 8-bit non-linear sRGB - let output_props = ColorProperties::new().with_color_range(SourceRange::FULL); - pppbuf.set_output_color_properties(output_props); - pppbuf.set_output_color_standard(ColorStandardType::SRGB); - - let mut pppbuf = - Buffer::new_param(&self.vpp_context, BufferType::ProcPipelineParameter, pppbuf)?; - - let mut picture = self.vpp_context.begin_picture(&mut self.vpp_surface)?; - picture.render_picture(&mut pppbuf)?; - unsafe { picture.end_picture()? } - - drop(pppbuf); - - Ok(&mut self.vpp_surface) - } } diff --git a/src/surface.rs b/src/surface.rs index 79226a1..bce700e 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -485,6 +485,12 @@ impl Surface { /// [`VAError::ERROR_OPERATION_FAILED`] if it's not supported. In that case, the caller should /// fall back to creating an [`Image`] manually and using [`Surface::copy_to_image`]. The /// [`SurfaceWithImage`] type encapsulates that pattern and should be used for this if possible. + /// + /// # Bugs + /// + /// - Mesa has a bug in its implementation of `vaDeriveImage` where the resulting [`Image`] + /// won't be mapped correctly and appear completely blank if this method is called before a + /// decode operation is submitted. pub fn derive_image(&mut self) -> Result { unsafe { let mut image = MaybeUninit::uninit();