00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "ImageControl.hh"
00028
00029 #include "TextureRender.hh"
00030 #include "App.hh"
00031 #include "SimpleCommand.hh"
00032
00033
00034 #ifndef _GNU_SOURCE
00035 #define _GNU_SOURCE
00036 #endif // _GNU_SOURCE
00037
00038
00039 #ifdef HAVE_CONFIG_H
00040 #include "config.h"
00041 #endif // HAVE_CONFIG_H
00042
00043 #ifdef HAVE_SYS_TYPES_H
00044 #include <sys/types.h>
00045 #endif // HAVE_SYS_TYPES_H
00046
00047 #include <cstdlib>
00048 #include <cstring>
00049 #include <cstdio>
00050
00051 #ifdef HAVE_CTYPE_H
00052 #include <ctype.h>
00053 #endif // HAVE_CTYPE_H
00054
00055 #include <iostream>
00056
00057 using namespace std;
00058
00059 namespace FbTk {
00060
00061
00062 unsigned long *ImageControl::sqrt_table = 0;
00063 #ifdef TIMEDCACHE
00064 bool ImageControl::s_timed_cache = true;
00065 #else
00066 bool ImageControl::s_timed_cache = false;
00067 #endif // TIMEDCACHE
00068
00069 namespace {
00070
00071 inline unsigned long bsqrt(unsigned long x) {
00072 if (x <= 0) return 0;
00073 if (x == 1) return 1;
00074
00075 unsigned long r = x >> 1;
00076 unsigned long q;
00077
00078 while (1) {
00079 q = x / r;
00080 if (q >= r) return r;
00081 r = (r + q) >> 1;
00082 }
00083 }
00084
00085 };
00086
00087 ImageControl::ImageControl(int screen_num, bool dither,
00088 int cpc, unsigned long cache_timeout, unsigned long cmax):
00089 m_dither(dither),
00090 m_colors(0),
00091 m_num_colors(0),
00092 m_colors_per_channel(cpc) {
00093
00094 Display *disp = FbTk::App::instance()->display();
00095
00096 m_screen_depth = DefaultDepth(disp, screen_num);
00097 m_screen_num = screen_num;
00098 m_root_window = RootWindow(disp, screen_num);
00099 m_visual = DefaultVisual(disp, screen_num);
00100 m_colormap = DefaultColormap(disp, screen_num);
00101
00102 cache_max = cmax;
00103
00104 if (cache_timeout && s_timed_cache) {
00105 m_timer.setTimeout(cache_timeout);
00106 RefCount<Command> clean_cache(new SimpleCommand<ImageControl>(*this, &ImageControl::cleanCache));
00107 m_timer.setCommand(clean_cache);
00108 m_timer.start();
00109 }
00110
00111 createColorTable();
00112 }
00113
00114
00115 ImageControl::~ImageControl() {
00116 if (sqrt_table) {
00117 delete [] sqrt_table;
00118 }
00119
00120 if (grad_xbuffer) {
00121 delete [] grad_xbuffer;
00122 }
00123
00124 if (grad_ybuffer) {
00125 delete [] grad_ybuffer;
00126 }
00127
00128 if (m_colors) {
00129 unsigned long *pixels = new unsigned long [m_num_colors];
00130
00131 for (unsigned int color = 0; color < m_num_colors; color++)
00132 *(pixels + color) = (*(m_colors + color)).pixel;
00133
00134 XFreeColors(FbTk::App::instance()->display(), m_colormap, pixels, m_num_colors, 0);
00135
00136 delete [] m_colors;
00137 }
00138
00139 if (cache.size() > 0) {
00140 CacheList::iterator it = cache.begin();
00141 CacheList::iterator it_end = cache.end();
00142 Display *disp = FbTk::App::instance()->display();
00143 for (; it != it_end; ++it) {
00144 XFreePixmap(disp, (*it)->pixmap);
00145 delete (*it);
00146 }
00147
00148 }
00149
00150 }
00151
00152
00153 Pixmap ImageControl::searchCache(unsigned int width, unsigned int height,
00154 const Texture &text) const {
00155
00156 if (text.pixmap().drawable() != None) {
00157
00158 CacheList::iterator it = cache.begin();
00159 CacheList::iterator it_end = cache.end();
00160 for (; it != it_end; ++it) {
00161 if ((*it)->texture_pixmap == text.pixmap().drawable() &&
00162 (*it)->width == width && (*it)->height == height &&
00163 (*it)->texture == text.type()) {
00164 (*it)->count++;
00165 return (*it)->pixmap;
00166 }
00167 }
00168 return None;
00169 }
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179 CacheList::iterator it = cache.begin();
00180 CacheList::iterator it_end = cache.end();
00181 for (; it != it_end; ++it) {
00182 if (((*it)->width == width) &&
00183 ((*it)->height == height) &&
00184 ((*it)->texture == text.type()) &&
00185 ((*it)->pixel1 == text.color().pixel())) {
00186 if (text.type() & FbTk::Texture::GRADIENT) {
00187 if ((*it)->pixel2 == text.colorTo().pixel()) {
00188 (*it)->count++;
00189 return (*it)->pixmap;
00190 }
00191 } else {
00192 (*it)->count++;
00193 return (*it)->pixmap;
00194 }
00195 }
00196 }
00197
00198 return None;
00199
00200 }
00201
00202
00203 Pixmap ImageControl::renderImage(unsigned int width, unsigned int height,
00204 const FbTk::Texture &texture) {
00205
00206 if (texture.type() & FbTk::Texture::PARENTRELATIVE)
00207 return ParentRelative;
00208
00209
00210 Pixmap pixmap = searchCache(width, height, texture);
00211 if (pixmap) {
00212 return pixmap;
00213 }
00214
00215
00216 TextureRender image(*this, width, height, m_colors, m_num_colors);
00217 pixmap = image.render(texture);
00218
00219 if (pixmap) {
00220
00221
00222 Cache *tmp = new Cache;
00223
00224 tmp->pixmap = pixmap;
00225 tmp->texture_pixmap = texture.pixmap().drawable();
00226 tmp->width = width;
00227 tmp->height = height;
00228 tmp->count = 1;
00229 tmp->texture = texture.type();
00230 tmp->pixel1 = texture.color().pixel();
00231
00232 if (texture.type() & FbTk::Texture::GRADIENT)
00233 tmp->pixel2 = texture.colorTo().pixel();
00234 else
00235 tmp->pixel2 = 0l;
00236
00237 cache.push_back(tmp);
00238
00239 if ((unsigned) cache.size() > cache_max)
00240 cleanCache();
00241
00242 return pixmap;
00243 }
00244
00245 return None;
00246 }
00247
00248
00249 void ImageControl::removeImage(Pixmap pixmap) {
00250 if (!pixmap)
00251 return;
00252
00253 CacheList::iterator it = cache.begin();
00254 CacheList::iterator it_end = cache.end();
00255 for (; it != it_end; ++it) {
00256 if ((*it)->pixmap == pixmap) {
00257 if ((*it)->count) {
00258 (*it)->count--;
00259 if (s_timed_cache) {
00260 cleanCache();
00261 return;
00262 }
00263 }
00264
00265 if ((*it)->count <= 0)
00266 cleanCache();
00267
00268 return;
00269 }
00270 }
00271 }
00272
00273
00274 void ImageControl::colorTables(const unsigned char **rmt, const unsigned char **gmt,
00275 const unsigned char **bmt,
00276 int *roff, int *goff, int *boff,
00277 int *rbit, int *gbit, int *bbit) const {
00278
00279 if (rmt) *rmt = red_color_table;
00280 if (gmt) *gmt = green_color_table;
00281 if (bmt) *bmt = blue_color_table;
00282
00283 if (roff) *roff = red_offset;
00284 if (goff) *goff = green_offset;
00285 if (boff) *boff = blue_offset;
00286
00287 if (rbit) *rbit = red_bits;
00288 if (gbit) *gbit = green_bits;
00289 if (bbit) *bbit = blue_bits;
00290 }
00291
00292
00293 void ImageControl::getXColorTable(XColor **c, int *n) {
00294 if (c) *c = m_colors;
00295 if (n) *n = m_num_colors;
00296 }
00297
00298
00299 void ImageControl::getGradientBuffers(unsigned int w,
00300 unsigned int h,
00301 unsigned int **xbuf,
00302 unsigned int **ybuf) {
00303
00304 if (w > grad_buffer_width) {
00305 if (grad_xbuffer) {
00306 delete [] grad_xbuffer;
00307 }
00308
00309 grad_buffer_width = w;
00310
00311 grad_xbuffer = new unsigned int[grad_buffer_width * 3];
00312 }
00313
00314 if (h > grad_buffer_height) {
00315 if (grad_ybuffer) {
00316 delete [] grad_ybuffer;
00317 }
00318
00319 grad_buffer_height = h;
00320
00321 grad_ybuffer = new unsigned int[grad_buffer_height * 3];
00322 }
00323
00324 *xbuf = grad_xbuffer;
00325 *ybuf = grad_ybuffer;
00326 }
00327
00328
00329 void ImageControl::installRootColormap() {
00330 XGrabServer(FbTk::App::instance()->display());
00331
00332
00333 Display *disp = FbTk::App::instance()->display();
00334 bool install = true;
00335 int i = 0, ncmap = 0;
00336 Colormap *cmaps =
00337 XListInstalledColormaps(disp, m_root_window, &ncmap);
00338
00339 if (cmaps) {
00340 for (i = 0; i < ncmap; i++) {
00341 if (*(cmaps + i) == m_colormap)
00342 install = false;
00343 }
00344
00345 if (install)
00346 XInstallColormap(disp, m_colormap);
00347
00348 XFree(cmaps);
00349 }
00350
00351 XUngrabServer(FbTk::App::instance()->display());
00352 }
00353
00354
00355 void ImageControl::setColorsPerChannel(int cpc) {
00356 if (cpc < 2) cpc = 2;
00357 if (cpc > 6) cpc = 6;
00358
00359 m_colors_per_channel = cpc;
00360 }
00361
00362
00363 unsigned long ImageControl::getSqrt(unsigned int x) const {
00364 if (! sqrt_table) {
00365
00366
00367 sqrt_table = new unsigned long[256 * 256 * 2 + 1];
00368 int i = 0;
00369
00370 for (; i < (256 * 256 * 2); i++)
00371 sqrt_table[i] = bsqrt(i);
00372 }
00373
00374 return sqrt_table[x];
00375 }
00376
00377 void ImageControl::cleanCache() {
00378 Display *disp = FbTk::App::instance()->display();
00379 std::list<CacheList::iterator> deadlist;
00380 CacheList::iterator it = cache.begin();
00381 CacheList::iterator it_end = cache.end();
00382 for (; it != it_end; ++it) {
00383 Cache *tmp = (*it);
00384 if (tmp->count <= 0) {
00385 XFreePixmap(disp, tmp->pixmap);
00386 deadlist.push_back(it);
00387 delete tmp;
00388 tmp=0;
00389 }
00390 }
00391
00392 std::list<CacheList::iterator>::iterator dead_it = deadlist.begin();
00393 std::list<CacheList::iterator>::iterator dead_it_end = deadlist.end();
00394 for (; dead_it != dead_it_end; ++dead_it) {
00395 cache.erase(*dead_it);
00396 }
00397
00398 }
00399
00400 void ImageControl::createColorTable() {
00401 Display *disp = FbTk::App::instance()->display();
00402
00403 grad_xbuffer = grad_ybuffer = (unsigned int *) 0;
00404 grad_buffer_width = grad_buffer_height = 0;
00405
00406 int count;
00407 XPixmapFormatValues *pmv = XListPixmapFormats(disp, &count);
00408
00409 if (pmv) {
00410 bits_per_pixel = 0;
00411 for (int i = 0; i < count; i++) {
00412 if (pmv[i].depth == m_screen_depth) {
00413 bits_per_pixel = pmv[i].bits_per_pixel;
00414 break;
00415 }
00416 }
00417
00418 XFree(pmv);
00419 }
00420
00421 if (bits_per_pixel == 0)
00422 bits_per_pixel = m_screen_depth;
00423 if (bits_per_pixel >= 24)
00424 setDither(false);
00425
00426 red_offset = green_offset = blue_offset = 0;
00427
00428 switch (visual()->c_class) {
00429 case TrueColor: {
00430 int i;
00431
00432
00433 unsigned long red_mask = visual()->red_mask,
00434 green_mask = visual()->green_mask,
00435 blue_mask = visual()->blue_mask;
00436
00437 while (! (red_mask & 1)) { red_offset++; red_mask >>= 1; }
00438 while (! (green_mask & 1)) { green_offset++; green_mask >>= 1; }
00439 while (! (blue_mask & 1)) { blue_offset++; blue_mask >>= 1; }
00440
00441 red_bits = 255 / red_mask;
00442 green_bits = 255 / green_mask;
00443 blue_bits = 255 / blue_mask;
00444
00445 for (i = 0; i < 256; i++) {
00446 red_color_table[i] = i / red_bits;
00447 green_color_table[i] = i / green_bits;
00448 blue_color_table[i] = i / blue_bits;
00449 }
00450 }
00451
00452 break;
00453
00454 case PseudoColor:
00455 case StaticColor: {
00456
00457 m_num_colors = m_colors_per_channel * m_colors_per_channel * m_colors_per_channel;
00458
00459 if (m_num_colors > static_cast<unsigned int>(1 << m_screen_depth)) {
00460 m_colors_per_channel = (1 << m_screen_depth) / 3;
00461 m_num_colors = m_colors_per_channel * m_colors_per_channel * m_colors_per_channel;
00462 }
00463
00464 if (m_colors_per_channel < 2 || m_num_colors > static_cast<unsigned int>(1 << m_screen_depth)) {
00465 fprintf(stderr, "ImageControl::ImageControl: invalid colormap size %d "
00466 "(%d/%d/%d) - reducing",
00467 m_num_colors, m_colors_per_channel, m_colors_per_channel,
00468 m_colors_per_channel);
00469
00470 m_colors_per_channel = (1 << m_screen_depth) / 3;
00471 }
00472
00473 m_colors = new XColor[m_num_colors];
00474
00475 int bits = 256 / m_colors_per_channel;
00476
00477 #ifndef ORDEREDPSEUDO
00478 bits = 255 / (m_colors_per_channel - 1);
00479 #endif // ORDEREDPSEUDO
00480
00481 red_bits = green_bits = blue_bits = bits;
00482
00483 for (unsigned int i = 0; i < 256; i++) {
00484 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
00485 i / bits;
00486 }
00487
00488 for (int r = 0, i = 0; r < m_colors_per_channel; r++) {
00489 for (int g = 0; g < m_colors_per_channel; g++) {
00490 for (int b = 0; b < m_colors_per_channel; b++, i++) {
00491 m_colors[i].red = (r * 0xffff) / (m_colors_per_channel - 1);
00492 m_colors[i].green = (g * 0xffff) / (m_colors_per_channel - 1);
00493 m_colors[i].blue = (b * 0xffff) / (m_colors_per_channel - 1);;
00494 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00495 }
00496 }
00497 }
00498
00499 for (unsigned int i = 0; i < m_num_colors; i++) {
00500 if (! XAllocColor(disp, m_colormap, &m_colors[i])) {
00501 fprintf(stderr, "couldn't alloc color %i %i %i\n",
00502 m_colors[i].red, m_colors[i].green, m_colors[i].blue);
00503 m_colors[i].flags = 0;
00504 } else
00505 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00506 }
00507
00508 XColor icolors[256];
00509 unsigned int incolors = (((1 << m_screen_depth) > 256) ? 256 : (1 << m_screen_depth));
00510
00511 for (unsigned int i = 0; i < incolors; i++)
00512 icolors[i].pixel = i;
00513
00514 XQueryColors(disp, m_colormap, icolors, incolors);
00515 for (unsigned int i = 0; i < m_num_colors; i++) {
00516 if (! m_colors[i].flags) {
00517 unsigned long chk = 0xffffffff, pixel, close = 0;
00518 char p = 2;
00519
00520 while (p--) {
00521 for (unsigned int ii = 0; ii < incolors; ii++) {
00522 int r = (m_colors[i].red - icolors[i].red) >> 8;
00523 int g = (m_colors[i].green - icolors[i].green) >> 8;
00524 int b = (m_colors[i].blue - icolors[i].blue) >> 8;
00525 pixel = (r * r) + (g * g) + (b * b);
00526
00527 if (pixel < chk) {
00528 chk = pixel;
00529 close = ii;
00530 }
00531
00532 m_colors[i].red = icolors[close].red;
00533 m_colors[i].green = icolors[close].green;
00534 m_colors[i].blue = icolors[close].blue;
00535
00536 if (XAllocColor(disp, m_colormap,
00537 &m_colors[i])) {
00538 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00539 break;
00540 }
00541 }
00542 }
00543 }
00544 }
00545
00546 break;
00547 }
00548
00549 case GrayScale:
00550 case StaticGray:
00551 {
00552
00553 if (visual()->c_class == StaticGray) {
00554 m_num_colors = 1 << m_screen_depth;
00555 } else {
00556 m_num_colors = m_colors_per_channel * m_colors_per_channel * m_colors_per_channel;
00557
00558 if (m_num_colors > static_cast<unsigned int>(1 << m_screen_depth)) {
00559 m_colors_per_channel = (1 << m_screen_depth) / 3;
00560 m_num_colors = m_colors_per_channel * m_colors_per_channel * m_colors_per_channel;
00561 }
00562 }
00563
00564 if (m_colors_per_channel < 2 || m_num_colors > static_cast<unsigned int>(1 << m_screen_depth)) {
00565 fprintf(stderr,"FbTk::ImageControl: invalid colormap size %d "
00566 "(%d/%d/%d) - reducing",
00567 m_num_colors, m_colors_per_channel, m_colors_per_channel,
00568 m_colors_per_channel);
00569
00570 m_colors_per_channel = (1 << m_screen_depth) / 3;
00571 }
00572
00573 m_colors = new XColor[m_num_colors];
00574
00575 int p, bits = 255 / (m_colors_per_channel - 1);
00576 red_bits = green_bits = blue_bits = bits;
00577
00578 for (unsigned int i = 0; i < 256; i++)
00579 red_color_table[i] = green_color_table[i] = blue_color_table[i] =
00580 i / bits;
00581
00582 for (unsigned int i = 0; i < m_num_colors; i++) {
00583 m_colors[i].red = (i * 0xffff) / (m_colors_per_channel - 1);
00584 m_colors[i].green = (i * 0xffff) / (m_colors_per_channel - 1);
00585 m_colors[i].blue = (i * 0xffff) / (m_colors_per_channel - 1);;
00586 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00587
00588 if (! XAllocColor(disp, m_colormap,
00589 &m_colors[i])) {
00590 fprintf(stderr, "Couldn't alloc color %i %i %i\n",
00591 m_colors[i].red, m_colors[i].green, m_colors[i].blue);
00592 m_colors[i].flags = 0;
00593 } else
00594 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00595 }
00596
00597
00598 XColor icolors[256];
00599 unsigned int incolors = (((1 << m_screen_depth) > 256) ? 256 :
00600 (1 << m_screen_depth));
00601
00602 for (unsigned int i = 0; i < incolors; i++)
00603 icolors[i].pixel = i;
00604
00605 XQueryColors(disp, m_colormap, icolors, incolors);
00606 for (unsigned int i = 0; i < m_num_colors; i++) {
00607 if (! m_colors[i].flags) {
00608 unsigned long chk = 0xffffffff, pixel, close = 0;
00609
00610 p = 2;
00611 while (p--) {
00612 for (unsigned int ii = 0; ii < incolors; ii++) {
00613 int r = (m_colors[i].red - icolors[i].red) >> 8;
00614 int g = (m_colors[i].green - icolors[i].green) >> 8;
00615 int b = (m_colors[i].blue - icolors[i].blue) >> 8;
00616 pixel = (r * r) + (g * g) + (b * b);
00617
00618 if (pixel < chk) {
00619 chk = pixel;
00620 close = ii;
00621 }
00622
00623 m_colors[i].red = icolors[close].red;
00624 m_colors[i].green = icolors[close].green;
00625 m_colors[i].blue = icolors[close].blue;
00626
00627 if (XAllocColor(disp, m_colormap, &m_colors[i])) {
00628 m_colors[i].flags = DoRed|DoGreen|DoBlue;
00629 break;
00630 }
00631 }
00632 }
00633 }
00634 }
00635
00636 break;
00637 }
00638
00639 default:
00640 cerr<<"FbTk::ImageControl: Unsupported visual"<<endl;
00641 break;
00642 }
00643 }
00644
00645 };