adding toolkit beginnings
authorMarius Nita <marius@cs.pdx.edu>
Fri, 1 Nov 2002 03:27:41 +0000 (03:27 +0000)
committerMarius Nita <marius@cs.pdx.edu>
Fri, 1 Nov 2002 03:27:41 +0000 (03:27 +0000)
16 files changed:
otk/Makefile.am [new file with mode: 0644]
otk/color.cc [new file with mode: 0644]
otk/color.hh [new file with mode: 0644]
otk/color.o [new file with mode: 0644]
otk/font.cc [new file with mode: 0644]
otk/font.hh [new file with mode: 0644]
otk/font.o [new file with mode: 0644]
otk/gccache.cc [new file with mode: 0644]
otk/gccache.hh [new file with mode: 0644]
otk/gccache.o [new file with mode: 0644]
otk/image.cc [new file with mode: 0644]
otk/image.hh [new file with mode: 0644]
otk/image.o [new file with mode: 0644]
otk/imagecontrol.cc [new file with mode: 0644]
otk/texture.cc [new file with mode: 0644]
otk/texture.hh [new file with mode: 0644]

diff --git a/otk/Makefile.am b/otk/Makefile.am
new file mode 100644 (file)
index 0000000..835874d
--- /dev/null
@@ -0,0 +1,35 @@
+DEFAULT_MENU=$(pkgdatadir)/menu
+DEFAULT_STYLE=$(pkgdatadir)/styles/mbdtex
+
+CXXFLAGS=`pkg-config --cflags xft` @CXXFLAGS@ \
+-DDEFAULTMENU=\"$(DEFAULT_MENU)\" \
+-DDEFAULTSTYLE=\"$(DEFAULT_STYLE)\" \
+-Dmultibyte\(\)=True
+
+INCLUDES= -I../src
+
+LDFLAGS=`pkg-config --libs xft`
+
+noinst_LIBRARIES=libotk.a
+
+libotk_a_SOURCES= color.cc font.cc gccache.cc image.cc imagecontrol.cc texture.cc
+
+MAINTAINERCLEANFILES= Makefile.in
+
+distclean-local:
+       rm -f *\~ *.orig *.rej .\#*
+
+# local dependencies
+color.o: color.cc color.hh ../src/basedisplay.hh ../src/timer.hh ../src/util.hh
+font.o: font.cc font.hh ../src/screen.hh color.hh texture.hh ../src/util.hh image.hh \
+ ../src/timer.hh ../src/basedisplay.hh ../src/workspace.hh ../src/xatom.hh ../src/blackbox.hh \
+ ../src/configuration.hh gccache.hh
+gccache.o: gccache.cc gccache.hh ../src/basedisplay.hh ../src/timer.hh ../src/util.hh \
+ color.hh
+image.o: image.cc ../src/blackbox.hh ../src/basedisplay.hh ../src/timer.hh ../src/util.hh \
+ ../src/configuration.hh ../src/xatom.hh gccache.hh color.hh image.hh texture.hh
+imagecontrol.o: imagecontrol.cc ../src/blackbox.hh ../src/basedisplay.hh ../src/timer.hh \
+ ../src/util.hh ../src/configuration.hh ../src/xatom.hh color.hh image.hh texture.hh
+texture.o: texture.cc texture.hh color.hh ../src/util.hh ../src/basedisplay.hh \
+ ../src/timer.hh image.hh ../src/screen.hh ../src/workspace.hh ../src/xatom.hh \
+ ../src/blackbox.hh ../src/configuration.hh
diff --git a/otk/color.cc b/otk/color.cc
new file mode 100644 (file)
index 0000000..ad30c7a
--- /dev/null
@@ -0,0 +1,223 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#ifdef HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#include <stdio.h>
+}
+
+#include <assert.h>
+
+#include "color.hh"
+#include "basedisplay.hh"
+
+
+BColor::ColorCache BColor::colorcache;
+bool BColor::cleancache = false;
+
+BColor::BColor(const BaseDisplay * const _display, unsigned int _screen)
+  : allocated(false), r(-1), g(-1), b(-1), p(0), dpy(_display), scrn(_screen)
+{}
+
+BColor::BColor(int _r, int _g, int _b,
+               const BaseDisplay * const _display, unsigned int _screen)
+  : allocated(false), r(_r), g(_g), b(_b), p(0), dpy(_display), scrn(_screen)
+{}
+
+
+BColor::BColor(const std::string &_name,
+               const BaseDisplay * const _display, unsigned int _screen)
+  : allocated(false), r(-1), g(-1), b(-1), p(0), dpy(_display), scrn(_screen),
+    colorname(_name) {
+  parseColorName();
+}
+
+
+BColor::~BColor(void) {
+  deallocate();
+}
+
+
+void BColor::setDisplay(const BaseDisplay * const _display,
+                        unsigned int _screen) {
+  if (_display == display() && _screen == screen()) {
+    // nothing to do
+    return;
+  }
+
+  deallocate();
+
+  dpy = _display;
+  scrn = _screen;
+
+  if (! colorname.empty()) {
+    parseColorName();
+  }
+}
+
+
+unsigned long BColor::pixel(void) const {
+  if (! allocated) {
+    // mutable
+    BColor *that = (BColor *) this;
+    that->allocate();
+  }
+
+  return p;
+}
+
+
+void BColor::parseColorName(void) {
+  assert(dpy != 0);
+
+  if (colorname.empty()) {
+    fprintf(stderr, "BColor: empty colorname, cannot parse (using black)\n");
+    setRGB(0, 0, 0);
+  }
+
+  if (scrn == ~(0u))
+    scrn = DefaultScreen(display()->getXDisplay());
+  Colormap colormap = display()->getScreenInfo(scrn)->getColormap();
+
+  // get rgb values from colorname
+  XColor xcol;
+  xcol.red = 0;
+  xcol.green = 0;
+  xcol.blue = 0;
+  xcol.pixel = 0;
+
+  if (! XParseColor(display()->getXDisplay(), colormap,
+                    colorname.c_str(), &xcol)) {
+    fprintf(stderr, "BColor::allocate: color parse error: \"%s\"\n",
+            colorname.c_str());
+    setRGB(0, 0, 0);
+    return;
+  }
+
+  setRGB(xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
+}
+
+
+void BColor::allocate(void) {
+  assert(dpy != 0);
+
+  if (scrn == ~(0u)) scrn = DefaultScreen(display()->getXDisplay());
+  Colormap colormap = display()->getScreenInfo(scrn)->getColormap();
+
+  if (! isValid()) {
+    if (colorname.empty()) {
+      fprintf(stderr, "BColor: cannot allocate invalid color (using black)\n");
+      setRGB(0, 0, 0);
+    } else {
+      parseColorName();
+    }
+  }
+
+  // see if we have allocated this color before
+  RGB rgb(display(), scrn, r, g, b);
+  ColorCache::iterator it = colorcache.find(rgb);
+  if (it != colorcache.end()) {
+    // found
+    allocated = true;
+    p = (*it).second.p;
+    (*it).second.count++;
+    return;
+  }
+
+  // allocate color from rgb values
+  XColor xcol;
+  xcol.red =   r | r << 8;
+  xcol.green = g | g << 8;
+  xcol.blue =  b | b << 8;
+  xcol.pixel = 0;
+
+  if (! XAllocColor(display()->getXDisplay(), colormap, &xcol)) {
+    fprintf(stderr, "BColor::allocate: color alloc error: rgb:%x/%x/%x\n",
+            r, g, b);
+    xcol.pixel = 0;
+  }
+
+  p = xcol.pixel;
+  allocated = true;
+
+  colorcache.insert(ColorCacheItem(rgb, PixelRef(p)));
+
+  if (cleancache)
+    doCacheCleanup();
+}
+
+
+void BColor::deallocate(void) {
+  if (! allocated)
+    return;
+
+  assert(dpy != 0);
+
+  ColorCache::iterator it = colorcache.find(RGB(display(), scrn, r, g, b));
+  if (it != colorcache.end()) {
+    if ((*it).second.count >= 1)
+      (*it).second.count--;
+  }
+
+  if (cleancache)
+    doCacheCleanup();
+
+  allocated = false;
+}
+
+
+BColor &BColor::operator=(const BColor &c) {
+  deallocate();
+
+  setRGB(c.r, c.g, c.b);
+  colorname = c.colorname;
+  dpy = c.dpy;
+  scrn = c.scrn;
+  return *this;
+}
+
+
+void BColor::cleanupColorCache(void) {
+  cleancache = true;
+}
+
+
+void BColor::doCacheCleanup(void) {
+  // ### TODO - support multiple displays!
+  ColorCache::iterator it = colorcache.begin();
+  if (it == colorcache.end()) {
+    // nothing to do
+    return;
+  }
+
+  const BaseDisplay* const display = (*it).first.display;
+  unsigned long *pixels = new unsigned long[ colorcache.size() ];
+  unsigned int i, count;
+
+  for (i = 0; i < display->getNumberOfScreens(); i++) {
+    count = 0;
+    it = colorcache.begin();
+
+    while (it != colorcache.end()) {
+      if ((*it).second.count != 0 || (*it).first.screen != i) {
+        ++it;
+        continue;
+      }
+
+      pixels[ count++ ] = (*it).second.p;
+      ColorCache::iterator it2 = it;
+      ++it;
+      colorcache.erase(it2);
+    }
+
+    if (count > 0)
+      XFreeColors(display->getXDisplay(),
+                  display->getScreenInfo(i)->getColormap(),
+                  pixels, count, 0);
+  }
+
+  delete [] pixels;
+  cleancache = false;
+}
diff --git a/otk/color.hh b/otk/color.hh
new file mode 100644 (file)
index 0000000..5f3cb95
--- /dev/null
@@ -0,0 +1,106 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef COLOR_HH
+#define COLOR_HH
+
+extern "C" {
+#include <X11/Xlib.h>
+}
+
+#include <map>
+#include <string>
+
+class BaseDisplay;
+
+class BColor {
+public:
+  BColor(const BaseDisplay * const _display = 0, unsigned int _screen = ~(0u));
+  BColor(int _r, int _g, int _b,
+         const BaseDisplay * const _display, unsigned int _screen = ~(0u));
+  BColor(const std::string &_name,
+         const BaseDisplay * const _display, unsigned int _screen = ~(0u));
+  ~BColor(void);
+
+  inline const std::string &name(void) const { return colorname; }
+
+  inline int   red(void) const { return r; }
+  inline int green(void) const { return g; }
+  inline int  blue(void) const { return b; }
+  void setRGB(int _r, int _g, int _b) {
+    deallocate();
+    r = _r;
+    g = _g;
+    b = _b;
+  }
+
+  inline const BaseDisplay *display(void) const { return dpy; }
+  inline unsigned int screen(void) const { return scrn; }
+  void setDisplay(const BaseDisplay * const _display,
+                  unsigned int _screen = ~(0u));
+
+  inline bool isAllocated(void) const { return allocated; }
+
+  inline bool isValid(void) const { return r != -1 && g != -1 && b != -1; }
+
+  unsigned long pixel(void) const;
+
+  // operators
+  BColor &operator=(const BColor &c);
+  inline bool operator==(const BColor &c) const
+  { return (r == c.r && b == c.b && b == c.b); }
+  inline bool operator!=(const BColor &c) const
+  { return (! operator==(c)); }
+
+  static void cleanupColorCache(void);
+
+private:
+  void parseColorName(void);
+  void allocate(void);
+  void deallocate(void);
+
+  bool allocated;
+  int r, g, b;
+  unsigned long p;
+  const BaseDisplay *dpy;
+  unsigned int scrn;
+  std::string colorname;
+
+  // global color allocator/deallocator
+  struct RGB {
+    const BaseDisplay* const display;
+    const unsigned int screen;
+    const int r, g, b;
+
+    RGB(void) : display(0), screen(~(0u)), r(-1), g(-1), b(-1) { }
+    RGB(const BaseDisplay * const a, const unsigned int b,
+        const int x, const int y, const int z)
+      : display(a), screen(b), r(x), g(y), b(z) {}
+    RGB(const RGB &x)
+      : display(x.display), screen(x.screen), r(x.r), g(x.g), b(x.b) {}
+
+    inline bool operator==(const RGB &x) const {
+      return display == x.display &&
+              screen == x.screen &&
+                   r == x.r && g == x.g && b == x.b;
+    }
+
+    inline bool operator<(const RGB &x) const {
+      unsigned long p1, p2;
+      p1 = (screen << 24 | r << 16 | g << 8 | b) & 0x00ffffff;
+      p2 = (x.screen << 24 | x.r << 16 | x.g << 8 | x.b) & 0x00ffffff;
+      return p1 < p2;
+    }
+  };
+  struct PixelRef {
+    const unsigned long p;
+    unsigned int count;
+    inline PixelRef(void) : p(0), count(0) { }
+    inline PixelRef(const unsigned long x) : p(x), count(1) { }
+  };
+  typedef std::map<RGB,PixelRef> ColorCache;
+  typedef ColorCache::value_type ColorCacheItem;
+  static ColorCache colorcache;
+  static bool cleancache;
+  static void doCacheCleanup(void);
+};
+
+#endif // COLOR_HH
diff --git a/otk/color.o b/otk/color.o
new file mode 100644 (file)
index 0000000..1fcba15
Binary files /dev/null and b/otk/color.o differ
diff --git a/otk/font.cc b/otk/font.cc
new file mode 100644 (file)
index 0000000..91f8ffb
--- /dev/null
@@ -0,0 +1,128 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#ifdef    HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif // HAVE_STDLIB_H
+}
+
+#include <iostream>
+#include <algorithm>
+
+using std::string;
+using std::cerr;
+using std::endl;
+
+#include "font.hh"
+#include "util.hh"
+#include "gccache.hh"
+#include "color.hh"
+
+string      BFont::_fallback_font   = "fixed";
+
+BFont::BFont(Display *d, BScreen *screen, const string &family, int size,
+             bool bold, bool italic, bool shadow, unsigned char offset, 
+             unsigned char tint, bool antialias) :
+                                          _display(d),
+                                          _screen(screen),
+                                          _family(family),
+                                          _simplename(False),
+                                          _size(size),
+                                          _bold(bold),
+                                          _italic(italic),
+                                          _antialias(antialias),
+                                          _shadow(shadow),
+                                          _offset(offset),
+                                          _tint(tint),
+                                          _xftfont(0) {
+  _valid = False;
+
+  _xftfont = XftFontOpen(_display, _screen->getScreenNumber(),
+                         XFT_FAMILY, XftTypeString,  _family.c_str(),
+                         XFT_SIZE,   XftTypeInteger, _size,
+                         XFT_WEIGHT, XftTypeInteger, (_bold ?
+                                                      XFT_WEIGHT_BOLD :
+                                                      XFT_WEIGHT_MEDIUM),
+                         XFT_SLANT,  XftTypeInteger, (_italic ?
+                                                      XFT_SLANT_ITALIC :
+                                                      XFT_SLANT_ROMAN),
+                         XFT_ANTIALIAS, XftTypeBool, _antialias,
+                         0);
+  if (! _xftfont)
+    return; // failure
+
+  _valid = True;
+}
+
+
+BFont::~BFont(void) {
+  if (_xftfont)
+    XftFontClose(_display, _xftfont);
+}
+
+
+void BFont::drawString(Drawable d, int x, int y, const BColor &color,
+                       const string &string) const {
+  assert(_valid);
+
+  XftDraw *draw = XftDrawCreate(_display, d, _screen->getVisual(),
+                                _screen->getColormap());
+  assert(draw);
+
+  if (_shadow) {
+    XftColor c;
+    c.color.red = 0;
+    c.color.green = 0;
+    c.color.blue = 0;
+    c.color.alpha = _tint | _tint << 8; // transparent shadow
+    c.pixel = BlackPixel(_display, _screen->getScreenNumber());
+
+    XftDrawStringUtf8(draw, &c, _xftfont, x + _offset,
+                      _xftfont->ascent + y + _offset,
+                      (XftChar8 *) string.c_str(),
+                      string.size());
+  }
+    
+  XftColor c;
+  c.color.red = color.red() | color.red() << 8;
+  c.color.green = color.green() | color.green() << 8;
+  c.color.blue = color.blue() | color.blue() << 8;
+  c.pixel = color.pixel();
+  c.color.alpha = 0xff | 0xff << 8; // no transparency in BColor yet
+
+  XftDrawStringUtf8(draw, &c, _xftfont, x, _xftfont->ascent + y,
+                    (XftChar8 *) string.c_str(), string.size());
+
+  XftDrawDestroy(draw);
+  return;
+}
+
+
+unsigned int BFont::measureString(const string &string) const {
+  assert(_valid);
+
+  XGlyphInfo info;
+
+  XftTextExtentsUtf8(_display, _xftfont, (XftChar8 *) string.c_str(),
+                     string.size(), &info);
+
+  return info.xOff + (_shadow ? _offset : 0);
+}
+
+
+unsigned int BFont::height(void) const {
+  assert(_valid);
+
+  return _xftfont->height + (_shadow ? _offset : 0);
+}
+
+
+unsigned int BFont::maxCharWidth(void) const {
+  assert(_valid);
+
+  return _xftfont->max_advance_width;
+}
diff --git a/otk/font.hh b/otk/font.hh
new file mode 100644 (file)
index 0000000..c070bbf
--- /dev/null
@@ -0,0 +1,82 @@
+// -*- mode: C++; indent-tabs-mode: nil; -*-
+#ifndef   __Font_hh
+#define   __Font_hh
+
+extern "C" {
+#include <X11/Xlib.h>
+
+#include <X11/Xft/Xft.h>
+}
+
+#include <assert.h>
+
+#include <string>
+
+class BGCCache;
+class BGCCacheItem;
+class BColor;
+
+#include "screen.hh"
+
+class BFont {
+  /*
+   * static members
+   */
+private:
+  static std::string  _fallback_font;
+
+public:
+  // the fallback is only used for X fonts, not for Xft fonts, since it is
+  // assumed that X fonts will be the fallback from Xft.
+  inline static std::string fallbackFont(void) { return _fallback_font; }
+  inline static void setFallbackFont(const std::string &f)
+    { _fallback_font = f; }
+
+  /*
+   * instance members
+   */
+private:
+  Display          *_display;
+  BScreen          *_screen;
+
+  std::string       _family;
+  bool              _simplename;  // true if not spec'd as a -*-* string
+  int               _size;
+  bool              _bold;
+  bool              _italic;
+
+  bool              _antialias;
+  bool              _shadow;
+  unsigned char     _offset;
+  unsigned char     _tint;
+
+  XftFont          *_xftfont;
+
+  bool createXftFont(void);
+  
+  bool              _valid;
+
+public:
+  // loads an Xft font
+  BFont(Display *d, BScreen *screen, const std::string &family, int size,
+        bool bold, bool italic, bool shadow, unsigned char offset, 
+        unsigned char tint, bool antialias = True);
+  virtual ~BFont(void);
+
+  inline bool valid(void) const { return _valid; }
+
+  inline std::string family(void) const { assert(_valid); return _family; }
+  inline int size(void) const { assert(_valid); return _size; }
+  inline bool bold(void) const { assert(_valid); return _bold; }
+  inline bool italic(void) const { assert(_valid); return _italic; }
+
+  unsigned int height(void) const;
+  unsigned int maxCharWidth(void) const;
+
+  unsigned int measureString(const std::string &string) const;
+
+  void drawString(Drawable d, int x, int y, const BColor &color,
+                  const std::string &string) const;
+};
+
+#endif // __Font_hh
diff --git a/otk/font.o b/otk/font.o
new file mode 100644 (file)
index 0000000..1882f7f
Binary files /dev/null and b/otk/font.o differ
diff --git a/otk/gccache.cc b/otk/gccache.cc
new file mode 100644 (file)
index 0000000..2ab37f3
--- /dev/null
@@ -0,0 +1,188 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#ifdef HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#include <stdio.h>
+}
+
+#include "gccache.hh"
+#include "basedisplay.hh"
+#include "color.hh"
+#include "util.hh"
+
+
+BGCCacheContext::~BGCCacheContext(void) {
+  if (gc)
+    XFreeGC(display->getXDisplay(), gc);
+}
+
+
+void BGCCacheContext::set(const BColor &_color,
+                          const XFontStruct * const _font,
+                          const int _function, const int _subwindow,
+                          int _linewidth) {
+  XGCValues gcv;
+  pixel = gcv.foreground = _color.pixel();
+  function = gcv.function = _function;
+  subwindow = gcv.subwindow_mode = _subwindow;
+  linewidth = gcv.line_width = _linewidth;
+  gcv.cap_style = CapProjecting;
+
+  unsigned long mask = GCForeground | GCFunction | GCSubwindowMode |
+    GCLineWidth | GCCapStyle;
+
+  if (_font) {
+    fontid = gcv.font = _font->fid;
+    mask |= GCFont;
+  } else {
+    fontid = 0;
+  }
+
+  XChangeGC(display->getXDisplay(), gc, mask, &gcv);
+}
+
+
+void BGCCacheContext::set(const XFontStruct * const _font) {
+  if (! _font) {
+    fontid = 0;
+    return;
+  }
+
+  XGCValues gcv;
+  fontid = gcv.font = _font->fid;
+  XChangeGC(display->getXDisplay(), gc, GCFont, &gcv);
+}
+
+
+BGCCache::BGCCache(const BaseDisplay * const _display,
+                   unsigned int screen_count)
+  : display(_display),  context_count(128u),
+    cache_size(16u), cache_buckets(8u * screen_count),
+    cache_total_size(cache_size * cache_buckets) {
+
+  contexts = new BGCCacheContext*[context_count];
+  unsigned int i;
+  for (i = 0; i < context_count; i++) {
+    contexts[i] = new BGCCacheContext(display);
+  }
+
+  cache = new BGCCacheItem*[cache_total_size];
+  for (i = 0; i < cache_total_size; ++i) {
+    cache[i] = new BGCCacheItem;
+  }
+}
+
+
+BGCCache::~BGCCache(void) {
+  std::for_each(contexts, contexts + context_count, PointerAssassin());
+  std::for_each(cache, cache + cache_total_size, PointerAssassin());
+  delete [] cache;
+  delete [] contexts;
+}
+
+
+BGCCacheContext *BGCCache::nextContext(unsigned int scr) {
+  Window hd = display->getScreenInfo(scr)->getRootWindow();
+
+  BGCCacheContext *c;
+
+  for (unsigned int i = 0; i < context_count; ++i) {
+    c = contexts[i];
+
+    if (! c->gc) {
+      c->gc = XCreateGC(display->getXDisplay(), hd, 0, 0);
+      c->used = false;
+      c->screen = scr;
+    }
+    if (! c->used && c->screen == scr)
+      return c;
+  }
+
+  fprintf(stderr, "BGCCache: context fault!\n");
+  abort();
+  return (BGCCacheContext*) 0; // not reached
+}
+
+
+void BGCCache::release(BGCCacheContext *ctx) {
+  ctx->used = false;
+}
+
+
+BGCCacheItem *BGCCache::find(const BColor &_color,
+                             const XFontStruct * const _font,
+                             int _function, int _subwindow, int _linewidth) {
+  const unsigned long pixel = _color.pixel();
+  const unsigned int screen = _color.screen();
+  const int key = _color.red() ^ _color.green() ^ _color.blue();
+  int k = (key % cache_size) * cache_buckets;
+  unsigned int i = 0; // loop variable
+  BGCCacheItem *c = cache[ k ], *prev = 0;
+
+  /*
+    this will either loop cache_buckets times then return/abort or
+    it will stop matching
+  */
+  while (c->ctx &&
+         (c->ctx->pixel != pixel || c->ctx->function != _function ||
+          c->ctx->subwindow != _subwindow || c->ctx->screen != screen ||
+          c->ctx->linewidth != _linewidth)) {
+    if (i < (cache_buckets - 1)) {
+      prev = c;
+      c = cache[ ++k ];
+      ++i;
+      continue;
+    }
+    if (c->count == 0 && c->ctx->screen == screen) {
+      // use this cache item
+      c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
+      c->ctx->used = true;
+      c->count = 1;
+      c->hits = 1;
+      return c;
+    }
+    // cache fault!
+    fprintf(stderr, "BGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen);
+    abort();
+  }
+
+  if (c->ctx) {
+    // reuse existing context
+    if (_font && _font->fid && _font->fid != c->ctx->fontid)
+      c->ctx->set(_font);
+    c->count++;
+    c->hits++;
+    if (prev && c->hits > prev->hits) {
+      cache[ k     ] = prev;
+      cache[ k - 1 ] = c;
+    }
+  } else {
+    c->ctx = nextContext(screen);
+    c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
+    c->ctx->used = true;
+    c->count = 1;
+    c->hits = 1;
+  }
+
+  return c;
+}
+
+
+void BGCCache::release(BGCCacheItem *_item) {
+  _item->count--;
+}
+
+
+void BGCCache::purge(void) {
+  for (unsigned int i = 0; i < cache_total_size; ++i) {
+    BGCCacheItem *d = cache[ i ];
+
+    if (d->ctx && d->count == 0) {
+      release(d->ctx);
+      d->ctx = 0;
+    }
+  }
+}
diff --git a/otk/gccache.hh b/otk/gccache.hh
new file mode 100644 (file)
index 0000000..499ed8a
--- /dev/null
@@ -0,0 +1,119 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef GCCACHE_HH
+#define GCCACHE_HH
+
+extern "C" {
+#include <X11/Xlib.h>
+}
+
+#include "basedisplay.hh"
+#include "color.hh"
+
+class BGCCacheItem;
+
+class BGCCacheContext {
+public:
+  void set(const BColor &_color, const XFontStruct * const _font,
+           const int _function, const int _subwindow, const int _linewidth);
+  void set(const XFontStruct * const _font);
+
+  ~BGCCacheContext(void);
+
+private:
+  BGCCacheContext(const BaseDisplay * const _display)
+    : display(_display), gc(0), pixel(0ul), fontid(0ul),
+      function(0), subwindow(0), used(false), screen(~(0u)), linewidth(0) {}
+
+  const BaseDisplay *display;
+  GC gc;
+  unsigned long pixel;
+  unsigned long fontid;
+  int function;
+  int subwindow;
+  bool used;
+  unsigned int screen;
+  int linewidth;
+
+  BGCCacheContext(const BGCCacheContext &_nocopy);
+  BGCCacheContext &operator=(const BGCCacheContext &_nocopy);
+
+  friend class BGCCache;
+  friend class BGCCacheItem;
+};
+
+class BGCCacheItem {
+public:
+  inline const GC &gc(void) const { return ctx->gc; }
+
+private:
+  BGCCacheItem(void) : ctx(0), count(0), hits(0), fault(false) { }
+
+  BGCCacheContext *ctx;
+  unsigned int count;
+  unsigned int hits;
+  bool fault;
+
+  BGCCacheItem(const BGCCacheItem &_nocopy);
+  BGCCacheItem &operator=(const BGCCacheItem &_nocopy);
+
+  friend class BGCCache;
+};
+
+class BGCCache {
+public:
+  BGCCache(const BaseDisplay * const _display, unsigned int screen_count);
+  ~BGCCache(void);
+
+  // cleans up the cache
+  void purge(void);
+
+  BGCCacheItem *find(const BColor &_color, const XFontStruct * const _font = 0,
+                     int _function = GXcopy, int _subwindow = ClipByChildren,
+                     int _linewidth = 0);
+  void release(BGCCacheItem *_item);
+
+private:
+  BGCCacheContext *nextContext(unsigned int _screen);
+  void release(BGCCacheContext *ctx);
+
+  // this is closely modelled after the Qt GC cache, but with some of the
+  // complexity stripped out
+  const BaseDisplay *display;
+
+  const unsigned int context_count;
+  const unsigned int cache_size;
+  const unsigned int cache_buckets;
+  const unsigned int cache_total_size;
+  BGCCacheContext **contexts;
+  BGCCacheItem **cache;
+};
+
+class BPen {
+public:
+  inline BPen(const BColor &_color,  const XFontStruct * const _font = 0,
+              int _linewidth = 0, int _function = GXcopy,
+              int _subwindow = ClipByChildren)
+    : color(_color), font(_font), linewidth(_linewidth), function(_function),
+      subwindow(_subwindow), cache(_color.display()->gcCache()), item(0) { }
+
+  inline ~BPen(void) { if (item) cache->release(item); }
+
+  inline const GC &gc(void) const {
+    if (! item) item = cache->find(color, font, function, subwindow,
+                                   linewidth);
+    return item->gc();
+  }
+
+private:
+  const BColor &color;
+  const XFontStruct *font;
+  int linewidth;
+  int function;
+  int subwindow;
+
+  mutable BGCCache *cache;
+  mutable BGCCacheItem *item;
+};
+
+
+#endif // GCCACHE_HH
diff --git a/otk/gccache.o b/otk/gccache.o
new file mode 100644 (file)
index 0000000..28af99d
Binary files /dev/null and b/otk/gccache.o differ
diff --git a/otk/image.cc b/otk/image.cc
new file mode 100644 (file)
index 0000000..21e9d69
--- /dev/null
@@ -0,0 +1,1677 @@
+// -*- mode: C++; indent-tabs-mode: nil; -*-
+
+#ifdef    HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+#ifdef    HAVE_STDIO_H
+#  include <stdio.h>
+#endif // HAVE_STDIO_H
+
+#include <algorithm>
+using std::max;
+using std::min;
+
+#include "blackbox.hh"
+#include "basedisplay.hh"
+#include "gccache.hh"
+#include "image.hh"
+#include "texture.hh"
+
+
+BImage::BImage(BImageControl *c, int w, int h) {
+  control = c;
+
+  width = (w > 0) ? w : 1;
+  height = (h > 0) ? h : 1;
+
+  red = new unsigned char[width * height];
+  green = new unsigned char[width * height];
+  blue = new unsigned char[width * height];
+
+  xtable = ytable = (unsigned int *) 0;
+
+  cpc = control->getColorsPerChannel();
+  cpccpc = cpc * cpc;
+
+  control->getColorTables(&red_table, &green_table, &blue_table,
+                          &red_offset, &green_offset, &blue_offset,
+                          &red_bits, &green_bits, &blue_bits);
+
+  if (control->getVisual()->c_class != TrueColor)
+    control->getXColorTable(&colors, &ncolors);
+}
+
+
+BImage::~BImage(void) {
+  delete [] red;
+  delete [] green;
+  delete [] blue;
+}
+
+
+Pixmap BImage::render(const BTexture &texture) {
+  if (texture.texture() & BTexture::Parent_Relative)
+    return ParentRelative;
+  else if (texture.texture() & BTexture::Solid)
+    return render_solid(texture);
+  else if (texture.texture() & BTexture::Gradient)
+    return render_gradient(texture);
+  return None;
+}
+
+
+Pixmap BImage::render_solid(const BTexture &texture) {
+  Pixmap pixmap = XCreatePixmap(control->getBaseDisplay()->getXDisplay(),
+                               control->getDrawable(), width,
+                               height, control->getDepth());
+  if (pixmap == None) {
+    fprintf(stderr, "BImage::render_solid: error creating pixmap\n");
+    return None;
+  }
+
+  Display *display = control->getBaseDisplay()->getXDisplay();
+
+  BPen pen(texture.color());
+  BPen penlight(texture.lightColor());
+  BPen penshadow(texture.shadowColor());
+
+  XFillRectangle(display, pixmap, pen.gc(), 0, 0, width, height);
+
+  if (texture.texture() & BTexture::Interlaced) {
+    BPen peninterlace(texture.colorTo());
+    for (unsigned int i = 0; i < height; i += 2)
+      XDrawLine(display, pixmap, peninterlace.gc(), 0, i, width, i);
+  }
+
+  int left = 0, top = 0, right = width - 1, bottom = height - 1;
+
+  if (texture.texture() & BTexture::Border) {
+    BPen penborder(texture.borderColor());
+    XDrawRectangle(display, pixmap, penborder.gc(),
+                   left, top, right, bottom);
+  }
+
+  if (texture.texture() & BTexture::Bevel1) {
+    if (texture.texture() & BTexture::Raised) {
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left, bottom, right, bottom);
+      XDrawLine(display, pixmap, penshadow.gc(),
+                right, bottom, right, top);
+
+      XDrawLine(display, pixmap, penlight.gc(),
+                left, top, right, top);
+      XDrawLine(display, pixmap, penlight.gc(),
+                left, bottom, left, top);
+    } else if (texture.texture() & BTexture::Sunken) {
+      XDrawLine(display, pixmap, penlight.gc(),
+                left, bottom, right, bottom);
+      XDrawLine(display, pixmap, penlight.gc(),
+                right, bottom, right, top);
+
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left, top, right, top);
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left, bottom, left, top);
+    }
+  } else if (texture.texture() & BTexture::Bevel2) {
+    if (texture.texture() & BTexture::Raised) {
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left + 1, bottom - 2, right - 2, bottom - 2);
+      XDrawLine(display, pixmap, penshadow.gc(),
+                right - 2, bottom - 2, right - 2, top + 1);
+
+      XDrawLine(display, pixmap, penlight.gc(),
+                left + 1, top + 1, right - 2, top + 1);
+      XDrawLine(display, pixmap, penlight.gc(),
+                left + 1, bottom - 2, left + 1, top + 1);
+    } else if (texture.texture() & BTexture::Sunken) {
+      XDrawLine(display, pixmap, penlight.gc(),
+                left + 1, bottom - 2, right - 2, bottom - 2);
+      XDrawLine(display, pixmap, penlight.gc(),
+                right - 2, bottom - 2, right - 2, top + 1);
+
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left + 1, top + 1, right - 2, top + 1);
+      XDrawLine(display, pixmap, penshadow.gc(),
+                left + 1, bottom - 2, left + 1, top + 1);
+    }
+  }
+
+  return pixmap;
+}
+
+
+Pixmap BImage::render_gradient(const BTexture &texture) {
+  bool inverted = False;
+
+  interlaced = texture.texture() & BTexture::Interlaced;
+
+  if (texture.texture() & BTexture::Sunken) {
+    from = texture.colorTo();
+    to = texture.color();
+
+    if (! (texture.texture() & BTexture::Invert)) inverted = True;
+  } else {
+    from = texture.color();
+    to = texture.colorTo();
+
+    if (texture.texture() & BTexture::Invert) inverted = True;
+  }
+
+  control->getGradientBuffers(width, height, &xtable, &ytable);
+
+  if (texture.texture() & BTexture::Diagonal) dgradient();
+  else if (texture.texture() & BTexture::Elliptic) egradient();
+  else if (texture.texture() & BTexture::Horizontal) hgradient();
+  else if (texture.texture() & BTexture::Pyramid) pgradient();
+  else if (texture.texture() & BTexture::Rectangle) rgradient();
+  else if (texture.texture() & BTexture::Vertical) vgradient();
+  else if (texture.texture() & BTexture::CrossDiagonal) cdgradient();
+  else if (texture.texture() & BTexture::PipeCross) pcgradient();
+
+  if (texture.texture() & BTexture::Bevel1) bevel1();
+  else if (texture.texture() & BTexture::Bevel2) bevel2();
+
+  if (texture.texture() & BTexture::Border) border(texture);
+
+  if (inverted) invert();
+
+  return renderPixmap();
+
+}
+
+
+static const unsigned char dither4[4][4] = {
+  {0, 4, 1, 5},
+  {6, 2, 7, 3},
+  {1, 5, 0, 4},
+  {7, 3, 6, 2}
+};
+
+
+/*
+ * Helper function for TrueColorDither and renderXImage
+ *
+ * This handles the proper setting of the image data based on the image depth
+ * and the machine's byte ordering
+ */
+static inline
+void assignPixelData(unsigned int bit_depth, unsigned char **data,
+                    unsigned long pixel) {
+  unsigned char *pixel_data = *data;
+  switch (bit_depth) {
+  case  8: //  8bpp
+    *pixel_data++ = pixel;
+    break;
+
+  case 16: // 16bpp LSB
+    *pixel_data++ = pixel;
+    *pixel_data++ = pixel >> 8;
+    break;
+
+  case 17: // 16bpp MSB
+    *pixel_data++ = pixel >> 8;
+    *pixel_data++ = pixel;
+    break;
+
+  case 24: // 24bpp LSB
+    *pixel_data++ = pixel;
+    *pixel_data++ = pixel >> 8;
+    *pixel_data++ = pixel >> 16;
+    break;
+
+  case 25: // 24bpp MSB
+    *pixel_data++ = pixel >> 16;
+    *pixel_data++ = pixel >> 8;
+    *pixel_data++ = pixel;
+    break;
+
+  case 32: // 32bpp LSB
+    *pixel_data++ = pixel;
+    *pixel_data++ = pixel >> 8;
+    *pixel_data++ = pixel >> 16;
+    *pixel_data++ = pixel >> 24;
+    break;
+
+  case 33: // 32bpp MSB
+    *pixel_data++ = pixel >> 24;
+    *pixel_data++ = pixel >> 16;
+    *pixel_data++ = pixel >> 8;
+    *pixel_data++ = pixel;
+    break;
+  }
+  *data = pixel_data; // assign back so we don't lose our place
+}
+
+
+// algorithm: ordered dithering... many many thanks to rasterman
+// (raster@rasterman.com) for telling me about this... portions of this
+// code is based off of his code in Imlib
+void BImage::TrueColorDither(unsigned int bit_depth, int bytes_per_line,
+                            unsigned char *pixel_data) {
+  unsigned int x, y, dithx, dithy, r, g, b, er, eg, eb, offset;
+  unsigned char *ppixel_data = pixel_data;
+  unsigned long pixel;
+
+  for (y = 0, offset = 0; y < height; y++) {
+    dithy = y & 0x3;
+
+    for (x = 0; x < width; x++, offset++) {
+      dithx = x & 0x3;
+      r = red[offset];
+      g = green[offset];
+      b = blue[offset];
+
+      er = r & (red_bits - 1);
+      eg = g & (green_bits - 1);
+      eb = b & (blue_bits - 1);
+
+      r = red_table[r];
+      g = green_table[g];
+      b = blue_table[b];
+
+      if ((dither4[dithy][dithx] < er) && (r < red_table[255])) r++;
+      if ((dither4[dithy][dithx] < eg) && (g < green_table[255])) g++;
+      if ((dither4[dithy][dithx] < eb) && (b < blue_table[255])) b++;
+
+      pixel = (r << red_offset) | (g << green_offset) | (b << blue_offset);
+      assignPixelData(bit_depth, &pixel_data, pixel);
+    }
+
+    pixel_data = (ppixel_data += bytes_per_line);
+  }
+}
+
+#ifdef ORDEREDPSEUDO
+const static unsigned char dither8[8][8] = {
+  { 0,  32, 8,  40, 2,  34, 10, 42},
+  { 48, 16, 56, 24, 50, 18, 58, 26},
+  { 12, 44, 4,  36, 14, 46, 6,  38},
+  { 60, 28, 52, 20, 62, 30, 54, 22},
+  { 3,  35, 11, 43, 1,  33, 9,  41},
+  { 51, 19, 59, 27, 49, 17, 57, 25},
+  { 15, 47, 7,  39, 13, 45, 5,  37},
+  { 63, 31, 55, 23, 61, 29, 53, 21}
+};
+
+void BImage::OrderedPseudoColorDither(int bytes_per_line,
+                                     unsigned char *pixel_data) {
+  unsigned int x, y, dithx, dithy, r, g, b, er, eg, eb, offset;
+  unsigned long pixel;
+  unsigned char *ppixel_data = pixel_data;
+
+  for (y = 0, offset = 0; y < height; y++) {
+    dithy = y & 7;
+
+    for (x = 0; x < width; x++, offset++) {
+      dithx = x & 7;
+
+      r = red[offset];
+      g = green[offset];
+      b = blue[offset];
+
+      er = r & (red_bits - 1);
+      eg = g & (green_bits - 1);
+      eb = b & (blue_bits - 1);
+
+      r = red_table[r];
+      g = green_table[g];
+      b = blue_table[b];
+
+      if ((dither8[dithy][dithx] < er) && (r < red_table[255])) r++;
+      if ((dither8[dithy][dithx] < eg) && (g < green_table[255])) g++;
+      if ((dither8[dithy][dithx] < eb) && (b < blue_table[255])) b++;
+
+      pixel = (r * cpccpc) + (g * cpc) + b;
+      *(pixel_data++) = colors[pixel].pixel;
+    }
+
+    pixel_data = (ppixel_data += bytes_per_line);
+  }
+}
+#endif
+
+void BImage::PseudoColorDither(int bytes_per_line, unsigned char *pixel_data) {
+  short *terr,
+    *rerr = new short[width + 2],
+    *gerr = new short[width + 2],
+    *berr = new short[width + 2],
+    *nrerr = new short[width + 2],
+    *ngerr = new short[width + 2],
+    *nberr = new short[width + 2];
+
+  int rr, gg, bb, rer, ger, ber;
+  int dd = 255 / control->getColorsPerChannel();
+  unsigned int x, y, r, g, b, offset;
+  unsigned long pixel;
+  unsigned char *ppixel_data = pixel_data;
+
+  for (x = 0; x < width; x++) {
+    *(rerr + x) = *(red + x);
+    *(gerr + x) = *(green + x);
+    *(berr + x) = *(blue + x);
+  }
+
+  *(rerr + x) = *(gerr + x) = *(berr + x) = 0;
+
+  for (y = 0, offset = 0; y < height; y++) {
+    if (y < (height - 1)) {
+      int i = offset + width;
+      for (x = 0; x < width; x++, i++) {
+       *(nrerr + x) = *(red + i);
+       *(ngerr + x) = *(green + i);
+       *(nberr + x) = *(blue + i);
+      }
+
+      *(nrerr + x) = *(red + (--i));
+      *(ngerr + x) = *(green + i);
+      *(nberr + x) = *(blue + i);
+    }
+
+    for (x = 0; x < width; x++) {
+      rr = rerr[x];
+      gg = gerr[x];
+      bb = berr[x];
+
+      if (rr > 255) rr = 255; else if (rr < 0) rr = 0;
+      if (gg > 255) gg = 255; else if (gg < 0) gg = 0;
+      if (bb > 255) bb = 255; else if (bb < 0) bb = 0;
+
+      r = red_table[rr];
+      g = green_table[gg];
+      b = blue_table[bb];
+
+      rer = rerr[x] - r*dd;
+      ger = gerr[x] - g*dd;
+      ber = berr[x] - b*dd;
+
+      pixel = (r * cpccpc) + (g * cpc) + b;
+      *pixel_data++ = colors[pixel].pixel;
+
+      r = rer >> 1;
+      g = ger >> 1;
+      b = ber >> 1;
+      rerr[x+1] += r;
+      gerr[x+1] += g;
+      berr[x+1] += b;
+      nrerr[x] += r;
+      ngerr[x] += g;
+      nberr[x] += b;
+    }
+
+    offset += width;
+
+    pixel_data = (ppixel_data += bytes_per_line);
+
+    terr = rerr;
+    rerr = nrerr;
+    nrerr = terr;
+
+    terr = gerr;
+    gerr = ngerr;
+    ngerr = terr;
+
+    terr = berr;
+    berr = nberr;
+    nberr = terr;
+  }
+
+  delete [] rerr;
+  delete [] gerr;
+  delete [] berr;
+  delete [] nrerr;
+  delete [] ngerr;
+  delete [] nberr;
+}
+
+XImage *BImage::renderXImage(void) {
+  XImage *image =
+    XCreateImage(control->getBaseDisplay()->getXDisplay(),
+                 control->getVisual(), control->getDepth(), ZPixmap, 0, 0,
+                 width, height, 32, 0);
+
+  if (! image) {
+    fprintf(stderr, "BImage::renderXImage: error creating XImage\n");
+    return (XImage *) 0;
+  }
+
+  // insurance policy
+  image->data = (char *) 0;
+
+  unsigned char *d = new unsigned char[image->bytes_per_line * (height + 1)];
+
+  unsigned int o = image->bits_per_pixel +
+    ((image->byte_order == MSBFirst) ? 1 : 0);
+
+  bool unsupported = False;
+
+  if (control->doDither() && width > 1 && height > 1) {
+    switch (control->getVisual()->c_class) {
+    case TrueColor:
+      TrueColorDither(o, image->bytes_per_line, d);
+      break;
+
+    case StaticColor:
+    case PseudoColor: {
+#ifdef ORDEREDPSEUDO
+      OrderedPseudoColorDither(image->bytes_per_line, d);
+#else
+      PseudoColorDither(image->bytes_per_line, d);
+#endif
+      break;
+    }
+
+    default:
+      unsupported = True;
+    }
+  } else {
+    unsigned int x, y, r, g, b, offset;
+    unsigned char *pixel_data = d, *ppixel_data = d;
+    unsigned long pixel;
+
+    switch (control->getVisual()->c_class) {
+    case StaticColor:
+    case PseudoColor:
+      for (y = 0, offset = 0; y < height; ++y) {
+        for (x = 0; x < width; ++x, ++offset) {
+         r = red_table[red[offset]];
+          g = green_table[green[offset]];
+         b = blue_table[blue[offset]];
+
+         pixel = (r * cpccpc) + (g * cpc) + b;
+         *pixel_data++ = colors[pixel].pixel;
+        }
+
+        pixel_data = (ppixel_data += image->bytes_per_line);
+      }
+
+      break;
+
+    case TrueColor:
+      for (y = 0, offset = 0; y < height; y++) {
+        for (x = 0; x < width; x++, offset++) {
+         r = red_table[red[offset]];
+         g = green_table[green[offset]];
+         b = blue_table[blue[offset]];
+
+         pixel = (r << red_offset) | (g << green_offset) | (b << blue_offset);
+         assignPixelData(o, &pixel_data, pixel);
+        }
+
+        pixel_data = (ppixel_data += image->bytes_per_line);
+      }
+
+      break;
+
+    case StaticGray:
+    case GrayScale:
+      for (y = 0, offset = 0; y < height; y++) {
+       for (x = 0; x < width; x++, offset++) {
+         r = *(red_table + *(red + offset));
+         g = *(green_table + *(green + offset));
+         b = *(blue_table + *(blue + offset));
+
+         g = ((r * 30) + (g * 59) + (b * 11)) / 100;
+         *pixel_data++ = colors[g].pixel;
+       }
+
+       pixel_data = (ppixel_data += image->bytes_per_line);
+      }
+
+      break;
+
+    default:
+      unsupported = True;
+    }
+  }
+
+  if (unsupported) {
+    fprintf(stderr, "BImage::renderXImage: unsupported visual\n");
+    delete [] d;
+    XDestroyImage(image);
+    return (XImage *) 0;
+  }
+
+  image->data = (char *) d;
+
+  return image;
+}
+
+
+Pixmap BImage::renderPixmap(void) {
+  Pixmap pixmap =
+    XCreatePixmap(control->getBaseDisplay()->getXDisplay(),
+                  control->getDrawable(), width, height, control->getDepth());
+
+  if (pixmap == None) {
+    fprintf(stderr, "BImage::renderPixmap: error creating pixmap\n");
+    return None;
+  }
+
+  XImage *image = renderXImage();
+
+  if (! image) {
+    XFreePixmap(control->getBaseDisplay()->getXDisplay(), pixmap);
+    return None;
+  }
+
+  if (! image->data) {
+    XDestroyImage(image);
+    XFreePixmap(control->getBaseDisplay()->getXDisplay(), pixmap);
+    return None;
+  }
+
+  XPutImage(control->getBaseDisplay()->getXDisplay(), pixmap,
+           DefaultGC(control->getBaseDisplay()->getXDisplay(),
+                     control->getScreenInfo()->getScreenNumber()),
+            image, 0, 0, 0, 0, width, height);
+
+  if (image->data) {
+    delete [] image->data;
+    image->data = NULL;
+  }
+
+  XDestroyImage(image);
+
+  return pixmap;
+}
+
+
+void BImage::bevel1(void) {
+  if (width > 2 && height > 2) {
+    unsigned char *pr = red, *pg = green, *pb = blue;
+
+    register unsigned char r, g, b, rr ,gg ,bb;
+    register unsigned int w = width, h = height - 1, wh = w * h;
+
+    while (--w) {
+      r = *pr;
+      rr = r + (r >> 1);
+      if (rr < r) rr = ~0;
+      g = *pg;
+      gg = g + (g >> 1);
+      if (gg < g) gg = ~0;
+      b = *pb;
+      bb = b + (b >> 1);
+      if (bb < b) bb = ~0;
+
+      *pr = rr;
+      *pg = gg;
+      *pb = bb;
+
+      r = *(pr + wh);
+      rr = (r >> 2) + (r >> 1);
+      if (rr > r) rr = 0;
+      g = *(pg + wh);
+      gg = (g >> 2) + (g >> 1);
+      if (gg > g) gg = 0;
+      b = *(pb + wh);
+      bb = (b >> 2) + (b >> 1);
+      if (bb > b) bb = 0;
+
+      *((pr++) + wh) = rr;
+      *((pg++) + wh) = gg;
+      *((pb++) + wh) = bb;
+    }
+
+    r = *pr;
+    rr = r + (r >> 1);
+    if (rr < r) rr = ~0;
+    g = *pg;
+    gg = g + (g >> 1);
+    if (gg < g) gg = ~0;
+    b = *pb;
+    bb = b + (b >> 1);
+    if (bb < b) bb = ~0;
+
+    *pr = rr;
+    *pg = gg;
+    *pb = bb;
+
+    r = *(pr + wh);
+    rr = (r >> 2) + (r >> 1);
+    if (rr > r) rr = 0;
+    g = *(pg + wh);
+    gg = (g >> 2) + (g >> 1);
+    if (gg > g) gg = 0;
+    b = *(pb + wh);
+    bb = (b >> 2) + (b >> 1);
+    if (bb > b) bb = 0;
+
+    *(pr + wh) = rr;
+    *(pg + wh) = gg;
+    *(pb + wh) = bb;
+
+    pr = red + width;
+    pg = green + width;
+    pb = blue + width;
+
+    while (--h) {
+      r = *pr;
+      rr = r + (r >> 1);
+      if (rr < r) rr = ~0;
+      g = *pg;
+      gg = g + (g >> 1);
+      if (gg < g) gg = ~0;
+      b = *pb;
+      bb = b + (b >> 1);
+      if (bb < b) bb = ~0;
+
+      *pr = rr;
+      *pg = gg;
+      *pb = bb;
+
+      pr += width - 1;
+      pg += width - 1;
+      pb += width - 1;
+
+      r = *pr;
+      rr = (r >> 2) + (r >> 1);
+      if (rr > r) rr = 0;
+      g = *pg;
+      gg = (g >> 2) + (g >> 1);
+      if (gg > g) gg = 0;
+      b = *pb;
+      bb = (b >> 2) + (b >> 1);
+      if (bb > b) bb = 0;
+
+      *(pr++) = rr;
+      *(pg++) = gg;
+      *(pb++) = bb;
+    }
+
+    r = *pr;
+    rr = r + (r >> 1);
+    if (rr < r) rr = ~0;
+    g = *pg;
+    gg = g + (g >> 1);
+    if (gg < g) gg = ~0;
+    b = *pb;
+    bb = b + (b >> 1);
+    if (bb < b) bb = ~0;
+
+    *pr = rr;
+    *pg = gg;
+    *pb = bb;
+
+    pr += width - 1;
+    pg += width - 1;
+    pb += width - 1;
+
+    r = *pr;
+    rr = (r >> 2) + (r >> 1);
+    if (rr > r) rr = 0;
+    g = *pg;
+    gg = (g >> 2) + (g >> 1);
+    if (gg > g) gg = 0;
+    b = *pb;
+    bb = (b >> 2) + (b >> 1);
+    if (bb > b) bb = 0;
+
+    *pr = rr;
+    *pg = gg;
+    *pb = bb;
+  }
+}
+
+
+void BImage::bevel2(void) {
+  if (width > 4 && height > 4) {
+    unsigned char r, g, b, rr ,gg ,bb, *pr = red + width + 1,
+      *pg = green + width + 1, *pb = blue + width + 1;
+    unsigned int w = width - 2, h = height - 1, wh = width * (height - 3);
+
+    while (--w) {
+      r = *pr;
+      rr = r + (r >> 1);
+      if (rr < r) rr = ~0;
+      g = *pg;
+      gg = g + (g >> 1);
+      if (gg < g) gg = ~0;
+      b = *pb;
+      bb = b + (b >> 1);
+      if (bb < b) bb = ~0;
+
+      *pr = rr;
+      *pg = gg;
+      *pb = bb;
+
+      r = *(pr + wh);
+      rr = (r >> 2) + (r >> 1);
+      if (rr > r) rr = 0;
+      g = *(pg + wh);
+      gg = (g >> 2) + (g >> 1);
+      if (gg > g) gg = 0;
+      b = *(pb + wh);
+      bb = (b >> 2) + (b >> 1);
+      if (bb > b) bb = 0;
+
+      *((pr++) + wh) = rr;
+      *((pg++) + wh) = gg;
+      *((pb++) + wh) = bb;
+    }
+
+    pr = red + width;
+    pg = green + width;
+    pb = blue + width;
+
+    while (--h) {
+      r = *pr;
+      rr = r + (r >> 1);
+      if (rr < r) rr = ~0;
+      g = *pg;
+      gg = g + (g >> 1);
+      if (gg < g) gg = ~0;
+      b = *pb;
+      bb = b + (b >> 1);
+      if (bb < b) bb = ~0;
+
+      *(++pr) = rr;
+      *(++pg) = gg;
+      *(++pb) = bb;
+
+      pr += width - 3;
+      pg += width - 3;
+      pb += width - 3;
+
+      r = *pr;
+      rr = (r >> 2) + (r >> 1);
+      if (rr > r) rr = 0;
+      g = *pg;
+      gg = (g >> 2) + (g >> 1);
+      if (gg > g) gg = 0;
+      b = *pb;
+      bb = (b >> 2) + (b >> 1);
+      if (bb > b) bb = 0;
+
+      *(pr++) = rr;
+      *(pg++) = gg;
+      *(pb++) = bb;
+
+      pr++; pg++; pb++;
+    }
+  }
+}
+
+
+void BImage::border(const BTexture &texture) {
+  if (width < 2 || height < 2) return;
+  
+  register unsigned int i;
+  int r = texture.borderColor().red(),
+    g = texture.borderColor().green(),
+    b = texture.borderColor().blue();
+
+  unsigned char *pr, *pg, *pb;
+
+  // top line
+  pr = red;
+  pg = green;
+  pb = blue;
+  for (i = 0; i < width; ++i) {
+    *pr++ = r;
+    *pg++ = g;
+    *pb++ = b;
+  }
+
+  if (height > 2) {
+    // left and right lines (pr,pg,pb are already lined up)
+    for (i = 1; i < height - 1; ++i) {
+      *pr = r;
+      *pg = g;
+      *pb = b;
+      pr += width - 1;
+      pg += width - 1;
+      pb += width - 1;
+      *pr++ = r;
+      *pg++ = g;
+      *pb++ = b;
+    }
+  }
+
+  // bottom line (pr,pg,pb are already lined up)
+  for (i = 0; i < width; ++i) {
+    *pr++ = r;
+    *pg++ = g;
+    *pb++ = b;
+  }
+}
+
+
+void BImage::invert(void) {
+  register unsigned int i, j, wh = (width * height) - 1;
+  unsigned char tmp;
+
+  for (i = 0, j = wh; j > i; j--, i++) {
+    tmp = *(red + j);
+    *(red + j) = *(red + i);
+    *(red + i) = tmp;
+
+    tmp = *(green + j);
+    *(green + j) = *(green + i);
+    *(green + i) = tmp;
+
+    tmp = *(blue + j);
+    *(blue + j) = *(blue + i);
+    *(blue + i) = tmp;
+  }
+}
+
+
+void BImage::dgradient(void) {
+  // diagonal gradient code was written by Mike Cole <mike@mydot.com>
+  // modified for interlacing by Brad Hughes
+
+  float drx, dgx, dbx, dry, dgy, dby, yr = 0.0, yg = 0.0, yb = 0.0,
+    xr = (float) from.red(),
+    xg = (float) from.green(),
+    xb = (float) from.blue();
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int w = width * 2, h = height * 2, *xt = xtable, *yt = ytable;
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  // Create X table
+  drx /= w;
+  dgx /= w;
+  dbx /= w;
+
+  for (x = 0; x < width; x++) {
+    *(xt++) = (unsigned char) (xr);
+    *(xt++) = (unsigned char) (xg);
+    *(xt++) = (unsigned char) (xb);
+
+    xr += drx;
+    xg += dgx;
+    xb += dbx;
+  }
+
+  // Create Y table
+  dry /= h;
+  dgy /= h;
+  dby /= h;
+
+  for (y = 0; y < height; y++) {
+    *(yt++) = ((unsigned char) yr);
+    *(yt++) = ((unsigned char) yg);
+    *(yt++) = ((unsigned char) yb);
+
+    yr += dry;
+    yg += dgy;
+    yb += dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal dgradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = *(xt++) + *(yt);
+        *(pg++) = *(xt++) + *(yt + 1);
+        *(pb++) = *(xt++) + *(yt + 2);
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = *(xt++) + *(yt);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = *(xt++) + *(yt + 1);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = *(xt++) + *(yt + 2);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = *(xt++) + *(yt);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = *(xt++) + *(yt + 1);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = *(xt++) + *(yt + 2);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
+
+
+void BImage::hgradient(void) {
+  float drx, dgx, dbx,
+    xr = (float) from.red(),
+    xg = (float) from.green(),
+    xb = (float) from.blue();
+  unsigned char *pr = red, *pg = green, *pb = blue;
+
+  register unsigned int x, y;
+
+  drx = (float) (to.red() - from.red());
+  dgx = (float) (to.green() - from.green());
+  dbx = (float) (to.blue() - from.blue());
+
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  if (interlaced && height > 2) {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (x = 0; x < width; x++, pr++, pg++, pb++) {
+      channel = (unsigned char) xr;
+      channel2 = (channel >> 1) + (channel >> 2);
+      if (channel2 > channel) channel2 = 0;
+      *pr = channel2;
+
+      channel = (unsigned char) xg;
+      channel2 = (channel >> 1) + (channel >> 2);
+      if (channel2 > channel) channel2 = 0;
+      *pg = channel2;
+
+      channel = (unsigned char) xb;
+      channel2 = (channel >> 1) + (channel >> 2);
+      if (channel2 > channel) channel2 = 0;
+      *pb = channel2;
+
+
+      channel = (unsigned char) xr;
+      channel2 = channel + (channel >> 3);
+      if (channel2 < channel) channel2 = ~0;
+      *(pr + width) = channel2;
+
+      channel = (unsigned char) xg;
+      channel2 = channel + (channel >> 3);
+      if (channel2 < channel) channel2 = ~0;
+      *(pg + width) = channel2;
+
+      channel = (unsigned char) xb;
+      channel2 = channel + (channel >> 3);
+      if (channel2 < channel) channel2 = ~0;
+      *(pb + width) = channel2;
+
+      xr += drx;
+      xg += dgx;
+      xb += dbx;
+    }
+
+    pr += width;
+    pg += width;
+    pb += width;
+
+    int offset;
+
+    for (y = 2; y < height; y++, pr += width, pg += width, pb += width) {
+      if (y & 1) offset = width; else offset = 0;
+
+      memcpy(pr, (red + offset), width);
+      memcpy(pg, (green + offset), width);
+      memcpy(pb, (blue + offset), width);
+    }
+  } else {
+    // normal hgradient
+    for (x = 0; x < width; x++) {
+      *(pr++) = (unsigned char) (xr);
+      *(pg++) = (unsigned char) (xg);
+      *(pb++) = (unsigned char) (xb);
+
+      xr += drx;
+      xg += dgx;
+      xb += dbx;
+    }
+
+    for (y = 1; y < height; y++, pr += width, pg += width, pb += width) {
+      memcpy(pr, red, width);
+      memcpy(pg, green, width);
+      memcpy(pb, blue, width);
+    }
+  }
+}
+
+
+void BImage::vgradient(void) {
+  float dry, dgy, dby,
+    yr = (float) from.red(),
+    yg = (float) from.green(),
+    yb = (float) from.blue();
+  unsigned char *pr = red, *pg = green, *pb = blue;
+
+  register unsigned int y;
+
+  dry = (float) (to.red() - from.red());
+  dgy = (float) (to.green() - from.green());
+  dby = (float) (to.blue() - from.blue());
+
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  if (interlaced) {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (y = 0; y < height; y++, pr += width, pg += width, pb += width) {
+      if (y & 1) {
+        channel = (unsigned char) yr;
+        channel2 = (channel >> 1) + (channel >> 2);
+        if (channel2 > channel) channel2 = 0;
+        memset(pr, channel2, width);
+
+        channel = (unsigned char) yg;
+        channel2 = (channel >> 1) + (channel >> 2);
+        if (channel2 > channel) channel2 = 0;
+        memset(pg, channel2, width);
+
+        channel = (unsigned char) yb;
+        channel2 = (channel >> 1) + (channel >> 2);
+        if (channel2 > channel) channel2 = 0;
+        memset(pb, channel2, width);
+      } else {
+        channel = (unsigned char) yr;
+        channel2 = channel + (channel >> 3);
+        if (channel2 < channel) channel2 = ~0;
+        memset(pr, channel2, width);
+
+        channel = (unsigned char) yg;
+        channel2 = channel + (channel >> 3);
+        if (channel2 < channel) channel2 = ~0;
+        memset(pg, channel2, width);
+
+        channel = (unsigned char) yb;
+        channel2 = channel + (channel >> 3);
+        if (channel2 < channel) channel2 = ~0;
+        memset(pb, channel2, width);
+      }
+
+      yr += dry;
+      yg += dgy;
+      yb += dby;
+    }
+  } else {
+    // normal vgradient
+    for (y = 0; y < height; y++, pr += width, pg += width, pb += width) {
+      memset(pr, (unsigned char) yr, width);
+      memset(pg, (unsigned char) yg, width);
+      memset(pb, (unsigned char) yb, width);
+
+      yr += dry;
+      yg += dgy;
+      yb += dby;
+    }
+  }
+}
+
+
+void BImage::pgradient(void) {
+  // pyramid gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  float yr, yg, yb, drx, dgx, dbx, dry, dgy, dby,
+    xr, xg, xb;
+  int rsign, gsign, bsign;
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue(),
+    *xt = xtable, *yt = ytable;
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  rsign = (drx < 0) ? -1 : 1;
+  gsign = (dgx < 0) ? -1 : 1;
+  bsign = (dbx < 0) ? -1 : 1;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; x++) {
+    *(xt++) = (unsigned char) ((xr < 0) ? -xr : xr);
+    *(xt++) = (unsigned char) ((xg < 0) ? -xg : xg);
+    *(xt++) = (unsigned char) ((xb < 0) ? -xb : xb);
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; y++) {
+    *(yt++) = ((unsigned char) ((yr < 0) ? -yr : yr));
+    *(yt++) = ((unsigned char) ((yg < 0) ? -yg : yg));
+    *(yt++) = ((unsigned char) ((yb < 0) ? -yb : yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal pgradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = (unsigned char) (tr - (rsign * (*(xt++) + *(yt))));
+        *(pg++) = (unsigned char) (tg - (gsign * (*(xt++) + *(yt + 1))));
+        *(pb++) = (unsigned char) (tb - (bsign * (*(xt++) + *(yt + 2))));
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = (unsigned char) (tr - (rsign * (*(xt++) + *(yt))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (gsign * (*(xt++) + *(yt + 1))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (bsign * (*(xt++) + *(yt + 2))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = (unsigned char) (tr - (rsign * (*(xt++) + *(yt))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (gsign * (*(xt++) + *(yt + 1))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (bsign * (*(xt++) + *(yt + 2))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
+
+
+void BImage::rgradient(void) {
+  // rectangle gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  float drx, dgx, dbx, dry, dgy, dby, xr, xg, xb, yr, yg, yb;
+  int rsign, gsign, bsign;
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int tr = to.red(), tg = to.green(), tb = to.blue(),
+    *xt = xtable, *yt = ytable;
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  rsign = (drx < 0) ? -2 : 2;
+  gsign = (dgx < 0) ? -2 : 2;
+  bsign = (dbx < 0) ? -2 : 2;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; x++) {
+    *(xt++) = (unsigned char) ((xr < 0) ? -xr : xr);
+    *(xt++) = (unsigned char) ((xg < 0) ? -xg : xg);
+    *(xt++) = (unsigned char) ((xb < 0) ? -xb : xb);
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; y++) {
+    *(yt++) = ((unsigned char) ((yr < 0) ? -yr : yr));
+    *(yt++) = ((unsigned char) ((yg < 0) ? -yg : yg));
+    *(yt++) = ((unsigned char) ((yb < 0) ? -yb : yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal rgradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = (unsigned char) (tr - (rsign * max(*(xt++), *(yt))));
+        *(pg++) = (unsigned char) (tg - (gsign * max(*(xt++), *(yt + 1))));
+        *(pb++) = (unsigned char) (tb - (bsign * max(*(xt++), *(yt + 2))));
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = (unsigned char) (tr - (rsign * max(*(xt++), *(yt))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (gsign * max(*(xt++), *(yt + 1))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (bsign * max(*(xt++), *(yt + 2))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = (unsigned char) (tr - (rsign * max(*(xt++), *(yt))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (gsign * max(*(xt++), *(yt + 1))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (bsign * max(*(xt++), *(yt + 2))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
+
+
+void BImage::egradient(void) {
+  // elliptic gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  float drx, dgx, dbx, dry, dgy, dby, yr, yg, yb, xr, xg, xb;
+  int rsign, gsign, bsign;
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int *xt = xtable, *yt = ytable,
+    tr = (unsigned long) to.red(),
+    tg = (unsigned long) to.green(),
+    tb = (unsigned long) to.blue();
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  rsign = (drx < 0) ? -1 : 1;
+  gsign = (dgx < 0) ? -1 : 1;
+  bsign = (dbx < 0) ? -1 : 1;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; x++) {
+    *(xt++) = (unsigned long) (xr * xr);
+    *(xt++) = (unsigned long) (xg * xg);
+    *(xt++) = (unsigned long) (xb * xb);
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; y++) {
+    *(yt++) = (unsigned long) (yr * yr);
+    *(yt++) = (unsigned long) (yg * yg);
+    *(yt++) = (unsigned long) (yb * yb);
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal egradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = (unsigned char)
+          (tr - (rsign * control->getSqrt(*(xt++) + *(yt))));
+        *(pg++) = (unsigned char)
+          (tg - (gsign * control->getSqrt(*(xt++) + *(yt + 1))));
+        *(pb++) = (unsigned char)
+          (tb - (bsign * control->getSqrt(*(xt++) + *(yt + 2))));
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = (unsigned char)
+            (tr - (rsign * control->getSqrt(*(xt++) + *(yt))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char)
+            (tg - (gsign * control->getSqrt(*(xt++) + *(yt + 1))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char)
+            (tb - (bsign * control->getSqrt(*(xt++) + *(yt + 2))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = (unsigned char)
+            (tr - (rsign * control->getSqrt(*(xt++) + *(yt))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char)
+          (tg - (gsign * control->getSqrt(*(xt++) + *(yt + 1))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char)
+            (tb - (bsign * control->getSqrt(*(xt++) + *(yt + 2))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
+
+
+void BImage::pcgradient(void) {
+  // pipe cross gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  float drx, dgx, dbx, dry, dgy, dby, xr, xg, xb, yr, yg, yb;
+  int rsign, gsign, bsign;
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int *xt = xtable, *yt = ytable,
+    tr = to.red(),
+    tg = to.green(),
+    tb = to.blue();
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  rsign = (drx < 0) ? -2 : 2;
+  gsign = (dgx < 0) ? -2 : 2;
+  bsign = (dbx < 0) ? -2 : 2;
+
+  xr = yr = (drx / 2);
+  xg = yg = (dgx / 2);
+  xb = yb = (dbx / 2);
+
+  // Create X table
+  drx /= width;
+  dgx /= width;
+  dbx /= width;
+
+  for (x = 0; x < width; x++) {
+    *(xt++) = (unsigned char) ((xr < 0) ? -xr : xr);
+    *(xt++) = (unsigned char) ((xg < 0) ? -xg : xg);
+    *(xt++) = (unsigned char) ((xb < 0) ? -xb : xb);
+
+    xr -= drx;
+    xg -= dgx;
+    xb -= dbx;
+  }
+
+  // Create Y table
+  dry /= height;
+  dgy /= height;
+  dby /= height;
+
+  for (y = 0; y < height; y++) {
+    *(yt++) = ((unsigned char) ((yr < 0) ? -yr : yr));
+    *(yt++) = ((unsigned char) ((yg < 0) ? -yg : yg));
+    *(yt++) = ((unsigned char) ((yb < 0) ? -yb : yb));
+
+    yr -= dry;
+    yg -= dgy;
+    yb -= dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal pcgradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = (unsigned char) (tr - (rsign * min(*(xt++), *(yt))));
+        *(pg++) = (unsigned char) (tg - (gsign * min(*(xt++), *(yt + 1))));
+        *(pb++) = (unsigned char) (tb - (bsign * min(*(xt++), *(yt + 2))));
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = (unsigned char) (tr - (rsign * min(*(xt++), *(yt))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (bsign * min(*(xt++), *(yt + 1))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (gsign * min(*(xt++), *(yt + 2))));
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = (unsigned char) (tr - (rsign * min(*(xt++), *(yt))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = (unsigned char) (tg - (gsign * min(*(xt++), *(yt + 1))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = (unsigned char) (tb - (bsign * min(*(xt++), *(yt + 2))));
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
+
+
+void BImage::cdgradient(void) {
+  // cross diagonal gradient -  based on original dgradient, written by
+  // Mosfet (mosfet@kde.org)
+  // adapted from kde sources for Blackbox by Brad Hughes
+
+  float drx, dgx, dbx, dry, dgy, dby, yr = 0.0, yg = 0.0, yb = 0.0,
+    xr = (float) from.red(),
+    xg = (float) from.green(),
+    xb = (float) from.blue();
+  unsigned char *pr = red, *pg = green, *pb = blue;
+  unsigned int w = width * 2, h = height * 2, *xt, *yt;
+
+  register unsigned int x, y;
+
+  dry = drx = (float) (to.red() - from.red());
+  dgy = dgx = (float) (to.green() - from.green());
+  dby = dbx = (float) (to.blue() - from.blue());
+
+  // Create X table
+  drx /= w;
+  dgx /= w;
+  dbx /= w;
+
+  for (xt = (xtable + (width * 3) - 1), x = 0; x < width; x++) {
+    *(xt--) = (unsigned char) xb;
+    *(xt--) = (unsigned char) xg;
+    *(xt--) = (unsigned char) xr;
+
+    xr += drx;
+    xg += dgx;
+    xb += dbx;
+  }
+
+  // Create Y table
+  dry /= h;
+  dgy /= h;
+  dby /= h;
+
+  for (yt = ytable, y = 0; y < height; y++) {
+    *(yt++) = (unsigned char) yr;
+    *(yt++) = (unsigned char) yg;
+    *(yt++) = (unsigned char) yb;
+
+    yr += dry;
+    yg += dgy;
+    yb += dby;
+  }
+
+  // Combine tables to create gradient
+
+  if (! interlaced) {
+    // normal cdgradient
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        *(pr++) = *(xt++) + *(yt);
+        *(pg++) = *(xt++) + *(yt + 1);
+        *(pb++) = *(xt++) + *(yt + 2);
+      }
+    }
+  } else {
+    // faked interlacing effect
+    unsigned char channel, channel2;
+
+    for (yt = ytable, y = 0; y < height; y++, yt += 3) {
+      for (xt = xtable, x = 0; x < width; x++) {
+        if (y & 1) {
+          channel = *(xt++) + *(yt);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pr++) = channel2;
+
+          channel = *(xt++) + *(yt + 1);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pg++) = channel2;
+
+          channel = *(xt++) + *(yt + 2);
+          channel2 = (channel >> 1) + (channel >> 2);
+          if (channel2 > channel) channel2 = 0;
+          *(pb++) = channel2;
+        } else {
+          channel = *(xt++) + *(yt);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pr++) = channel2;
+
+          channel = *(xt++) + *(yt + 1);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pg++) = channel2;
+
+          channel = *(xt++) + *(yt + 2);
+          channel2 = channel + (channel >> 3);
+          if (channel2 < channel) channel2 = ~0;
+          *(pb++) = channel2;
+        }
+      }
+    }
+  }
+}
diff --git a/otk/image.hh b/otk/image.hh
new file mode 100644 (file)
index 0000000..85ad287
--- /dev/null
@@ -0,0 +1,144 @@
+// -*- mode: C++; indent-tabs-mode: nil; -*-
+#ifndef   __Image_hh
+#define   __Image_hh
+
+extern "C" {
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+}
+
+#include <list>
+
+#include "timer.hh"
+#include "basedisplay.hh"
+#include "color.hh"
+
+class BImageControl;
+class BTexture;
+
+class BImage {
+private:
+  BImageControl *control;
+  bool interlaced;
+  XColor *colors;
+
+  BColor from, to;
+  int red_offset, green_offset, blue_offset, red_bits, green_bits, blue_bits,
+    ncolors, cpc, cpccpc;
+  unsigned char *red, *green, *blue, *red_table, *green_table, *blue_table;
+  unsigned int width, height, *xtable, *ytable;
+
+  void TrueColorDither(unsigned int bit_depth, int bytes_per_line,
+                      unsigned char *pixel_data);
+  void PseudoColorDither(int bytes_per_line, unsigned char *pixel_data);
+#ifdef ORDEREDPSEUDO
+  void OrderedPseudoColorDither(int bytes_per_line, unsigned char *pixel_data);
+#endif
+
+  Pixmap renderPixmap(void);
+  Pixmap render_solid(const BTexture &texture);
+  Pixmap render_gradient(const BTexture &texture);
+
+  XImage *renderXImage(void);
+
+  void invert(void);
+  void bevel1(void);
+  void bevel2(void);
+  void border(const BTexture &texture);
+  void dgradient(void);
+  void egradient(void);
+  void hgradient(void);
+  void pgradient(void);
+  void rgradient(void);
+  void vgradient(void);
+  void cdgradient(void);
+  void pcgradient(void);
+
+
+public:
+  BImage(BImageControl *c, int w, int h);
+  ~BImage(void);
+
+  Pixmap render(const BTexture &texture);
+};
+
+
+class BImageControl : public TimeoutHandler {
+public:
+  struct CachedImage {
+    Pixmap pixmap;
+
+    unsigned int count, width, height;
+    unsigned long pixel1, pixel2, texture;
+  };
+
+  BImageControl(BaseDisplay *dpy, const ScreenInfo *scrn,
+                bool _dither= False, int _cpc = 4,
+                unsigned long cache_timeout = 300000l,
+                unsigned long cmax = 200l);
+  virtual ~BImageControl(void);
+
+  inline BaseDisplay *getBaseDisplay(void) const { return basedisplay; }
+
+  inline bool doDither(void) { return dither; }
+
+  inline const ScreenInfo *getScreenInfo(void) { return screeninfo; }
+
+  inline Window getDrawable(void) const { return window; }
+
+  inline Visual *getVisual(void) { return screeninfo->getVisual(); }
+
+  inline int getBitsPerPixel(void) const { return bits_per_pixel; }
+  inline int getDepth(void) const { return screen_depth; }
+  inline int getColorsPerChannel(void) const
+    { return colors_per_channel; }
+
+  unsigned long getSqrt(unsigned int x);
+
+  Pixmap renderImage(unsigned int width, unsigned int height,
+                     const BTexture &texture);
+
+  void installRootColormap(void);
+  void removeImage(Pixmap pixmap);
+  void getColorTables(unsigned char **rmt, unsigned char **gmt,
+                      unsigned char **bmt,
+                      int *roff, int *goff, int *boff,
+                      int *rbit, int *gbit, int *bbit);
+  void getXColorTable(XColor **c, int *n);
+  void getGradientBuffers(unsigned int w, unsigned int h,
+                          unsigned int **xbuf, unsigned int **ybuf);
+  void setDither(bool d) { dither = d; }
+  void setColorsPerChannel(int cpc);
+
+  virtual void timeout(void);
+
+private:
+  bool dither;
+  BaseDisplay *basedisplay;
+  const ScreenInfo *screeninfo;
+  BTimer *timer;
+
+  Colormap colormap;
+
+  Window window;
+  XColor *colors;
+  int colors_per_channel, ncolors, screen_number, screen_depth,
+    bits_per_pixel, red_offset, green_offset, blue_offset,
+    red_bits, green_bits, blue_bits;
+  unsigned char red_color_table[256], green_color_table[256],
+    blue_color_table[256];
+  unsigned int *grad_xbuffer, *grad_ybuffer, grad_buffer_width,
+    grad_buffer_height;
+  unsigned long *sqrt_table, cache_max;
+
+  typedef std::list<CachedImage> CacheContainer;
+  CacheContainer cache;
+
+  Pixmap searchCache(const unsigned int width, const unsigned int height,
+                     const unsigned long texture,
+                     const BColor &c1, const BColor &c2);
+};
+
+
+#endif // __Image_hh
+
diff --git a/otk/image.o b/otk/image.o
new file mode 100644 (file)
index 0000000..5a67530
Binary files /dev/null and b/otk/image.o differ
diff --git a/otk/imagecontrol.cc b/otk/imagecontrol.cc
new file mode 100644 (file)
index 0000000..7d091bb
--- /dev/null
@@ -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);
+}
+
diff --git a/otk/texture.cc b/otk/texture.cc
new file mode 100644 (file)
index 0000000..22454c5
--- /dev/null
@@ -0,0 +1,183 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#ifdef HAVE_CONFIG_H
+#  include "../config.h"
+#endif // HAVE_CONFIG_H
+
+extern "C" {
+#include <stdio.h>
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+}
+
+#include <assert.h>
+
+#include "texture.hh"
+#include "basedisplay.hh"
+#include "image.hh"
+#include "screen.hh"
+#include "blackbox.hh"
+
+using std::string;
+
+
+BTexture::BTexture(const BaseDisplay * const _display,
+                   unsigned int _screen, BImageControl* _ctrl)
+  : c(_display, _screen), ct(_display, _screen),
+    lc(_display, _screen), sc(_display, _screen), bc(_display, _screen), t(0),
+    dpy(_display), ctrl(_ctrl), scrn(_screen) { }
+
+
+BTexture::BTexture(const string &d, const BaseDisplay * const _display,
+                   unsigned int _screen, BImageControl* _ctrl)
+  : c(_display, _screen), ct(_display, _screen),
+    lc(_display, _screen), sc(_display, _screen), bc(_display, _screen), t(0),
+    dpy(_display), ctrl(_ctrl), scrn(_screen) {
+  setDescription(d);
+}
+
+
+void BTexture::setColor(const BColor &cc) {
+  c = cc;
+  c.setDisplay(display(), screen());
+
+  unsigned char r, g, b, rr, gg, bb;
+
+  // calculate the light color
+  r = c.red();
+  g = c.green();
+  b = c.blue();
+  rr = r + (r >> 1);
+  gg = g + (g >> 1);
+  bb = b + (b >> 1);
+  if (rr < r) rr = ~0;
+  if (gg < g) gg = ~0;
+  if (bb < b) bb = ~0;
+  lc = BColor(rr, gg, bb, display(), screen());
+
+  // calculate the shadow color
+  r = c.red();
+  g = c.green();
+  b = c.blue();
+  rr = (r >> 2) + (r >> 1);
+  gg = (g >> 2) + (g >> 1);
+  bb = (b >> 2) + (b >> 1);
+  if (rr > r) rr = 0;
+  if (gg > g) gg = 0;
+  if (bb > b) bb = 0;
+  sc = BColor(rr, gg, bb, display(), screen());
+}
+
+
+void BTexture::setDescription(const string &d) {
+  descr.erase();
+  descr.reserve(d.length());
+
+  string::const_iterator it = d.begin(), end = d.end();
+  for (; it != end; ++it)
+    descr += tolower(*it);
+
+  if (descr.find("parentrelative") != string::npos) {
+    setTexture(BTexture::Parent_Relative);
+  } else {
+    setTexture(0);
+
+    if (descr.find("gradient") != string::npos) {
+      addTexture(BTexture::Gradient);
+      if (descr.find("crossdiagonal") != string::npos)
+        addTexture(BTexture::CrossDiagonal);
+      else if (descr.find("rectangle") != string::npos)
+        addTexture(BTexture::Rectangle);
+      else if (descr.find("pyramid") != string::npos)
+        addTexture(BTexture::Pyramid);
+      else if (descr.find("pipecross") != string::npos)
+        addTexture(BTexture::PipeCross);
+      else if (descr.find("elliptic") != string::npos)
+        addTexture(BTexture::Elliptic);
+      else if (descr.find("horizontal") != string::npos)
+        addTexture(BTexture::Horizontal);
+      else if (descr.find("vertical") != string::npos)
+        addTexture(BTexture::Vertical);
+      else
+        addTexture(BTexture::Diagonal);
+    } else {
+      addTexture(BTexture::Solid);
+    }
+
+    if (descr.find("sunken") != string::npos)
+      addTexture(BTexture::Sunken);
+    else if (descr.find("flat") != string::npos)
+      addTexture(BTexture::Flat);
+    else
+      addTexture(BTexture::Raised);
+
+    if (texture() & BTexture::Flat) {
+      if (descr.find("border") != string::npos)
+        addTexture(BTexture::Border);
+    } else {
+      if (descr.find("bevel2") != string::npos)
+        addTexture(BTexture::Bevel2);
+      else
+        addTexture(BTexture::Bevel1);
+    }
+
+    if (descr.find("interlaced") != string::npos)
+      addTexture(BTexture::Interlaced);
+  }
+}
+
+void BTexture::setDisplay(const BaseDisplay * const _display,
+                          const unsigned int _screen) {
+  if (_display == display() && _screen == screen()) {
+    // nothing to do
+    return;
+  }
+
+  dpy = _display;
+  scrn = _screen;
+  c.setDisplay(_display, _screen);
+  ct.setDisplay(_display, _screen);
+  lc.setDisplay(_display, _screen);
+  sc.setDisplay(_display, _screen);
+  bc.setDisplay(_display, _screen);
+}
+
+
+BTexture& BTexture::operator=(const BTexture &tt) {
+  c  = tt.c;
+  ct = tt.ct;
+  lc = tt.lc;
+  sc = tt.sc;
+  bc = tt.bc;
+  descr = tt.descr;
+  t  = tt.t;
+  dpy = tt.dpy;
+  scrn = tt.scrn;
+  ctrl = tt.ctrl;
+
+  return *this;
+}
+
+
+Pixmap BTexture::render(const unsigned int width, const unsigned int height,
+                        const Pixmap old) {
+  assert(display() != 0);
+  assert(texture() != BTexture::NoTexture);
+
+  if (texture() == (BTexture::Flat | BTexture::Solid))
+    return None;
+  if (texture() == BTexture::Parent_Relative)
+    return ParentRelative;
+
+  if (screen() == ~(0u))
+    scrn = DefaultScreen(display()->getXDisplay());
+
+  assert(ctrl != 0);
+  Pixmap ret = ctrl->renderImage(width, height, *this);
+
+  if (old)
+    ctrl->removeImage(old);
+
+  return ret;
+}
diff --git a/otk/texture.hh b/otk/texture.hh
new file mode 100644 (file)
index 0000000..514a058
--- /dev/null
@@ -0,0 +1,92 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef TEXTURE_HH
+#define TEXTURE_HH
+
+#include "color.hh"
+#include "util.hh"
+class BImageControl;
+
+#include <string>
+
+class BTexture {
+public:
+  enum Type {
+    // No texture
+    NoTexture           = (0),    
+    // bevel options
+    Flat                = (1l<<0),
+    Sunken              = (1l<<1),
+    Raised              = (1l<<2),
+    // textures
+    Solid               = (1l<<3),
+    Gradient            = (1l<<4),
+    // gradients
+    Horizontal          = (1l<<5),
+    Vertical            = (1l<<6),
+    Diagonal            = (1l<<7),
+    CrossDiagonal       = (1l<<8),
+    Rectangle           = (1l<<9),
+    Pyramid             = (1l<<10),
+    PipeCross           = (1l<<11),
+    Elliptic            = (1l<<12),
+    // bevel types
+    Bevel1              = (1l<<13),
+    Bevel2              = (1l<<14),
+    // flat border
+    Border              = (1l<<15),
+    // inverted image
+    Invert              = (1l<<16),
+    // parent relative image
+    Parent_Relative     = (1l<<17),
+    // fake interlaced image
+    Interlaced          = (1l<<18)
+  };
+
+  BTexture(const BaseDisplay * const _display = 0,
+           unsigned int _screen = ~(0u), BImageControl* _ctrl = 0);
+  BTexture(const std::string &_description,
+           const BaseDisplay * const _display = 0,
+           unsigned int _screen = ~(0u), BImageControl* _ctrl = 0);
+
+  void setColor(const BColor &_color);
+  void setColorTo(const BColor &_colorTo) { ct = _colorTo; }
+  void setBorderColor(const BColor &_borderColor) { bc = _borderColor; }
+
+  const BColor &color(void) const { return c; }
+  const BColor &colorTo(void) const { return ct; }
+  const BColor &lightColor(void) const { return lc; }
+  const BColor &shadowColor(void) const { return sc; }
+  const BColor &borderColor(void) const { return bc; }
+
+  unsigned long texture(void) const { return t; }
+  void setTexture(const unsigned long _texture) { t  = _texture; }
+  void addTexture(const unsigned long _texture) { t |= _texture; }
+
+  BTexture &operator=(const BTexture &tt);
+  inline bool operator==(const BTexture &tt)
+  { return (c == tt.c && ct == tt.ct && lc == tt.lc &&
+            sc == tt.sc && t == tt.t); }
+  inline bool operator!=(const BTexture &tt)
+  { return (! operator==(tt)); }
+
+  const BaseDisplay *display(void) const { return dpy; }
+  unsigned int screen(void) const { return scrn; }
+  void setDisplay(const BaseDisplay * const _display,
+                  const unsigned int _screen);
+  void setImageControl(BImageControl* _ctrl) { ctrl = _ctrl; }
+  const std::string &description(void) const { return descr; }
+  void setDescription(const std::string &d);
+
+  Pixmap render(const unsigned int width, const unsigned int height,
+                const Pixmap old = 0);
+
+private:
+  BColor c, ct, lc, sc, bc;
+  std::string descr;
+  unsigned long t;
+  const BaseDisplay *dpy;
+  BImageControl *ctrl;
+  unsigned int scrn;
+};
+
+#endif // TEXTURE_HH