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 | |
---|
13 | static AW_rgb16 CRAZY_PINK("#C0C"); |
---|
14 | static AW_rgb16 convert_xcolorname(const char *xcolorname, bool& failed); |
---|
15 | |
---|
16 | void 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 | |
---|
24 | AW_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 | |
---|
56 | const 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 | |
---|
90 | void 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 | |
---|
140 | void 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 | |
---|
221 | static 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 | |
---|