From: Marius Nita Date: Fri, 1 Nov 2002 03:27:41 +0000 (+0000) Subject: adding toolkit beginnings X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=85c41a1aec90b8daefc425596ea34b6f9d0e643c;p=chaz%2Fopenbox adding toolkit beginnings --- diff --git a/otk/Makefile.am b/otk/Makefile.am new file mode 100644 index 00000000..835874d5 --- /dev/null +++ b/otk/Makefile.am @@ -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 index 00000000..ad30c7a3 --- /dev/null +++ b/otk/color.cc @@ -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 +} + +#include + +#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 index 00000000..5f3cb955 --- /dev/null +++ b/otk/color.hh @@ -0,0 +1,106 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef COLOR_HH +#define COLOR_HH + +extern "C" { +#include +} + +#include +#include + +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 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 index 00000000..1fcba15c Binary files /dev/null and b/otk/color.o differ diff --git a/otk/font.cc b/otk/font.cc new file mode 100644 index 00000000..91f8ffb6 --- /dev/null +++ b/otk/font.cc @@ -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 +#endif // HAVE_STDLIB_H +} + +#include +#include + +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 index 00000000..c070bbff --- /dev/null +++ b/otk/font.hh @@ -0,0 +1,82 @@ +// -*- mode: C++; indent-tabs-mode: nil; -*- +#ifndef __Font_hh +#define __Font_hh + +extern "C" { +#include + +#include +} + +#include + +#include + +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 index 00000000..1882f7f1 Binary files /dev/null and b/otk/font.o differ diff --git a/otk/gccache.cc b/otk/gccache.cc new file mode 100644 index 00000000..2ab37f34 --- /dev/null +++ b/otk/gccache.cc @@ -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 +} + +#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 index 00000000..499ed8a4 --- /dev/null +++ b/otk/gccache.hh @@ -0,0 +1,119 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef GCCACHE_HH +#define GCCACHE_HH + +extern "C" { +#include +} + +#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 index 00000000..28af99de Binary files /dev/null and b/otk/gccache.o differ diff --git a/otk/image.cc b/otk/image.cc new file mode 100644 index 00000000..21e9d690 --- /dev/null +++ b/otk/image.cc @@ -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 +#endif // HAVE_STDIO_H + +#include +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 + // 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 index 00000000..85ad287d --- /dev/null +++ b/otk/image.hh @@ -0,0 +1,144 @@ +// -*- mode: C++; indent-tabs-mode: nil; -*- +#ifndef __Image_hh +#define __Image_hh + +extern "C" { +#include +#include +} + +#include + +#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 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 index 00000000..5a675308 Binary files /dev/null and b/otk/image.o differ diff --git a/otk/imagecontrol.cc b/otk/imagecontrol.cc new file mode 100644 index 00000000..7d091bb8 --- /dev/null +++ b/otk/imagecontrol.cc @@ -0,0 +1,561 @@ +// -*- mode: C++; indent-tabs-mode: nil; -*- + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif // HAVE_CONFIG_H + +extern "C" { +#ifdef HAVE_STDIO_H +# include +#endif // HAVE_STDIO_H + +#ifdef HAVE_CTYPE_H +# include +#endif // HAVE_CTYPE_H + +#include +} + +#include + +#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 index 00000000..22454c59 --- /dev/null +++ b/otk/texture.cc @@ -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 +#ifdef HAVE_CTYPE_H +#include +#endif +} + +#include + +#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 index 00000000..514a0584 --- /dev/null +++ b/otk/texture.hh @@ -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 + +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