source: branches/stable/WINDOW/AW_rgb.cxx

Last change on this file was 16936, checked in by westram, 7 years ago
File size: 8.3 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : AW_rgb.cxx                                     //
4//   Purpose   :                                                //
5//                                                              //
6//   Coded by Ralf Westram (coder@reallysoft.de) in June 2016   //
7//   http://www.arb-home.de/                                    //
8//                                                              //
9// ============================================================ //
10
11#include "aw_rgb.hxx"
12
13static AW_rgb16 CRAZY_PINK("#C0C");
14static AW_rgb16 convert_xcolorname(const char *xcolorname, bool& failed);
15
16void AW_rgb16::rescale(uint16_t my_max) {
17    float scale = MAX/my_max;
18
19    R = R*scale + 0.5;
20    G = G*scale + 0.5;
21    B = B*scale + 0.5;
22}
23
24AW_rgb16::AW_rgb16(const char *colorname) {
25    /*! converts colorname
26     * @param colorname  either color hexstring (allowed for orange: '#F80', '#ff8800', '#fFF888000' or '#ffff88880000') or X color name (e.g. "green")
27     */
28
29    bool failed = (colorname[0] != '#');
30    if (!failed) {
31        const char    *end;
32        unsigned long  num    = strtoul(colorname+1, const_cast<char**>(&end), 16);
33        int            length = end-colorname-1;
34
35        switch (length) {
36            case  3: R = num >>  8; G = (num >>  4) & 0xf;    B = num & 0xf;    rescale(0xf);   break;
37            case  6: R = num >> 16; G = (num >>  8) & 0xff;   B = num & 0xff;   rescale(0xff);  break;
38            case  9: R = num >> 24; G = (num >> 12) & 0xfff;  B = num & 0xfff;  rescale(0xfff); break;
39            case 12: R = num >> 32; G = (num >> 16) & 0xffff; B = num & 0xffff; break;
40
41            default:
42                aw_assert(0); // hex color spec has invalid length
43                failed = true;
44                break;
45        }
46    }
47
48    if (failed) *this = convert_xcolorname(colorname, failed);
49
50    if (failed) {
51        fprintf(stderr, "Warning: Failed to interpret color '%s' - fallback to crazy pink\n", colorname);
52        *this = CRAZY_PINK;
53    }
54}
55
56const char *AW_rgb16::ascii() const {
57    const int   FIXEDLEN = 1+3*4;
58    static char buffer[FIXEDLEN+1];
59
60#if defined(ASSERTION_USED)
61    int printed =
62#endif
63        sprintf(buffer, "#%04x%04x%04x", R, G, B);
64
65    aw_assert(printed == FIXEDLEN);
66
67    return buffer;
68}
69
70// --------------------------------------------------------------------------------
71
72#ifdef UNIT_TESTS
73#ifndef TEST_UNIT_H
74#include <test_unit.h>
75#endif
76
77#include <arb_msg.h>
78
79#define TEST_ASCII_COLOR_CONVERT(i,o) TEST_EXPECT_EQUAL(AW_rgb16(i).ascii(),o)
80#define TEST_ASCII_COLOR_IDENT(c)     TEST_ASCII_COLOR_CONVERT(c,c);
81
82#define TEST_NORMALIZED_CONVERSION(c) TEST_EXPECT_EQUAL(AW_rgb16(AW_rgb_normalized(AW_rgb16(c))).ascii(), c);
83
84#define TEST_NORMALIZED_CONTAINS(col,expected) TEST_EXPECT_EQUAL(GBS_global_string("(%.2f,%.2f,%.2f)",                  \
85                                                                                   (col).r(), (col).g(), (col).b()),    \
86                                                                 expected)
87
88#define TEST_DIFF_CONTAINS(diff,expected) TEST_NORMALIZED_CONTAINS(diff,expected)
89
90void TEST_rgb() {
91    // Note: more related tests in AW_preset.cxx@RGB_TESTS
92
93    // check construction from diff.-length color specifications
94    AW_rgb16 orange4("#f80");
95    AW_rgb16 orange8("#ff8800");
96    AW_rgb16 orange12("#fff888000");
97    AW_rgb16 orange16("#ffff88880000");
98
99    TEST_EXPECT(orange16 == AW_rgb16(65535, 34952, 0));
100    TEST_EXPECT(orange12 == orange16);
101    TEST_EXPECT(orange8  == orange12);
102    TEST_EXPECT(orange4  == orange8);
103
104    AW_rgb16 lblue4("#004");
105    AW_rgb16 lblue8("#000044");
106    AW_rgb16 lblue12("#000000444");
107    AW_rgb16 lblue16("#000000004444");
108
109    TEST_EXPECT(lblue16 == AW_rgb16(0, 0, 17476));
110    TEST_EXPECT(lblue12 == lblue16);
111    TEST_EXPECT(lblue8  == lblue12);
112    TEST_EXPECT(lblue4  == lblue8);
113
114    TEST_EXPECT_EQUAL(lblue8.ascii(), "#000000004444");
115
116    TEST_ASCII_COLOR_CONVERT("#1"   "2"   "3",   "#1111" "2222" "3333");
117    TEST_ASCII_COLOR_CONVERT("#45"  "67"  "89",  "#4545" "6767" "8989");
118    TEST_ASCII_COLOR_CONVERT("#678" "9ab" "cde", "#6786" "9ab9" "cdec");
119    TEST_ASCII_COLOR_CONVERT("#001" "010" "100", "#0010" "0100" "1001");
120
121    TEST_ASCII_COLOR_IDENT("#0123456789ab");
122    TEST_ASCII_COLOR_IDENT("#fedcba987654");
123
124    // conversion normalized -> 16bit for special values
125    TEST_EXPECT_EQUAL(AW_rgb16(AW_rgb_normalized(0.25,  0.5,    0.75  )).ascii(), "#3fff" "7fff" "bfff");
126    TEST_EXPECT_EQUAL(AW_rgb16(AW_rgb_normalized(1./3,  1./5,   1./7  )).ascii(), "#5555" "3333" "2492");
127    TEST_EXPECT_EQUAL(AW_rgb16(AW_rgb_normalized(.1,    .01,    .001  )).ascii(), "#1999" "028f" "0041");
128    TEST_EXPECT_EQUAL(AW_rgb16(AW_rgb_normalized(.0001, .00005, .00002)).ascii(), "#0006" "0003" "0001");
129
130    // test conversion 16bit -> normalized -> 16bit is stable
131    TEST_NORMALIZED_CONVERSION("#000000000000");
132    TEST_NORMALIZED_CONVERSION("#0123456789ab");
133    TEST_NORMALIZED_CONVERSION("#ffffffffffff");
134
135    // invalid colors
136    TEST_EXPECT(AW_rgb16("kashdkjasdh") == CRAZY_PINK);
137    TEST_EXPECT(AW_rgb16("")            == CRAZY_PINK);
138}
139
140void TEST_rgb_diff() {
141    AW_rgb16 orange("#f80");
142    AW_rgb16 blue("#004");
143
144    AW_rgb_diff blue2orange = orange - blue;
145    AW_rgb16    calc_orange = blue + blue2orange;
146    TEST_EXPECT(calc_orange == orange);
147
148    AW_rgb_diff orange2blue = -blue2orange;
149    AW_rgb16    calc_blue   = orange + orange2blue;
150    TEST_EXPECT(calc_blue   == blue);
151
152    for (float part = 0.1; part<1.0; part += 0.1) {
153        AW_rgb16 mix1 = orange +     part * orange2blue;
154        AW_rgb16 mix2 = blue   + (1-part) * blue2orange;
155        TEST_EXPECT(mix1 == mix2);
156    }
157
158    // check that color calculation does not overflow:
159    AW_rgb16    black("#000");
160    AW_rgb16    white("#fff");
161    AW_rgb_diff black2white = white-black;
162
163    AW_rgb16 whiter = white + black2white;
164    TEST_EXPECT_EQUAL(whiter.ascii(), "#ffffffffffff");
165    TEST_EXPECT(whiter == white);
166
167    AW_rgb16 blacker = black - black2white;
168    TEST_EXPECT_EQUAL(blacker.ascii(), "#000000000000");
169    TEST_EXPECT(blacker == black);
170
171    // summarized diff:
172    AW_rgb16 red("#f00");
173    AW_rgb16 green("#0f0");
174
175    AW_rgb_diff blue2red   = red-blue;
176    AW_rgb_diff blue2green = green-blue;
177
178    TEST_DIFF_CONTAINS(blue2red,   "(1.00,0.00,-0.27)");
179    TEST_DIFF_CONTAINS(blue2green, "(0.00,1.00,-0.27)");
180    TEST_DIFF_CONTAINS(blue2green.abs(), "(0.00,1.00,0.27)");
181
182    {
183        AW_rgb_diff sum        = blue2red       + blue2green;
184        AW_rgb_diff abssum     = blue2red.abs() + blue2green.abs();
185        AW_rgb_diff maxabsdiff = max(blue2red.abs(), blue2green.abs());
186
187        TEST_DIFF_CONTAINS(sum,        "(1.00,1.00,-0.53)");
188        TEST_DIFF_CONTAINS(abssum,     "(1.00,1.00,0.53)");
189        TEST_DIFF_CONTAINS(maxabsdiff, "(1.00,1.00,0.27)");
190    }
191
192    AW_rgb16 magenta("#f0f");
193    AW_rgb16 cyan("#0ff");
194
195    AW_rgb_diff orange2magenta = magenta-orange;
196    AW_rgb_diff orange2cyan    = cyan-orange;
197
198    TEST_DIFF_CONTAINS(orange2magenta, "(0.00,-0.53,1.00)");
199    TEST_DIFF_CONTAINS(orange2cyan,    "(-1.00,0.47,1.00)");
200
201    {
202        AW_rgb_diff sum        = orange2magenta       + orange2cyan;
203        AW_rgb_diff abssum     = orange2magenta.abs() + orange2cyan.abs();
204        AW_rgb_diff maxabsdiff = max(orange2magenta.abs(), orange2cyan.abs());
205
206        TEST_DIFF_CONTAINS(sum,        "(-1.00,-0.07,2.00)");
207        TEST_DIFF_CONTAINS(abssum,     "(1.00,1.00,2.00)");
208        TEST_DIFF_CONTAINS(maxabsdiff, "(1.00,0.53,1.00)");
209    }
210
211}
212
213#endif // UNIT_TESTS
214
215// --------------------------------------------------------------------------------
216
217#include "aw_root.hxx"
218#include "aw_window.hxx"
219#include "aw_window_Xm.hxx"
220
221static AW_rgb16 convert_xcolorname(const char *xcolorname, bool& failed) {
222    XColor col, unused;
223    if (AW_root::SINGLETON) { // only works if window was set (by caller)
224        AW_root_Motif *mroot = AW_root::SINGLETON->prvt;
225        failed = !XLookupColor(mroot->display,
226                               mroot->colormap,
227                               xcolorname,
228                               &col,     // exact color
229                               &unused); // screen-color
230    }
231    else {
232        failed = true;
233    }
234
235    return
236        failed
237        ? AW_rgb16()
238        : AW_rgb16(col.red, col.green, col.blue);
239}
240
Note: See TracBrowser for help on using the repository browser.