1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2002 Marko Macek
5  */
6 #include "config.h"
7 #include "ycolor.h"
8 #include "yxapp.h"
9 #include "ascii.h"
10 
11 #ifdef CONFIG_XFREETYPE
12 #include <ft2build.h>
13 #include <X11/Xft/Xft.h>
14 #define INIT_XFREETYPE(Member, Value) , Member(Value)
15 #else
16 #define INIT_XFREETYPE(Member, Value)
17 #endif
18 
19 #include "intl.h"
20 #include <stdlib.h> // for strtol
21 
22 #include <cstdint>
23 
24 YColorName YColor::black("rgb:00/00/00");
25 YColorName YColor::white("rgb:FF/FF/FF");
26 
display()27 static inline Display* display()  { return xapp->display(); }
colormap()28 static inline Colormap colormap() { return xapp->colormap(); }
visual()29 static inline Visual*  visual()   { return xapp->visual(); }
vdepth()30 static inline unsigned vdepth()   { return xapp->depth(); }
31 
32 typedef unsigned short Word;
33 
34 class YPixel {
35 public:
YPixel(unsigned long pix,unsigned long col)36     YPixel(unsigned long pix, unsigned long col) :
37         fPixel(pix), fColor(col), fBright(), fDarken()
38         INIT_XFREETYPE(fXftColor, nullptr) { }
39 
40     ~YPixel();
41 
pixel() const42     unsigned long pixel() const { return fPixel; }
color() const43     unsigned long color() const { return fColor; }
44     YColor brighter();
45     YColor darker();
46 
47     static unsigned long
color(Word r,Word g,Word b,Word a)48     color(Word r, Word g, Word b, Word a)
49     {
50         if (sizeof(unsigned long) < 8) {
51             return (r & 0xFF00) << 16 | (g & 0xFF00) << 8 | ((b >> 8) & 0xFF)
52                  | ((a & 0xFF) << 24);
53         }
54         else {
55             return (uint64_t) r << 32 | (uint64_t) g << 16 | b
56                  | (uint64_t) (a & 0xFF) << 48;
57         }
58     }
red() const59     unsigned short red() const {
60         if (sizeof(unsigned long) < 8)
61             return (fColor >> 16 & 0xFF00);
62         else
63             return (uint64_t(fColor) >> 32 & 0xFFFF);
64     }
green() const65     unsigned short green() const {
66         if (sizeof(unsigned long) < 8)
67             return (fColor >>  8 & 0xFF00);
68         else
69             return (uint64_t(fColor) >> 16 & 0xFFFF);
70     }
blue() const71     unsigned short blue() const {
72         if (sizeof(unsigned long) < 8)
73             return (fColor <<  8 & 0xFF00);
74         else
75             return (uint64_t(fColor) & 0xFFFF);
76     }
alpha() const77     unsigned short alpha() const {
78         if (sizeof(unsigned long) < 8)
79             return (fColor >> 24 & 0xFF);
80         else
81             return (uint64_t(fColor) >> 48 & 0xFF);
82     }
83 
84 private:
85     unsigned long fPixel;
86     unsigned long fColor;
87     YColor fBright;
88     YColor fDarken;
89 
90 #ifdef CONFIG_XFREETYPE
91     XftColor* fXftColor;
92     XftColor* allocXft();
93 public:
xftColor()94     XftColor* xftColor() { return fXftColor ? fXftColor : allocXft(); }
95 #endif
96 };
97 
98 static class YPixelCache {
99 public:
black()100     YPixel* black() {
101         if (pixels.isEmpty() || pixels[0]->pixel() != 0) {
102             pixels.insert(0, new YPixel(xapp ? xapp->black() : 0, 0));
103         }
104         return pixels[0];
105     }
106 
get(Word r,Word g,Word b,Word a=0)107     YPixel* get(Word r, Word g, Word b, Word a = 0) {
108         if (a == 0 && xapp->alpha() && validOpacity(fOpacity)) {
109             a = opacityAlpha(fOpacity);
110         }
111         unsigned long color = YPixel::color(r, g, b, a);
112         int lo = 0, hi = pixels.getCount();
113 
114         while (lo < hi) {
115             const int pv = (lo + hi) / 2;
116             YPixel* pivot = pixels[pv];
117 
118             if (color < pivot->color()) {
119                 lo = pv + 1;
120             }
121             else if (pivot->color() < color) {
122                 hi = pv;
123             }
124             else {
125                 return pivot;
126             }
127         }
128         unsigned long allocated = alloc(r, g, b, a);
129         if (allocated == 0)
130             return black();
131         else {
132             YPixel* pixel = new YPixel(allocated, color);
133             pixels.insert(lo, pixel);
134             return pixel;
135         }
136     }
137 
setOpacity(int opacity)138     void setOpacity(int opacity) {
139         fOpacity = opacity;
140     }
141 
getOpacity()142     int getOpacity() {
143         return fOpacity;
144     }
145 
YPixelCache()146     YPixelCache() :
147         fOpacity(0)
148     {
149     }
150 
151 private:
152     unsigned long alloc(Word r, Word g, Word b, Word a);
153 
154     int fOpacity;
155     YObjectArray<YPixel> pixels;
156 
157 } cache;
158 
~YPixel()159 YPixel::~YPixel() {
160 #ifdef CONFIG_XFREETYPE
161     if (fXftColor) {
162         if (xapp) {
163             XftColorFree(display(), visual(), colormap(), fXftColor);
164         }
165         delete fXftColor;
166     }
167 #endif
168 }
169 
YColor(unsigned char r,unsigned char g,unsigned char b,unsigned char a)170 YColor::YColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
171     : fPixel(nullptr)
172 {
173     int previous = cache.getOpacity();
174     cache.setOpacity(a ? (a * MaxOpacity / 255) : MaxOpacity);
175     fPixel = cache.get(r << 8 | r, g << 8 | g, b << 8 | b);
176     cache.setOpacity(previous);
177 }
178 
alloc(const char * name,int opacity)179 void YColor::alloc(const char* name, int opacity) {
180     if (name && name[0]) {
181         if (*name == '[') {
182             long opaq(strtol(++name, (char **) &name, 10));
183             if (inrange<long>(opaq, MinOpacity, MaxOpacity)) {
184                 opacity = int(opaq);
185             }
186             name += (*name == ']');
187         }
188         cache.setOpacity(opacity);
189         alloc(name);
190         cache.setOpacity(0);
191     }
192 }
193 
alloc(const char * name)194 void YColor::alloc(const char* name) {
195     if (name && 0 == strncmp(name, "rgba:", 5)) {
196         unsigned short color[4] = { 0, 0, 0, 0 };
197         bool valid = false;
198         int k = 0, n = 12;
199         for (const char* str = name + 5; -4 <= n; ++str, n -= 4) {
200             const char c = *str;
201             const int hex = ASCII::hexDigit(c);
202             if (0 <= hex) {
203                 if (0 <= n) {
204                     color[k] |= hex << n;
205                 }
206             }
207             else {
208                 while (0 <= n && n <= 8) {
209                     color[k] |= (color[k] >> (12 - n));
210                     n = max(-4, n - (12 - n));
211                 }
212                 if (c != '/' || n != -4 || ++k == 4) {
213                     valid = (c == 0 && k == 3 && n == -4);
214                     break;
215                 }
216                 n = 16;
217             }
218         }
219         if (valid) {
220             int previous = cache.getOpacity();
221             if (previous == 0) {
222                 cache.setOpacity(MaxOpacity * color[3] / 0xFFFF);
223             }
224             fPixel = cache.get(color[0], color[1], color[2]);
225             cache.setOpacity(previous);
226             return;
227         }
228         else if (testOnce(name, __LINE__)) {
229             msg(_("Could not parse color \"%s\""), name);
230         }
231     }
232     else if (nonempty(name)) {
233         XColor color;
234         if (XParseColor(display(), colormap(), name, &color)) {
235             fPixel = cache.get(color.red, color.green, color.blue);
236             return;
237         }
238 
239         if (testOnce(name, __LINE__))
240             msg(_("Could not parse color \"%s\""), name);
241 
242         if (strncmp(name, "rgb:rgb:", 8) == 0)
243             return alloc(name + 4);
244 
245         mstring str(name);
246         if (str.match("^[0-9a-f]{6}$", "i") != null)
247             return alloc("#" + str);
248         if (str.match("^#[0-9a-f]{5}$", "i") != null)
249             return alloc(str + &name[5]);
250 
251         mstring rgb(str.match("rgb:[0-9a-f]{2}/[0-9a-f]{2}/[0-9a-f]{2}", "i"));
252         if (rgb != null)
253             return alloc(rgb);
254         rgb = str.match("[0-9a-f]{2}/[0-9a-f]{2}/[0-9a-f]{2}", "i");
255         if (rgb != null)
256             return alloc("rgb:" + rgb);
257     }
258     fPixel = cache.black();
259 }
260 
reverse()261 YColor& YColor::reverse() {
262     if (fPixel) {
263         fPixel = cache.get(0xFFFF - fPixel->red(),
264                            0xFFFF - fPixel->green(),
265                            0xFFFF - fPixel->blue(),
266                            fPixel->alpha());
267     }
268     return *this;
269 }
270 
271 #ifdef CONFIG_XFREETYPE
allocXft()272 XftColor* YPixel::allocXft() {
273     fXftColor = new XftColor;
274     XRenderColor color = { red(), green(), blue(), 0xffff };
275     XftColorAllocValue(display(), visual(), colormap(), &color, fXftColor);
276     return fXftColor;
277 }
278 
xftColor()279 XftColor* YColor::xftColor() {
280     return fPixel->xftColor();
281 }
282 #endif
283 
284 unsigned long
alloc(Word r,Word g,Word b,Word a)285 YPixelCache::alloc(Word r, Word g, Word b, Word a)
286 {
287     XColor color = { 0, r, g, b, (DoRed | DoGreen | DoBlue), 0 };
288     Visual *visual = ::visual();
289 
290     if (visual->c_class == TrueColor) {
291         unsigned depth = vdepth(), high, unused = 0;
292 
293         int red_shift = int(lowbit(visual->red_mask));
294         int red_prec = int(highbit(visual->red_mask)) - red_shift + 1;
295         int green_shift = int(lowbit(visual->green_mask));
296         int green_prec = int(highbit(visual->green_mask)) - green_shift + 1;
297         int blue_shift = int(lowbit(visual->blue_mask));
298         int blue_prec = int(highbit(visual->blue_mask)) - blue_shift + 1;
299 
300         high = 1
301              + highbit(visual->red_mask | visual->green_mask | visual->blue_mask);
302         if (high < depth) {
303             if (a) {
304                 unused = a;
305             }
306             else if (validOpacity(fOpacity)) {
307                 unused = (((1U << (depth - high)) - 1U) * fOpacity) / MaxOpacity;
308             }
309             else {
310                 unused = ((1U << (depth - high)) - 1U);
311             }
312             unused <<= high;
313         }
314 
315         color.pixel = (unused +
316                        ((color.red >> (16 - red_prec)) << red_shift) +
317                        ((color.green >> (16 - green_prec)) << green_shift) +
318                        ((color.blue >> (16 - blue_prec)) << blue_shift));
319 
320     }
321     else if (Success == XAllocColor(display(), colormap(), &color)) {
322         unsigned j, ncells;
323         double d = 65536. * 65536. * 24;
324         XColor clr;
325         unsigned long pix;
326         unsigned long d_red, d_green, d_blue;
327         double u_red, u_green, u_blue;
328 
329         pix = 0xFFFFFFFF;
330         ncells = unsigned(DisplayCells(display(), xapp->screen()));
331         for (j = 0; j < ncells; j++) {
332             clr.pixel = j;
333             XQueryColor(display(), colormap(), &clr);
334 
335             d_red   = color.red   - clr.red;
336             d_green = color.green - clr.green;
337             d_blue  = color.blue  - clr.blue;
338 
339             u_red   = 3UL * (d_red   * d_red);
340             u_green = 4UL * (d_green * d_green);
341             u_blue  = 2UL * (d_blue  * d_blue);
342 
343             double d1 = u_red + u_blue + u_green;
344 
345             if (pix == 0xFFFFFFFF || d1 < d) {
346                 pix = j;
347                 d = d1;
348             }
349         }
350         if (pix != 0xFFFFFFFF) {
351             clr.pixel = pix;
352             XQueryColor(display(), colormap(), &clr);
353             /*DBG(("color=%04X:%04X:%04X, match=%04X:%04X:%04X\n",
354                    color.red, color.blue, color.green,
355                    clr.red, clr.blue, clr.green));*/
356             color = clr;
357         }
358         if (XAllocColor(display(), colormap(), &color) == 0) {
359             if (color.red + color.green + color.blue >= 32768)
360                 color.pixel = xapp->white();
361             else
362                 color.pixel = xapp->black();
363         }
364     }
365     return color.pixel;
366 }
367 
darken(unsigned short color)368 inline unsigned short darken(unsigned short color) {
369     return color * 2 / 3;
370 }
371 
bright(unsigned short color)372 inline unsigned short bright(unsigned short color) {
373     return min(color * 4 / 3, 0xFFFF);
374 }
375 
darker()376 YColor YPixel::darker() {
377     if (fDarken == false)
378         fDarken = YColor( cache.get(darken(red()),
379                                     darken(green()),
380                                     darken(blue()),
381                                     alpha()));
382     return fDarken;
383 }
384 
brighter()385 YColor YPixel::brighter() {
386     if (fBright == false)
387         fBright = YColor( cache.get(bright(red()),
388                                     bright(green()),
389                                     bright(blue()),
390                                     alpha()));
391     return fBright;
392 }
393 
pixel()394 unsigned long YColor::pixel() {
395     return fPixel ? fPixel->pixel() : xapp->black();
396 }
397 
darker()398 YColor YColor::darker() {
399     return fPixel ? fPixel->darker() : *this;
400 }
401 
brighter()402 YColor YColor::brighter() {
403     return fPixel ? fPixel->brighter() : *this;
404 }
405 
operator ==(YColor & c)406 bool YColor::operator==(YColor& c) {
407     return fPixel && c.fPixel && fPixel->pixel() == c.fPixel->pixel();
408 }
409 
operator !=(YColor & c)410 bool YColor::operator!=(YColor& c) {
411     return !(*this == c);
412 }
413 
red()414 unsigned char YColor::red()   { return fPixel->red()   >> 8; }
green()415 unsigned char YColor::green() { return fPixel->green() >> 8; }
blue()416 unsigned char YColor::blue()  { return fPixel->blue()  >> 8; }
alpha()417 unsigned char YColor::alpha() { return fPixel->alpha() >> 8; }
418 
419 // vim: set sw=4 ts=4 et:
420