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
,
69 if (! ctrl
) ctrl
= this;
74 setColorsPerChannel(_cpc
);
79 timer
= new BTimer(basedisplay
, this);
80 timer
->setTimeout(cache_timeout
);
87 colors
= (XColor
*) 0;
90 grad_xbuffer
= grad_ybuffer
= (unsigned int *) 0;
91 grad_buffer_width
= grad_buffer_height
= 0;
93 sqrt_table
= (unsigned long *) 0;
95 screen_depth
= screeninfo
->getDepth();
96 window
= screeninfo
->getRootWindow();
97 screen_number
= screeninfo
->getScreenNumber();
98 colormap
= screeninfo
->getColormap();
101 XPixmapFormatValues
*pmv
= XListPixmapFormats(basedisplay
->getXDisplay(),
105 for (int i
= 0; i
< count
; i
++)
106 if (pmv
[i
].depth
== screen_depth
) {
107 bits_per_pixel
= pmv
[i
].bits_per_pixel
;
114 if (bits_per_pixel
== 0) bits_per_pixel
= screen_depth
;
115 if (bits_per_pixel
>= 24) setDither(False
);
117 red_offset
= green_offset
= blue_offset
= 0;
119 switch (getVisual()->c_class
) {
123 // compute color tables
124 unsigned long red_mask
= getVisual()->red_mask
,
125 green_mask
= getVisual()->green_mask
,
126 blue_mask
= getVisual()->blue_mask
;
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; }
132 red_bits
= 255 / red_mask
;
133 green_bits
= 255 / green_mask
;
134 blue_bits
= 255 / blue_mask
;
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
;
146 ncolors
= colors_per_channel
* colors_per_channel
* colors_per_channel
;
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
;
153 if (colors_per_channel
< 2 || ncolors
> (1 << screen_depth
)) {
155 i18n(ImageSet
, ImageInvalidColormapSize
,
156 "BImageControl::BImageControl: invalid colormap size %d "
157 "(%d/%d/%d) - reducing"),
158 ncolors
, colors_per_channel
, colors_per_channel
,
161 colors_per_channel
= (1 << screen_depth
) / 3;
164 colors
= new XColor
[ncolors
];
166 fprintf(stderr
, i18n(ImageSet
, ImageErrorAllocatingColormap
,
167 "BImageControl::BImageControl: error allocating "
172 int i
= 0, ii
, p
, r
, g
, b
,
175 bits
= 256 / colors_per_channel
;
176 #else // !ORDEREDPSEUDO
177 bits
= 255 / (colors_per_channel
- 1);
178 #endif // ORDEREDPSEUDO
180 red_bits
= green_bits
= blue_bits
= bits
;
182 for (i
= 0; i
< 256; i
++)
183 red_color_table
[i
] = green_color_table
[i
] = blue_color_table
[i
] =
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
;
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
);
202 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
207 int incolors
= (((1 << screen_depth
) > 256) ? 256 : (1 << screen_depth
));
209 for (i
= 0; i
< incolors
; i
++)
210 icolors
[i
].pixel
= i
;
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;
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
);
230 colors
[i
].red
= icolors
[close
].red
;
231 colors
[i
].green
= icolors
[close
].green
;
232 colors
[i
].blue
= icolors
[close
].blue
;
234 if (XAllocColor(basedisplay
->getXDisplay(), colormap
,
236 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
249 if (getVisual()->c_class
== StaticGray
) {
250 ncolors
= 1 << screen_depth
;
252 ncolors
= colors_per_channel
* colors_per_channel
* colors_per_channel
;
254 if (ncolors
> (1 << screen_depth
)) {
255 colors_per_channel
= (1 << screen_depth
) / 3;
257 colors_per_channel
* colors_per_channel
* colors_per_channel
;
261 if (colors_per_channel
< 2 || ncolors
> (1 << screen_depth
)) {
263 i18n(ImageSet
, ImageInvalidColormapSize
,
264 "BImageControl::BImageControl: invalid colormap size %d "
265 "(%d/%d/%d) - reducing"),
266 ncolors
, colors_per_channel
, colors_per_channel
,
269 colors_per_channel
= (1 << screen_depth
) / 3;
272 colors
= new XColor
[ncolors
];
275 i18n(ImageSet
, ImageErrorAllocatingColormap
,
276 "BImageControl::BImageControl: error allocating colormap\n"));
280 int i
= 0, ii
, p
, bits
= 255 / (colors_per_channel
- 1);
281 red_bits
= green_bits
= blue_bits
= bits
;
283 for (i
= 0; i
< 256; i
++)
284 red_color_table
[i
] = green_color_table
[i
] = blue_color_table
[i
] =
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
;
293 if (! XAllocColor(basedisplay
->getXDisplay(), colormap
,
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
);
300 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
305 int incolors
= (((1 << screen_depth
) > 256) ? 256 :
306 (1 << screen_depth
));
308 for (i
= 0; i
< incolors
; i
++)
309 icolors
[i
].pixel
= i
;
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;
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
);
329 colors
[i
].red
= icolors
[close
].red
;
330 colors
[i
].green
= icolors
[close
].green
;
331 colors
[i
].blue
= icolors
[close
].blue
;
333 if (XAllocColor(basedisplay
->getXDisplay(), colormap
,
335 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
348 i18n(ImageSet
, ImageUnsupVisual
,
349 "BImageControl::BImageControl: unsupported visual %d\n"),
350 getVisual()->c_class
);
356 BImageControl::~BImageControl(void) {
357 delete [] sqrt_table
;
359 delete [] grad_xbuffer
;
361 delete [] grad_ybuffer
;
364 unsigned long *pixels
= new unsigned long [ncolors
];
366 for (int i
= 0; i
< ncolors
; i
++)
367 *(pixels
+ i
) = (*(colors
+ i
)).pixel
;
369 XFreeColors(basedisplay
->getXDisplay(), colormap
, pixels
, ncolors
, 0);
374 if (! cache
.empty()) {
376 fprintf(stderr
, i18n(ImageSet
, ImagePixmapRelease
,
377 "BImageContol::~BImageControl: pixmap cache - "
378 "releasing %d pixmaps\n"), cache
.size());
380 CacheContainer::iterator it
= cache
.begin();
381 const CacheContainer::iterator end
= cache
.end();
382 for (; it
!= end
; ++it
)
383 XFreePixmap(basedisplay
->getXDisplay(), it
->pixmap
);
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
) {
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()) {
421 Pixmap
BImageControl::renderImage(unsigned int width
, unsigned int height
,
422 const BTexture
&texture
) {
423 if (texture
.texture() & BTexture::Parent_Relative
) return ParentRelative
;
425 Pixmap pixmap
= searchCache(width
, height
, texture
.texture(),
426 texture
.color(), texture
.colorTo());
427 if (pixmap
) return pixmap
;
429 BImage
image(this, width
, height
);
430 pixmap
= image
.render(texture
);
441 tmp
.texture
= texture
.texture();
442 tmp
.pixel1
= texture
.color().pixel();
444 if (texture
.texture() & BTexture::Gradient
)
445 tmp
.pixel2
= texture
.colorTo().pixel();
449 cache
.push_back(tmp
);
451 if (cache
.size() > cache_max
) {
453 fprintf(stderr
, i18n(ImageSet
, ImagePixmapCacheLarge
,
454 "BImageControl::renderImage: cache is large, "
455 "forcing cleanout\n"));
465 void BImageControl::removeImage(Pixmap pixmap
) {
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)
484 void BImageControl::getColorTables(unsigned char **rmt
, unsigned char **gmt
,
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
;
492 if (roff
) *roff
= red_offset
;
493 if (goff
) *goff
= green_offset
;
494 if (boff
) *boff
= blue_offset
;
496 if (rbit
) *rbit
= red_bits
;
497 if (gbit
) *gbit
= green_bits
;
498 if (bbit
) *bbit
= blue_bits
;
502 void BImageControl::getXColorTable(XColor
**c
, int *n
) {
508 void BImageControl::getGradientBuffers(unsigned int w
,
513 if (w
> grad_buffer_width
) {
515 delete [] grad_xbuffer
;
517 grad_buffer_width
= w
;
519 grad_xbuffer
= new unsigned int[grad_buffer_width
* 3];
522 if (h
> grad_buffer_height
) {
524 delete [] grad_ybuffer
;
526 grad_buffer_height
= h
;
528 grad_ybuffer
= new unsigned int[grad_buffer_height
* 3];
531 *xbuf
= grad_xbuffer
;
532 *ybuf
= grad_ybuffer
;
536 void BImageControl::installRootColormap(void) {
539 XListInstalledColormaps(basedisplay
->getXDisplay(), window
, &ncmap
);
543 for (int i
= 0; i
< ncmap
; i
++)
544 if (*(cmaps
+ i
) == colormap
)
548 XInstallColormap(basedisplay
->getXDisplay(), colormap
);
555 void BImageControl::setColorsPerChannel(int cpc
) {
556 if (cpc
< 2) cpc
= 2;
557 if (cpc
> 6) cpc
= 6;
559 colors_per_channel
= cpc
;
563 unsigned long BImageControl::getSqrt(unsigned int x
) {
565 // build sqrt table for use with elliptic gradient
567 sqrt_table
= new unsigned long[(256 * 256 * 2) + 1];
569 for (int i
= 0; i
< (256 * 256 * 2); i
++)
570 *(sqrt_table
+ i
) = bsqrt(i
);
573 return (*(sqrt_table
+ x
));
577 struct ZeroRefCheck
{
578 inline bool operator()(const BImageControl::CachedImage
&image
) const {
579 return (image
.count
== 0);
583 struct CacheCleaner
{
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
);
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
);