KColor is coming this Monday...

Zack Rusin zack at kde.org
Fri May 25 16:49:41 BST 2007


On Thursday 24 May 2007 05:48:31 pm Matthew Woehlke wrote:
> KColor adds additional color spaces over QColor, better
> darken()/lighten(), and /finally/ generic blending.
>
> Status of KColor:
> - HLS conversion to/from RGB seems to work (it's symmetrical, at least)
> - blend() handles BlendNormal and BlendReplace
> - lighten()/darken() would work if HSY conversion was implemented (it
> could be made to work using HSL instead)
>
> In order to check this in, I need to rename the current kcolortest (to
> what? kimageeffectstest?) so the KColor test suite can be properly
> named. I also need to edit KColor (the header) to point to kcolor.h
> instead of kcolordlg.h (huh? There is already a correct redirection
> header KColorDlg...); I did not see any uses of this in libs, sdk,
> utils, and base (is there a way to do a more exhaustive check?). I will
> probably do all things at once. Now is the time to voice any objections
> :-).
>
> The code currently resides in playground/libs/ui/kcolor.
>
> Stuff to do still:
> - BlendAdd, BlendMultiply - I'm in no hurry because adding these is BC
> and of course there are no users yet :-).
> - HSY conversion - will be done before Monday, hopefully.
> - HSV conversion - should be ready some time before 4.0*
> - Setters - probably not for Monday, but soon; also BC.
>
> ...and there are other things, but nothing I expect to have done Monday
> (and they are "nice" things, not required things, so they may wait until
> need arises).
>
> (* HSV is a cakewalk compared to HSY... there isn't linear algebra
> involved :-).)

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

Now don't get me wrong, I won't be advocating not putting this in kdelibs at 
all, what i will do is explain a little how graphics works and why this is 
going to be confusing and unfortunate situation for KDE 
developers/applications so that people actually understand what is going on 
here.

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. The differences between HSL and HSV colorspaces are 
pretty minuscule from the point of view of applications, almost non-existant. 
Where it does matter is color-selectors, which are hard to do in an intuitive 
fashion over *RGB colorspace. The main reason for HSL is that it can neatly 
be represented in a spectrum over a wheel. That representation doesn't by any 
means require a need of a specific color class - it's just a color picker, 
internally using something that will be anyway converted to *RGB. 

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. 
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

The spectrum produced by QColor is on the left, the spectrum produced by 
KColor is on the right. For 99% of applications the non-linearity of HSV in 
QColor is /exactly/ what applications want. And of course as much as 
parameters to either version of QColor/KColor method can be adjusted to 
produce similar results, the intrinsic properties of those colorspaces will 
remain the same.

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". The example showing the 
spectrum's is at http://ktown.kde.org/~zrusin/test2.tar.bz2 .

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

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.
What I think would be a lot better idea is introducing a class/namespace 
called KGraphicsUtils or similar and adding blendColor method to it. 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. 
Now since I know there would be at least one person who would complain about 
the speed of such an implementation, to the color example that I mentioned 
above I added a benchmark for it. If you define TEST_BLEND on top of the file 
it will show you the time required to perform 10000 blendColor calls 
implemented as above and tell you how much time it takes to issue 10000 
fprintf calls to give you some kind of frame of reference.
On my laptop 10000 blendColor calls takes ~280ms (yes, that miliseconds, 10000 
calls takes ~280 miliseconds). While 10000 fprintf calls takes ~340ms. So 
yeah, and lets be serious if an application needs to blend 10 colors that's 
already a lot. I can't imagine applications getting to 100 and even if they 
would they still wouldn't even notice this implementation on the benchmarking 
profile.

Furthermore I think introducing a class with such prominent name as KColor is 
a really bad idea. 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)
2) Do an operation on KColor (most likely lighten/darken which mean lossy H* 
colorspace and back conversion #2)
3) convert them back to QColor (lossy conversion #3). 
So this is by far not ideal. 

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.

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.

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)

But again, I'm not arguing for anything I just wanted to explain how those 
things work and point out what I'd do. I also apologize up front but I don't 
think I'll have more time to contribute anything else to this thread. I do 
hope though that this explanation makes things a little clearer for people 
who are not that familiar with graphics.

Zack




More information about the kde-core-devel mailing list