From kde-core-devel Fri May 25 15:49:41 2007 From: Zack Rusin Date: Fri, 25 May 2007 15:49:41 +0000 To: kde-core-devel Subject: Re: KColor is coming this Monday... Message-Id: <200705251149.42002.zack () kde ! org> X-MARC-Message: https://marc.info/?l=kde-core-devel&m=118010836706918 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