From 8f992f7cef4b7ba648b515fd69290fb4e1015562 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 2 Mar 2025 15:16:07 +0100 Subject: [PATCH] color-management: add more capabilities --- src/backends/metal/present.rs | 4 + src/cmm/cmm_luminance.rs | 1 - src/cmm/cmm_manager.rs | 2 - src/cmm/cmm_primaries.rs | 10 -- src/ifs/color_management.rs | 89 ++++++----- .../wp_color_management_output_v1.rs | 3 +- ...wp_color_management_surface_feedback_v1.rs | 3 +- .../color_management/wp_color_manager_v1.rs | 75 ++++++++-- .../wp_image_description_creator_params_v1.rs | 140 +++++++++++++++--- .../wp_image_description_v1.rs | 6 +- src/ifs/wl_surface.rs | 29 +++- .../wp_color_management_surface_v1.rs | 24 ++- src/renderer.rs | 4 +- 13 files changed, 293 insertions(+), 97 deletions(-) rename src/ifs/{color_management => wl_surface}/wp_color_management_surface_v1.rs (66%) diff --git a/src/backends/metal/present.rs b/src/backends/metal/present.rs index 35a299c3..3af4f794 100644 --- a/src/backends/metal/present.rs +++ b/src/backends/metal/present.rs @@ -560,6 +560,10 @@ impl MetalConnector { } return None; }; + if ct.cd.id != self.state.color_manager.srgb_srgb().id { + // Direct scanout requires identical color descriptions. + return None; + } if ct.alpha.is_some() { // Direct scanout with alpha factor is not supported. return None; diff --git a/src/cmm/cmm_luminance.rs b/src/cmm/cmm_luminance.rs index 6f5100af..760f50f5 100644 --- a/src/cmm/cmm_luminance.rs +++ b/src/cmm/cmm_luminance.rs @@ -17,7 +17,6 @@ impl Luminance { white: F64(80.0), }; - #[expect(dead_code)] pub const BT1886: Self = Self { min: F64(0.01), max: F64(100.0), diff --git a/src/cmm/cmm_manager.rs b/src/cmm/cmm_manager.rs index 1244e2d6..6837fadc 100644 --- a/src/cmm/cmm_manager.rs +++ b/src/cmm/cmm_manager.rs @@ -100,12 +100,10 @@ impl ColorManager { &self.srgb_linear } - #[expect(dead_code)] pub fn windows_scrgb(&self) -> &Rc { &self.windows_scrgb } - #[expect(dead_code)] pub fn get_description( self: &Rc, named_primaries: Option, diff --git a/src/cmm/cmm_primaries.rs b/src/cmm/cmm_primaries.rs index 39f0be05..5541d8ee 100644 --- a/src/cmm/cmm_primaries.rs +++ b/src/cmm/cmm_primaries.rs @@ -3,23 +3,14 @@ use {crate::utils::ordered_float::F64, std::hash::Hash}; #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum NamedPrimaries { Srgb, - #[expect(dead_code)] PalM, - #[expect(dead_code)] Pal, - #[expect(dead_code)] Ntsc, - #[expect(dead_code)] GenericFilm, - #[expect(dead_code)] Bt2020, - #[expect(dead_code)] Cie1931Xyz, - #[expect(dead_code)] DciP3, - #[expect(dead_code)] DisplayP3, - #[expect(dead_code)] AdobeRgb, } @@ -103,7 +94,6 @@ impl Primaries { }; } impl NamedPrimaries { - #[expect(dead_code)] pub const fn primaries(self) -> Primaries { match self { NamedPrimaries::Srgb => Primaries::SRGB, diff --git a/src/ifs/color_management.rs b/src/ifs/color_management.rs index 21a66ec8..d55efdbf 100644 --- a/src/ifs/color_management.rs +++ b/src/ifs/color_management.rs @@ -1,56 +1,63 @@ +pub use consts::*; + pub mod wp_color_management_output_v1; pub mod wp_color_management_surface_feedback_v1; -pub mod wp_color_management_surface_v1; pub mod wp_color_manager_v1; pub mod wp_image_description_creator_icc_v1; pub mod wp_image_description_creator_params_v1; pub mod wp_image_description_info_v1; pub mod wp_image_description_v1; +const PRIMARIES_MUL: f64 = 1_000_000.0; +const PRIMARIES_MUL_INV: f64 = 1.0 / PRIMARIES_MUL; + +const MIN_LUM_MUL: f64 = 10_000.0; +const MIN_LUM_MUL_INV: f64 = 1.0 / MIN_LUM_MUL; + #[expect(dead_code)] mod consts { - pub(super) const RENDER_INTENT_PERCEPTUAL: u32 = 0; - pub(super) const RENDER_INTENT_RELATIVE: u32 = 1; - pub(super) const RENDER_INTENT_SATURATION: u32 = 2; - pub(super) const RENDER_INTENT_ABSOLUTE: u32 = 3; - pub(super) const RENDER_INTENT_RELATIVE_BPC: u32 = 4; + pub const RENDER_INTENT_PERCEPTUAL: u32 = 0; + pub const RENDER_INTENT_RELATIVE: u32 = 1; + pub const RENDER_INTENT_SATURATION: u32 = 2; + pub const RENDER_INTENT_ABSOLUTE: u32 = 3; + pub const RENDER_INTENT_RELATIVE_BPC: u32 = 4; - pub(super) const FEATURE_ICC_V2_V4: u32 = 0; - pub(super) const FEATURE_PARAMETRIC: u32 = 1; - pub(super) const FEATURE_SET_PRIMARIES: u32 = 2; - pub(super) const FEATURE_SET_TF_POWER: u32 = 3; - pub(super) const FEATURE_SET_LUMINANCES: u32 = 4; - pub(super) const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5; - pub(super) const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6; - pub(super) const FEATURE_WINDOWS_SCRGB: u32 = 7; + pub const FEATURE_ICC_V2_V4: u32 = 0; + pub const FEATURE_PARAMETRIC: u32 = 1; + pub const FEATURE_SET_PRIMARIES: u32 = 2; + pub const FEATURE_SET_TF_POWER: u32 = 3; + pub const FEATURE_SET_LUMINANCES: u32 = 4; + pub const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5; + pub const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6; + pub const FEATURE_WINDOWS_SCRGB: u32 = 7; - pub(super) const PRIMARIES_SRGB: u32 = 1; - pub(super) const PRIMARIES_PAL_M: u32 = 2; - pub(super) const PRIMARIES_PAL: u32 = 3; - pub(super) const PRIMARIES_NTSC: u32 = 4; - pub(super) const PRIMARIES_GENERIC_FILM: u32 = 5; - pub(super) const PRIMARIES_BT2020: u32 = 6; - pub(super) const PRIMARIES_CIE1931_XYZ: u32 = 7; - pub(super) const PRIMARIES_DCI_P3: u32 = 8; - pub(super) const PRIMARIES_DISPLAY_P3: u32 = 9; - pub(super) const PRIMARIES_ADOBE_RGB: u32 = 10; + pub const PRIMARIES_SRGB: u32 = 1; + pub const PRIMARIES_PAL_M: u32 = 2; + pub const PRIMARIES_PAL: u32 = 3; + pub const PRIMARIES_NTSC: u32 = 4; + pub const PRIMARIES_GENERIC_FILM: u32 = 5; + pub const PRIMARIES_BT2020: u32 = 6; + pub const PRIMARIES_CIE1931_XYZ: u32 = 7; + pub const PRIMARIES_DCI_P3: u32 = 8; + pub const PRIMARIES_DISPLAY_P3: u32 = 9; + pub const PRIMARIES_ADOBE_RGB: u32 = 10; - pub(super) const TRANSFER_FUNCTION_BT1886: u32 = 1; - pub(super) const TRANSFER_FUNCTION_GAMMA22: u32 = 2; - pub(super) const TRANSFER_FUNCTION_GAMMA28: u32 = 3; - pub(super) const TRANSFER_FUNCTION_ST240: u32 = 4; - pub(super) const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5; - pub(super) const TRANSFER_FUNCTION_LOG_100: u32 = 6; - pub(super) const TRANSFER_FUNCTION_LOG_316: u32 = 7; - pub(super) const TRANSFER_FUNCTION_XVYCC: u32 = 8; - pub(super) const TRANSFER_FUNCTION_SRGB: u32 = 9; - pub(super) const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10; - pub(super) const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11; - pub(super) const TRANSFER_FUNCTION_ST428: u32 = 12; - pub(super) const TRANSFER_FUNCTION_HLG: u32 = 13; + pub const TRANSFER_FUNCTION_BT1886: u32 = 1; + pub const TRANSFER_FUNCTION_GAMMA22: u32 = 2; + pub const TRANSFER_FUNCTION_GAMMA28: u32 = 3; + pub const TRANSFER_FUNCTION_ST240: u32 = 4; + pub const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5; + pub const TRANSFER_FUNCTION_LOG_100: u32 = 6; + pub const TRANSFER_FUNCTION_LOG_316: u32 = 7; + pub const TRANSFER_FUNCTION_XVYCC: u32 = 8; + pub const TRANSFER_FUNCTION_SRGB: u32 = 9; + pub const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10; + pub const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11; + pub const TRANSFER_FUNCTION_ST428: u32 = 12; + pub const TRANSFER_FUNCTION_HLG: u32 = 13; - pub(super) const CAUSE_LOW_VERSION: u32 = 0; - pub(super) const CAUSE_UNSUPPORTED: u32 = 1; - pub(super) const CAUSE_OPERATING_SYSTEM: u32 = 2; - pub(super) const CAUSE_NO_OUTPUT: u32 = 3; + pub const CAUSE_LOW_VERSION: u32 = 0; + pub const CAUSE_UNSUPPORTED: u32 = 1; + pub const CAUSE_OPERATING_SYSTEM: u32 = 2; + pub const CAUSE_NO_OUTPUT: u32 = 3; } diff --git a/src/ifs/color_management/wp_color_management_output_v1.rs b/src/ifs/color_management/wp_color_management_output_v1.rs index e23d6ebb..bbcb58a8 100644 --- a/src/ifs/color_management/wp_color_management_output_v1.rs +++ b/src/ifs/color_management/wp_color_management_output_v1.rs @@ -43,10 +43,11 @@ impl WpColorManagementOutputV1RequestHandler for WpColorManagementOutputV1 { client: self.client.clone(), version: self.version, tracker: Default::default(), + description: self.client.state.color_manager.srgb_srgb().clone(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; - obj.send_ready(0); + obj.send_ready(); Ok(()) } } diff --git a/src/ifs/color_management/wp_color_management_surface_feedback_v1.rs b/src/ifs/color_management/wp_color_management_surface_feedback_v1.rs index 6fb30be6..0b166fe6 100644 --- a/src/ifs/color_management/wp_color_management_surface_feedback_v1.rs +++ b/src/ifs/color_management/wp_color_management_surface_feedback_v1.rs @@ -30,10 +30,11 @@ impl WpColorManagementSurfaceFeedbackV1 { client: self.client.clone(), version: self.version, tracker: Default::default(), + description: self.client.state.color_manager.srgb_srgb().clone(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; - obj.send_ready(0); + obj.send_ready(); Ok(()) } } diff --git a/src/ifs/color_management/wp_color_manager_v1.rs b/src/ifs/color_management/wp_color_manager_v1.rs index acdf3d9d..05546d97 100644 --- a/src/ifs/color_management/wp_color_manager_v1.rs +++ b/src/ifs/color_management/wp_color_manager_v1.rs @@ -2,15 +2,27 @@ use { crate::{ client::{Client, ClientError}, globals::{Global, GlobalName}, - ifs::color_management::{ - consts::{ - FEATURE_PARAMETRIC, PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL, - TRANSFER_FUNCTION_SRGB, + ifs::{ + color_management::{ + consts::{ + FEATURE_PARAMETRIC, FEATURE_SET_LUMINANCES, FEATURE_SET_PRIMARIES, + FEATURE_WINDOWS_SCRGB, PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020, + PRIMARIES_CIE1931_XYZ, PRIMARIES_DCI_P3, PRIMARIES_DISPLAY_P3, + PRIMARIES_GENERIC_FILM, PRIMARIES_NTSC, PRIMARIES_PAL, PRIMARIES_PAL_M, + PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL, TRANSFER_FUNCTION_BT1886, + TRANSFER_FUNCTION_EXT_LINEAR, TRANSFER_FUNCTION_EXT_SRGB, + TRANSFER_FUNCTION_GAMMA22, TRANSFER_FUNCTION_GAMMA28, + TRANSFER_FUNCTION_LOG_100, TRANSFER_FUNCTION_LOG_316, TRANSFER_FUNCTION_SRGB, + TRANSFER_FUNCTION_ST240, TRANSFER_FUNCTION_ST428, TRANSFER_FUNCTION_ST2084_PQ, + }, + wp_color_management_output_v1::WpColorManagementOutputV1, + wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1, + wp_image_description_creator_params_v1::WpImageDescriptionCreatorParamsV1, + wp_image_description_v1::WpImageDescriptionV1, + }, + wl_surface::wp_color_management_surface_v1::{ + WpColorManagementSurfaceV1, WpColorManagementSurfaceV1Error, }, - wp_color_management_output_v1::WpColorManagementOutputV1, - wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1, - wp_color_management_surface_v1::WpColorManagementSurfaceV1, - wp_image_description_creator_params_v1::WpImageDescriptionCreatorParamsV1, }, leaks::Tracker, object::{Object, Version}, @@ -63,8 +75,30 @@ impl WpColorManagerV1 { fn send_capabilities(&self) { self.send_supported_intent(RENDER_INTENT_PERCEPTUAL); self.send_supported_feature(FEATURE_PARAMETRIC); + self.send_supported_feature(FEATURE_SET_PRIMARIES); + self.send_supported_feature(FEATURE_SET_LUMINANCES); + self.send_supported_feature(FEATURE_WINDOWS_SCRGB); + self.send_supported_tf_named(TRANSFER_FUNCTION_BT1886); + self.send_supported_tf_named(TRANSFER_FUNCTION_GAMMA22); + self.send_supported_tf_named(TRANSFER_FUNCTION_GAMMA28); + self.send_supported_tf_named(TRANSFER_FUNCTION_ST240); + self.send_supported_tf_named(TRANSFER_FUNCTION_EXT_LINEAR); + self.send_supported_tf_named(TRANSFER_FUNCTION_LOG_100); + self.send_supported_tf_named(TRANSFER_FUNCTION_LOG_316); self.send_supported_tf_named(TRANSFER_FUNCTION_SRGB); + self.send_supported_tf_named(TRANSFER_FUNCTION_EXT_SRGB); + self.send_supported_tf_named(TRANSFER_FUNCTION_ST2084_PQ); + self.send_supported_tf_named(TRANSFER_FUNCTION_ST428); self.send_supported_primaries_named(PRIMARIES_SRGB); + self.send_supported_primaries_named(PRIMARIES_PAL_M); + self.send_supported_primaries_named(PRIMARIES_PAL); + self.send_supported_primaries_named(PRIMARIES_NTSC); + self.send_supported_primaries_named(PRIMARIES_GENERIC_FILM); + self.send_supported_primaries_named(PRIMARIES_BT2020); + self.send_supported_primaries_named(PRIMARIES_CIE1931_XYZ); + self.send_supported_primaries_named(PRIMARIES_DCI_P3); + self.send_supported_primaries_named(PRIMARIES_DISPLAY_P3); + self.send_supported_primaries_named(PRIMARIES_ADOBE_RGB); self.send_done(); } @@ -123,15 +157,17 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 { } fn get_surface(&self, req: GetSurface, _slf: &Rc) -> Result<(), Self::Error> { - let _ = self.client.lookup(req.surface)?; + let surface = self.client.lookup(req.surface)?; let obj = Rc::new(WpColorManagementSurfaceV1 { id: req.id, client: self.client.clone(), version: self.version, tracker: Default::default(), + surface: surface.clone(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; + obj.install()?; Ok(()) } @@ -170,6 +206,9 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 { client: self.client.clone(), version: self.version, tracker: Default::default(), + tf: Default::default(), + primaries: Default::default(), + luminance: Default::default(), }); track!(self.client, obj); self.client.add_client_obj(&obj)?; @@ -178,10 +217,20 @@ impl WpColorManagerV1RequestHandler for WpColorManagerV1 { fn create_windows_scrgb( &self, - _req: CreateWindowsScrgb, + req: CreateWindowsScrgb, _slf: &Rc, ) -> Result<(), Self::Error> { - Err(WpColorManagerV1Error::CreateWindowsScrgbNotSupported) + let obj = Rc::new(WpImageDescriptionV1 { + id: req.image_description, + client: self.client.clone(), + version: self.version, + tracker: Default::default(), + description: self.client.state.color_manager.windows_scrgb().clone(), + }); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + obj.send_ready(); + Ok(()) } } @@ -222,7 +271,7 @@ pub enum WpColorManagerV1Error { ClientError(Box), #[error("create_icc_creator is not supported")] CreateIccCreatorNotSupported, - #[error("create_windows_scrgb is not supported")] - CreateWindowsScrgbNotSupported, + #[error(transparent)] + Surface(#[from] WpColorManagementSurfaceV1Error), } efrom!(WpColorManagerV1Error, ClientError); diff --git a/src/ifs/color_management/wp_image_description_creator_params_v1.rs b/src/ifs/color_management/wp_image_description_creator_params_v1.rs index c17684d6..a4d7dc2f 100644 --- a/src/ifs/color_management/wp_image_description_creator_params_v1.rs +++ b/src/ifs/color_management/wp_image_description_creator_params_v1.rs @@ -1,12 +1,27 @@ use { crate::{ client::{Client, ClientError}, + cmm::{ + cmm_luminance::Luminance, + cmm_primaries::{NamedPrimaries, Primaries}, + cmm_transfer_function::TransferFunction, + }, ifs::color_management::{ - consts::{PRIMARIES_SRGB, TRANSFER_FUNCTION_SRGB}, + MIN_LUM_MUL_INV, PRIMARIES_MUL_INV, + consts::{ + PRIMARIES_ADOBE_RGB, PRIMARIES_BT2020, PRIMARIES_CIE1931_XYZ, PRIMARIES_DCI_P3, + PRIMARIES_DISPLAY_P3, PRIMARIES_GENERIC_FILM, PRIMARIES_NTSC, PRIMARIES_PAL, + PRIMARIES_PAL_M, PRIMARIES_SRGB, TRANSFER_FUNCTION_BT1886, + TRANSFER_FUNCTION_EXT_LINEAR, TRANSFER_FUNCTION_EXT_SRGB, + TRANSFER_FUNCTION_GAMMA22, TRANSFER_FUNCTION_GAMMA28, TRANSFER_FUNCTION_LOG_100, + TRANSFER_FUNCTION_LOG_316, TRANSFER_FUNCTION_SRGB, TRANSFER_FUNCTION_ST240, + TRANSFER_FUNCTION_ST428, TRANSFER_FUNCTION_ST2084_PQ, + }, wp_image_description_v1::WpImageDescriptionV1, }, leaks::Tracker, object::{Object, Version}, + utils::ordered_float::F64, wire::{ WpImageDescriptionCreatorParamsV1Id, wp_image_description_creator_params_v1::{ @@ -16,7 +31,7 @@ use { }, }, }, - std::rc::Rc, + std::{cell::Cell, rc::Rc}, thiserror::Error, }; @@ -25,30 +40,74 @@ pub struct WpImageDescriptionCreatorParamsV1 { pub client: Rc, pub version: Version, pub tracker: Tracker, + pub tf: Cell>, + pub primaries: Cell, Primaries)>>, + pub luminance: Cell>, } impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreatorParamsV1 { type Error = WpImageDescriptionCreatorParamsV1Error; fn create(&self, req: Create, _slf: &Rc) -> Result<(), Self::Error> { + let Some(transfer_function) = self.tf.get() else { + return Err(WpImageDescriptionCreatorParamsV1Error::TfNotSet); + }; + let Some((named_primaries, primaries)) = self.primaries.get() else { + return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesNotSet); + }; + let default_luminance = match transfer_function { + TransferFunction::Bt1886 => Luminance::BT1886, + TransferFunction::St2084Pq => Luminance::ST2084_PQ, + _ => Luminance::SRGB, + }; + let mut luminance = self.luminance.get().unwrap_or(default_luminance); + if transfer_function == TransferFunction::St2084Pq { + luminance.max.0 = luminance.min.0 + 10_000.0; + } + if luminance.max.0 <= luminance.min.0 || luminance.white.0 <= luminance.min.0 { + return Err(WpImageDescriptionCreatorParamsV1Error::MinLuminanceTooLow); + } + let description = self.client.state.color_manager.get_description( + named_primaries, + primaries, + luminance, + transfer_function, + ); let obj = Rc::new(WpImageDescriptionV1 { id: req.image_description, client: self.client.clone(), version: self.version, tracker: Default::default(), + description, }); track!(self.client, obj); self.client.add_client_obj(&obj)?; - obj.send_ready(0); + obj.send_ready(); self.client.remove_obj(self)?; Ok(()) } fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc) -> Result<(), Self::Error> { - if req.tf != TRANSFER_FUNCTION_SRGB { - return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf( - req.tf, - )); + let tf = match req.tf { + TRANSFER_FUNCTION_BT1886 => TransferFunction::Bt1886, + TRANSFER_FUNCTION_GAMMA22 => TransferFunction::Gamma22, + TRANSFER_FUNCTION_GAMMA28 => TransferFunction::Gamma28, + TRANSFER_FUNCTION_ST240 => TransferFunction::St240, + TRANSFER_FUNCTION_EXT_LINEAR => TransferFunction::Linear, + TRANSFER_FUNCTION_LOG_100 => TransferFunction::Log100, + TRANSFER_FUNCTION_LOG_316 => TransferFunction::Log316, + TRANSFER_FUNCTION_SRGB => TransferFunction::Srgb, + TRANSFER_FUNCTION_EXT_SRGB => TransferFunction::ExtSrgb, + TRANSFER_FUNCTION_ST2084_PQ => TransferFunction::St2084Pq, + TRANSFER_FUNCTION_ST428 => TransferFunction::St428, + _ => { + return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf( + req.tf, + )); + } + }; + if self.tf.replace(Some(tf)).is_some() { + return Err(WpImageDescriptionCreatorParamsV1Error::TfAlreadySet); } Ok(()) } @@ -62,20 +121,55 @@ impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreat req: SetPrimariesNamed, _slf: &Rc, ) -> Result<(), Self::Error> { - if req.primaries != PRIMARIES_SRGB { - return Err( - WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries), - ); + let primaries = match req.primaries { + PRIMARIES_SRGB => NamedPrimaries::Srgb, + PRIMARIES_PAL_M => NamedPrimaries::PalM, + PRIMARIES_PAL => NamedPrimaries::Pal, + PRIMARIES_NTSC => NamedPrimaries::Ntsc, + PRIMARIES_GENERIC_FILM => NamedPrimaries::GenericFilm, + PRIMARIES_BT2020 => NamedPrimaries::Bt2020, + PRIMARIES_CIE1931_XYZ => NamedPrimaries::Cie1931Xyz, + PRIMARIES_DCI_P3 => NamedPrimaries::DciP3, + PRIMARIES_DISPLAY_P3 => NamedPrimaries::DisplayP3, + PRIMARIES_ADOBE_RGB => NamedPrimaries::AdobeRgb, + _ => { + return Err( + WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries), + ); + } + }; + let primaries = (Some(primaries), primaries.primaries()); + if self.primaries.replace(Some(primaries)).is_some() { + return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesAlreadySet); } Ok(()) } - fn set_primaries(&self, _req: SetPrimaries, _slf: &Rc) -> Result<(), Self::Error> { - Err(WpImageDescriptionCreatorParamsV1Error::SetPrimariesNotSupported) + fn set_primaries(&self, req: SetPrimaries, _slf: &Rc) -> Result<(), Self::Error> { + let map = |n: i32| F64(n as f64 * PRIMARIES_MUL_INV); + let primaries = Primaries { + r: (map(req.r_x), map(req.r_y)), + g: (map(req.g_x), map(req.g_y)), + b: (map(req.b_x), map(req.b_y)), + wp: (map(req.w_x), map(req.w_y)), + }; + let primaries = (None, primaries); + if self.primaries.replace(Some(primaries)).is_some() { + return Err(WpImageDescriptionCreatorParamsV1Error::PrimariesAlreadySet); + } + Ok(()) } - fn set_luminances(&self, _req: SetLuminances, _slf: &Rc) -> Result<(), Self::Error> { - Err(WpImageDescriptionCreatorParamsV1Error::SetLuminancesNotSupported) + fn set_luminances(&self, req: SetLuminances, _slf: &Rc) -> Result<(), Self::Error> { + let luminance = Luminance { + min: F64(req.min_lum as f64 * MIN_LUM_MUL_INV), + max: F64(req.max_lum as f64), + white: F64(req.reference_lum as f64), + }; + if self.luminance.replace(Some(luminance)).is_some() { + return Err(WpImageDescriptionCreatorParamsV1Error::LuminancesAlreadySet); + } + Ok(()) } fn set_mastering_display_primaries( @@ -120,15 +214,23 @@ pub enum WpImageDescriptionCreatorParamsV1Error { SetMasteringLuminanceNotSupported, #[error("set_mastering_display_primaries is not supported")] SetMasteringDisplayPrimariesNotSupported, - #[error("set_luminances is not supported")] - SetLuminancesNotSupported, - #[error("set_primaries is not supported")] - SetPrimariesNotSupported, #[error("{} is not a supported named primary", .0)] UnsupportedPrimaries(u32), #[error("set_tf_power is not supported")] SetTfPowerNotSupported, #[error("{} is not a supported named transfer function", .0)] UnsupportedTf(u32), + #[error("The transfer function has already been set")] + TfAlreadySet, + #[error("The primaries have already been set")] + PrimariesAlreadySet, + #[error("The luminances have already been set")] + LuminancesAlreadySet, + #[error("The minimum luminance is too low")] + MinLuminanceTooLow, + #[error("The transfer function was not set")] + TfNotSet, + #[error("The primaries were not set")] + PrimariesNotSet, } efrom!(WpImageDescriptionCreatorParamsV1Error, ClientError); diff --git a/src/ifs/color_management/wp_image_description_v1.rs b/src/ifs/color_management/wp_image_description_v1.rs index 82d05875..00ec4d02 100644 --- a/src/ifs/color_management/wp_image_description_v1.rs +++ b/src/ifs/color_management/wp_image_description_v1.rs @@ -1,6 +1,7 @@ use { crate::{ client::{Client, ClientError}, + cmm::cmm_description::ColorDescription, ifs::color_management::wp_image_description_info_v1::WpImageDescriptionInfoV1, leaks::Tracker, object::{Object, Version}, @@ -15,6 +16,7 @@ pub struct WpImageDescriptionV1 { pub client: Rc, pub version: Version, pub tracker: Tracker, + pub description: Rc, } impl WpImageDescriptionV1 { @@ -27,10 +29,10 @@ impl WpImageDescriptionV1 { }); } - pub fn send_ready(&self, identity: u32) { + pub fn send_ready(&self) { self.client.event(Ready { self_id: self.id, - identity, + identity: self.description.id.into(), }); } } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 8f7b4cf2..cc0f97f0 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -5,6 +5,7 @@ pub mod ext_session_lock_surface_v1; pub mod tray; pub mod wl_subsurface; pub mod wp_alpha_modifier_surface_v1; +pub mod wp_color_management_surface_v1; pub mod wp_commit_timer_v1; pub mod wp_fifo_v1; pub mod wp_fractional_scale_v1; @@ -22,6 +23,7 @@ use { crate::{ backend::KeyState, client::{Client, ClientError}, + cmm::cmm_description::ColorDescription, cursor_user::{CursorUser, CursorUserId}, damage::DamageMatrix, drm_feedback::DrmFeedback, @@ -104,6 +106,7 @@ use { rc::{Rc, Weak}, }, thiserror::Error, + wp_color_management_surface_v1::WpColorManagementSurfaceV1, zwp_idle_inhibitor_v1::ZwpIdleInhibitorV1, }; @@ -332,6 +335,8 @@ pub struct WlSurface { commit_timer: CloneCell>>, before_latch_listener: EventListener, is_opaque: Cell, + color_management_surface: CloneCell>>, + color_description: CloneCell>>, } impl Debug for WlSurface { @@ -461,6 +466,7 @@ struct PendingState { fifo_barrier_wait: bool, commit_time: Option, tray_item_ack_serial: Option, + color_description: Option>>, } struct AttachedSubsurfaceState { @@ -513,6 +519,7 @@ impl PendingState { opt!(alpha_multiplier); opt!(commit_time); opt!(tray_item_ack_serial); + opt!(color_description); { let (dx1, dy1) = self.offset; let (dx2, dy2) = mem::take(&mut next.offset); @@ -670,6 +677,8 @@ impl WlSurface { commit_timer: Default::default(), before_latch_listener: EventListener::new(slf.clone()), is_opaque: Cell::new(false), + color_management_surface: Default::default(), + color_description: Default::default(), } } @@ -1137,6 +1146,11 @@ impl WlSurface { } } } + let mut color_description_changed = false; + if let Some(desc) = pending.color_description.take() { + color_description_changed = true; + self.color_description.set(desc); + } let mut alpha_changed = false; if let Some(alpha) = pending.alpha_multiplier.take() { alpha_changed = true; @@ -1144,8 +1158,11 @@ impl WlSurface { } let buffer_abs_pos = self.buffer_abs_pos.get(); let mut max_surface_size = buffer_abs_pos.size(); - let mut damage_full = - scale_changed || buffer_transform_changed || viewport_changed || alpha_changed; + let mut damage_full = scale_changed + || buffer_transform_changed + || viewport_changed + || alpha_changed + || color_description_changed; let mut buffer_changed = false; let mut old_raw_size = None; let (mut dx, mut dy) = mem::take(&mut pending.offset); @@ -1658,6 +1675,13 @@ impl WlSurface { pub fn opaque_region(&self) -> Option> { self.opaque_region.get() } + + pub fn color_description(&self) -> Rc { + match self.color_description.get() { + Some(cd) => cd, + None => self.client.state.color_manager.srgb_srgb().clone(), + } + } } object_base! { @@ -1689,6 +1713,7 @@ impl Object for WlSurface { self.text_input_connections.clear(); self.fifo.take(); self.commit_timer.take(); + self.color_management_surface.take(); } } diff --git a/src/ifs/color_management/wp_color_management_surface_v1.rs b/src/ifs/wl_surface/wp_color_management_surface_v1.rs similarity index 66% rename from src/ifs/color_management/wp_color_management_surface_v1.rs rename to src/ifs/wl_surface/wp_color_management_surface_v1.rs index 6d8cf7f3..4666c53d 100644 --- a/src/ifs/color_management/wp_color_management_surface_v1.rs +++ b/src/ifs/wl_surface/wp_color_management_surface_v1.rs @@ -1,7 +1,7 @@ use { crate::{ client::{Client, ClientError}, - ifs::color_management::consts::RENDER_INTENT_PERCEPTUAL, + ifs::{color_management, wl_surface::WlSurface}, leaks::Tracker, object::{Object, Version}, wire::{ @@ -21,12 +21,26 @@ pub struct WpColorManagementSurfaceV1 { pub client: Rc, pub version: Version, pub tracker: Tracker, + pub surface: Rc, +} + +impl WpColorManagementSurfaceV1 { + pub fn install(self: &Rc) -> Result<(), WpColorManagementSurfaceV1Error> { + if self.surface.color_management_surface.is_some() { + return Err(WpColorManagementSurfaceV1Error::HasSurface); + } + self.surface + .color_management_surface + .set(Some(self.clone())); + Ok(()) + } } impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 { type Error = WpColorManagementSurfaceV1Error; fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.surface.color_management_surface.take(); self.client.remove_obj(self)?; Ok(()) } @@ -36,12 +50,13 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 { req: SetImageDescription, _slf: &Rc, ) -> Result<(), Self::Error> { - let _ = self.client.lookup(req.image_description)?; - if req.render_intent != RENDER_INTENT_PERCEPTUAL { + if req.render_intent != color_management::RENDER_INTENT_PERCEPTUAL { return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent( req.render_intent, )); } + let desc = self.client.lookup(req.image_description)?; + self.surface.pending.borrow_mut().color_description = Some(Some(desc.description.clone())); Ok(()) } @@ -50,6 +65,7 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 { _req: UnsetImageDescription, _slf: &Rc, ) -> Result<(), Self::Error> { + self.surface.pending.borrow_mut().color_description = Some(None); Ok(()) } } @@ -69,5 +85,7 @@ pub enum WpColorManagementSurfaceV1Error { ClientError(Box), #[error("{} is not a supported render intent", .0)] UnsupportedRenderIntent(u32), + #[error("wl_surface already has a color-management extension")] + HasSurface, } efrom!(WpColorManagementSurfaceV1Error, ClientError); diff --git a/src/renderer.rs b/src/renderer.rs index 22b73182..afa16502 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -444,7 +444,7 @@ impl Renderer<'_> { bounds: Option<&Rect>, ) { let alpha = surface.alpha(); - let cd = self.state.color_manager.srgb_srgb(); + let cd = surface.color_description(); if let Some(tex) = buffer.buffer.get_texture(surface) { let mut opaque = surface.opaque(); if !opaque && tex.format().has_alpha { @@ -463,7 +463,7 @@ impl Renderer<'_> { AcquireSync::Unnecessary, buffer.release_sync, opaque, - cd, + &cd, ); } else if let Some(color) = &buffer.buffer.color { if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {