Bug 401154

Summary: gwenview should not change image colors if system-wide color management is enabled
Product: [Applications] gwenview Reporter: Misha Aizatulin <avatar>
Component: generalAssignee: Gwenview Bugs <gwenview-bugs-null>
Status: RESOLVED FIXED    
Severity: normal CC: alex.kdebugzilla, avatar, karolbienkowski, nate
Priority: NOR    
Version: 18.04.3   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed In: 20.08.0
Sentry Crash Report:
Attachments: Make configurable whether ICC is used or not

Description Misha Aizatulin 2018-11-18 00:25:02 UTC
I set sytem-wide color management profile via oyranos. Now images in gwenview are oversaturated. What apparently happens is that gwenview applies the system profile on its own, thus effectively the profile is applied twice. This is easy to demonstrate by printing the screen.

STEPS TO REPRODUCE
oyranos-monitor -d 0 "LG Display 1070 _xorg.icc" 
open an image in gwenview 
save the screenshot
open the screenshot in gwenvew - it is very different from the original, but should be the same.
Comment 1 Misha Aizatulin 2018-11-18 00:27:42 UTC
For comparison, gimp, imagemagick, and showfoto do not suffer from this issue.
Comment 2 Christoph Feck 2018-11-18 11:59:18 UTC
How does oyranos work in Plasma? As far as I know, it can only set gamma ramps, but not apply correct color transformations.
Comment 3 Misha Aizatulin 2018-11-18 15:42:08 UTC
Dunno. I imagine it sets some "system-wide ICC profile", whatever that is, which applies to the whole screen. 

Presumably gwenview then retrieves that profile and applies that again. This line is suspect:
lib/cms/cmsprofile.cpp:224:        static Atom icc_atom = XInternAtom(QX11Info::display(), "_ICC_PROFILE", True);

Does this mean "retrieve the ICC profile of the current display"? If so, I think this might be a mistake - there should be no reason for gwenview to know this profile. It is already being applied to the whole screen, you shouldn't be applying it again.
Comment 5 Nate Graham 2018-11-19 21:10:45 UTC
Would you like to try your hand at producing a patch, Misha?

Documentation is located at:
- https://community.kde.org/Get_Involved/development
- https://community.kde.org/Infrastructure/Phabricator#Posting_a_Patch_using_the_website
Comment 6 Misha Aizatulin 2018-11-24 15:38:46 UTC
Here's a patch against 49a52e748904ae41b4991f6828ce50de9c9e05c9 (system is too old to compile tip sadly). Obviously you'll need to adjust it.

I actually like the double-profile behaviour in about 10% of cases (when the images are really dark), and I'm not sure how color management works on other systems, so I made it a config option.

diff --git a/app/generalconfigpage.ui b/app/generalconfigpage.ui
index d874d8a5..1efe9d63 100644
--- a/app/generalconfigpage.ui
+++ b/app/generalconfigpage.ui
@@ -25,6 +25,13 @@
      </property>
     </widget>
    </item>
+   <item row="0" column="1">
+    <widget class="QCheckBox" name="kcfg_NoICC">
+     <property name="text">
+       <string>No ICC</string>
+     </property>
+    </widget>
+   </item>
    <item row="1" column="1">
     <spacer name="verticalSpacer">
      <property name="orientation">
diff --git a/lib/cms/cmsprofile.cpp b/lib/cms/cmsprofile.cpp
index 56f38d5a..eec7b6ef 100644
--- a/lib/cms/cmsprofile.cpp
+++ b/lib/cms/cmsprofile.cpp
@@ -28,6 +28,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA
 #include <iodevicejpegsourcemanager.h>
 #include <jpegerrormanager.h>
 
+#include <lib/gwenviewconfig.h>
+
 extern "C" {
 #include <cms/iccjpeg.h>
 }
@@ -212,7 +214,7 @@ Profile::Ptr Profile::getMonitorProfile()
     // Get the profile from you config file if the user has set it.
     // if the user allows override through the atom, do this:
 #ifdef HAVE_X11
-    if (QX11Info::isPlatformX11()) {
+    if (QX11Info::isPlatformX11() && !GwenviewConfig::noICC()) {
         // get the current screen...
         int screen = -1;
 
diff --git a/lib/gwenviewconfig.kcfg b/lib/gwenviewconfig.kcfg
index 417b4ad0..f991e945 100644
--- a/lib/gwenviewconfig.kcfg
+++ b/lib/gwenviewconfig.kcfg
@@ -222,6 +222,9 @@
         <entry name="ListVideos" type="Bool">
             <default>true</default>
         </entry>
+        <entry name="NoICC" type="Bool">
+            <default>false</default>
+        </entry>
     </group>
 
     <group name="Print">
Comment 7 Nate Graham 2018-11-24 21:08:45 UTC
Thanks for the patch! It might be a good starting point. However...

> I actually like the double-profile behaviour in about 10% of cases
> (when the images are really dark)
This seems conceptually incorrect, even if the result looks good.

Is there some programmatic way for Gwenview to check and see whether a color profile is already in use?
Comment 8 Misha Aizatulin 2018-11-24 21:28:15 UTC
Yeah, I agree it is conceptually incorrect. 

> Is there some programmatic way for Gwenview to check and see whether a color profile is already in use?

My guess is as good as yours I'm afraid. Try asking oyranos devs perhaps?

One thing that is still puzzling is that if I set Lab.icc (the one with mad colours) to be the system profile, gwenview doesn't seem to apply it again in full. You can see it if you change Lab.icc back to sRGB - the image in gwenview remains a bit brighter, but not mad.
Comment 9 Alex Fliker 2020-02-08 11:16:46 UTC
Suffering from a similar problem. In my case its DisplayCal + ArgyllCMS. JPEG are being shown correctly, but TIFF and PNG images suffer from being oversaturated. In such cases I use showFoto, but it's inconvenient. Any progress on fixing this?
Comment 10 karolbienkowski 2020-03-22 17:36:42 UTC
I'm using colord with colord-kde and I have the same issue. Should Gwenview even use color correction on it's own? Shouldn't it be decided systemwide? I think that's the issue here.
Comment 11 Christoph Feck 2020-04-15 19:10:35 UTC
No, it needs to stay in Gwenview, because every image could have a different color profile. Gwenview should convert it to the current monitor's profile (or just sRGB, in case none is set).

Someone familiar with Linux color management needs to check if Gwenview tries to get the current profile, or if more code needs to be added.
Comment 12 Misha Aizatulin 2020-04-15 19:33:14 UTC
> Gwenview should convert it to the current monitor's profile (or just sRGB, in case none is set).

I might be misunderstanding how color management works, but is applying a profile a composable operation? If so, is it then correct to say that the job of gwenview is to apply the image profile and the job of X is to apply the system profile? If that is correct then why does gwenview need to know the system profile?
Comment 13 Christoph Feck 2020-04-15 20:14:41 UTC
Both conversions (from image to CIEXYZ, and from CIEXYZ to monitor) are merged into a single transformation inside liblcms2. Applications don't transfer CIEXYZ data to the X11 server; they only transfer (hardware) (s)RGB.
The purpose of the CMS server is to ensure that applications that are not aware of the CMS are treated as sRGB input (usually, but that is changeable), and then the server can apply the monitor color space transformations according to its profile.

If this was a two step transformation, then you would effectively need to go from image color space to sRGB, then from sRGB to the monitor space, and this could cause loss of color data, because both the image and the monitor could be wider than sRGB.

If CMSs were a deal when X11 was invented, it would have required that applications send CIEXYZ data, instead of RGB data. But alas...
Comment 14 Misha Aizatulin 2020-04-15 21:08:08 UTC
Got it. So if I understand correctly, gwenview tells the system (presumably X) "I do my own colour management, please don't modify my output", but the system is ignoring that? Perhaps the thing that modifies output here is not X, but some component that oyranos relies on? In fact, looks like there's a bug report to that extent:
https://github.com/oyranos-cms/oyranos/issues/60
Comment 15 Alex Fliker 2020-04-15 21:10:58 UTC
Maybe worth taking a look at showFoto app? It's handling color in a correct way.
Comment 16 karolbienkowski 2020-04-16 20:57:01 UTC
I tried to understand what's going on and this is a lot of information so I might have gotten some thing wrong.
There's a thing called "_ICC_PROFILE" X11 atom (simply a preference) that is supposed to keep monitor ICC profile in X. It can be used by any app that wants do to some color correction. If someone uses colord or oyranos then the profile in X11 was set by it and it also makes sure that all programs that keep this 
Window Property are color-corrected. So it seems to me, that maybe a window can decide if it wants to be corrected? Food for thought.
Some apps want to do their own color correction. this is from cmsprofile.cpp getMonitorProfile() function in gwenview: 

```
 static Atom icc_atom = XInternAtom(QX11Info::display(), "_ICC_PROFILE", True);

        if (XGetWindowProperty(QX11Info::display(),
                               QX11Info::appRootWindow(screen),
                               icc_atom,
                               0,
                               INT_MAX,
                               False,
                               XA_CARDINAL,
                               &type,
                               &format,
                               &nitems,
                               &bytes_after,
                               (unsigned char **) &str) == Success
                ) {
            hProfile = cmsOpenProfileFromMem((void*)str, nitems);
```

It checks if there's a profile on a window and if it is, then it is saved to hProfile and, later, used. There's problem with that logic. If there's a profile, then most likely the windows is already being color-corrected by system-wide cms. I don't know if setting global "_ICC_PROFILE" X11 atom and leaving it up to apps to actually correct themselves is a common workflow, but I doubt it.

If there's no profile, the generic sRGB one is created. In rasterimageview.cpp you can find that monitor and image profiles are merged (doesn't matter if monitor actually has any profile, bc now it uses generic sRGB)

All in all, I think gwenview should not care about monitor profile, let it be applied by system-wide cms and just apply image profile on top of it.
Comment 17 karolbienkowski 2020-04-16 21:13:01 UTC
(In reply to Misha Aizatulin from comment #14)
> Got it. So if I understand correctly, gwenview tells the system (presumably
> X) "I do my own colour management, please don't modify my output", but the
> system is ignoring that? Perhaps the thing that modifies output here is not
> X, but some component that oyranos relies on? In fact, looks like there's a
> bug report to that extent:
> https://github.com/oyranos-cms/oyranos/issues/60

I don't thinks that's the case. I don't know exactly how color management in X works but I think Gwenview doesn't message the rest of the system about it's cms. It probably can disable cms by removing icc profile from it's window property? Never checked that, did anyone?

I think it's simply that there are two cms engines working at once lcms in Gwenview and whatever system-wide cms is put in place. They get their monitor profile from the same source so it's used two times. I don't know any way to make them know about each other
Comment 18 karolbienkowski 2020-04-16 21:18:47 UTC
So I made a simple fix to check my theory and replaced whole getMonitorProfile() function with just return getSRgbProfile(); 
Then, I used pictures from http://www.color.org/version4html.xalter to check if image profiles are correctly applied and everything seems to be all right. System-wide cms (kolord) takes care of my monitor profile and gwenview applies image profiles.
Comment 19 Christoph Feck 2020-04-17 00:31:29 UTC
Fix from comment 18 will make transformation a two-step process, instead of a single transformation, with the possible color loss as indicated in comment 13. If there is no way to tell the system wide CMS that the contents is already color corrected, it is probably the only possible workaround. If you prepare a patch, I suggest to propose it for review via phabricator.

I really hope Wayland is ICC ready by design, not as an afterthought.
Comment 20 karolbienkowski 2020-04-17 08:25:22 UTC
(In reply to Christoph Feck from comment #19)
> Fix from comment 18 will make transformation a two-step process, instead of
> a single transformation, with the possible color loss as indicated in
> comment 13. If there is no way to tell the system wide CMS that the contents
> is already color corrected, it is probably the only possible workaround. If
> you prepare a patch, I suggest to propose it for review via phabricator.

I would gladly take care of this issue, but I need to know what is a proper behaviour. In comment #15 someone said that showFoto works well, but from my colord-kde experience, every time some app tries to apply color correction from monitor, it gets applied twice. Some apps, like Gimp or Darktable allow you to only set image color correction (by setting in-app monitor profile to sRGB). 

Actually ShowFoto has a problem that correction isn't applied the first time it's changed in settings so maybe that's why it looked correct at first.

It looks to me, that as long as there is system-wide correction the transformation will always be a two-step process. 

I would love if people using oyranos, colord-gtk or any other system-wide cms would check how all those apps (gimp, ShowFoto, Gwenview, Darktable, Eye of Gnome, etc) behave and if they produce correct images for them.

From my perspective the simplest fix is to make Gwenview forget that monitor ICC profile ever existed. Is there a legitimate use case that requires someone to use color correction of images, but no full screen (remembering that Gwenview is not a photo editor)?
Comment 21 Misha Aizatulin 2020-04-18 18:51:38 UTC
Created attachment 127667 [details]
Make configurable whether ICC is used or not
Comment 22 Misha Aizatulin 2020-04-18 18:52:37 UTC
Here's the change I use which just adds a configuration option.
Comment 23 Alex Fliker 2020-04-18 21:54:55 UTC
So, my personal breakdown:
GIMP - works correctly, when asked to convert to working profile I refuse, keeping the integrated one;
RawTherapee - the smoothest experience, I am using it as a reference;
showFoto - works correctly, when asked to convert to monitor's profile I refuse, keeping the integrated one.
I would switch to showFoto (though I still find Gwenview more convenient), but you can't shut up this annoying question on whether you want to convert color profile or not :-|
Adding a configuration switch to Gwenview sounds nice!
Comment 24 karolbienkowski 2020-04-19 11:17:31 UTC
(In reply to Misha Aizatulin from comment #21)
> Created attachment 127667 [details]
> Make configurable whether ICC is used or not

I should have said that I already posted my patch: https://phabricator.kde.org/D28918

It's based on Your work, but I added more comments and made the image auto-update after applying the profile (It didn't change unless it was moved or zoomed).
Comment 25 Nate Graham 2020-05-29 16:37:09 UTC
Git commit b76047f541340df561c2d7b91b825814923843b5 by Nate Graham, on behalf of Karol Bieńkowski.
Committed on 29/05/2020 at 16:37.
Pushed by ngraham into branch 'master'.

Make using monitor color profile optional

Summary: BUG: 401154

Reviewers: #gwenview, #vdg, ngraham

Reviewed By: #gwenview, #vdg, ngraham

Subscribers: ngraham

Tags: #gwenview

Differential Revision: https://phabricator.kde.org/D28918

M  +163  -95   app/advancedconfigpage.ui
M  +2    -0    app/configdialog.cpp
M  +4    -1    lib/cms/cmsprofile.cpp
M  +6    -0    lib/documentview/rasterimageview.cpp
M  +1    -0    lib/documentview/rasterimageview.h
M  +1    -0    lib/documentview/rasterimageviewadapter.cpp
M  +4    -0    lib/gwenviewconfig.kcfg

https://invent.kde.org/graphics/gwenview/commit/b76047f541340df561c2d7b91b825814923843b5