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