KColor is coming this Monday...

Matthew Woehlke mw_triad at users.sourceforge.net
Fri May 25 19:49:24 BST 2007

Zack Rusin wrote:
> Hey, it's a little unfortunate that you decided to dismiss my worries the last 
> time you mentioned this. 

You'll have to remind me what worries those were.

> Now all those mentioned H* colorspaces are by definition non-linear 
> transformations of the RGB colorspace. So they, again by definition, lose 
> precision on conversions.

But in 64-bit floating point math (which KColor uses exclusively), the 
loss is negligible. In fact the test suite *requires* that KColor( 
someQColor ).convert( KColor::Hs? ) gives back the original color (at 
least in 8-bpp) when cast back to a QColor. That is the result must 
QColor::operator== the input. So I fail to see why this is a concern.

> The differences between HSL and HSV colorspaces are 
> pretty minuscule from the point of view of applications, almost non-existant. 

Um... no, there is actually quite a big difference between HSV and 
HS[LY]. In the former, you cannot make Qt::red any brighter. In the 
latter you can make anything except Qt::white brighter. That's pretty 
much the main point; lighten() and darken() do not really work correctly 
in HSV color space which is why QColor::lighten() and QColor::darken() 
are broken IMO.

> Now the argument people often use for HSL over HSV is linearity over 
> lightness. That argument, altough mathematically sound betrays somewhat a 
> lack of practical experience in usage of HSL. Due to linearity over 
> lightness, colors darkened in a lineary fashion will appear smudged, grayed 
> out, when transformed in HSL.

While this is perceptively true, it also represents as you say a correct 

I also object to your comment regarding "lack of practical experience in 
usage of HSL". I originally wrote KColor::lighten() and darken() for use 
in what will be the Ion style eventually (KDE 4.1 perhaps); I do indeed 
have experience using the HSL model.

> Underneath I'm posting a url with a jpg so that everyone can compare QColor's 
> HSV and KColor HSL color spectrum. The image shows four bands - red, green, 
> blue and white. 
> Now notice that while white produces virtually identical results for both, 
> the results for dark colors (especially nicely visible in the red and blue 
> spectrums) for one of the *Color classes appear virtually gray not deep-dark 
> of the given color. Can you guess which one is which? Please look at this
> image now: 
> http://ktown.kde.org/~zrusin/color.jpg

There's something wrong with that, with saturation==1 HSL does not 
produce "gray" colors. In fact, HSL s=1, v=[0,0.5] should map exactly to 
HSV s=0, v=[0,1] and HSL s=1, v=[0.5,1] should map exactly to HSV 
s=[0,1], v=1. If you are not seeing this then something is wrong with 
the algorithm you are using (and if that's my algorithm, well then it 
needs to be fixed :-)). How did you make this image?

Note: the test suite will most likely verify things like this when it is 
more complete.

> When you say "give me a deep dark of this color" you usually mean "give me a 
> deep dark of this color" not "give me a deep gray".

...and that's exactly what *HSV* does, and exactly the problem HSL 
*avoids*. If you ask for a dark version of (255, 128, 128) in HSV, you 
get... gray. In HSL you get a vibrant red. HSV adjusts intensity and 
saturation /at the same time/, which is the problem. HSL does not.

> So in the purely usability sense the new lightening methods are going to be a 
> disservice to KDE applications using them.

Given the above I hope you can see why I reject this assertion. :-)

> Now moving forward to the blend method. I think it's a neat addition. What I 
> think is pretty silly is introducing another API and another implementation 
> on top of what Qt already does and provides.

I've been asking for months where to find blend() in Qt. No one has told 
me, and I haven't found one. Therefore I fail to see how you can say 
that Qt already provides this. In fact, since you subsequently give an 
example of how to implement blend() anyway, rather than referring me to 
an existing function in Qt, I can only assume that as far as you know, 
there is no such function in Qt.

> What I think would be a lot better idea is introducing a class/namespace 
> called KGraphicsUtils or similar and adding blendColor method to it.

If Qt supported HSL and HSY color spaces I would probably do that, or at 
minimum I would not be making KColor as complex as it is. I wouldn't 
need to.

> The implementation would look like: 
> QColor blendColor(const QColor &one, const QColor &two,
>                               QPainter::CompositionMode comp =
>                                   QPainter::CompositionMode_SourceOver)
> {
>     QImage img(1, 1, QImage::Format_ARGB32_Premultiplied);
>     img.fill(0);
>     QPainter p(&img);
>     p.fillRect(0, 0, 1, 1, one);
>     p.setCompositionMode(comp);
>     p.fillRect(0, 0, 1, 1, two);
>     p.end();
>     return img.pixel(0, 0);
> }
> The huge advantage of this method is that it produces exactly the same results 
> QPainter will produce and it works with all the QPainter composition mode's. 

Ok, so you addressed the horrible performance profile, thanks for making 
me feel more complicated about all the heavy math KColor is doing ;-). 
You didn't address that this also suffers the same *limits* as Qt, nor 
is it able to operate in color spaces other than RGB.

> Furthermore I think introducing a class with such prominent name as KColor is 
> a really bad idea.

I'm not irrevocably attached to the name, but given what it does, I'm 
not sure what else would be appropriate (KExtColor?). Besides, bad 
enough that I already have to type eight extra characters to call 
blend() :-). (Do we have a global KDE namespace? I.e. KDE::blend()?)

> It's a really bad idea because it means that KDE 
> developers will by default use this class and when they do they introduce 
> losy conversions all the time. The bottom line is that to paint KDE 
> applications need to convert to QColor at some point anyway. So the usage of 
> KColor in most cases will be.
> 1) Convert QColor to KColor (due to differences in the way these classes 
> internally store colors, that's already one lossy conversion)

If you looked at the code for KColor and QColor you would know that this 
is actually totally untrue. QColor uses 16 bits to store color 
information (and frequently only 8 are set), whereas KColor uses 
something like 48*. Funny, that doesn't /sound/ lossy :-).

(*I'd have to check the IEEE standard to know the exact number.)

> 2) Do an operation on KColor (most likely lighten/darken which mean lossy H* 
> colorspace and back conversion #2)

Help! I lost 24 bits of precision (not likely, 8 is probably a worst 
case), leaving me with "only" 24 'accurate' bits!

> 3) convert them back to QColor (lossy conversion #3). 
> So this is by far not ideal. 

This is the only truly "lossy" conversion... except that last I checked 
X is 8bpp anyway, so you're being reduced to 8bpp no matter what. This 
entire 'loss of precision' argument is just FUD.

> Addition of KColor means KDE gets third color class. There's QColor, KColor 
> and Pigment's KoColor. Of these three really only two make sense. The first 
> due to its usage in Qt rendering framework and the third due to providing 
> meaningful features that QColor doesn't. The last time that discussion came 
> up Pigment developers mentioned (very reasonably so, given its quite special 
> usage right now) that they're not ready to commit to binary compatibility.

Yes, I was part of that discussion IIRC, which is why KColor is here. 
Ideally I think I would rather see KoColor take on this role, or 
possibly KoColor can eventually merge into KColor, although KoColor as I 
understand is /really/ heavyweight.

> Pigment color classes actually do some very cool things and its existence 
> makes natural sense. Of course the usefulness of those features is somewhat 
> limited to very specific applications at the moment, but I could see in the 
> future KDE applications wanting to have fine grained color-space management. 
> And if by then Qt will not provide proper color-space management then it 
> would make natural sense to move Pigment up the stack. So by then KDE will 
> have three color classes all in core libraries.

...or (and particularly for KDE5) KoColor and KColor can merge. Actually 
I think I would like to see this, there are lots of really great things 
KColor could do along the lines of what KoColor can do. I guess the main 
difference is I'm willing to commit to BC over KDE4 with what I've got 
so far.

> So in the opinion of someone, who is practically the daddy of every single 
> pixel visible on the KDE 4 desktop (from drivers, acceleration architectures, 
> xrender implementation through the Qt rendering framework) I think KDE would 
> be a lot better served by:
> - KGraphicsUtils with blendColor (as defined above, for the lazy maybe with 
> one additional parameter that would specify opacity of the second color and 
> would just call QPainter::setOpacity(qreal) method). 
> - If there are applications that would really like to see darkening of colors 
> producing shades of gray instead of deep color then addition of
> QColor KGraphicsUtils::darkenInHsl(const QColor &clr, qreal val); 
> QColor KGraphicsUtils::lightenInHsl(const QColor &clr, qreal val);
> would make some sene. It wouldn't make any real difference in terms of speed 
> because the to-from QColor rgb conversion has to apply in any case anyway and 
> it wouldn't introduce a very prominently named class that would make the 
> transition to a lot more interesting addition, like Pigment, a lot more 
> confusing in the future)

So in short, you would like to name KColor something else. :-) Any 

"Still the prettiest." -- Legolas
(as quoted in The Very Secret Diaries by Cassandra Claire)

More information about the kde-core-devel mailing list