| 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 | |
|---|