]> Dogcows Code - chaz/openbox/blob - src/imagecontrol.cc
big fat commit..
[chaz/openbox] / src / imagecontrol.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #ifdef HAVE_STDIO_H
9 # include <stdio.h>
10 #endif // HAVE_STDIO_H
11
12 #ifdef HAVE_CTYPE_H
13 # include <ctype.h>
14 #endif // HAVE_CTYPE_H
15
16 #include <X11/Xlib.h>
17 }
18
19 #include <algorithm>
20
21 #include "blackbox.hh"
22 #include "basedisplay.hh"
23 #include "color.hh"
24 #include "image.hh"
25 #include "texture.hh"
26
27 static unsigned long bsqrt(unsigned long x) {
28 if (x <= 0) return 0;
29 if (x == 1) return 1;
30
31 unsigned long r = x >> 1;
32 unsigned long q;
33
34 while (1) {
35 q = x / r;
36 if (q >= r) return r;
37 r = (r + q) >> 1;
38 }
39 }
40
41 BImageControl *ctrl = 0;
42
43 BImageControl::BImageControl(BaseDisplay *dpy, const ScreenInfo *scrn,
44 bool _dither, int _cpc,
45 unsigned long cache_timeout,
46 unsigned long cmax) {
47 if (! ctrl) ctrl = this;
48
49 basedisplay = dpy;
50 screeninfo = scrn;
51 setDither(_dither);
52 setColorsPerChannel(_cpc);
53
54 cache_max = cmax;
55 if (cache_timeout) {
56 timer = new BTimer(basedisplay, this);
57 timer->setTimeout(cache_timeout);
58 timer->start();
59 } else {
60 timer = (BTimer *) 0;
61 }
62
63 colors = (XColor *) 0;
64 ncolors = 0;
65
66 grad_xbuffer = grad_ybuffer = (unsigned int *) 0;
67 grad_buffer_width = grad_buffer_height = 0;
68
69 sqrt_table = (unsigned long *) 0;
70
71 screen_depth = screeninfo->getDepth();
72 window = screeninfo->getRootWindow();
73 screen_number = screeninfo->getScreenNumber();
74 colormap = screeninfo->getColormap();
75
76 int count;
77 XPixmapFormatValues *pmv = XListPixmapFormats(basedisplay->getXDisplay(),
78 &count);
79 if (pmv) {
80 bits_per_pixel = 0;
81 for (int i = 0; i < count; i++)
82 if (pmv[i].depth == screen_depth) {
83 bits_per_pixel = pmv[i].bits_per_pixel;
84 break;
85 }
86
87 XFree(pmv);
88 }
89
90 if (bits_per_pixel == 0) bits_per_pixel = screen_depth;
91 if (bits_per_pixel >= 24) setDither(False);
92
93 red_offset = green_offset = blue_offset = 0;
94
95 switch (getVisual()->c_class) {
96 case TrueColor: {
97 int i;
98
99 // compute color tables
100 unsigned long red_mask = getVisual()->red_mask,
101 green_mask = getVisual()->green_mask,
102 blue_mask = getVisual()->blue_mask;
103
104 while (! (red_mask & 1)) { red_offset++; red_mask >>= 1; }
105 while (! (green_mask & 1)) { green_offset++; green_mask >>= 1; }
106 while (! (blue_mask & 1)) { blue_offset++; blue_mask >>= 1; }
107
108 red_bits = 255 / red_mask;
109 green_bits = 255 / green_mask;
110 blue_bits = 255 / blue_mask;
111
112 for (i = 0; i < 256; i++) {
113 red_color_table[i] = i / red_bits;
114 green_color_table[i] = i / green_bits;
115 blue_color_table[i] = i / blue_bits;
116 }
117 break;
118 }
119
120 case PseudoColor:
121 case StaticColor: {
122 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
123
124 if (ncolors > (1 << screen_depth)) {
125 colors_per_channel = (1 << screen_depth) / 3;
126 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
127 }
128
129 if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
130 fprintf(stderr,
131 "BImageControl::BImageControl: invalid colormap size %d "
132 "(%d/%d/%d) - reducing",
133 ncolors, colors_per_channel, colors_per_channel,
134 colors_per_channel);
135
136 colors_per_channel = (1 << screen_depth) / 3;
137 }
138
139 colors = new XColor[ncolors];
140 if (! colors) {
141 fprintf(stderr, "BImageControl::BImageControl: error allocating "
142 "colormap\n");
143 exit(1);
144 }
145
146 int i = 0, ii, p, r, g, b,
147
148 #ifdef ORDEREDPSEUDO
149 bits = 256 / colors_per_channel;
150 #else // !ORDEREDPSEUDO
151 bits = 255 / (colors_per_channel - 1);
152 #endif // ORDEREDPSEUDO
153
154 red_bits = green_bits = blue_bits = bits;
155
156 for (i = 0; i < 256; i++)
157 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
158 i / bits;
159
160 for (r = 0, i = 0; r < colors_per_channel; r++)
161 for (g = 0; g < colors_per_channel; g++)
162 for (b = 0; b < colors_per_channel; b++, i++) {
163 colors[i].red = (r * 0xffff) / (colors_per_channel - 1);
164 colors[i].green = (g * 0xffff) / (colors_per_channel - 1);
165 colors[i].blue = (b * 0xffff) / (colors_per_channel - 1);;
166 colors[i].flags = DoRed|DoGreen|DoBlue;
167 }
168
169 for (i = 0; i < ncolors; i++) {
170 if (! XAllocColor(basedisplay->getXDisplay(), colormap, &colors[i])) {
171 fprintf(stderr, "couldn't alloc color %i %i %i\n",
172 colors[i].red, colors[i].green, colors[i].blue);
173 colors[i].flags = 0;
174 } else {
175 colors[i].flags = DoRed|DoGreen|DoBlue;
176 }
177 }
178
179 XColor icolors[256];
180 int incolors = (((1 << screen_depth) > 256) ? 256 : (1 << screen_depth));
181
182 for (i = 0; i < incolors; i++)
183 icolors[i].pixel = i;
184
185 XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
186 for (i = 0; i < ncolors; i++) {
187 if (! colors[i].flags) {
188 unsigned long chk = 0xffffffff, pixel, close = 0;
189
190 p = 2;
191 while (p--) {
192 for (ii = 0; ii < incolors; ii++) {
193 r = (colors[i].red - icolors[i].red) >> 8;
194 g = (colors[i].green - icolors[i].green) >> 8;
195 b = (colors[i].blue - icolors[i].blue) >> 8;
196 pixel = (r * r) + (g * g) + (b * b);
197
198 if (pixel < chk) {
199 chk = pixel;
200 close = ii;
201 }
202
203 colors[i].red = icolors[close].red;
204 colors[i].green = icolors[close].green;
205 colors[i].blue = icolors[close].blue;
206
207 if (XAllocColor(basedisplay->getXDisplay(), colormap,
208 &colors[i])) {
209 colors[i].flags = DoRed|DoGreen|DoBlue;
210 break;
211 }
212 }
213 }
214 }
215 }
216
217 break;
218 }
219
220 case GrayScale:
221 case StaticGray: {
222 if (getVisual()->c_class == StaticGray) {
223 ncolors = 1 << screen_depth;
224 } else {
225 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
226
227 if (ncolors > (1 << screen_depth)) {
228 colors_per_channel = (1 << screen_depth) / 3;
229 ncolors =
230 colors_per_channel * colors_per_channel * colors_per_channel;
231 }
232 }
233
234 if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
235 fprintf(stderr,
236 "BImageControl::BImageControl: invalid colormap size %d "
237 "(%d/%d/%d) - reducing",
238 ncolors, colors_per_channel, colors_per_channel,
239 colors_per_channel);
240
241 colors_per_channel = (1 << screen_depth) / 3;
242 }
243
244 colors = new XColor[ncolors];
245 if (! colors) {
246 fprintf(stderr,
247 "BImageControl::BImageControl: error allocating colormap\n");
248 exit(1);
249 }
250
251 int i = 0, ii, p, bits = 255 / (colors_per_channel - 1);
252 red_bits = green_bits = blue_bits = bits;
253
254 for (i = 0; i < 256; i++)
255 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
256 i / bits;
257
258 for (i = 0; i < ncolors; i++) {
259 colors[i].red = (i * 0xffff) / (colors_per_channel - 1);
260 colors[i].green = (i * 0xffff) / (colors_per_channel - 1);
261 colors[i].blue = (i * 0xffff) / (colors_per_channel - 1);;
262 colors[i].flags = DoRed|DoGreen|DoBlue;
263
264 if (! XAllocColor(basedisplay->getXDisplay(), colormap,
265 &colors[i])) {
266 fprintf(stderr, "couldn't alloc color %i %i %i\n",
267 colors[i].red, colors[i].green, colors[i].blue);
268 colors[i].flags = 0;
269 } else {
270 colors[i].flags = DoRed|DoGreen|DoBlue;
271 }
272 }
273
274 XColor icolors[256];
275 int incolors = (((1 << screen_depth) > 256) ? 256 :
276 (1 << screen_depth));
277
278 for (i = 0; i < incolors; i++)
279 icolors[i].pixel = i;
280
281 XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
282 for (i = 0; i < ncolors; i++) {
283 if (! colors[i].flags) {
284 unsigned long chk = 0xffffffff, pixel, close = 0;
285
286 p = 2;
287 while (p--) {
288 for (ii = 0; ii < incolors; ii++) {
289 int r = (colors[i].red - icolors[i].red) >> 8;
290 int g = (colors[i].green - icolors[i].green) >> 8;
291 int b = (colors[i].blue - icolors[i].blue) >> 8;
292 pixel = (r * r) + (g * g) + (b * b);
293
294 if (pixel < chk) {
295 chk = pixel;
296 close = ii;
297 }
298
299 colors[i].red = icolors[close].red;
300 colors[i].green = icolors[close].green;
301 colors[i].blue = icolors[close].blue;
302
303 if (XAllocColor(basedisplay->getXDisplay(), colormap,
304 &colors[i])) {
305 colors[i].flags = DoRed|DoGreen|DoBlue;
306 break;
307 }
308 }
309 }
310 }
311 }
312
313 break;
314 }
315
316 default:
317 fprintf(stderr, "BImageControl::BImageControl: unsupported visual %d\n",
318 getVisual()->c_class);
319 exit(1);
320 }
321 }
322
323
324 BImageControl::~BImageControl(void) {
325 delete [] sqrt_table;
326
327 delete [] grad_xbuffer;
328
329 delete [] grad_ybuffer;
330
331 if (colors) {
332 unsigned long *pixels = new unsigned long [ncolors];
333
334 for (int i = 0; i < ncolors; i++)
335 *(pixels + i) = (*(colors + i)).pixel;
336
337 XFreeColors(basedisplay->getXDisplay(), colormap, pixels, ncolors, 0);
338
339 delete [] colors;
340 }
341
342 if (! cache.empty()) {
343 //#ifdef DEBUG
344 fprintf(stderr, "BImageContol::~BImageControl: pixmap cache - "
345 "releasing %d pixmaps\n", cache.size());
346 //#endif
347 CacheContainer::iterator it = cache.begin();
348 const CacheContainer::iterator end = cache.end();
349 for (; it != end; ++it)
350 XFreePixmap(basedisplay->getXDisplay(), it->pixmap);
351 }
352 if (timer) {
353 timer->stop();
354 delete timer;
355 }
356 }
357
358
359 Pixmap BImageControl::searchCache(const unsigned int width,
360 const unsigned int height,
361 const unsigned long texture,
362 const BColor &c1, const BColor &c2) {
363 if (cache.empty())
364 return None;
365
366 CacheContainer::iterator it = cache.begin();
367 const CacheContainer::iterator end = cache.end();
368 for (; it != end; ++it) {
369 CachedImage& tmp = *it;
370 if (tmp.width == width && tmp.height == height &&
371 tmp.texture == texture && tmp.pixel1 == c1.pixel())
372 if (texture & BTexture::Gradient) {
373 if (tmp.pixel2 == c2.pixel()) {
374 tmp.count++;
375 return tmp.pixmap;
376 }
377 } else {
378 tmp.count++;
379 return tmp.pixmap;
380 }
381 }
382 return None;
383 }
384
385
386 Pixmap BImageControl::renderImage(unsigned int width, unsigned int height,
387 const BTexture &texture) {
388 if (texture.texture() & BTexture::Parent_Relative) return ParentRelative;
389
390 Pixmap pixmap = searchCache(width, height, texture.texture(),
391 texture.color(), texture.colorTo());
392 if (pixmap) return pixmap;
393
394 BImage image(this, width, height);
395 pixmap = image.render(texture);
396
397 if (! pixmap)
398 return None;
399
400 CachedImage tmp;
401
402 tmp.pixmap = pixmap;
403 tmp.width = width;
404 tmp.height = height;
405 tmp.count = 1;
406 tmp.texture = texture.texture();
407 tmp.pixel1 = texture.color().pixel();
408
409 if (texture.texture() & BTexture::Gradient)
410 tmp.pixel2 = texture.colorTo().pixel();
411 else
412 tmp.pixel2 = 0l;
413
414 cache.push_back(tmp);
415
416 if (cache.size() > cache_max) {
417 #ifdef DEBUG
418 fprintf(stderr, "BImageControl::renderImage: cache is large, "
419 "forcing cleanout\n");
420 #endif // DEBUG
421
422 timeout();
423 }
424
425 return pixmap;
426 }
427
428
429 void BImageControl::removeImage(Pixmap pixmap) {
430 if (! pixmap)
431 return;
432
433 CacheContainer::iterator it = cache.begin();
434 const CacheContainer::iterator end = cache.end();
435 for (; it != end; ++it) {
436 CachedImage &tmp = *it;
437 if (tmp.pixmap == pixmap && tmp.count > 0)
438 tmp.count--;
439 }
440
441 if (! timer)
442 timeout();
443 }
444
445
446 void BImageControl::getColorTables(unsigned char **rmt, unsigned char **gmt,
447 unsigned char **bmt,
448 int *roff, int *goff, int *boff,
449 int *rbit, int *gbit, int *bbit) {
450 if (rmt) *rmt = red_color_table;
451 if (gmt) *gmt = green_color_table;
452 if (bmt) *bmt = blue_color_table;
453
454 if (roff) *roff = red_offset;
455 if (goff) *goff = green_offset;
456 if (boff) *boff = blue_offset;
457
458 if (rbit) *rbit = red_bits;
459 if (gbit) *gbit = green_bits;
460 if (bbit) *bbit = blue_bits;
461 }
462
463
464 void BImageControl::getXColorTable(XColor **c, int *n) {
465 if (c) *c = colors;
466 if (n) *n = ncolors;
467 }
468
469
470 void BImageControl::getGradientBuffers(unsigned int w,
471 unsigned int h,
472 unsigned int **xbuf,
473 unsigned int **ybuf)
474 {
475 if (w > grad_buffer_width) {
476 if (grad_xbuffer)
477 delete [] grad_xbuffer;
478
479 grad_buffer_width = w;
480
481 grad_xbuffer = new unsigned int[grad_buffer_width * 3];
482 }
483
484 if (h > grad_buffer_height) {
485 if (grad_ybuffer)
486 delete [] grad_ybuffer;
487
488 grad_buffer_height = h;
489
490 grad_ybuffer = new unsigned int[grad_buffer_height * 3];
491 }
492
493 *xbuf = grad_xbuffer;
494 *ybuf = grad_ybuffer;
495 }
496
497
498 void BImageControl::installRootColormap(void) {
499 int ncmap = 0;
500 Colormap *cmaps =
501 XListInstalledColormaps(basedisplay->getXDisplay(), window, &ncmap);
502
503 if (cmaps) {
504 bool install = True;
505 for (int i = 0; i < ncmap; i++)
506 if (*(cmaps + i) == colormap)
507 install = False;
508
509 if (install)
510 XInstallColormap(basedisplay->getXDisplay(), colormap);
511
512 XFree(cmaps);
513 }
514 }
515
516
517 void BImageControl::setColorsPerChannel(int cpc) {
518 if (cpc < 2) cpc = 2;
519 if (cpc > 6) cpc = 6;
520
521 colors_per_channel = cpc;
522 }
523
524
525 unsigned long BImageControl::getSqrt(unsigned int x) {
526 if (! sqrt_table) {
527 // build sqrt table for use with elliptic gradient
528
529 sqrt_table = new unsigned long[(256 * 256 * 2) + 1];
530
531 for (int i = 0; i < (256 * 256 * 2); i++)
532 *(sqrt_table + i) = bsqrt(i);
533 }
534
535 return (*(sqrt_table + x));
536 }
537
538
539 struct ZeroRefCheck {
540 inline bool operator()(const BImageControl::CachedImage &image) const {
541 return (image.count == 0);
542 }
543 };
544
545 struct CacheCleaner {
546 Display *display;
547 ZeroRefCheck ref_check;
548 CacheCleaner(Display *d): display(d) {}
549 inline void operator()(const BImageControl::CachedImage& image) const {
550 if (ref_check(image))
551 XFreePixmap(display, image.pixmap);
552 }
553 };
554
555
556 void BImageControl::timeout(void) {
557 CacheCleaner cleaner(basedisplay->getXDisplay());
558 std::for_each(cache.begin(), cache.end(), cleaner);
559 cache.remove_if(cleaner.ref_check);
560 }
561
This page took 0.068519 seconds and 5 git commands to generate.