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