source: tags/ms_r16q2/WINDOW/AW_rgb.cxx

Last change on this file was 14983, checked in by westram, 8 years ago
  • add some functions for color-calculation with AW_rgb_diff: abs, op+, max
File size: 8.7 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#ifdef ARB_GTK
140    // x-colors (doesn't work w/o GUI in motif version)
141    TEST_EXPECT_EQUAL(AW_rgb16("white").ascii(), "#ffffffffffff");
142    TEST_EXPECT_EQUAL(AW_rgb16("black").ascii(), "#000000000000");
143#endif
144}
145
146void TEST_rgb_diff() {
147    AW_rgb16 orange("#f80");
148    AW_rgb16 blue("#004");
149
150    AW_rgb_diff blue2orange = orange - blue;
151    AW_rgb16    calc_orange = blue + blue2orange;
152    TEST_EXPECT(calc_orange == orange);
153
154    AW_rgb_diff orange2blue = -blue2orange;
155    AW_rgb16    calc_blue   = orange + orange2blue;
156    TEST_EXPECT(calc_blue   == blue);
157
158    for (float part = 0.1; part<1.0; part += 0.1) {
159        AW_rgb16 mix1 = orange +     part * orange2blue;
160        AW_rgb16 mix2 = blue   + (1-part) * blue2orange;
161        TEST_EXPECT(mix1 == mix2);
162    }
163
164    // check that color calculation does not overflow:
165    AW_rgb16    black("#000");
166    AW_rgb16    white("#fff");
167    AW_rgb_diff black2white = white-black;
168
169    AW_rgb16 whiter = white + black2white;
170    TEST_EXPECT_EQUAL(whiter.ascii(), "#ffffffffffff");
171    TEST_EXPECT(whiter == white);
172
173    AW_rgb16 blacker = black - black2white;
174    TEST_EXPECT_EQUAL(blacker.ascii(), "#000000000000");
175    TEST_EXPECT(blacker == black);
176
177    // summarized diff:
178    AW_rgb16 red("#f00");
179    AW_rgb16 green("#0f0");
180
181    AW_rgb_diff blue2red   = red-blue;
182    AW_rgb_diff blue2green = green-blue;
183
184    TEST_DIFF_CONTAINS(blue2red,   "(1.00,0.00,-0.27)");
185    TEST_DIFF_CONTAINS(blue2green, "(0.00,1.00,-0.27)");
186    TEST_DIFF_CONTAINS(blue2green.abs(), "(0.00,1.00,0.27)");
187
188    {
189        AW_rgb_diff sum        = blue2red       + blue2green;
190        AW_rgb_diff abssum     = blue2red.abs() + blue2green.abs();
191        AW_rgb_diff maxabsdiff = max(blue2red.abs(), blue2green.abs());
192
193        TEST_DIFF_CONTAINS(sum,        "(1.00,1.00,-0.53)");
194        TEST_DIFF_CONTAINS(abssum,     "(1.00,1.00,0.53)");
195        TEST_DIFF_CONTAINS(maxabsdiff, "(1.00,1.00,0.27)");
196    }
197
198    AW_rgb16 magenta("#f0f");
199    AW_rgb16 cyan("#0ff");
200
201    AW_rgb_diff orange2magenta = magenta-orange;
202    AW_rgb_diff orange2cyan    = cyan-orange;
203
204    TEST_DIFF_CONTAINS(orange2magenta, "(0.00,-0.53,1.00)");
205    TEST_DIFF_CONTAINS(orange2cyan,    "(-1.00,0.47,1.00)");
206
207    {
208        AW_rgb_diff sum        = orange2magenta       + orange2cyan;
209        AW_rgb_diff abssum     = orange2magenta.abs() + orange2cyan.abs();
210        AW_rgb_diff maxabsdiff = max(orange2magenta.abs(), orange2cyan.abs());
211
212        TEST_DIFF_CONTAINS(sum,        "(-1.00,-0.07,2.00)");
213        TEST_DIFF_CONTAINS(abssum,     "(1.00,1.00,2.00)");
214        TEST_DIFF_CONTAINS(maxabsdiff, "(1.00,0.53,1.00)");
215    }
216
217}
218
219#endif // UNIT_TESTS
220
221// --------------------------------------------------------------------------------
222//      WM-dependent code
223
224#if defined(ARB_GTK)
225
226# include <gdk/gdk.h>
227
228#else // ARB_MOTIF
229
230#include "aw_root.hxx"
231#include "aw_window.hxx"
232#include "aw_window_Xm.hxx"
233
234#endif
235
236static AW_rgb16 convert_xcolorname(const char *xcolorname, bool& failed) {
237#if defined(ARB_GTK)
238    GdkColor col;
239    failed = !gdk_color_parse(xcolorname, &col);
240#else // ARB_MOTIF
241    XColor   col, unused;
242    if (AW_root::SINGLETON) { // only works if window was set (by caller)
243        AW_root_Motif *mroot = AW_root::SINGLETON->prvt;
244        failed = !XLookupColor(mroot->display,
245                               mroot->colormap,
246                               xcolorname,
247                               &col,     // exact color
248                               &unused); // screen-color
249    }
250    else {
251        failed = true;
252    }
253#endif
254
255    return
256        failed
257        ? AW_rgb16()
258        : AW_rgb16(col.red, col.green, col.blue);
259}
260
Note: See TracBrowser for help on using the repository browser.