// Image.cc for Blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// stupid macros needed to access some functions in version 2 of the GNU C
// library
#ifndef   _GNU_SOURCE
#define   _GNU_SOURCE
#endif // _GNU_SOURCE

#ifdef    HAVE_CONFIG_H
#  include "config.h"
#endif // HAVE_CONFIG_H

#include "BaseDisplay.hh"
#include "Image.hh"

#ifdef    HAVE_SYS_TYPES_H
#  include <sys/types.h>
#endif // HAVE_SYS_TYPES_H

#ifndef u_int32_t
#  ifdef uint_32_t
typedef uint32_t u_int32_t;
#  else
#    ifdef __uint32_t
typedef __uint32_t u_int32_t;
#    else
typedef unsigned int u_int32_t;
#    endif
#  endif
#endif

#ifdef    STDC_HEADERS
#  include <stdlib.h>
#  include <string.h>
#endif // STDC_HEADERS

#ifdef    HAVE_STDIO_H
#  include <stdio.h>
#endif // HAVE_STDIO_H

#ifdef    HAVE_CTYPE_H
#  include <ctype.h>
#endif // HAVE_CTYPE_H

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


BImage::BImage(BImageControl *c, unsigned int w, unsigned int h)
{
	control = c;

	width = ((signed) w > 0) ? w : 1;
	height = ((signed) 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)
{
	if (red) delete [] red;
	if (green) delete [] green;
	if (blue) delete [] blue;
}


Pixmap BImage::render(BTexture *texture)
{
	if (texture->getTexture() & BImage_ParentRelative)
		return ParentRelative;
	else if (texture->getTexture() & BImage_Solid)
		return render_solid(texture);
	else if (texture->getTexture() & BImage_Gradient)
		return render_gradient(texture);

	return None;
}


Pixmap BImage::render_solid(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;
	}

	XGCValues gcv;
	GC gc, hgc, lgc;

	gcv.foreground = texture->getColor()->getPixel();
	gcv.fill_style = FillSolid;
	gc = XCreateGC(control->getBaseDisplay()->getXDisplay(), pixmap,
	               GCForeground | GCFillStyle, &gcv);

	gcv.foreground = texture->getHiColor()->getPixel();
	hgc = XCreateGC(control->getBaseDisplay()->getXDisplay(), pixmap,
	                GCForeground, &gcv);

	gcv.foreground = texture->getLoColor()->getPixel();
	lgc = XCreateGC(control->getBaseDisplay()->getXDisplay(), pixmap,
	                GCForeground, &gcv);

	XFillRectangle(control->getBaseDisplay()->getXDisplay(), pixmap, gc, 0, 0,
	               width, height);

#ifdef    INTERLACE
	if (texture->getTexture() & BImage_Interlaced) {
		gcv.foreground = texture->getColorTo()->getPixel();
		GC igc = XCreateGC(control->getBaseDisplay()->getXDisplay(), pixmap,
		                   GCForeground, &gcv);

		register unsigned int i = 0;
		for (; i < height; i += 2)
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, igc,
			          0, i, width, i);

		XFreeGC(control->getBaseDisplay()->getXDisplay(), igc);
	}
#endif // INTERLACE


	if (texture->getTexture() & BImage_Bevel1) {
		if (texture->getTexture() & BImage_Raised) {
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          0, height - 1, width - 1, height - 1);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          width - 1, height - 1, width - 1, 0);

			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          0, 0, width - 1, 0);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          0, height - 1, 0, 0);
		} else if (texture->getTexture() & BImage_Sunken) {
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          0, height - 1, width - 1, height - 1);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          width - 1, height - 1, width - 1, 0);

			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          0, 0, width - 1, 0);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          0, height - 1, 0, 0);
		}
	} else if (texture->getTexture() & BImage_Bevel2) {
		if (texture->getTexture() & BImage_Raised) {
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          1, height - 3, width - 3, height - 3);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          width - 3, height - 3, width - 3, 1);

			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          1, 1, width - 3, 1);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          1, height - 3, 1, 1);
		} else if (texture->getTexture() & BImage_Sunken) {
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          1, height - 3, width - 3, height - 3);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, hgc,
			          width - 3, height - 3, width - 3, 1);

			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          1, 1, width - 3, 1);
			XDrawLine(control->getBaseDisplay()->getXDisplay(), pixmap, lgc,
			          1, height - 3, 1, 1);
		}
	}

	XFreeGC(control->getBaseDisplay()->getXDisplay(), gc);
	XFreeGC(control->getBaseDisplay()->getXDisplay(), hgc);
	XFreeGC(control->getBaseDisplay()->getXDisplay(), lgc);

	return pixmap;
}


Pixmap BImage::render_gradient(BTexture *texture)
{
	int inverted = 0;

#ifdef    INTERLACE
	interlaced = texture->getTexture() & BImage_Interlaced;
#endif // INTERLACE

	if (texture->getTexture() & BImage_Sunken) {
		from = texture->getColorTo();
		to = texture->getColor();

		if (! (texture->getTexture() & BImage_Invert)) inverted = 1;
	} else {
		from = texture->getColor();
		to = texture->getColorTo();

		if (texture->getTexture() & BImage_Invert) inverted = 1;
	}

	control->getGradientBuffers(width, height, &xtable, &ytable);

	if (texture->getTexture() & BImage_Diagonal) dgradient();
	else if (texture->getTexture() & BImage_Elliptic) egradient();
	else if (texture->getTexture() & BImage_Horizontal) hgradient();
	else if (texture->getTexture() & BImage_Pyramid) pgradient();
	else if (texture->getTexture() & BImage_Rectangle) rgradient();
	else if (texture->getTexture() & BImage_Vertical) vgradient();
	else if (texture->getTexture() & BImage_CrossDiagonal) cdgradient();
	else if (texture->getTexture() & BImage_PipeCross) pcgradient();

	if (texture->getTexture() & BImage_Bevel1) bevel1();
	else if (texture->getTexture() & BImage_Bevel2) bevel2();

	if (inverted) invert();

	Pixmap pixmap = renderPixmap();

	return pixmap;

}


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)];
	register unsigned int x, y, dithx, dithy, r, g, b, o, er, eg, eb, offset;

	unsigned char *pixel_data = d, *ppixel_data = d;
	unsigned long pixel;

	o = image->bits_per_pixel + ((image->byte_order == MSBFirst) ? 1 : 0);

	if (control->doDither() && width > 1 && height > 1) {
		unsigned char dither4[4][4] = { {0, 4, 1, 5},
		                                {6, 2, 7, 3},
		                                {1, 5, 0, 4},
		                                {7, 3, 6, 2} };

#ifdef    ORDEREDPSEUDO
		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 } };
#endif // ORDEREDPSEUDO

		switch (control->getVisual()->c_class) {
		case TrueColor:
			// 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

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

					switch (o) {
					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;
					}
				}

				pixel_data = (ppixel_data += image->bytes_per_line);
			}

			break;

		case StaticColor:
		case PseudoColor:

			{
#ifndef   ORDEREDPSEUDO
				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();

				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;
#endif // ORDEREDPSEUDO

				for (y = 0, offset = 0; y < height; y++) {
#ifdef    ORDEREDPSEUDO
					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 += image->bytes_per_line);
				}
#else // !ORDEREDPSEUDO
					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 += image->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;
#endif // ORDEREDPSUEDO

				break;
			}

			/*
			   case StaticGray:
			   case GrayScale:
			   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 & 0x7;
			   eg = g & 0x7;
			   eb = b & 0x7;

			   if ((dither[dithy][dithx] < er) && (r < (256 - 8)))
			   r += 8;
			   if ((dither[dithy][dithx] < (eg << 1)) && (g < (256 - 4)))
			   g += 4;
			   if ((dither[dithy][dithx] < eb) && (b < (256 - 8)))
			   b += 8;

			   r = *(red_table + r);
			   g = *(green_table + g);
			   b = *(blue_table + b);

			   g = ((r * 30) + (g * 59) + (b * 11)) / 100;
			   *pixel_data++ = colors[g].pixel;
			   }

			   pixel_data = (ppixel_data += image->bytes_per_line);
			   }

			   break;
			*/

		default:
			fprintf(stderr,
			        "BImage::renderXImage: unsupported visual\n");
			delete [] d;
			XDestroyImage(image);
			return (XImage *) 0;
		}
	}
	else
	{
		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);

					switch (o) {
					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;
					}
				}

				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:
			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;
	} else 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::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->getRed(),
	             xg = (float) from->getGreen(),
	                  xb = (float) from->getBlue();
	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->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	// 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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


void BImage::hgradient(void) {
	float drx, dgx, dbx,
	xr = (float) from->getRed(),
	     xg = (float) from->getGreen(),
	          xb = (float) from->getBlue();
	unsigned char *pr = red, *pg = green, *pb = blue;

	register unsigned int x, y;

	drx = (float) (to->getRed() - from->getRed());
	dgx = (float) (to->getGreen() - from->getGreen());
	dbx = (float) (to->getBlue() - from->getBlue());

	drx /= width;
	dgx /= width;
	dbx /= width;

#ifdef    INTERLACE
	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 {
#endif // INTERLACE

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

#ifdef    INTERLACE
	}
#endif // INTERLACE

}


void BImage::vgradient(void) {
	float dry, dgy, dby,
	yr = (float) from->getRed(),
	     yg = (float) from->getGreen(),
	          yb = (float) from->getBlue();
	unsigned char *pr = red, *pg = green, *pb = blue;

	register unsigned int y;

	dry = (float) (to->getRed() - from->getRed());
	dgy = (float) (to->getGreen() - from->getGreen());
	dby = (float) (to->getBlue() - from->getBlue());

	dry /= height;
	dgy /= height;
	dby /= height;

#ifdef    INTERLACE
	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 {
#endif // INTERLACE

		// 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;
		}

#ifdef    INTERLACE
	}
#endif // INTERLACE

}


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->getRed(), tg = to->getGreen(), tb = to->getBlue(),
	                                     *xt = xtable, *yt = ytable;

	register unsigned int x, y;

	dry = drx = (float) (to->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


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->getRed(), tg = to->getGreen(), tb = to->getBlue(),
	                                     *xt = xtable, *yt = ytable;

	register unsigned int x, y;

	dry = drx = (float) (to->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


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->getRed(),
	                                      tg = (unsigned long) to->getGreen(),
	                                           tb = (unsigned long) to->getBlue();

	register unsigned int x, y;

	dry = drx = (float) (to->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


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->getRed(),
	                                      tg = to->getGreen(),
	                                           tb = to->getBlue();

	register unsigned int x, y;

	dry = drx = (float) (to->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


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->getRed(),
	             xg = (float) from->getGreen(),
	                  xb = (float) from->getBlue();
	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->getRed() - from->getRed());
	dgy = dgx = (float) (to->getGreen() - from->getGreen());
	dby = dbx = (float) (to->getBlue() - from->getBlue());

	// 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

#ifdef    INTERLACE
	if (! interlaced) {
#endif // INTERLACE

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

#ifdef    INTERLACE
	} 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;
				}
			}
		}
	}
#endif // INTERLACE

}


BImageControl::BImageControl(BaseDisplay *dpy, ScreenInfo *scrn, Bool _dither,
                             int _cpc, unsigned long cache_timeout,
                             unsigned long cmax)
{
	basedisplay = dpy;
	screeninfo = scrn;
	setDither(_dither);
	setColorsPerChannel(_cpc);

	cache_max = cmax;
#ifdef    TIMEDCACHE
	if (cache_timeout) {
		timer = new BTimer(basedisplay, this);
		timer->setTimeout(cache_timeout);
		timer->start();
	} else
		timer = (BTimer *) 0;
#endif // TIMEDCACHE

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

	int count;
	XPixmapFormatValues *pmv = XListPixmapFormats(basedisplay->getXDisplay(),
	                           &count);
	root_colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);

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

			basedisplay->grab();

			for (i = 0; i < ncolors; i++)
				if (! XAllocColor(basedisplay->getXDisplay(), getColormap(),
				                  &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;

			basedisplay->ungrab();

			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(), getColormap(), 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(), getColormap(),
							                &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;

			basedisplay->grab();
			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(), getColormap(),
				                  &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;
			}

			basedisplay->ungrab();

			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(), getColormap(), 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(), getColormap(),
							                &colors[i])) {
								colors[i].flags = DoRed|DoGreen|DoBlue;
								break;
							}
						}
					}
				}
			}

			break;
		}

	default:
		fprintf(stderr,
		        "BImageControl::BImageControl: unsupported visual %d\n",
		        getVisual()->c_class);
		exit(1);
	}

	cache = new LinkedList<Cache>;
}


BImageControl::~BImageControl(void) {
	if (sqrt_table) {
		delete [] sqrt_table;
	}

	if (grad_xbuffer) {
		delete [] grad_xbuffer;
	}

	if (grad_ybuffer) {
		delete [] grad_ybuffer;
	}

	if (colors) {
		unsigned long *pixels = new unsigned long [ncolors];

		int i;
		for (i = 0; i < ncolors; i++)
			*(pixels + i) = (*(colors + i)).pixel;

		XFreeColors(basedisplay->getXDisplay(), getColormap(),
		            pixels, ncolors, 0);

		delete [] colors;
	}

	if (cache->count()) {
		int i, n = cache->count();
		fprintf(stderr,
		        "BImageContol::~BImageControl: pixmap cache - "
		        "releasing %d pixmaps\n", n);

		for (i = 0; i < n; i++) {
			Cache *tmp = cache->first();
			XFreePixmap(basedisplay->getXDisplay(), tmp->pixmap);
			cache->remove(tmp);
			delete tmp;
		}

#ifdef    TIMEDCACHE
		if (timer) {
			timer->stop();
			delete timer;
		}
#endif // TIMEDCACHE
	}

	delete cache;
}


Pixmap BImageControl::searchCache(unsigned int width, unsigned int height,
                                  unsigned long texture,
                                  BColor *c1, BColor *c2) {
	if (cache->count()) {
		LinkedListIterator<Cache> it(cache);

		for (; it.current(); it++) {
			if ((it.current()->width == width) &&
			        (it.current()->height == height) &&
			        (it.current()->texture == texture) &&
			        (it.current()->pixel1 == c1->getPixel()))
				if (texture & BImage_Gradient) {
					if (it.current()->pixel2 == c2->getPixel()) {
						it.current()->count++;
						return it.current()->pixmap;
					}
				} else {
					it.current()->count++;
					return it.current()->pixmap;
				}
		}
	}

	return None;
}


Pixmap BImageControl::renderImage(unsigned int width, unsigned int height,
                                  BTexture *texture) {
	if (texture->getTexture() & BImage_ParentRelative) return ParentRelative;

	Pixmap pixmap = searchCache(width, height, texture->getTexture(),
	                            texture->getColor(), texture->getColorTo());
	if (pixmap) return pixmap;

	BImage image(this, width, height);
	pixmap = image.render(texture);

	if (pixmap) {
		Cache *tmp = new Cache;

		tmp->pixmap = pixmap;
		tmp->width = width;
		tmp->height = height;
		tmp->count = 1;
		tmp->texture = texture->getTexture();
		tmp->pixel1 = texture->getColor()->getPixel();

		if (texture->getTexture() & BImage_Gradient)
			tmp->pixel2 = texture->getColorTo()->getPixel();
		else
			tmp->pixel2 = 0l;

		cache->insert(tmp);

		if ((unsigned) cache->count() > cache_max) {
#ifdef    DEBUG
			fprintf(stderr,
			        "BImageControl::renderImage: cache is large, "
			        "forcing cleanout\n");
#endif // DEBUG

			timeout();
		}

		return pixmap;
	}

	return None;
}


void BImageControl::removeImage(Pixmap pixmap) {
	if (pixmap) {
		LinkedListIterator<Cache> it(cache);
		for (; it.current(); it++) {
			if (it.current()->pixmap == pixmap) {
				Cache *tmp = it.current();

				if (tmp->count) {
					tmp->count--;

#ifdef    TIMEDCACHE
					if (! timer) timeout();
#else // !TIMEDCACHE
					if (! tmp->count) timeout();
#endif // TIMEDCACHE
				}

				return;
			}
		}
	}
}


unsigned long BImageControl::getColor(const char *colorname,
                                      unsigned char *r, unsigned char *g,
                                      unsigned char *b)
{
	XColor color;
	color.pixel = 0;

	if (! XParseColor(basedisplay->getXDisplay(), getColormap(),
	                  colorname, &color)) {
		fprintf(stderr, "BImageControl::getColor: color parse error: \"%s\"\n",
		        colorname);
	} else if (! XAllocColor(basedisplay->getXDisplay(), getColormap(),
	                         &color)) {
		fprintf(stderr, "BImageControl::getColor: color alloc error: \"%s\"\n",
		        colorname);
	}

	if (color.red == 65535) *r = 0xff;
	else *r = (unsigned char) (color.red / 0xff);
	if (color.green == 65535) *g = 0xff;
	else *g = (unsigned char) (color.green / 0xff);
	if (color.blue == 65535) *b = 0xff;
	else *b = (unsigned char) (color.blue / 0xff);

	return color.pixel;
}


unsigned long BImageControl::getColor(const char *colorname) {
	XColor color;
	color.pixel = 0;

	if (! XParseColor(basedisplay->getXDisplay(), getColormap(),
	                  colorname, &color)) {
		fprintf(stderr, "BImageControl::getColor: color parse error: \"%s\"\n",
		        colorname);
	} else if (! XAllocColor(basedisplay->getXDisplay(), getColormap(),
	                         &color)) {
		fprintf(stderr, "BImageControl::getColor: color alloc error: \"%s\"\n",
		        colorname);
	}

	return color.pixel;
}


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) {
	basedisplay->grab();

	Bool install = True;
	int i = 0, ncmap = 0;
	Colormap *cmaps =
	    XListInstalledColormaps(basedisplay->getXDisplay(), window, &ncmap);

	if (cmaps) {
		for (i = 0; i < ncmap; i++)
			if (*(cmaps + i) == getColormap())
				install = False;

		if (install)
			XInstallColormap(basedisplay->getXDisplay(), getColormap());

		XFree(cmaps);
	}

	basedisplay->ungrab();
}


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];
		int i = 0;

		for (; i < (256 * 256 * 2); i++)
			*(sqrt_table + i) = bsqrt(i);
	}

	return (*(sqrt_table + x));
}


void BImageControl::parseTexture(BTexture *texture, char *t) {
	if ((! texture) || (! t)) return;

	int t_len = strlen(t) + 1, i;
	char *ts = new char[t_len];
	if (! ts) return;

	// convert to lower case
	for (i = 0; i < t_len; i++)
		*(ts + i) = tolower(*(t + i));

	if (strstr(ts, "parentrelative")) {
		texture->setTexture(BImage_ParentRelative);
	} else {
		texture->setTexture(0);

		if (strstr(ts, "solid"))
			texture->addTexture(BImage_Solid);
		else if (strstr(ts, "gradient")) {
			texture->addTexture(BImage_Gradient);
			if (strstr(ts, "crossdiagonal"))
				texture->addTexture(BImage_CrossDiagonal);
			else if (strstr(ts, "rectangle"))
				texture->addTexture(BImage_Rectangle);
			else if (strstr(ts, "pyramid"))
				texture->addTexture(BImage_Pyramid);
			else if (strstr(ts, "pipecross"))
				texture->addTexture(BImage_PipeCross);
			else if (strstr(ts, "elliptic"))
				texture->addTexture(BImage_Elliptic);
			else if (strstr(ts, "diagonal"))
				texture->addTexture(BImage_Diagonal);
			else if (strstr(ts, "horizontal"))
				texture->addTexture(BImage_Horizontal);
			else if (strstr(ts, "vertical"))
				texture->addTexture(BImage_Vertical);
			else
				texture->addTexture(BImage_Diagonal);
		} else
			texture->addTexture(BImage_Solid);

		if (strstr(ts, "raised"))
			texture->addTexture(BImage_Raised);
		else if (strstr(ts, "sunken"))
			texture->addTexture(BImage_Sunken);
		else if (strstr(ts, "flat"))
			texture->addTexture(BImage_Flat);
		else
			texture->addTexture(BImage_Raised);

		if (! (texture->getTexture() & BImage_Flat))
			if (strstr(ts, "bevel2"))
				texture->addTexture(BImage_Bevel2);
			else
				texture->addTexture(BImage_Bevel1);

#ifdef    INTERLACE
		if (strstr(ts, "interlaced"))
			texture->addTexture(BImage_Interlaced);
#endif // INTERLACE
	}

	delete [] ts;
}


void BImageControl::parseColor(BColor *color, char *c) {
	if (! color) return;

	if (color->isAllocated()) {
		unsigned long pixel = color->getPixel();

		XFreeColors(basedisplay->getXDisplay(), getColormap(), &pixel, 1, 0);

		color->setPixel(0l);
		color->setRGB(0, 0, 0);
		color->setAllocated(False);
	}

	if (c) {
		unsigned char r, g, b;

		color->setPixel(getColor(c, &r, &g, &b));
		color->setRGB(r, g, b);
		color->setAllocated(True);
	}
}


void BImageControl::timeout(void) {
	LinkedListIterator<Cache> it(cache);
	for (; it.current(); it++) {
		Cache *tmp = it.current();

		if (tmp->count <= 0) {
			XFreePixmap(basedisplay->getXDisplay(), tmp->pixmap);
			cache->remove(tmp);
			delete tmp;
		}
	}
}


syntax highlighted by Code2HTML, v. 0.9.1