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