]> icculus.org git repositories - mikachu/openbox.git/blob - otk/imagecontrol.cc
remove the word "sticky" from everywhere, and replace with "all desktops". change...
[mikachu/openbox.git] / 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 }