Color manipulation functions in kdelibs?

Matthew Woehlke mw_triad at users.sourceforge.net
Mon Dec 11 22:52:03 GMT 2006


Clarence Dang wrote:
> [adding Krita people to CC as I'm sure they have more to say]

Good idea, although it occurs to me that Krita will probably want to 
have its own set of functions, specifically so they can handle things 
like 16-bit color (and - can I hope? - 32-bit FP color a.k.a. HDR 
color). But you are right that they might have suggestions.

Although, now that you make me think about it, maybe we *should* go 
all-out and have a KColor class that stores info in HDR (doubles) and 
has automatic conversion on demand? You could make it so that only one 
of HSV, HLS, RGB has to be valid at a time and recalculate the others as 
needed; this would allow near-lossless stacking of operations, and give 
a performance benefit when successive operations use the same color 
space (since you wouldn't need to convert until the end).

It also occurs to me that we should probably expose things like 
'desaturate', 'colorize', etc; i.e. all the things the icon effects do. 
Although I expect these would mostly be convenience wrappers for 
particular permutations of blend*/lighten/darken.

To really take things to the extreme, should we implement all of the 
blending modes that high-end paint apps support (i.e. 'dodge', 'burn', 
'overlay', 'screen', etc)?

> I think blending/darken/lighten functions are a good idea.
> For RGB functions, start reading
? /trunk/KDE/kdegraphics/kolourpaint/widgets/toolbars/kpcolortoolbar.cpp
> from "roundUp2".  Not particularly stunning implementations but it's 
> BSD-licensed.

Actually, for everything but HLS conversion, I have implementations for 
most things that are (c) me (and can easily write the rest), so I can 
license them however I need to. Since the response seems positive, can 
someone please point me in the direction of what I need to do to get 
them into kdelibs? Does this sound like a candidate for a new, static 
class (e.g. Clarence's suggestion of "KColorBlend"), in which case I 
guess I would just offer the relevant .cpp/.h?

However (so far I looked at kde-3.5.5, not trunk) this does bring up an 
interesting notion. What do you think of this revised prototype?

QColor blend???(const QColor& color1, const QColor& color2,
                 double k = 0.5, double r = 0.5,
                 int flags = BLEND_NORMAL | BLEND_CAP | <see below>)

...where the meaning of 'k' and 'r' depends on 'flags' (which is why I 
am not choosing to follow Brad's otherwise excellent advice of renaming 
it to 'blendFactor'). Some initial thoughts on the value of 'flags' 
(omitting 'BLEND_' for brevity):

NORMAL - ( ( ( color1 * ki ) + ( color2 * k ) ) * ( 2.0 * r ) )
Use 2.0 * r so that the default CAP behavior still allows lightening the 
result. Note that k=0.5, r=1.0 gives the same result as k=r=1.0 in 
MULTIPLY mode.

ADD - result is ( ( color1 * k ) + ( color2 * r ) )
With k = r = 1.0, this would be a straight 'additive blend', but of 
course allows just about anything.

MULTIPLY - ( ( color1 * r ) * blend???( color2, white, k ) )
Not the cleanest formula; basically a traditional multiply blend with 
'k' controlling the "opacity", but take the original color times 'r' 
before doing a traditional multiply blend.

CAP - as a flag applied to any of the above, treat 'k' and 'r' as 
between 0.0 and 1.0.

AFFECT_VAL - only meaningful in NORMAL and HLS/HSV color spaces; the 
final multiply by 'r' will affect the 'L'/'B' component (with no 
AFFECT_* flags, the multiply by 'r' does nothing).

AFFECT_HUE - like AFFECT_VAL, but for the 'H' (hue) component.

AFFECT_SAT - like AFFECT_VAL, but for the 'S' (saturation) component.

AFFECT_{R|G|B|A} - these should be obvious. :-) Only meaningful in 
NORMAL mode and (except AFFECT_A) RGB color space.

AFFECT_ALL - convenience constant for _R|_G|_B|_A (which is equivalent 
to _HUE|_SAT|_VAL|_A; see below)

PRESERVE_{R|G|B|HUE|SAT|VAL|A} - Don't blend <attribute> (for NORMAL, 
does not exclude doing the 'r' blend, although the AFFECT_<attrib> would 
still need to be set); i.e. use <attribute> from color1.

(...where 'ki', 'ri' are ( 1.0 - k ), ( 1.0 - r) respectively)

The default flags would be:
blendRGB - NORMAL | CAP | AFFECT_ALL
blendHLS/blendHSV - NORMAL | CAP | AFFECT_VAL | AFFECT_A
These seem to be sensible for most uses. Also the BLEND_AFFECT_* values 
would overlap, e.g. BLEND_AFFECT_R == BLEND_AFFECT_VAL, etc, since it 
makes no sense to use them in opposing color spaces.

There are probably other modes that could be added as well. Such an 
implementation would be good because it would allow new modes to be 
added without introducing new API's or breaking existing code.

If I am guessing correctly what Clarence meant by 
'accessibility{Low|High}Contrast', I think these would be better suited 
to a different function, e.g. 'generateColor(const QColor& contrast, 
const QColor& guess, int flags)'. Since the arguments are similar, I 
suppose you could lump this into the blend* family, but it doesn't seem 
like a good idea.

> On Saturday 09 December 2006 08:27, Matthew Woehlke wrote:
>> I originally posted this to kde-devel where it apparently went over like
>> a lead balloon (judging from how it has been completely ignored). Since
>> this would affect kdelibs (I assume), I am re-posting to kde-core-devel
>> (if it's off topic, I apologize, and someone please let me know). I'm
>> also cross-posting to kde-usability because the usability folks were
>> asking for almost exactly this sort of thing, so I'm hoping to get some
>> support. :-)
>>
>> Note: this would be for KDE4 (I wasn't entirely clear on that originally).
>>
>> Matthew Woehlke wrote:
>>> I'm wondering if the following functions or equivalents exist somewhere
>>> in either kdebase or kdelibs (my guess would be kdefx). If not, can they
>>> be added? know some of these already exist in at least private
>>> implementations...
>>>
>>> ====
>>> QColor blendRGB(const QColor& c1, const QColor& c2, double k,
>>>                  bool trunc=true)
>>> QColor blendHSV(const QColor& c1, const QColor& c2, double k,
>>>                  bool trunc=true)
>>> QColor blendHLS(const QColor& c1, const QColor& c2, double k,
>>>                  bool trunc=true)
>>> -
>>> Blends between two colors in the specified color space. 'k' is the blend
>>> factor, e.g. k=0 gives c1, k=1 gives c2. If 'trunc' is false, allow
>>> values < 0 or > 1 (otherwise clamp to [0, 1]), e.g. blendRGB(Qt::gray,
>>> Qt::white, -1, false) gives Qt::black, blendHSV(Qt::red, Qt::green, 2.0,
>>> false) gives Qt::blue, etc.). If either color is Qt::white or Qt::black,
>>> then HLS/HSV treat it as having H and S equal to the other color.
>>>
>>> ====
>>> double blend(double a, double b, double k)
>>> -
>>> Blends between two values, i.e. 'return a*(1.0-k) + b*k;'. Used
>>> internally by the above, or useful to
>>>
>>> ====
>>> void toHLS(const QColor& c, int& h, int&l, int&s, bool true_luma=false)
>>> QColor fromHLS(int h, int l, int s, bool true_luma=false)
>>> -
>>> Convert to/from HLS space (different from HSV space; L=1 means white).
>>> If 'true_luma' is true, use 'real' values for L to give pure colors,
>>> instead of 0.5 so that the 'true gray value' of any given HLS is equal to
>>> L.
>>>
>>> ====
>>> QColor darken(const QColor& c, double k)
>>> QColor lighten(const QColor& c, double k)
>>> -
>>> Similar to blendHLS(c, base, k, false) where base is Qt::black (darken)
>>> or Qt::white (lighten). So k=0 gives c, k=1 gives base, k=0.5 gives a
>>> color halfway between c and base in HLS space (H,S unaltered), k=-1
>>> gives a color twice as far from base as c in HLS space, etc.
>>>
>>>
>>> ...and the above should probably all have Qrgba versions as well.
>>>
>>> I've used these in a style I wrote, have seen similar implementations
>>> elsewhere (in fact, I adapted my toHLS/fromHLS from qt-style-bluecurve),
>>> and just copied them into part of KDevelop. For KDE4 it seems like it
>>> would be nice for these to be available in a central spot.
>> Except for toHLS I can provide most of these with little effort. The
>> toHLS implementation I currently have is GPL (c) RedHat; if that's a
>> problem I have a version I wrote a long time ago that would need some
>> work before it could go into KDE.
>>
>> ...and, as mentioned, the usability folks were specifically asking for
>> functions to mix colors, i.e. the blend* family above. Actually, this
>> list is incomplete from their perspective because we also need a
>> function (or class(*) of functions) to determine the contrast between
>> two colors. They are also asking for a function to generate 'possible
>> colors' from another color; that is going to be a bit more complex but
>> is something that should be looked into.
>>
>> (* Why a class? Because for accessibility reasons, there may need to be
>> as many as three versions; one for 'normal' vision, one for people that
>> can't see green, and one for people that can't see color.)

-- 
Matthew
"Lost a planet, Obi Wan has? How embarrassing..."
  -- Yoda (Star Wars II: Attack of the Clones)



More information about the kde-core-devel mailing list