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