summaryrefslogtreecommitdiff
path: root/otk/imagecontrol.cc
diff options
context:
space:
mode:
Diffstat (limited to 'otk/imagecontrol.cc')
-rw-r--r--otk/imagecontrol.cc561
1 files changed, 561 insertions, 0 deletions
diff --git a/otk/imagecontrol.cc b/otk/imagecontrol.cc
new file mode 100644
index 00000000..7d091bb8
--- /dev/null
+++ b/otk/imagecontrol.cc
@@ -0,0 +1,561 @@
+// -*- mode: C++; indent-tabs-mode: nil; -*-
+
+#ifdef HAVE_CONFIG_H
+# include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif // HAVE_STDIO_H
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif // HAVE_CTYPE_H
+
+#include <X11/Xlib.h>
+}
+
+#include <algorithm>
+
+#include "blackbox.hh"
+#include "basedisplay.hh"
+#include "color.hh"
+#include "image.hh"
+#include "texture.hh"
+
+static unsigned long bsqrt(unsigned long x) {
+ if (x <= 0) return 0;
+ if (x == 1) return 1;
+
+ unsigned long r = x >> 1;
+ unsigned long q;
+
+ while (1) {
+ q = x / r;
+ if (q >= r) return r;
+ r = (r + q) >> 1;
+ }
+}
+
+BImageControl *ctrl = 0;
+
+BImageControl::BImageControl(BaseDisplay *dpy, const ScreenInfo *scrn,
+ bool _dither, int _cpc,
+ unsigned long cache_timeout,
+ unsigned long cmax) {
+ if (! ctrl) ctrl = this;
+
+ basedisplay = dpy;
+ screeninfo = scrn;
+ setDither(_dither);
+ setColorsPerChannel(_cpc);
+
+ cache_max = cmax;
+ if (cache_timeout) {
+ timer = new BTimer(basedisplay, this);
+ timer->setTimeout(cache_timeout);
+ timer->start();
+ } else {
+ timer = (BTimer *) 0;
+ }
+
+ colors = (XColor *) 0;
+ ncolors = 0;
+
+ grad_xbuffer = grad_ybuffer = (unsigned int *) 0;
+ grad_buffer_width = grad_buffer_height = 0;
+
+ sqrt_table = (unsigned long *) 0;
+
+ screen_depth = screeninfo->getDepth();
+ window = screeninfo->getRootWindow();
+ screen_number = screeninfo->getScreenNumber();
+ colormap = screeninfo->getColormap();
+
+ int count;
+ XPixmapFormatValues *pmv = XListPixmapFormats(basedisplay->getXDisplay(),
+ &count);
+ if (pmv) {
+ bits_per_pixel = 0;
+ for (int i = 0; i < count; i++)
+ if (pmv[i].depth == screen_depth) {
+ bits_per_pixel = pmv[i].bits_per_pixel;
+ break;
+ }
+
+ XFree(pmv);
+ }
+
+ if (bits_per_pixel == 0) bits_per_pixel = screen_depth;
+ if (bits_per_pixel >= 24) setDither(False);
+
+ red_offset = green_offset = blue_offset = 0;
+
+ switch (getVisual()->c_class) {
+ case TrueColor: {
+ int i;
+
+ // compute color tables
+ unsigned long red_mask = getVisual()->red_mask,
+ green_mask = getVisual()->green_mask,
+ blue_mask = getVisual()->blue_mask;
+
+ while (! (red_mask & 1)) { red_offset++; red_mask >>= 1; }
+ while (! (green_mask & 1)) { green_offset++; green_mask >>= 1; }
+ while (! (blue_mask & 1)) { blue_offset++; blue_mask >>= 1; }
+
+ red_bits = 255 / red_mask;
+ green_bits = 255 / green_mask;
+ blue_bits = 255 / blue_mask;
+
+ for (i = 0; i < 256; i++) {
+ red_color_table[i] = i / red_bits;
+ green_color_table[i] = i / green_bits;
+ blue_color_table[i] = i / blue_bits;
+ }
+ break;
+ }
+
+ case PseudoColor:
+ case StaticColor: {
+ ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
+
+ if (ncolors > (1 << screen_depth)) {
+ colors_per_channel = (1 << screen_depth) / 3;
+ ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
+ }
+
+ if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
+ fprintf(stderr,
+ "BImageControl::BImageControl: invalid colormap size %d "
+ "(%d/%d/%d) - reducing",
+ ncolors, colors_per_channel, colors_per_channel,
+ colors_per_channel);
+
+ colors_per_channel = (1 << screen_depth) / 3;
+ }
+
+ colors = new XColor[ncolors];
+ if (! colors) {
+ fprintf(stderr, "BImageControl::BImageControl: error allocating "
+ "colormap\n");
+ exit(1);
+ }
+
+ int i = 0, ii, p, r, g, b,
+
+#ifdef ORDEREDPSEUDO
+ bits = 256 / colors_per_channel;
+#else // !ORDEREDPSEUDO
+ bits = 255 / (colors_per_channel - 1);
+#endif // ORDEREDPSEUDO
+
+ red_bits = green_bits = blue_bits = bits;
+
+ for (i = 0; i < 256; i++)
+ red_color_table[i] = green_color_table[i] = blue_color_table[i] =
+ i / bits;
+
+ for (r = 0, i = 0; r < colors_per_channel; r++)
+ for (g = 0; g < colors_per_channel; g++)
+ for (b = 0; b < colors_per_channel; b++, i++) {
+ colors[i].red = (r * 0xffff) / (colors_per_channel - 1);
+ colors[i].green = (g * 0xffff) / (colors_per_channel - 1);
+ colors[i].blue = (b * 0xffff) / (colors_per_channel - 1);;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+
+ for (i = 0; i < ncolors; i++) {
+ if (! XAllocColor(basedisplay->getXDisplay(), colormap, &colors[i])) {
+ fprintf(stderr, "couldn't alloc color %i %i %i\n",
+ colors[i].red, colors[i].green, colors[i].blue);
+ colors[i].flags = 0;
+ } else {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ }
+
+ XColor icolors[256];
+ int incolors = (((1 << screen_depth) > 256) ? 256 : (1 << screen_depth));
+
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+
+ XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
+ for (i = 0; i < ncolors; i++) {
+ if (! colors[i].flags) {
+ unsigned long chk = 0xffffffff, pixel, close = 0;
+
+ p = 2;
+ while (p--) {
+ for (ii = 0; ii < incolors; ii++) {
+ r = (colors[i].red - icolors[i].red) >> 8;
+ g = (colors[i].green - icolors[i].green) >> 8;
+ b = (colors[i].blue - icolors[i].blue) >> 8;
+ pixel = (r * r) + (g * g) + (b * b);
+
+ if (pixel < chk) {
+ chk = pixel;
+ close = ii;
+ }
+
+ colors[i].red = icolors[close].red;
+ colors[i].green = icolors[close].green;
+ colors[i].blue = icolors[close].blue;
+
+ if (XAllocColor(basedisplay->getXDisplay(), colormap,
+ &colors[i])) {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case GrayScale:
+ case StaticGray: {
+ if (getVisual()->c_class == StaticGray) {
+ ncolors = 1 << screen_depth;
+ } else {
+ ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
+
+ if (ncolors > (1 << screen_depth)) {
+ colors_per_channel = (1 << screen_depth) / 3;
+ ncolors =
+ colors_per_channel * colors_per_channel * colors_per_channel;
+ }
+ }
+
+ if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
+ fprintf(stderr,
+ "BImageControl::BImageControl: invalid colormap size %d "
+ "(%d/%d/%d) - reducing",
+ ncolors, colors_per_channel, colors_per_channel,
+ colors_per_channel);
+
+ colors_per_channel = (1 << screen_depth) / 3;
+ }
+
+ colors = new XColor[ncolors];
+ if (! colors) {
+ fprintf(stderr,
+ "BImageControl::BImageControl: error allocating colormap\n");
+ exit(1);
+ }
+
+ int i = 0, ii, p, bits = 255 / (colors_per_channel - 1);
+ red_bits = green_bits = blue_bits = bits;
+
+ for (i = 0; i < 256; i++)
+ red_color_table[i] = green_color_table[i] = blue_color_table[i] =
+ i / bits;
+
+ for (i = 0; i < ncolors; i++) {
+ colors[i].red = (i * 0xffff) / (colors_per_channel - 1);
+ colors[i].green = (i * 0xffff) / (colors_per_channel - 1);
+ colors[i].blue = (i * 0xffff) / (colors_per_channel - 1);;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+
+ if (! XAllocColor(basedisplay->getXDisplay(), colormap,
+ &colors[i])) {
+ fprintf(stderr, "couldn't alloc color %i %i %i\n",
+ colors[i].red, colors[i].green, colors[i].blue);
+ colors[i].flags = 0;
+ } else {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ }
+
+ XColor icolors[256];
+ int incolors = (((1 << screen_depth) > 256) ? 256 :
+ (1 << screen_depth));
+
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+
+ XQueryColors(basedisplay->getXDisplay(), colormap, icolors, incolors);
+ for (i = 0; i < ncolors; i++) {
+ if (! colors[i].flags) {
+ unsigned long chk = 0xffffffff, pixel, close = 0;
+
+ p = 2;
+ while (p--) {
+ for (ii = 0; ii < incolors; ii++) {
+ int r = (colors[i].red - icolors[i].red) >> 8;
+ int g = (colors[i].green - icolors[i].green) >> 8;
+ int b = (colors[i].blue - icolors[i].blue) >> 8;
+ pixel = (r * r) + (g * g) + (b * b);
+
+ if (pixel < chk) {
+ chk = pixel;
+ close = ii;
+ }
+
+ colors[i].red = icolors[close].red;
+ colors[i].green = icolors[close].green;
+ colors[i].blue = icolors[close].blue;
+
+ if (XAllocColor(basedisplay->getXDisplay(), colormap,
+ &colors[i])) {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ fprintf(stderr, "BImageControl::BImageControl: unsupported visual %d\n",
+ getVisual()->c_class);
+ exit(1);
+ }
+}
+
+
+BImageControl::~BImageControl(void) {
+ delete [] sqrt_table;
+
+ delete [] grad_xbuffer;
+
+ delete [] grad_ybuffer;
+
+ if (colors) {
+ unsigned long *pixels = new unsigned long [ncolors];
+
+ for (int i = 0; i < ncolors; i++)
+ *(pixels + i) = (*(colors + i)).pixel;
+
+ XFreeColors(basedisplay->getXDisplay(), colormap, pixels, ncolors, 0);
+
+ delete [] colors;
+ }
+
+ if (! cache.empty()) {
+ //#ifdef DEBUG
+ fprintf(stderr, "BImageContol::~BImageControl: pixmap cache - "
+ "releasing %d pixmaps\n", cache.size());
+ //#endif
+ CacheContainer::iterator it = cache.begin();
+ const CacheContainer::iterator end = cache.end();
+ for (; it != end; ++it)
+ XFreePixmap(basedisplay->getXDisplay(), it->pixmap);
+ }
+ if (timer) {
+ timer->stop();
+ delete timer;
+ }
+}
+
+
+Pixmap BImageControl::searchCache(const unsigned int width,
+ const unsigned int height,
+ const unsigned long texture,
+ const BColor &c1, const BColor &c2) {
+ if (cache.empty())
+ return None;
+
+ CacheContainer::iterator it = cache.begin();
+ const CacheContainer::iterator end = cache.end();
+ for (; it != end; ++it) {
+ CachedImage& tmp = *it;
+ if (tmp.width == width && tmp.height == height &&
+ tmp.texture == texture && tmp.pixel1 == c1.pixel())
+ if (texture & BTexture::Gradient) {
+ if (tmp.pixel2 == c2.pixel()) {
+ tmp.count++;
+ return tmp.pixmap;
+ }
+ } else {
+ tmp.count++;
+ return tmp.pixmap;
+ }
+ }
+ return None;
+}
+
+
+Pixmap BImageControl::renderImage(unsigned int width, unsigned int height,
+ const BTexture &texture) {
+ if (texture.texture() & BTexture::Parent_Relative) return ParentRelative;
+
+ Pixmap pixmap = searchCache(width, height, texture.texture(),
+ texture.color(), texture.colorTo());
+ if (pixmap) return pixmap;
+
+ BImage image(this, width, height);
+ pixmap = image.render(texture);
+
+ if (! pixmap)
+ return None;
+
+ CachedImage tmp;
+
+ tmp.pixmap = pixmap;
+ tmp.width = width;
+ tmp.height = height;
+ tmp.count = 1;
+ tmp.texture = texture.texture();
+ tmp.pixel1 = texture.color().pixel();
+
+ if (texture.texture() & BTexture::Gradient)
+ tmp.pixel2 = texture.colorTo().pixel();
+ else
+ tmp.pixel2 = 0l;
+
+ cache.push_back(tmp);
+
+ if (cache.size() > cache_max) {
+#ifdef DEBUG
+ fprintf(stderr, "BImageControl::renderImage: cache is large, "
+ "forcing cleanout\n");
+#endif // DEBUG
+
+ timeout();
+ }
+
+ return pixmap;
+}
+
+
+void BImageControl::removeImage(Pixmap pixmap) {
+ if (! pixmap)
+ return;
+
+ CacheContainer::iterator it = cache.begin();
+ const CacheContainer::iterator end = cache.end();
+ for (; it != end; ++it) {
+ CachedImage &tmp = *it;
+ if (tmp.pixmap == pixmap && tmp.count > 0)
+ tmp.count--;
+ }
+
+ if (! timer)
+ timeout();
+}
+
+
+void BImageControl::getColorTables(unsigned char **rmt, unsigned char **gmt,
+ unsigned char **bmt,
+ int *roff, int *goff, int *boff,
+ int *rbit, int *gbit, int *bbit) {
+ if (rmt) *rmt = red_color_table;
+ if (gmt) *gmt = green_color_table;
+ if (bmt) *bmt = blue_color_table;
+
+ if (roff) *roff = red_offset;
+ if (goff) *goff = green_offset;
+ if (boff) *boff = blue_offset;
+
+ if (rbit) *rbit = red_bits;
+ if (gbit) *gbit = green_bits;
+ if (bbit) *bbit = blue_bits;
+}
+
+
+void BImageControl::getXColorTable(XColor **c, int *n) {
+ if (c) *c = colors;
+ if (n) *n = ncolors;
+}
+
+
+void BImageControl::getGradientBuffers(unsigned int w,
+ unsigned int h,
+ unsigned int **xbuf,
+ unsigned int **ybuf)
+{
+ if (w > grad_buffer_width) {
+ if (grad_xbuffer)
+ delete [] grad_xbuffer;
+
+ grad_buffer_width = w;
+
+ grad_xbuffer = new unsigned int[grad_buffer_width * 3];
+ }
+
+ if (h > grad_buffer_height) {
+ if (grad_ybuffer)
+ delete [] grad_ybuffer;
+
+ grad_buffer_height = h;
+
+ grad_ybuffer = new unsigned int[grad_buffer_height * 3];
+ }
+
+ *xbuf = grad_xbuffer;
+ *ybuf = grad_ybuffer;
+}
+
+
+void BImageControl::installRootColormap(void) {
+ int ncmap = 0;
+ Colormap *cmaps =
+ XListInstalledColormaps(basedisplay->getXDisplay(), window, &ncmap);
+
+ if (cmaps) {
+ bool install = True;
+ for (int i = 0; i < ncmap; i++)
+ if (*(cmaps + i) == colormap)
+ install = False;
+
+ if (install)
+ XInstallColormap(basedisplay->getXDisplay(), colormap);
+
+ XFree(cmaps);
+ }
+}
+
+
+void BImageControl::setColorsPerChannel(int cpc) {
+ if (cpc < 2) cpc = 2;
+ if (cpc > 6) cpc = 6;
+
+ colors_per_channel = cpc;
+}
+
+
+unsigned long BImageControl::getSqrt(unsigned int x) {
+ if (! sqrt_table) {
+ // build sqrt table for use with elliptic gradient
+
+ sqrt_table = new unsigned long[(256 * 256 * 2) + 1];
+
+ for (int i = 0; i < (256 * 256 * 2); i++)
+ *(sqrt_table + i) = bsqrt(i);
+ }
+
+ return (*(sqrt_table + x));
+}
+
+
+struct ZeroRefCheck {
+ inline bool operator()(const BImageControl::CachedImage &image) const {
+ return (image.count == 0);
+ }
+};
+
+struct CacheCleaner {
+ Display *display;
+ ZeroRefCheck ref_check;
+ CacheCleaner(Display *d): display(d) {}
+ inline void operator()(const BImageControl::CachedImage& image) const {
+ if (ref_check(image))
+ XFreePixmap(display, image.pixmap);
+ }
+};
+
+
+void BImageControl::timeout(void) {
+ CacheCleaner cleaner(basedisplay->getXDisplay());
+ std::for_each(cache.begin(), cache.end(), cleaner);
+ cache.remove_if(cleaner.ref_check);
+}
+