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