Color management Gtk4

From Inkscape Wiki
Jump to navigation Jump to search

Introduction

Inkscape has supported color management for some time using ICC color profiles. ICC color profiles are used three places in Inkscape:

  1. For specifying a particular color (see Fill and Stroke dialog). The profile and the color value are stored in the SVG file (see the SPObject based ColorProfile class).
  2. For color adjustment of monitor(s).
  3. For pre-flighting output.

The latter two uses are controlled by settings in the "Color management" subsection of the Input/Output section of the Preferences dialog.

The current code has some problems, particularly, the monitor adjustment code. These problems include

  • Only one profile can be specified even if there are multiple displays that need different corrections.
  • Retrieving a profile from a display doesn't appear to work.
  • The code for retrieving display profiles relies on X11 functionality that is missing under Wayland or other windowing systems.
  • The code was written for Gtk2 which defined a Gdk screen per monitor. In Gtk3, there is only one screen that can have multiple monitors. Gtk4 removes Gdk screen completely.
  • Only one color transform can be specified at a time due to the use of global variables.
  • The code is quite messy, based on GObject, and is poorly documented.
  • The drawing doesn't always update properly.

For per monitor color correction to work under X11, a ICC profile must be attached to each monitor, verify by:

  xprop -display :0.0 -len 14 -root _ICC_PROFILE

Spec: https://www.burtonini.com/computing/x-icc-profiles-spec-latest.html

Darktable has a color management test: darktable-cmstest

A redesigned system

Each monitor should have its own color transform. This color transform should be updated when:

  • The user changes a color transform related preference. Note: the canvas widget triggers a redraw whenever a color transform preference is changed.
  • The canvas is moved to a different monitor (if Inkscape handles monitor color corrections, see below). In Gtk4 there is Gdk::Surface::signal_enter_monitor than can be used for this. In Gtk3, there was Gtk::Widget::signal_screen_changed but as of 3.20 there is only one screen. One could check which monitor the canvas is on each time there is a draw and if the monitor changes, recalculate the color transform.

Per monitor color corrections should either:

  • Be handled by the windowing system. On most Linux (Gnome, KDE) systems, colord can be used to set a system wide color correction for each monitor (see, for example, the "Color" page for in Gnome's "Settings"). Windows and OSX have their own color corrections frameworks.
  • Be handled by Inkscape where the user can select which color correction to use for each monitor (it is the users responsibility to ensure that the windowing system does not also color correct as this would over-correct).

Inkscape should NOT try to obtain per-monitor color corrections from the windowing system.

  • For Linux there are two ways of doing so:
    • Using X11 (this, of course assumes X11 is being used and not Wayland): X11 has an "Atom" for finding the color correction for each monitor. The primary monitor's atom is "_ICC_PROFILE", additional monitors have atoms of the form "_ICC_PROFILE_n" where n is the additional screen number (starting at 1). On, at least my system only the primary monitor's color profile atom is defined. I could not, having spent hours searching the Internet, find out how to set "_ICC_PROFILE_1". One possible tool, that may have worked, doesn't support V4 ICC files. Inkscape 1.3 tries to do corrections this way when the "Retrieve profile from display" box is checked.
    • Using colord: This is the framework that Gnome and KDE use to set per monitor color corrections. As this would be by default the same as used by the system wide monitor correction, there is probably no added advantage in supporting this method.

The color transform is defined as a cmsHTRANSFORM (void *), this is a pointer to a data structure defined by lcms2. We need to track it, not only to avoid recreating it but also to delete it when replaced or no longer needed. It would probably best be stored per monitor in the CMSSystem class, currently there only one instance that gets continually update.

Test program

A test program for examining monitor information can be found at: https://gitlab.com/Tavmjong/gtk_sandbox/-/tree/master/gtk_monitor

Notes:

  • Output of Gdk::Display::get_name() is of the form "wayland-n" for Wayland and ":n" for X11, where n is a number.
  • Wayland has proper device model names, x11 has generic names (DP-1, DP-2).
  • Gtk4 with X11 lists all seat devices such as "Power Button", "Logitech M310/310t"; Gtk4 with Wayland lists a small set of devices including "Wayland Keyboard" and "Wayland Pointer".
  • Gtk3 has Gtk::Window::signal_screen_changed but Gtk has only one screen since 3.20 so it is useless to track moving a window between monitors. Gtk4 has Gtk::Surface::signal_enter_monitor(). This signal is not fired under X11. The Gtk4 function Gdk::Display::get_monitor_at_surface() does work on both X11 and Wayland.
  • The initial monitor is set wrong under Gtk3/Gtk4 x11/Wayland (as seen while constructing the Window, and in the signal handlers for realizing and mapping the window (it is always set to the second monitor). Under x11, the monitor is correct when using a button to trigger code to check the monitor. Under Wayland, the window has to be moved to a different monitor and back before the correct monitor is shown via the button. This is probably a Gtk bug.

Inkscape 1.3

This documents the ege-color-prof-tracker code as of 1.3.

  • On start-up:
    • ege-color_prof_tracker_new is called with no target widget (why?).
    • target_screen_change_cb
    • ege-color_prof_tracker_new is called with LayerSelector as target widget (why?).
    • If x11:
      • add_xll_tracking_for_screen called:
        • The X11 atoms _ICC_PROFILE (monitor 0) and _ICC_PROFILE_n (where n >= 1 is monitor number) are searched for.
        • On my system, _ICC_PROFILE_1 is never defined. I can find no way (after hours of internet searching) to set _ICC_PROFILE_1. Various sites suggest abandoning using X11 atoms and relying on colord.
    • target_screen_change_cb
  • On event handling:
    • After EVERY event (mouse, scroll, etc.) event_after_cb is called.
      • If the event is a GDK_CONFIGURE event (change of window size or position), there is a check to see if the gdk window is on a different monitor.
      • Note, on Wayland, the GDK_CONFIGURE event always reports an x, y as 0, 0.
      • If the monitor has changed, the tracker "changed" signal triggers a call to SPDesktopWidget::color_profile_event, this updates the canvas cms key (a checksum of profile buffer) corresponding to the new monitor.
    • CanvasPrivate::commit_tiles takes the value of prefs.from_display:
      • True: calls cms_system->get_display_transform_monitor(q->_cms_key). This uses the display profile linked to a monitor.
      • False: calls cms_system->get_display_transform_system(). This uses the display profile set in the preferences dialog (same profile for all monitors).