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"
43 #include "basedisplay.hh"
48 static unsigned long bsqrt(unsigned long x
) {
52 unsigned long r
= x
>> 1;
62 BImageControl
*ctrl
= 0;
64 BImageControl::BImageControl(BaseDisplay
*dpy
, const ScreenInfo
*scrn
,
65 bool _dither
, int _cpc
,
66 unsigned long cache_timeout
,
68 if (! ctrl
) ctrl
= this;
73 setColorsPerChannel(_cpc
);
78 timer
= new BTimer(basedisplay
, this);
79 timer
->setTimeout(cache_timeout
);
86 colors
= (XColor
*) 0;
89 grad_xbuffer
= grad_ybuffer
= (unsigned int *) 0;
90 grad_buffer_width
= grad_buffer_height
= 0;
92 sqrt_table
= (unsigned long *) 0;
94 screen_depth
= screeninfo
->getDepth();
95 window
= screeninfo
->getRootWindow();
96 screen_number
= screeninfo
->getScreenNumber();
97 colormap
= screeninfo
->getColormap();
100 XPixmapFormatValues
*pmv
= XListPixmapFormats(basedisplay
->getXDisplay(),
104 for (int i
= 0; i
< count
; i
++)
105 if (pmv
[i
].depth
== screen_depth
) {
106 bits_per_pixel
= pmv
[i
].bits_per_pixel
;
113 if (bits_per_pixel
== 0) bits_per_pixel
= screen_depth
;
114 if (bits_per_pixel
>= 24) setDither(False
);
116 red_offset
= green_offset
= blue_offset
= 0;
118 switch (getVisual()->c_class
) {
122 // compute color tables
123 unsigned long red_mask
= getVisual()->red_mask
,
124 green_mask
= getVisual()->green_mask
,
125 blue_mask
= getVisual()->blue_mask
;
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; }
131 red_bits
= 255 / red_mask
;
132 green_bits
= 255 / green_mask
;
133 blue_bits
= 255 / blue_mask
;
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
;
145 ncolors
= colors_per_channel
* colors_per_channel
* colors_per_channel
;
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
;
152 if (colors_per_channel
< 2 || ncolors
> (1 << screen_depth
)) {
154 i18n(ImageSet
, ImageInvalidColormapSize
,
155 "BImageControl::BImageControl: invalid colormap size %d "
156 "(%d/%d/%d) - reducing"),
157 ncolors
, colors_per_channel
, colors_per_channel
,
160 colors_per_channel
= (1 << screen_depth
) / 3;
163 colors
= new XColor
[ncolors
];
165 fprintf(stderr
, i18n(ImageSet
, ImageErrorAllocatingColormap
,
166 "BImageControl::BImageControl: error allocating "
171 int i
= 0, ii
, p
, r
, g
, b
,
174 bits
= 256 / colors_per_channel
;
175 #else // !ORDEREDPSEUDO
176 bits
= 255 / (colors_per_channel
- 1);
177 #endif // ORDEREDPSEUDO
179 red_bits
= green_bits
= blue_bits
= bits
;
181 for (i
= 0; i
< 256; i
++)
182 red_color_table
[i
] = green_color_table
[i
] = blue_color_table
[i
] =
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
;
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
);
201 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
206 int incolors
= (((1 << screen_depth
) > 256) ? 256 : (1 << screen_depth
));
208 for (i
= 0; i
< incolors
; i
++)
209 icolors
[i
].pixel
= i
;
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;
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
);
229 colors
[i
].red
= icolors
[close
].red
;
230 colors
[i
].green
= icolors
[close
].green
;
231 colors
[i
].blue
= icolors
[close
].blue
;
233 if (XAllocColor(basedisplay
->getXDisplay(), colormap
,
235 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
248 if (getVisual()->c_class
== StaticGray
) {
249 ncolors
= 1 << screen_depth
;
251 ncolors
= colors_per_channel
* colors_per_channel
* colors_per_channel
;
253 if (ncolors
> (1 << screen_depth
)) {
254 colors_per_channel
= (1 << screen_depth
) / 3;
256 colors_per_channel
* colors_per_channel
* colors_per_channel
;
260 if (colors_per_channel
< 2 || ncolors
> (1 << screen_depth
)) {
262 i18n(ImageSet
, ImageInvalidColormapSize
,
263 "BImageControl::BImageControl: invalid colormap size %d "
264 "(%d/%d/%d) - reducing"),
265 ncolors
, colors_per_channel
, colors_per_channel
,
268 colors_per_channel
= (1 << screen_depth
) / 3;
271 colors
= new XColor
[ncolors
];
274 i18n(ImageSet
, ImageErrorAllocatingColormap
,
275 "BImageControl::BImageControl: error allocating colormap\n"));
279 int i
= 0, ii
, p
, bits
= 255 / (colors_per_channel
- 1);
280 red_bits
= green_bits
= blue_bits
= bits
;
282 for (i
= 0; i
< 256; i
++)
283 red_color_table
[i
] = green_color_table
[i
] = blue_color_table
[i
] =
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
;
292 if (! XAllocColor(basedisplay
->getXDisplay(), colormap
,
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
);
299 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
304 int incolors
= (((1 << screen_depth
) > 256) ? 256 :
305 (1 << screen_depth
));
307 for (i
= 0; i
< incolors
; i
++)
308 icolors
[i
].pixel
= i
;
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;
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
);
328 colors
[i
].red
= icolors
[close
].red
;
329 colors
[i
].green
= icolors
[close
].green
;
330 colors
[i
].blue
= icolors
[close
].blue
;
332 if (XAllocColor(basedisplay
->getXDisplay(), colormap
,
334 colors
[i
].flags
= DoRed
|DoGreen
|DoBlue
;
347 i18n(ImageSet
, ImageUnsupVisual
,
348 "BImageControl::BImageControl: unsupported visual %d\n"),
349 getVisual()->c_class
);
355 BImageControl::~BImageControl(void) {
356 delete [] sqrt_table
;
358 delete [] grad_xbuffer
;
360 delete [] grad_ybuffer
;
363 unsigned long *pixels
= new unsigned long [ncolors
];
365 for (int i
= 0; i
< ncolors
; i
++)
366 *(pixels
+ i
) = (*(colors
+ i
)).pixel
;
368 XFreeColors(basedisplay
->getXDisplay(), colormap
, pixels
, ncolors
, 0);
373 if (! cache
.empty()) {
375 fprintf(stderr
, i18n(ImageSet
, ImagePixmapRelease
,
376 "BImageContol::~BImageControl: pixmap cache - "
377 "releasing %d pixmaps\n"), cache
.size());
379 CacheContainer::iterator it
= cache
.begin();
380 const CacheContainer::iterator end
= cache
.end();
381 for (; it
!= end
; ++it
)
382 XFreePixmap(basedisplay
->getXDisplay(), it
->pixmap
);
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
) {
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()) {
420 Pixmap
BImageControl::renderImage(unsigned int width
, unsigned int height
,
421 const BTexture
&texture
) {
422 if (texture
.texture() & BTexture::Parent_Relative
) return ParentRelative
;
424 Pixmap pixmap
= searchCache(width
, height
, texture
.texture(),
425 texture
.color(), texture
.colorTo());
426 if (pixmap
) return pixmap
;
428 BImage
image(this, width
, height
);
429 pixmap
= image
.render(texture
);
440 tmp
.texture
= texture
.texture();
441 tmp
.pixel1
= texture
.color().pixel();
443 if (texture
.texture() & BTexture::Gradient
)
444 tmp
.pixel2
= texture
.colorTo().pixel();
448 cache
.push_back(tmp
);
450 if (cache
.size() > cache_max
) {
452 fprintf(stderr
, i18n(ImageSet
, ImagePixmapCacheLarge
,
453 "BImageControl::renderImage: cache is large, "
454 "forcing cleanout\n"));
464 void BImageControl::removeImage(Pixmap pixmap
) {
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)
483 void BImageControl::getColorTables(unsigned char **rmt
, unsigned char **gmt
,
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
;
491 if (roff
) *roff
= red_offset
;
492 if (goff
) *goff
= green_offset
;
493 if (boff
) *boff
= blue_offset
;
495 if (rbit
) *rbit
= red_bits
;
496 if (gbit
) *gbit
= green_bits
;
497 if (bbit
) *bbit
= blue_bits
;
501 void BImageControl::getXColorTable(XColor
**c
, int *n
) {
507 void BImageControl::getGradientBuffers(unsigned int w
,
512 if (w
> grad_buffer_width
) {
514 delete [] grad_xbuffer
;
516 grad_buffer_width
= w
;
518 grad_xbuffer
= new unsigned int[grad_buffer_width
* 3];
521 if (h
> grad_buffer_height
) {
523 delete [] grad_ybuffer
;
525 grad_buffer_height
= h
;
527 grad_ybuffer
= new unsigned int[grad_buffer_height
* 3];
530 *xbuf
= grad_xbuffer
;
531 *ybuf
= grad_ybuffer
;
535 void BImageControl::installRootColormap(void) {
538 XListInstalledColormaps(basedisplay
->getXDisplay(), window
, &ncmap
);
542 for (int i
= 0; i
< ncmap
; i
++)
543 if (*(cmaps
+ i
) == colormap
)
547 XInstallColormap(basedisplay
->getXDisplay(), colormap
);
554 void BImageControl::setColorsPerChannel(int cpc
) {
555 if (cpc
< 2) cpc
= 2;
556 if (cpc
> 6) cpc
= 6;
558 colors_per_channel
= cpc
;
562 unsigned long BImageControl::getSqrt(unsigned int x
) {
564 // build sqrt table for use with elliptic gradient
566 sqrt_table
= new unsigned long[(256 * 256 * 2) + 1];
568 for (int i
= 0; i
< (256 * 256 * 2); i
++)
569 *(sqrt_table
+ i
) = bsqrt(i
);
572 return (*(sqrt_table
+ x
));
576 struct ZeroRefCheck
{
577 inline bool operator()(const BImageControl::CachedImage
&image
) const {
578 return (image
.count
== 0);
582 struct CacheCleaner
{
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
);
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
);