Menu

#2948 Inconsistent color scaling

obsolete: 8.6b2
closed-fixed
5
2012-02-15
2012-02-10
Andy Goth
No

The Tk_GetColor() man page states: "When fewer than 16 bits are provided for each color, they represent the most significant bits of the color. For example, #3a7 is the same as #3000a0007000." The [photo] man page references the Tk_GetColor() man page regarding how color names are interpreted.

I just tested this in MS-Windows XP by putting the color #3a7 into a photo image and into a widget background. I then took a screenshot using The GIMP and read back the color values. The photo contained #33aa77, but the widget background contained #30a070. The colors are visibly different when compared side-by-side. I'm not sure how this inconsistency is possible, unless not everything is calling Tk_GetColor().

I prefer the bit replication technique used by photo, even though it's contrary to the man page. It makes #fff map to white, not light gray.

Discussion

  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    This looks like a simplification in the
    function XParseColor (xlib/xcolors.c),
    which assumes that the color string is
    either 7 or 13 characters (i=2 or i=4),
    because for other values the used
    formulas don't do as expected:

    colorPtr->red = (((unsigned short) red) << (4 * (4 - i)))
    | ((unsigned short) red);
    colorPtr->green = (((unsigned short) green) << (4 * (4 - i)))
    | ((unsigned short) green);
    colorPtr->blue = (((unsigned short) blue) << (4 * (4 - i)))
    | ((unsigned short) blue);

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10
    • assigned_to: hobbs --> nijtmans
     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    Suggested fix attached

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    In case you get some strange javascript when trying to get to the
    attachment (this is a sourceforge bug!!!). Here is the patch inline

    *** xlib/xcolors.c.orig Fri Feb 10 10:21:51 2012
    --- xlib/xcolors.c Fri Feb 10 10:59:10 2012
    ***************
    *** 891,902 ****
    if (sscanf(spec+1, fmt, &red, &green, &blue) != 3) {
    return 0;
    }
    ! colorPtr->red = (((unsigned short) red) << (4 * (4 - i)))
    ! | ((unsigned short) red);
    ! colorPtr->green = (((unsigned short) green) << (4 * (4 - i)))
    ! | ((unsigned short) green);
    ! colorPtr->blue = (((unsigned short) blue) << (4 * (4 - i)))
    ! | ((unsigned short) blue);
    } else {
    if (!FindColor(spec, colorPtr)) {
    return 0;
    --- 891,914 ----
    if (sscanf(spec+1, fmt, &red, &green, &blue) != 3) {
    return 0;
    }
    ! switch (i) {
    ! case 1:
    ! colorPtr->red = 0x1111 * (unsigned short) red;
    ! colorPtr->green = 0x1111 * (unsigned short) green;
    ! colorPtr->blue = 0x1111 * (unsigned short) blue;
    ! case 2:
    ! colorPtr->red = (((unsigned short) red) << 8) | (unsigned short) red;
    ! colorPtr->green = (((unsigned short) green) << 8) | (unsigned short) green;
    ! colorPtr->blue = (((unsigned short) blue) << 8) | (unsigned short) blue;
    ! case 3:
    ! colorPtr->red = (((unsigned short) red) << 4) | (((unsigned short) red) >> 8);
    ! colorPtr->green = (((unsigned short) green) << 4) | (((unsigned short) green) >> 8);
    ! colorPtr->blue = (((unsigned short) blue) << 4) | (((unsigned short) blue) >> 8);
    ! case 4:
    ! colorPtr->red = (unsigned short) red;
    ! colorPtr->green = (unsigned short) green;
    ! colorPtr->blue = (unsigned short) blue;
    ! }
    } else {
    if (!FindColor(spec, colorPtr)) {
    return 0;

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    Oops, forgot the breaks in the switch statement, but you get the idea ;-(

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    Guess what. Tried the same on UNIX and the same problem there. Looking
    in the XParseColor manual:
    The R, G, and B represent single hexadecimal digits. When
    fewer than 16 bits each are specified, they represent the
    most-significant bits of the value (unlike the ``rgb:'' syn-
    tax, in which values are scaled). For example, the string
    ``#3a7'' is the same as ``#3000a0007000''.

    This means that Tk_GetColor() behaves as documented,
    the photo handler doesn't use Tk_GetColor for the
    color parsing. However, as the whole world is using
    the #hhh syntax as suggested here, I still recommend
    to fix this. As a proof, here the relevant part of css-3:

    The format of an RGB value in hexadecimal notation is a
    ‘#’ immediately followed by either three or six hexadecimal
    characters. The three-digit RGB notation (#rgb) is converted
    into six-digit form (#rrggbb) by replicating digits, not by adding
    zeros. For example, #fb0 expands to #ffbb00. This ensures that
    white (#ffffff) can be specified with the short notation (#fff) and
    removes any dependencies on the color depth of the display.

    The best Tk can do is implement this replication before
    transferring the string to XParseColor.

     
  • Donal K. Fellows

    The photo code used to use Tk_GetColor exclusively (or was it the objectified version?) but I changed it because that's still a brutally slow piece of code when you're dealing with a very large number of colors to parse (i.e., in an image!) When doing that, I maximized the color spread in the local color parser as I thought that most likely to give a pleasing effect in practice.

    I'm still not keen on using Tk_GetColor in the photo code as I'd like to avoid the memory management overhead where possible (and the color _won't_ be kept around for long).

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    >When doing that, I maximized the color spread in
    >the local color parser as I thought that most likely to give a pleasing
    >effect in practice.
    I agree with that change! But I think that color handling in
    other places should do the same, and the change should
    be documented.

    Anyway, I will come up with a proposal for you to
    evaluate. (please forget the patch I gave earlier!)

     
  • Andy Goth

    Andy Goth - 2012-02-10

    Thank you very much for the quick response! I've got a few bug reports, some with patches attached, that have been languishing in this tracker for years with no attention, so I was afraid to submit a report here. Instead I composed an email to tcl-core, then at the last second decided to dump it here anyway. Apparently I did the right thing!

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-10

    proposed fix committed in branch bug-3486474

    Solution:
    - For Windows, rewrote XParseColor to
    do the right thing in the fastest way: just
    do the byte replication in a small buffer, and
    use strtol in stead of sscanf.
    - For other platforms, put a little wrapper
    TkParseColor around XParseColor. This
    function does the byte replication if
    necessary and the calls the original
    XParseColor.

    Please, evaluate!

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-11

    Little addendum committed, in fact only meant for Tk 8.6:

    The function Tk_NameOfColor always outsput a 13-byte
    color name, but if the color is exactly equivalent to a
    converted 7-byte color-name then the shorter name
    should be output. Not all external implementations
    of colors understand the 13-byte variant, so this
    is generally safer.

    With that, we have a kind of 'canonical' color name,
    in which the 7-byte variant is prefered, and the
    13-byte variant is used otherwise.

    Please evaluate!. Do others find that a good idea as well?

    Regards,
    Jan Nijtmans

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-12

    Committed improved (speeded up) implementations of
    XParseColor and TkParseColor:
    - XParseColor now uses _strtoi64() function to parse
    the hex-string in a single step, so the only thing left
    is a little bit-magic getting the colors right. Should be
    much faster than the original implementation.
    - TkParseColor now loops through the characters
    forwards first, then backwards, which can be
    optimized much better by the C-compiler

    I consider this ready to be committed, but
    evaluation by others is still highly appreciated.

     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-15
    • status: open --> closed-fixed
     
  • Jan Nijtmans

    Jan Nijtmans - 2012-02-15

    Fix committed to core-8-4-branch, core-8-5-branch and trunk.