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