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