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)
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
31 #endif // HAVE_STDIO_H
35 #endif // HAVE_CTYPE_H
42 #include "blackbox.hh"
44 #include "BaseDisplay.hh"
49 static unsigned long bsqrt(unsigned long x) {
53 unsigned long r = x >> 1;
63 BImageControl *ctrl = 0;
65 BImageControl::BImageControl(BaseDisplay *dpy, const ScreenInfo *scrn,
66 bool _dither, int _cpc,
67 unsigned long cache_timeout,
70 if (! ctrl) ctrl = this;
75 setColorsPerChannel(_cpc);
80 timer = new BTimer(basedisplay, this);
81 timer->setTimeout(cache_timeout);
88 colors = (XColor *) 0;
91 grad_xbuffer = grad_ybuffer = (unsigned int *) 0;
92 grad_buffer_width = grad_buffer_height = 0;
94 sqrt_table = (unsigned long *) 0;
96 screen_depth = screeninfo->getDepth();
97 window = screeninfo->getRootWindow();
98 screen_number = screeninfo->getScreenNumber();
99 colormap = screeninfo->getColormap();
102 XPixmapFormatValues *pmv = XListPixmapFormats(basedisplay->getXDisplay(),
106 for (int i = 0; i < count; i++)
107 if (pmv[i].depth == screen_depth) {
108 bits_per_pixel = pmv[i].bits_per_pixel;
115 if (bits_per_pixel == 0) bits_per_pixel = screen_depth;
116 if (bits_per_pixel >= 24) setDither(False);
118 red_offset = green_offset = blue_offset = 0;
120 switch (getVisual()->c_class) {
124 // compute color tables
125 unsigned long red_mask = getVisual()->red_mask,
126 green_mask = getVisual()->green_mask,
127 blue_mask = getVisual()->blue_mask;
129 while (! (red_mask & 1)) { red_offset++; red_mask >>= 1; }
130 while (! (green_mask & 1)) { green_offset++; green_mask >>= 1; }
131 while (! (blue_mask & 1)) { blue_offset++; blue_mask >>= 1; }
133 red_bits = 255 / red_mask;
134 green_bits = 255 / green_mask;
135 blue_bits = 255 / blue_mask;
137 for (i = 0; i < 256; i++) {
138 red_color_table[i] = i / red_bits;
139 green_color_table[i] = i / green_bits;
140 blue_color_table[i] = i / blue_bits;
147 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
149 if (ncolors > (1 << screen_depth)) {
150 colors_per_channel = (1 << screen_depth) / 3;
151 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
154 if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
156 i18n(ImageSet, ImageInvalidColormapSize,
157 "BImageControl::BImageControl: invalid colormap size %d "
158 "(%d/%d/%d) - reducing"),
159 ncolors, colors_per_channel, colors_per_channel,
162 colors_per_channel = (1 << screen_depth) / 3;
165 colors = new XColor[ncolors];
167 fprintf(stderr, i18n(ImageSet, ImageErrorAllocatingColormap,
168 "BImageControl::BImageControl: error allocating "
173 int i = 0, ii, p, r, g, b,
176 bits = 256 / colors_per_channel;
177 #else // !ORDEREDPSEUDO
178 bits = 255 / (colors_per_channel - 1);
179 #endif // ORDEREDPSEUDO
181 red_bits = green_bits = blue_bits = bits;
183 for (i = 0; i < 256; i++)
184 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
187 for (r = 0, i = 0; r < colors_per_channel; r++)
188 for (g = 0; g < colors_per_channel; g++)
189 for (b = 0; b < colors_per_channel; b++, i++) {
190 colors[i].red = (r * 0xffff) / (colors_per_channel - 1);
191 colors[i].green = (g * 0xffff) / (colors_per_channel - 1);
192 colors[i].blue = (b * 0xffff) / (colors_per_channel - 1);;
193 colors[i].flags = DoRed|DoGreen|DoBlue;
196 for (i = 0; i < ncolors; i++) {
197 if (! XAllocColor(basedisplay->getXDisplay(), colormap, &colors[i])) {
198 fprintf(stderr, i18n(ImageSet, ImageColorAllocFail,
199 "couldn't alloc color %i %i %i\n"),
200 colors[i].red, colors[i].green, colors[i].blue);
203 colors[i].flags = DoRed|DoGreen|DoBlue;
208 int incolors = (((1 << screen_depth) > 256) ? 256 : (1 << screen_depth));
210 for (i = 0; i < incolors; i++)
211 icolors[i].pixel = i;
213 XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
214 for (i = 0; i < ncolors; i++) {
215 if (! colors[i].flags) {
216 unsigned long chk = 0xffffffff, pixel, close = 0;
220 for (ii = 0; ii < incolors; ii++) {
221 r = (colors[i].red - icolors[i].red) >> 8;
222 g = (colors[i].green - icolors[i].green) >> 8;
223 b = (colors[i].blue - icolors[i].blue) >> 8;
224 pixel = (r * r) + (g * g) + (b * b);
231 colors[i].red = icolors[close].red;
232 colors[i].green = icolors[close].green;
233 colors[i].blue = icolors[close].blue;
235 if (XAllocColor(basedisplay->getXDisplay(), colormap,
237 colors[i].flags = DoRed|DoGreen|DoBlue;
250 if (getVisual()->c_class == StaticGray) {
251 ncolors = 1 << screen_depth;
253 ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
255 if (ncolors > (1 << screen_depth)) {
256 colors_per_channel = (1 << screen_depth) / 3;
258 colors_per_channel * colors_per_channel * colors_per_channel;
262 if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
264 i18n(ImageSet, ImageInvalidColormapSize,
265 "BImageControl::BImageControl: invalid colormap size %d "
266 "(%d/%d/%d) - reducing"),
267 ncolors, colors_per_channel, colors_per_channel,
270 colors_per_channel = (1 << screen_depth) / 3;
273 colors = new XColor[ncolors];
276 i18n(ImageSet, ImageErrorAllocatingColormap,
277 "BImageControl::BImageControl: error allocating colormap\n"));
281 int i = 0, ii, p, bits = 255 / (colors_per_channel - 1);
282 red_bits = green_bits = blue_bits = bits;
284 for (i = 0; i < 256; i++)
285 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
288 for (i = 0; i < ncolors; i++) {
289 colors[i].red = (i * 0xffff) / (colors_per_channel - 1);
290 colors[i].green = (i * 0xffff) / (colors_per_channel - 1);
291 colors[i].blue = (i * 0xffff) / (colors_per_channel - 1);;
292 colors[i].flags = DoRed|DoGreen|DoBlue;
294 if (! XAllocColor(basedisplay->getXDisplay(), colormap,
296 fprintf(stderr, i18n(ImageSet, ImageColorAllocFail,
297 "couldn't alloc color %i %i %i\n"),
298 colors[i].red, colors[i].green, colors[i].blue);
301 colors[i].flags = DoRed|DoGreen|DoBlue;
306 int incolors = (((1 << screen_depth) > 256) ? 256 :
307 (1 << screen_depth));
309 for (i = 0; i < incolors; i++)
310 icolors[i].pixel = i;
312 XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
313 for (i = 0; i < ncolors; i++) {
314 if (! colors[i].flags) {
315 unsigned long chk = 0xffffffff, pixel, close = 0;
319 for (ii = 0; ii < incolors; ii++) {
320 int r = (colors[i].red - icolors[i].red) >> 8;
321 int g = (colors[i].green - icolors[i].green) >> 8;
322 int b = (colors[i].blue - icolors[i].blue) >> 8;
323 pixel = (r * r) + (g * g) + (b * b);
330 colors[i].red = icolors[close].red;
331 colors[i].green = icolors[close].green;
332 colors[i].blue = icolors[close].blue;
334 if (XAllocColor(basedisplay->getXDisplay(), colormap,
336 colors[i].flags = DoRed|DoGreen|DoBlue;
349 i18n(ImageSet, ImageUnsupVisual,
350 "BImageControl::BImageControl: unsupported visual %d\n"),
351 getVisual()->c_class);
357 BImageControl::~BImageControl(void) {
358 delete [] sqrt_table;
360 delete [] grad_xbuffer;
362 delete [] grad_ybuffer;
365 unsigned long *pixels = new unsigned long [ncolors];
368 for (i = 0; i < ncolors; i++)
369 *(pixels + i) = (*(colors + i)).pixel;
371 XFreeColors(basedisplay->getXDisplay(), colormap, pixels, ncolors, 0);
376 if (! cache.empty()) {
378 fprintf(stderr, i18n(ImageSet, ImagePixmapRelease,
379 "BImageContol::~BImageControl: pixmap cache - "
380 "releasing %d pixmaps\n"), cache.size());
382 CacheContainer::iterator it = cache.begin();
383 const CacheContainer::iterator end = cache.end();
384 for (; it != end; ++it) {
385 XFreePixmap(basedisplay->getXDisplay(), (*it).pixmap);
397 Pixmap BImageControl::searchCache(const unsigned int width,
398 const unsigned int height,
399 const unsigned long texture,
400 const BColor &c1, const BColor &c2) {
404 CacheContainer::iterator it = cache.begin();
405 const CacheContainer::iterator end = cache.end();
406 for (; it != end; ++it) {
407 CachedImage& tmp = *it;
408 if ((tmp.width == width) && (tmp.height == height) &&
409 (tmp.texture == texture) && (tmp.pixel1 == c1.pixel()))
410 if (texture & BTexture::Gradient) {
411 if (tmp.pixel2 == c2.pixel()) {
424 Pixmap BImageControl::renderImage(unsigned int width, unsigned int height,
425 const BTexture &texture) {
426 if (texture.texture() & BTexture::Parent_Relative) return ParentRelative;
428 Pixmap pixmap = searchCache(width, height, texture.texture(),
429 texture.color(), texture.colorTo());
430 if (pixmap) return pixmap;
432 BImage image(this, width, height);
433 pixmap = image.render(texture);
444 tmp.texture = texture.texture();
445 tmp.pixel1 = texture.color().pixel();
447 if (texture.texture() & BTexture::Gradient)
448 tmp.pixel2 = texture.colorTo().pixel();
452 cache.push_back(tmp);
454 if (cache.size() > cache_max) {
456 fprintf(stderr, i18n(ImageSet, ImagePixmapCacheLarge,
457 "BImageControl::renderImage: cache is large, "
458 "forcing cleanout\n"));
468 void BImageControl::removeImage(Pixmap pixmap) {
472 CacheContainer::iterator it = cache.begin();
473 const CacheContainer::iterator end = cache.end();
474 for (; it != end; ++it) {
475 CachedImage &tmp = *it;
476 if (tmp.pixmap == pixmap && tmp.count > 0)
487 void BImageControl::getColorTables(unsigned char **rmt, unsigned char **gmt,
489 int *roff, int *goff, int *boff,
490 int *rbit, int *gbit, int *bbit) {
491 if (rmt) *rmt = red_color_table;
492 if (gmt) *gmt = green_color_table;
493 if (bmt) *bmt = blue_color_table;
495 if (roff) *roff = red_offset;
496 if (goff) *goff = green_offset;
497 if (boff) *boff = blue_offset;
499 if (rbit) *rbit = red_bits;
500 if (gbit) *gbit = green_bits;
501 if (bbit) *bbit = blue_bits;
505 void BImageControl::getXColorTable(XColor **c, int *n) {
511 void BImageControl::getGradientBuffers(unsigned int w,
516 if (w > grad_buffer_width) {
518 delete [] grad_xbuffer;
521 grad_buffer_width = w;
523 grad_xbuffer = new unsigned int[grad_buffer_width * 3];
526 if (h > grad_buffer_height) {
528 delete [] grad_ybuffer;
531 grad_buffer_height = h;
533 grad_ybuffer = new unsigned int[grad_buffer_height * 3];
536 *xbuf = grad_xbuffer;
537 *ybuf = grad_ybuffer;
541 void BImageControl::installRootColormap(void) {
544 XListInstalledColormaps(basedisplay->getXDisplay(), window, &ncmap);
548 for (int i = 0; i < ncmap; i++)
549 if (*(cmaps + i) == colormap)
553 XInstallColormap(basedisplay->getXDisplay(), colormap);
560 void BImageControl::setColorsPerChannel(int cpc) {
561 if (cpc < 2) cpc = 2;
562 if (cpc > 6) cpc = 6;
564 colors_per_channel = cpc;
568 unsigned long BImageControl::getSqrt(unsigned int x) {
570 // build sqrt table for use with elliptic gradient
572 sqrt_table = new unsigned long[(256 * 256 * 2) + 1];
574 for (int i = 0; i < (256 * 256 * 2); i++)
575 *(sqrt_table + i) = bsqrt(i);
578 return (*(sqrt_table + x));
582 struct ZeroRefCheck {
583 inline bool operator()(const BImageControl::CachedImage &image) const {
584 return (image.count == 0);
588 struct CacheCleaner {
590 ZeroRefCheck ref_check;
591 CacheCleaner(Display *d): display(d) {}
592 inline void operator()(const BImageControl::CachedImage& image) const {
593 if (ref_check(image))
594 XFreePixmap(display, image.pixmap);
599 void BImageControl::timeout(void) {
600 CacheCleaner cleaner(basedisplay->getXDisplay());
601 std::for_each(cache.begin(), cache.end(), cleaner);
602 cache.remove_if(cleaner.ref_check);