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
);