From dca0df25556f94c77a40f4b9c2745f3568cb459c Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 29 Mar 2026 15:34:53 +0200 Subject: [PATCH] cmm: store render intent --- src/cmm.rs | 1 + src/cmm/cmm_description.rs | 12 +++- src/cmm/cmm_luminance.rs | 18 +++-- src/cmm/cmm_render_intent.rs | 29 ++++++++ src/cmm/cmm_tests.rs | 6 +- src/cursor.rs | 4 ++ src/damage.rs | 11 ++- src/gfx_api.rs | 8 ++- src/gfx_apis/vulkan/renderer.rs | 34 ++++++--- src/ifs/color_management.rs | 1 + src/ifs/wl_surface.rs | 12 +++- .../wp_color_management_surface_v1.rs | 13 ++-- src/portal/ptr_gui.rs | 13 +++- src/renderer.rs | 69 ++++++++++++++----- src/renderer/renderer_base.rs | 30 ++++++-- src/state.rs | 6 +- 16 files changed, 207 insertions(+), 60 deletions(-) create mode 100644 src/cmm/cmm_render_intent.rs diff --git a/src/cmm.rs b/src/cmm.rs index 6e4700de..9c359c9d 100644 --- a/src/cmm.rs +++ b/src/cmm.rs @@ -3,6 +3,7 @@ pub mod cmm_eotf; pub mod cmm_luminance; pub mod cmm_manager; pub mod cmm_primaries; +pub mod cmm_render_intent; #[cfg(test)] mod cmm_tests; pub mod cmm_transform; diff --git a/src/cmm/cmm_description.rs b/src/cmm/cmm_description.rs index 9e406938..85a0a71a 100644 --- a/src/cmm/cmm_description.rs +++ b/src/cmm/cmm_description.rs @@ -5,6 +5,7 @@ use { cmm_luminance::{Luminance, TargetLuminance, white_balance}, cmm_manager::Shared, cmm_primaries::{NamedPrimaries, Primaries}, + cmm_render_intent::RenderIntent, cmm_transform::{ColorMatrix, Local, Xyz, bradford_adjustment}, }, utils::ordered_float::F64, @@ -39,12 +40,17 @@ pub struct ColorDescription { } impl LinearColorDescription { - pub fn color_transform(&self, target: &Self) -> ColorMatrix { + pub fn color_transform(&self, target: &Self, intent: RenderIntent) -> ColorMatrix { let mut mat = target.local_from_xyz; if self.luminance != target.luminance { - mat *= white_balance(&self.luminance, &target.luminance, target.primaries.wp); + mat *= white_balance( + &self.luminance, + &target.luminance, + target.primaries.wp, + intent, + ); } - if self.primaries.wp != target.primaries.wp { + if self.primaries.wp != target.primaries.wp && intent.bradford_adjustment() { mat *= bradford_adjustment(self.primaries.wp, target.primaries.wp); } mat * self.xyz_from_local diff --git a/src/cmm/cmm_luminance.rs b/src/cmm/cmm_luminance.rs index 90b57274..8371a33d 100644 --- a/src/cmm/cmm_luminance.rs +++ b/src/cmm/cmm_luminance.rs @@ -1,5 +1,8 @@ use crate::{ - cmm::cmm_transform::{ColorMatrix, Xyz}, + cmm::{ + cmm_render_intent::RenderIntent, + cmm_transform::{ColorMatrix, Xyz}, + }, utils::ordered_float::F64, }; @@ -68,12 +71,19 @@ impl Default for Luminance { } #[expect(non_snake_case)] -pub fn white_balance(from: &Luminance, to: &Luminance, w_to: (F64, F64)) -> ColorMatrix { +pub fn white_balance( + from: &Luminance, + to: &Luminance, + w_to: (F64, F64), + intent: RenderIntent, +) -> ColorMatrix { let a = ((from.max - from.min) / (to.max - to.min) * (to.white - to.min) / (from.white - from.min)) .0; - // let d = ((from.min - to.min) / (to.max - to.min)).0.max(0.0); - let d = 0.0; + let d = match intent.black_point_compensation() { + true => 0.0, + false => ((from.min - to.min) / (to.max - to.min)).0, + }; let s = a - d; let (F64(x_to), F64(y_to)) = w_to; let X_to = x_to / y_to; diff --git a/src/cmm/cmm_render_intent.rs b/src/cmm/cmm_render_intent.rs new file mode 100644 index 00000000..b187e83f --- /dev/null +++ b/src/cmm/cmm_render_intent.rs @@ -0,0 +1,29 @@ +use crate::{ifs::color_management::RENDER_INTENT_PERCEPTUAL, object::Version}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] +pub enum RenderIntent { + #[default] + Perceptual, +} + +impl RenderIntent { + pub fn from_wayland(intent: u32, _version: Version) -> Option { + let res = match intent { + RENDER_INTENT_PERCEPTUAL => Self::Perceptual, + _ => return None, + }; + Some(res) + } + + pub fn black_point_compensation(self) -> bool { + match self { + RenderIntent::Perceptual => true, + } + } + + pub fn bradford_adjustment(self) -> bool { + match self { + RenderIntent::Perceptual => true, + } + } +} diff --git a/src/cmm/cmm_tests.rs b/src/cmm/cmm_tests.rs index c2f71805..5f3564df 100644 --- a/src/cmm/cmm_tests.rs +++ b/src/cmm/cmm_tests.rs @@ -136,7 +136,7 @@ mod matrices { mod transforms { use crate::cmm::{ cmm_eotf::Eotf, cmm_luminance::Luminance, cmm_manager::ColorManager, - cmm_primaries::Primaries, + cmm_primaries::Primaries, cmm_render_intent::RenderIntent, }; fn check(p1: Primaries, p2: Primaries, expected: [[f64; 4]; 3]) { @@ -155,7 +155,9 @@ mod transforms { }; let d1 = d(p1); let d2 = d(p2); - let m = d1.linear.color_transform(&d2.linear); + let m = d1 + .linear + .color_transform(&d2.linear, RenderIntent::Perceptual); println!("{:#?}", m); assert!((m.0[0][0].0 - expected[0][0]).abs() < 0.001); assert!((m.0[0][1].0 - expected[0][1]).abs() < 0.001); diff --git a/src/cursor.rs b/src/cursor.rs index efcd7339..fbbf138d 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,6 +1,7 @@ use { crate::{ async_engine::AsyncEngine, + cmm::cmm_render_intent::RenderIntent, fixed::Fixed, format::ARGB8888, gfx_api::{AcquireSync, AlphaMode, GfxContext, GfxError, GfxTexture, ReleaseSync}, @@ -398,6 +399,7 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -424,6 +426,7 @@ impl Cursor for StaticCursor { ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -468,6 +471,7 @@ impl Cursor for AnimatedCursor { ReleaseSync::None, false, renderer.state.color_manager.srgb_gamma22(), + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } diff --git a/src/damage.rs b/src/damage.rs index a4785155..da38856a 100644 --- a/src/damage.rs +++ b/src/damage.rs @@ -1,7 +1,7 @@ use { crate::{ async_engine::AsyncEngine, - cmm::cmm_manager::ColorManager, + cmm::{cmm_manager::ColorManager, cmm_render_intent::RenderIntent}, fixed::Fixed, ifs::wl_output::WlOutputGlobal, rect::{Rect, Region}, @@ -169,7 +169,14 @@ impl DamageVisualizer { if region.is_not_empty() { let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis; let color = base_color * (1.0 - age); - renderer.fill_boxes2(region.rects(), &color, srgb, dx, dy); + renderer.fill_boxes2( + region.rects(), + &color, + srgb, + RenderIntent::Perceptual, + dx, + dy, + ); used = used.union_cow(®ion).into_owned(); } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index e908a9ac..09aa13b1 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -1,7 +1,10 @@ use { crate::{ allocator::Allocator, - cmm::cmm_description::{ColorDescription, LinearColorDescription}, + cmm::{ + cmm_description::{ColorDescription, LinearColorDescription}, + cmm_render_intent::RenderIntent, + }, cpu_worker::CpuWorker, cursor::Cursor, damage::DamageVisualizer, @@ -255,6 +258,7 @@ pub struct FillRect { pub rect: FramebufferRect, pub color: Color, pub alpha: Option, + pub render_intent: RenderIntent, pub cd: Rc, } @@ -277,6 +281,7 @@ pub struct CopyTexture { pub release_sync: ReleaseSync, pub alpha: Option, pub opaque: bool, + pub render_intent: RenderIntent, pub cd: Rc, pub alpha_mode: AlphaMode, } @@ -512,6 +517,7 @@ impl dyn GfxFramebuffer { release_sync, false, texture_cd, + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT); diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 3d3d1411..04dc3102 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -4,6 +4,7 @@ use { cmm::{ cmm_description::{ColorDescription, LinearColorDescription, LinearColorDescriptionId}, cmm_eotf::{Eotf, EotfPow, bt1886_eotf_args, bt1886_inv_eotf_args}, + cmm_render_intent::RenderIntent, cmm_transform::ColorMatrix, }, cpu_worker::PendingJob, @@ -817,9 +818,12 @@ impl VulkanRenderer { RenderPass::FrameBuffer => fb_cd, }; let tf = target_cd.eotf; - let color = memory - .color_transforms - .apply_to_color(&fr.cd, target_cd, fr.color); + let color = memory.color_transforms.apply_to_color( + &fr.cd, + target_cd, + fr.render_intent, + fr.color, + ); let color = color.to_array2(tf, fr.alpha); let source_type = match color[3] < 1.0 { false => TexSourceType::Opaque, @@ -883,6 +887,7 @@ impl VulkanRenderer { let color_management_data_address = memory.color_transforms.get_offset( &ct.cd.linear, target_cd, + ct.render_intent, self.device.uniform_buffer_offset_mask, &mut memory.uniform_buffer_writer, ); @@ -934,6 +939,7 @@ impl VulkanRenderer { memory.blend_buffer_color_management_data_address = memory.color_transforms.get_offset( &bb_cd.linear, fb_cd, + RenderIntent::Perceptual, self.device.uniform_buffer_offset_mask, &mut memory.uniform_buffer_writer, ); @@ -1192,9 +1198,12 @@ impl VulkanRenderer { if let Some(clear) = clear && clear_rects.is_not_empty() { - let color = memory - .color_transforms - .apply_to_color(clear_cd, target_cd, *clear); + let color = memory.color_transforms.apply_to_color( + clear_cd, + target_cd, + RenderIntent::Perceptual, + *clear, + ); let clear_value = ClearValue { color: ClearColorValue { float32: color.to_array(target_cd.eotf), @@ -2312,7 +2321,7 @@ where #[derive(Default)] struct ColorTransforms { - map: AHashMap<[LinearColorDescriptionId; 2], ColorTransform>, + map: AHashMap<([LinearColorDescriptionId; 2], RenderIntent), ColorTransform>, } struct ColorTransform { @@ -2325,14 +2334,15 @@ impl ColorTransforms { &mut self, src: &LinearColorDescription, dst: &ColorDescription, + intent: RenderIntent, ) -> Option<&mut ColorTransform> { if src.embeds_into(&dst.linear) { return None; } - let ct = match self.map.entry([src.id, dst.linear.id]) { + let ct = match self.map.entry(([src.id, dst.linear.id], intent)) { Entry::Occupied(o) => o.into_mut(), Entry::Vacant(e) => { - let matrix = src.color_transform(&dst.linear); + let matrix = src.color_transform(&dst.linear, intent); let ct = ColorTransform { matrix, offset: None, @@ -2347,9 +2357,10 @@ impl ColorTransforms { &mut self, src: &LinearColorDescription, dst: &ColorDescription, + intent: RenderIntent, mut color: Color, ) -> Color { - if let Some(ct) = self.get_or_create(src, dst) { + if let Some(ct) = self.get_or_create(src, dst, intent) { color = ct.matrix * color; }; color @@ -2359,10 +2370,11 @@ impl ColorTransforms { &mut self, src: &LinearColorDescription, dst: &ColorDescription, + intent: RenderIntent, uniform_buffer_offset_mask: DeviceSize, writer: &mut GenericBufferWriter, ) -> Option { - let ct = self.get_or_create(src, dst)?; + let ct = self.get_or_create(src, dst, intent)?; if ct.offset.is_none() { let data = ColorManagementData { matrix: ct.matrix.to_f32(), diff --git a/src/ifs/color_management.rs b/src/ifs/color_management.rs index ed955e1d..b739916d 100644 --- a/src/ifs/color_management.rs +++ b/src/ifs/color_management.rs @@ -27,6 +27,7 @@ mod consts { pub const RENDER_INTENT_SATURATION: u32 = 2; pub const RENDER_INTENT_ABSOLUTE: u32 = 3; pub const RENDER_INTENT_RELATIVE_BPC: u32 = 4; + pub const RENDER_INTENT_ABSOLUTE_NO_ADAPTATION: u32 = 5; pub const FEATURE_ICC_V2_V4: u32 = 0; pub const FEATURE_PARAMETRIC: u32 = 1; diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index c9d6051e..3025eaf2 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -26,7 +26,7 @@ use { crate::{ backend::{ButtonState, KeyState}, client::{Client, ClientError}, - cmm::cmm_description::ColorDescription, + cmm::{cmm_description::ColorDescription, cmm_render_intent::RenderIntent}, cursor_user::{CursorUser, CursorUserId}, damage::DamageMatrix, drm_feedback::DrmFeedback, @@ -337,6 +337,7 @@ pub struct WlSurface { color_management_feedback: CopyHashMap>, color_description: CloneCell>>, + render_intent: Cell, color_representation_surface: CloneCell>>, alpha_mode: Cell, } @@ -476,7 +477,7 @@ struct PendingState { fifo_barrier_wait: bool, commit_time: Option, tray_item_ack_serial: Option, - color_description: Option>>, + color_description: Option)>>, serial: Option, alpha_mode: Option, surface_release: SmallVec<[SurfaceRelease; 1]>, @@ -689,6 +690,7 @@ impl WlSurface { color_management_surface: Default::default(), color_management_feedback: Default::default(), color_description: Default::default(), + render_intent: Default::default(), color_representation_surface: Default::default(), alpha_mode: Default::default(), } @@ -1210,7 +1212,9 @@ impl WlSurface { let mut color_description_changed = false; if let Some(desc) = pending.color_description.take() { color_description_changed = true; + let (intent, desc) = desc.unzip(); self.color_description.set(desc); + self.render_intent.set(intent.unwrap_or_default()); } let mut alpha_mode_changed = false; if let Some(alpha_mode) = pending.alpha_mode.take() @@ -1756,6 +1760,10 @@ impl WlSurface { } } + pub fn render_intent(&self) -> RenderIntent { + self.render_intent.get() + } + pub fn add_color_management_feedback(&self, fb: &Rc) { self.color_management_feedback.set(fb.id, fb.clone()); } diff --git a/src/ifs/wl_surface/wp_color_management_surface_v1.rs b/src/ifs/wl_surface/wp_color_management_surface_v1.rs index 33ad5df5..9783aa87 100644 --- a/src/ifs/wl_surface/wp_color_management_surface_v1.rs +++ b/src/ifs/wl_surface/wp_color_management_surface_v1.rs @@ -1,7 +1,8 @@ use { crate::{ client::{Client, ClientError}, - ifs::{color_management, wl_surface::WlSurface}, + cmm::cmm_render_intent::RenderIntent, + ifs::wl_surface::WlSurface, leaks::Tracker, object::{Object, Version}, wire::{ @@ -51,16 +52,16 @@ impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 { req: SetImageDescription, _slf: &Rc, ) -> Result<(), Self::Error> { - if req.render_intent != color_management::RENDER_INTENT_PERCEPTUAL { + let Some(intent) = RenderIntent::from_wayland(req.render_intent, self.version) else { return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent( req.render_intent, )); - } + }; let desc = self.client.lookup(req.image_description)?; - if desc.description.is_none() { + let Some(desc) = &desc.description else { return Err(WpColorManagementSurfaceV1Error::NotReady); - } - self.surface.pending.borrow_mut().color_description = Some(desc.description.clone()); + }; + self.surface.pending.borrow_mut().color_description = Some(Some((intent, desc.clone()))); Ok(()) } diff --git a/src/portal/ptr_gui.rs b/src/portal/ptr_gui.rs index 4962bca8..1484a583 100644 --- a/src/portal/ptr_gui.rs +++ b/src/portal/ptr_gui.rs @@ -2,7 +2,7 @@ use { crate::{ allocator::{BO_USE_RENDERING, BufferObject, BufferUsage}, async_engine::{Phase, SpawnedFuture}, - cmm::cmm_manager::ColorManager, + cmm::{cmm_manager::ColorManager, cmm_render_intent::RenderIntent}, cursor::KnownCursor, fixed::Fixed, format::ARGB8888, @@ -207,7 +207,12 @@ impl GuiElement for Button { (x1, y1 + border, x1 + border, y2 - border), (x2 - border, y1 + border, x2, y2 - border), ]; - r.fill_boxes_f(&rects, &self.border_color.get(), srgb); + r.fill_boxes_f( + &rects, + &self.border_color.get(), + srgb, + RenderIntent::Perceptual, + ); } { let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)]; @@ -215,7 +220,7 @@ impl GuiElement for Button { true => self.bg_color.get(), false => self.bg_hover_color.get(), }; - r.fill_boxes_f(&rects, &color, srgb); + r.fill_boxes_f(&rects, &color, srgb, RenderIntent::Perceptual); } if let Some(tex) = self.tex.get() { let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get()); @@ -233,6 +238,7 @@ impl GuiElement for Button { ReleaseSync::None, false, srgb_srgb, + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -336,6 +342,7 @@ impl GuiElement for Label { ReleaseSync::None, false, color_manager.srgb_gamma22(), + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } diff --git a/src/renderer.rs b/src/renderer.rs index 5fb98647..93f4268e 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,5 +1,6 @@ use { crate::{ + cmm::cmm_render_intent::RenderIntent, gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect}, icons::{IconState, SizedIcons}, ifs::wl_surface::{ @@ -80,6 +81,7 @@ impl Renderer<'_> { let theme = &self.state.theme; let srgb_srgb = self.state.color_manager.srgb_gamma22(); let srgb = &srgb_srgb.linear; + let perceptual = RenderIntent::Perceptual; if let Some(fs) = &fullscreen { fs.node_render(self, x, y, None); } else { @@ -97,7 +99,7 @@ impl Renderer<'_> { let bar_bg = self.base.scale_rect(bar_bg); let c = theme.colors.bar_background.get(); self.base - .fill_scaled_boxes(slice::from_ref(&bar_bg), &c, None, srgb); + .fill_scaled_boxes(slice::from_ref(&bar_bg), &c, None, srgb, perceptual); self.base.sync(); let rd = output.render_data.borrow_mut(); if let Some(aw) = &rd.active_workspace { @@ -106,7 +108,7 @@ impl Renderer<'_> { false => theme.colors.focused_title_background.get(), }; self.base - .fill_boxes2(slice::from_ref(&aw.rect), &c, srgb, x, y); + .fill_boxes2(slice::from_ref(&aw.rect), &c, srgb, perceptual, x, y); } let mut c = theme.colors.separator.get(); if let Some(ws) = &ws @@ -114,18 +116,30 @@ impl Renderer<'_> { { c = theme.colors.focused_title_background.get(); } - self.base - .fill_boxes2(slice::from_ref(&rd.bar_separator), &c, srgb, x, y); + self.base.fill_boxes2( + slice::from_ref(&rd.bar_separator), + &c, + srgb, + perceptual, + x, + y, + ); let c = theme.colors.unfocused_title_background.get(); self.base - .fill_boxes2(&rd.inactive_workspaces, &c, srgb, x, y); + .fill_boxes2(&rd.inactive_workspaces, &c, srgb, perceptual, x, y); let c = theme.colors.captured_unfocused_title_background.get(); self.base - .fill_boxes2(&rd.captured_inactive_workspaces, &c, srgb, x, y); + .fill_boxes2(&rd.captured_inactive_workspaces, &c, srgb, perceptual, x, y); self.base.sync(); let c = theme.colors.attention_requested_background.get(); - self.base - .fill_boxes2(&rd.attention_requested_workspaces, &c, srgb, x, y); + self.base.fill_boxes2( + &rd.attention_requested_workspaces, + &c, + srgb, + perceptual, + x, + y, + ); let scale = output.global.persistent.scale.get(); for title in &rd.titles { let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y); @@ -143,6 +157,7 @@ impl Renderer<'_> { ReleaseSync::None, false, self.state.color_manager.srgb_gamma22(), + perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -166,6 +181,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -210,7 +226,7 @@ impl Renderer<'_> { let color = self.state.theme.colors.highlight.get(); let bounds = output.workspace_rect_rel.get().move_(x, y); self.base.sync(); - self.base.fill_boxes(&[bounds], &color, srgb); + self.base.fill_boxes(&[bounds], &color, srgb, perceptual); } } @@ -232,6 +248,7 @@ impl Renderer<'_> { std::slice::from_ref(&pos.at_point(x, y)), &Color::from_srgba_straight(20, 20, 20, 255), &self.state.color_manager.srgb_gamma22().linear, + RenderIntent::Perceptual, ); if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale) && let Some(texture) = tex.texture() @@ -253,6 +270,7 @@ impl Renderer<'_> { ReleaseSync::None, false, self.state.color_manager.srgb_gamma22(), + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -263,19 +281,23 @@ impl Renderer<'_> { { let srgb_srgb = self.state.color_manager.srgb_gamma22(); let srgb = &srgb_srgb.linear; + let perceptual = RenderIntent::Perceptual; let rd = container.render_data.borrow_mut(); let c = self.state.theme.colors.unfocused_title_background.get(); - self.base.fill_boxes2(&rd.title_rects, &c, srgb, x, y); + self.base + .fill_boxes2(&rd.title_rects, &c, srgb, perceptual, x, y); let c = self.state.theme.colors.focused_title_background.get(); self.base - .fill_boxes2(&rd.active_title_rects, &c, srgb, x, y); + .fill_boxes2(&rd.active_title_rects, &c, srgb, perceptual, x, y); let c = self.state.theme.colors.attention_requested_background.get(); self.base - .fill_boxes2(&rd.attention_title_rects, &c, srgb, x, y); + .fill_boxes2(&rd.attention_title_rects, &c, srgb, perceptual, x, y); let c = self.state.theme.colors.separator.get(); - self.base.fill_boxes2(&rd.underline_rects, &c, srgb, x, y); + self.base + .fill_boxes2(&rd.underline_rects, &c, srgb, perceptual, x, y); let c = self.state.theme.colors.border.get(); - self.base.fill_boxes2(&rd.border_rects, &c, srgb, x, y); + self.base + .fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y); if let Some(lar) = &rd.last_active_rect { let c = self .state @@ -284,7 +306,7 @@ impl Renderer<'_> { .focused_inactive_title_background .get(); self.base - .fill_boxes2(std::slice::from_ref(lar), &c, srgb, x, y); + .fill_boxes2(std::slice::from_ref(lar), &c, srgb, perceptual, x, y); } if let Some(titles) = rd.titles.get(&self.base.scale) { for title in titles { @@ -305,6 +327,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -382,6 +405,7 @@ impl Renderer<'_> { &color, None, &self.state.color_manager.srgb_gamma22().linear, + RenderIntent::Perceptual, ); } @@ -392,6 +416,7 @@ impl Renderer<'_> { slice::from_ref(rect), &color, &self.state.color_manager.srgb_gamma22().linear, + RenderIntent::Perceptual, ); } @@ -469,6 +494,7 @@ impl Renderer<'_> { let buf = &buffer.buffer.buf; let alpha = surface.alpha(); let cd = surface.color_description(); + let intent = surface.render_intent(); let alpha_mode = surface.alpha_mode(); if let Some(tex) = buf.get_texture(surface) { let mut opaque = surface.opaque(); @@ -489,6 +515,7 @@ impl Renderer<'_> { buffer.release_sync, opaque, &cd, + intent, alpha_mode, ); } else if let Some(color) = &buf.color { @@ -503,7 +530,7 @@ impl Renderer<'_> { ); self.base.sync(); self.base - .fill_scaled_boxes(&[rect], &color, alpha, &cd.linear); + .fill_scaled_boxes(&[rect], &color, alpha, &cd.linear, intent); } } } else { @@ -539,21 +566,23 @@ impl Renderer<'_> { ]; let srgb_srgb = self.state.color_manager.srgb_gamma22(); let srgb = &srgb_srgb.linear; - self.base.fill_boxes(&borders, &bc, srgb); + let perceptual = RenderIntent::Perceptual; + self.base.fill_boxes(&borders, &bc, srgb, perceptual); let title = [Rect::new_sized_saturating( x + bw, y + bw, pos.width() - 2 * bw, th, )]; - self.base.fill_boxes(&title, &tc, srgb); + self.base.fill_boxes(&title, &tc, srgb, perceptual); let title_underline = [Rect::new_sized_saturating( x + bw, y + bw + th, pos.width() - 2 * bw, tuh, )]; - self.base.fill_boxes(&title_underline, &uc, srgb); + self.base + .fill_boxes(&title_underline, &uc, srgb, perceptual); let rect = floating.title_rect.get().move_(x, y); let bounds = self.base.scale_rect(rect); let (mut x1, y1) = rect.position(); @@ -586,6 +615,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + perceptual, AlphaMode::PremultipliedElectrical, ); } @@ -609,6 +639,7 @@ impl Renderer<'_> { ReleaseSync::None, false, srgb_srgb, + perceptual, AlphaMode::PremultipliedElectrical, ); } diff --git a/src/renderer/renderer_base.rs b/src/renderer/renderer_base.rs index 22d30cc1..af1ae926 100644 --- a/src/renderer/renderer_base.rs +++ b/src/renderer/renderer_base.rs @@ -1,6 +1,9 @@ use { crate::{ - cmm::cmm_description::{ColorDescription, LinearColorDescription}, + cmm::{ + cmm_description::{ColorDescription, LinearColorDescription}, + cmm_render_intent::RenderIntent, + }, gfx_api::{ AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, ReleaseSync, SampleRect, @@ -70,12 +73,19 @@ impl RendererBase<'_> { color: &Color, alpha: Option, cd: &Rc, + render_intent: RenderIntent, ) { - self.fill_boxes3(boxes, color, alpha, cd, 0, 0, true); + self.fill_boxes3(boxes, color, alpha, cd, render_intent, 0, 0, true); } - pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color, cd: &Rc) { - self.fill_boxes3(boxes, color, None, cd, 0, 0, false); + pub fn fill_boxes( + &mut self, + boxes: &[Rect], + color: &Color, + cd: &Rc, + render_intent: RenderIntent, + ) { + self.fill_boxes3(boxes, color, None, cd, render_intent, 0, 0, false); } pub fn fill_boxes2( @@ -83,10 +93,11 @@ impl RendererBase<'_> { boxes: &[Rect], color: &Color, cd: &Rc, + render_intent: RenderIntent, dx: i32, dy: i32, ) { - self.fill_boxes3(boxes, color, None, cd, dx, dy, false); + self.fill_boxes3(boxes, color, None, cd, render_intent, dx, dy, false); } fn fill_boxes3( @@ -95,6 +106,7 @@ impl RendererBase<'_> { color: &Color, alpha: Option, cd: &Rc, + render_intent: RenderIntent, dx: i32, dy: i32, scaled: bool, @@ -120,6 +132,7 @@ impl RendererBase<'_> { ), color: *color, alpha, + render_intent, cd: cd.clone(), })); } @@ -130,8 +143,9 @@ impl RendererBase<'_> { boxes: &[(f32, f32, f32, f32)], color: &Color, cd: &Rc, + render_intent: RenderIntent, ) { - self.fill_boxes2_f(boxes, color, cd, 0.0, 0.0); + self.fill_boxes2_f(boxes, color, cd, render_intent, 0.0, 0.0); } pub fn fill_boxes2_f( @@ -139,6 +153,7 @@ impl RendererBase<'_> { boxes: &[(f32, f32, f32, f32)], color: &Color, cd: &Rc, + render_intent: RenderIntent, dx: f32, dy: f32, ) { @@ -160,6 +175,7 @@ impl RendererBase<'_> { ), color: *color, alpha: None, + render_intent, cd: cd.clone(), })); } @@ -180,6 +196,7 @@ impl RendererBase<'_> { release_sync: ReleaseSync, opaque: bool, cd: &Rc, + render_intent: RenderIntent, alpha_mode: AlphaMode, ) { // log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8); @@ -227,6 +244,7 @@ impl RendererBase<'_> { acquire_sync, release_sync, opaque, + render_intent, cd: cd.clone(), alpha_mode, })); diff --git a/src/state.rs b/src/state.rs index 4eded0c4..03a949dc 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,7 +13,10 @@ use { cli::RunArgs, client::{Client, ClientCaps, ClientId, Clients, NUM_CACHED_SERIAL_RANGES, SerialRange}, clientmem::ClientMemOffset, - cmm::{cmm_description::ColorDescription, cmm_manager::ColorManager}, + cmm::{ + cmm_description::ColorDescription, cmm_manager::ColorManager, + cmm_render_intent::RenderIntent, + }, compositor::{LIBEI_SOCKET, LogLevel}, config::ConfigProxy, control_center::{ @@ -1332,6 +1335,7 @@ impl State { release_sync, false, src_cd, + RenderIntent::Perceptual, AlphaMode::PremultipliedElectrical, ); if render_hardware_cursors