Problem with Lab 32f and Cmyk 32f.
Closed, ResolvedPublic


In Krita, we can have floating point spaces. For these spaces, KoColorSpaceMaths knows that the unitvalue is 1.0, which is important for all sorts of maths.

However, for Lab and CMYK, the unit value isn't 1. For lab it's 0-100, -128-127, -128-127 and CMYK is a complete mess in this case.

This means that all KoColorSpaceMaths operations for these are broken. Including fromNormalisedChannels(which I use for the selector) and KoColor::fromXML(needed for KPL).

There was a similar issue for the specific colorselector, which ademko fixed back then by making Krita ask LCMS what the maximum and minimum values for a certain value are. However, these are stored in KoColorChannel, which doesn't seem to live close to KoColorSpaceMaths at all.

I do NOT know how to fix this. I lack the technical knowledge.

woltherav created this task.Nov 8 2016, 1:36 PM

Let me see if I've traced the problem correctly.

The Lab color space is defined in libs/pigment/colorspaces/KoLabColorSpace.h (the basic, 16-bit version) and plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.h (the floating point version).
Its colorToXml implementation uses KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA (defined in libs/pigment/KoColorSpaceMaths.h). This uses the unitValue defined in the .cpp counterpart (which is 1). This is why saving the palette works correctly for these spaces, because it does not alter the color's values. Now, colorFromXml uses the same as above, so the problem IMO is not there.

That leaves fromNormalisedChannels as the culprit?

KoLabF32Traits is defined in libs/pigment/KoLabColorSpaceTraits.h. The normalization is inherited from KoColorSpaceTrait (libs/pigment/KoColorSpaceTraits.h):

inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) {
        Q_ASSERT((int)values.count() == (int)channels_nb);
        channels_type c;
        for (uint i = 0; i < channels_nb; i++) {
            float b = qBound((float)KoColorSpaceMathsTraits<channels_type>::min,
                             (float)KoColorSpaceMathsTraits<channels_type>::unitValue * values[i],
            c = (channels_type)b;
            nativeArray(pixel)[i] = c;


This binds the possible range for *every Lab channel* to whatever is defined in KoColorSpaceMaths.h, which is the range [0, 1] (according to the CPP counterpart).

lsegovia added a revision: Restricted Differential Revision.Nov 28 2016, 1:34 PM

Lab should be fixed by D3525. I'll appreciate any comments.
As for CMYK, I'm not sure how to fix it. What is the acceptable value range for each component in LCMS?

rempt added a comment.Dec 7 2016, 9:48 AM

I'll push D3525. For CMYK, well, there are no range limits in lcms2, afaik. Photoshop always seems to show CMYK in 0..100% -- maybe we can do something with that as well?

In T4488#70257, @rempt wrote:

I'll push D3525. For CMYK, well, there are no range limits in lcms2, afaik. Photoshop always seems to show CMYK in 0..100% -- maybe we can do something with that as well?

I'll try and see. As for the normalisation, can someone test it?

lsegovia added a comment.EditedDec 8 2016, 12:39 AM

More comments as regards CMYK: I can't figure out where Krita gets the value range for each component. For ints this range is correct, but for float it is completely broken.

EDIT: I'm checking the patch. With "Chemical proof", each component's range makes no sense (from 0 to some random number). If I swap it for another one e.g. "Wide Gamut CMYK Simulation" from Corel, the ranges are as expected.

lsegovia added a revision: Restricted Differential Revision.Dec 9 2016, 12:32 AM
This comment was removed by lsegovia.

See also this bug:

Values can be obtained from two places: IccProfile::getFloatUIMinMax or by the default constructor of KoChannelInfo.

With "Chemical proof", all the color ranges obtained from the ICC profile are invalid (getFloatUIMinMax returns [0, 0]). With the profile from Corel, it works only when using floating point. I don't know which are the acceptable values for LittleCMS.

Apart from that, if one tries to fix an arbitrary range in code, the selectors start jumping when moving the cursor in one of the color channels.

From "Unbounded Color Engines" at the LittleCMS site:

Clipping happens because several reasons. One is the
encoding of the device space. If we use a 8 or 16 bit representation
of the color space, all encodeable values are inside device gamut
by definition, and there is no way to represent out of gamut values.
For example, in the traditional 8-bit encoding, device values goes
from 0 to 255. This is encoded in one byte taking all available bits,
and therefore there is no way to represent negative numbers or
values over 255. 16 bit gives more precision but still clips values to
be inside realizable gamut.

Um, does this mean that the [0-100] range was only for floating point? Then using [0-255] or [0-65535] was completely correct for integer color spaces ._.

Yes, that would be right?

@dkazakov, I made a branch for this "feature" (see the related commit). I still need to figure out how to fix the colorspace ops.

lsegovia lowered the priority of this task from Normal to Low.May 2 2018, 11:52 AM

Everyone, heads up. I realised I didn't have enough working knowledge on color spaces and whatnot, so I decided to take a course on digital image processing at my uni this semester.

Since I'm also taking part in GSoC (I'll implement the practical part of my MSc thesis for Blender), I expect I'll be able to pick this back up after August for either KDE Season of Code or GSoC 2019.

Okay! Good luck with your GSoC!