

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file Bitmap.cpp
    @brief Implementation of Bitmap and BitmapUpdate: RGBA raster storage, drawing
    primitives, integer-ratio resizers, BMP file (de)serialization and the
    RLE/difference codecs used for incremental remote-display updates.
    See Bitmap.h for the pixel layout (top-down RGBA, R in the lowest byte)
    and buffer-ownership rules the routines here rely on. */
#include "Bitmap.h"
#include "PsyTime.h"
#include "UnitTestFramework.h"


namespace cmlabs {

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Bitmap::Bitmap() {
	init(0, 0);
}

Bitmap::Bitmap(const char* filename) {
	init(0, 0);
	if (!this->readFromFile(filename)) {
		printf("Error initialising Bitmap from file '%s'...\n", (char*) filename);
	}
}

Bitmap::Bitmap(uint32 w, uint32 h) {
	init(w, h);
}

Bitmap::Bitmap(char* imgData, uint32 w, uint32 h, bool takeData) {
	if (takeData) {
		width = w;
		height = h;
		size = w * h * 4;
		data = imgData;
		//setMaxUpdates(0);
		setBitmapUpdated();
	}
	else {
		init(w, h);
		memcpy(data, imgData, size);
	}
}

Bitmap::Bitmap(const char* imgData, uint32 size, uint32 w, uint32 h, uint32 linestep) {
	init(imgData, size, w, h, "RGBA", linestep);
}
Bitmap::Bitmap(const char* imgData, uint32 size, uint32 w, uint32 h, const char* encoding) {
	init(imgData, size, w, h, encoding, size/h);
}
Bitmap::Bitmap(const char* imgData, uint32 size, uint32 w, uint32 h, const char* encoding, uint32 linestep) {
	init(imgData, size, w, h, encoding, linestep);
}

bool Bitmap::init(const char* imgData, uint32 size, uint32 width, uint32 height, const char* encoding, uint32 linestep) {
	if ((stricmp(encoding, "RGBA") == 0) && (linestep == width * 4)) {
		init(width, height);
		memcpy(data, imgData, size);
		return true;
	}

	uint32 len = 0;
	char* newData = DataToEncodingFormat(imgData, size, width, height, encoding, linestep, "rgba", len);

	if (!newData || !len)
		return false;

	this->width = width;
	this->height = height;
	this->size = len;
	this->data = newData;
	//setMaxUpdates(0);
	setBitmapUpdated();
	return true;

	//uint32 dstBytesPerLine = width * 4;

	//const char* src;
	//char* dst = this->data;
	//uint32 c, l, x;
	//uint32 srcPadding;
	//double scaleFactor;

	//// Write bitmap data
	//uint32 srcPixelSize;
	//bool isUnsigned = true;
	//uint8 floatSize = 0;
	//double minVal = 0, maxVal = 0;
	//if (stricmp(encoding, "rgba") == 0)
	//	srcPixelSize = 4;
	//else if (stricmp(encoding, "rgb8") == 0)
	//	srcPixelSize = 3;
	//else if (stricmp(encoding, "16UC1") == 0) {
	//	srcPixelSize = 2;
	//	srcPadding = linestep - (srcPixelSize * width);
	//	src = imgData;
	//	maxVal = -1000000000.0;
	//	minVal = 1000000000.0;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			if ((*(uint16*)src) < minVal)
	//				minVal = *(uint16*)src;
	//			else if ((*(uint16*)src) > maxVal)
	//				maxVal = *(uint16*)src;
	//			src += 2;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else if (stricmp(encoding, "32FC1") == 0) {
	//	srcPixelSize = 4;
	//	srcPadding = linestep - (srcPixelSize * width);
	//	floatSize = 4;
	//	maxVal = -1000000000.0;
	//	minVal = 1000000000.0;
	//	src = imgData;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			if ((*(float32*)src) < minVal)
	//				minVal = *(float32*)src;
	//			else if ((*(float32*)src) > maxVal)
	//				maxVal = *(float32*)src;
	//			src += 4;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else if (stricmp(encoding, "8PC1") == 0) {
	//	srcPixelSize = 1;
	//	srcPadding = linestep - (srcPixelSize * width);
	//	maxVal = -1000000000.0;
	//	minVal = 1000000000.0;
	//	src = imgData;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			if ((*(int8*)src) < minVal)
	//				minVal = *(int8*)src;
	//			else if ((*(int8*)src) > maxVal)
	//				maxVal = *(int8*)src;
	//			src += 1;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else
	//	srcPixelSize = (uint32)(linestep / width);

	//srcPadding = linestep - (srcPixelSize * width);
	//src = imgData;

	//if (stricmp(encoding, "16UC1") == 0) {
	//	if (minVal < maxVal)
	//		scaleFactor = 1 / (maxVal - minVal)*255.0;
	//	else
	//		scaleFactor = 1;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			*dst = dst[1] = dst[2] = (uint8)(((*(uint16*)src) - minVal) * scaleFactor);
	//			dst[3] = 0;
	//			dst += 4;
	//			src += 2;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else if (stricmp(encoding, "32FC1") == 0) {
	//	if (minVal < maxVal)
	//		scaleFactor = 1 / (maxVal - minVal)*255.0;
	//	else
	//		scaleFactor = 1;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
	//			dst[3] = 0;
	//			dst += 4;
	//			src += 4;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else if (stricmp(encoding, "8PC1") == 0) {
	//	if (minVal < maxVal)
	//		scaleFactor = 1 / (maxVal - 0)*255.0;
	//	else
	//		scaleFactor = 1;
	//	for (l = 0; l < height; l++) {
	//		for (c = 0; c < width; c++) {
	//			if (*(int8*)src >= 0) {
	//				dst[0] = 0;
	//				dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
	//			}
	//			else {
	//				dst[0] = 50;
	//				dst[1] = 0;
	//			}
	//			dst[2] = dst[3] = 0;
	//			dst += 4;
	//			src += 1;
	//		}
	//		src += srcPadding;
	//	}
	//}
	//else if (srcPixelSize == 4) {
	//	for (l = 0; l < height; l++) {
	//		memcpy(dst, src, dstBytesPerLine);
	//		src += linestep;
	//		dst += dstBytesPerLine;
	//	}
	//}
	//else if (srcPixelSize == 3) {
	//	for (l = 0; l < height; l++) {
	//		x = 0;
	//		for (c = 0; c < width; c++) {
	//			*dst++ = src[x];
	//			*dst++ = src[x + 1];
	//			*dst++ = src[x + 2];
	//			*dst++ = 0;
	//			x += srcPixelSize;
	//		}
	//		src += linestep;
	//	}
	//}
	//else if (srcPixelSize == 2) {
	//	for (l = 0; l < height; l++) {
	//		x = 0;
	//		for (c = 0; c < width; c++) {
	//			*dst++ = 0;
	//			*dst++ = src[x + 1];
	//			*dst++ = src[x];
	//			*dst++ = 0;
	//			x += srcPixelSize;
	//		}
	//		src += linestep;
	//	}
	//}
	//else if (srcPixelSize == 1) {
	//	for (l = 0; l < height; l++) {
	//		x = 0;
	//		for (c = 0; c < width; c++) {
	//			*dst = dst[1] = dst[2] = src[x];
	//			dst[3] = 0;
	//			dst += 4;
	//			x += srcPixelSize;
	//		}
	//		src += linestep;
	//	}
	//}
	//else
	//	return false;
	//return true;
}

Bitmap::~Bitmap() {
	reset();
}

Bitmap::Bitmap(BitmapUpdate* update) {
	if (update == NULL) {
		init(0, 0);
		return;
	}

	int width = update->width;
	int height = update->height;

	if ((width <= 0) || (height <= 0) ) {
		init(0, 0);
		return;
	}

	if (!update->type == BitmapUpdate::FULL) {
		init(width, height);
		this->fillBox(0, 0, width, height, Color(0,0,0));
		updateBitmap(update, false);
	}
	else
		updateBitmap(update, true);
}

Bitmap::Bitmap(DataMessage* msg) {
	bool ok;
	width = (uint32)msg->getInt("Width", ok);
	height = (uint32)msg->getInt("Height", ok);
	data = msg->getDataCopy("BitmapData", size);
	if (!data || !width || !height || !size)
		reset();
	else
		setBitmapUpdated();
}

DataMessage* Bitmap::toMessage() {
	if (!data || !width || !height || !size)
		return NULL;

	DataMessage* msg = new DataMessage();
	msg->setData("BitmapData", data, size);
	msg->setInt("Width", width);
	msg->setInt("Height", height);
	return msg;
}

bool Bitmap::init(uint32 w, uint32 h) {
	width = w;
	height = h;
	size = w * h * 4;
	data = NULL;
	if (w > 0)
		data = new char[size];
	//setMaxUpdates(0);
	setBitmapUpdated();
	return true;
}





//int Bitmap::getMaxUpdates() {
//	return params.get("MaxUpdates").toInt();
//}
//
//bool Bitmap::setMaxUpdates(int max) {
//	params.put("MaxUpdates", JString(max));
//	if (aux != NULL) {
//		ObjectCollection* dict = (ObjectCollection*) aux;
//		if (dict->getCount() > max) {
//			dict->removeAll();
//		}
//	}
//	return true;
//}

bool Bitmap::addBitmapUpdateRegion(const Box& updateBox) {
	if (hasBitmapBeenTotallyChanged()) {
		return true;
	}
	double x, y;
	if ( (x = updateBox.getLeftX()) < 0) {
		updateRegion.grow(x, 0);
		updateRegion.move(-1*x, 0);
	}
	if ( (y = updateBox.getUpperY()) < 0) {
		updateRegion.grow(0, y);
		updateRegion.move(0, -1*y);
	}
	updateRegion.growToBoundingBox(updateBox);
	return true;
}


bool Bitmap::resetBitmapUpdates() {
	updateRegion = Box();
	return true;
}

bool Bitmap::setBitmapUpdated() {
	updateRegion = Box(0,0,(double)width, (double)height);
	return true;
}

bool Bitmap::hasBitmapBeenUpdated() {
	return (updateRegion.getArea() > 0);
}

bool Bitmap::hasBitmapBeenTotallyChanged() {
	return (updateRegion.getArea() > 0);
}




bool Bitmap::reset() {
	delete [] data;
	data = NULL;
	return true;
}

Bitmap* Bitmap::getCopy(const Box& box) {
	Bitmap* bitmap;
	uint32 x = (uint32) box.upperLeft.x;
	uint32 y = (uint32) box.upperLeft.y;
	uint32 w = (uint32) box.size.w;
	uint32 h = (uint32) box.size.h;

	if (box.upperLeft.x != (uint32) box.upperLeft.x)
		w++;
	if (box.upperLeft.y != (uint32) box.upperLeft.y)
		h++;
	if (box.size.w != (uint32) box.size.w)
		w++;
	if (box.size.h != (uint32) box.size.h)
		h++;

	bitmap = new Bitmap(w, h);
	bitmap->drawBitmap(this, x, y, 0, 0, w, h);
	return bitmap;
}


char* Bitmap::getGrayScaleDataCopy(uint32 &len) {
	if (size <= 0)
		return NULL;
	len = width*height;
	char* output = new char[len];
	uint32* src = (uint32*) data;
	char* dst = output;

	for (uint32 n=0; n<len; n++) {
		*dst++ = GET_GRAY_FROM_RGB(*src);
		src++;
	}
	return output;
}

bool Bitmap::copyGrayScaleData(char* src, uint32 len) {
	if (len < width*height)
		return false;
	uint32* dst = (uint32*) data;

	for (uint32 n=0; n<len; n++) {
		*dst++ = GET_RGB_FROM_GRAY(*src);
		src++;
	}
	return true;
}

bool Bitmap::updateBitmap(BitmapUpdate* update, bool takeData) {

	if (update == NULL)
		return false;

	//int updateWidth = update->width;
	//int updateHeight = update->height;
	//long updateSize = update->getDataSize();
	//char* updateData = update->getDataLink();

	if (update->type == BitmapUpdate::FULL) {
		if (update->size != (update->width * update->height * 4))
			return false;
		reset();
		if (takeData) {
			width = update->width;
			height = update->height;
			size = update->size;
			data = update->data;
			update->width = update->height = 0;
			update->size = 0;
			update->data = NULL;
			//update->clearDataFields();
		}
		else {
			if ( (update->width != width) || (update->height != height) ) {
				reset();
				init(update->width, update->height);
			}
			memcpy(data, update->data, size);
		}
	}
	else if (update->type == BitmapUpdate::RUNLENGTH) {

		if ((update->width != width) || (update->height != height)) {
			reset();
			init(update->width, update->height);
		}

		uint32* src = (uint32*) update->data;
		uint32* dst = (uint32*) data;
		int ctrl;
		int len = 0;
		int totallen = 0;
		int pos = 0;
		int tsize = update->size/4;
		int n;
		while (pos < tsize) {
			ctrl = (*src >> 24);
			len = (*src & 0xFFFFFF);
			src++;
			pos++;
			switch(ctrl) {
				case DATACTRL:
					memcpy(dst, src, len*4);
					src += len;
					dst += len;
					totallen += len;
					pos += len;
					break;
				case RLECTRL:
					for (n=0; n<len; n++)
						*dst++ = src[0];
					src++;
					// dst += len;
					totallen += len;
					pos++;
					break;
				default:
					printf("Wrong CTRL: %d ", ctrl);
					pos++;
					break;
			}
		}
	}
	else if (update->type == BitmapUpdate::RL16) {

		if ((update->width != width) || (update->height != height)) {
			reset();
			init(update->width, update->height);
		}

		uint32* src = (uint32*)update->data;
		uint32* dst = (uint32*)data;
		int ctrl;
		int len = 0;
		int totallen = 0;
		int pos = 0;
		int tsize = update->size/4;
		int n;
		unsigned int i1, i2;
		while (pos < tsize) {
			ctrl = (*src >> 24);
			len = (*src & 0xFFFFFF);
			src++;
			pos++;
			switch(ctrl) {
				case DATACTRL:
					for (n=0; n<len; n++) {
						*dst++ = GET_FIRSTRGB_FROM_DRGB(*src);
						*dst++ = GET_SECONDRGB_FROM_DRGB(*src);
						src++;
					}
				//	memcpy(dst, src, len*4);
				//	src += len;
				//	dst += len;
					pos+=len;
					totallen+=len+len;
					break;
				case RLECTRL:
					i1 = GET_FIRSTRGB_FROM_DRGB(*src);
					i2 = GET_SECONDRGB_FROM_DRGB(*src);
					src++;
					for (n=0; n<len; n++) {
						*dst++ = i1;
						*dst++ = i2;
					}
					totallen+=len+len;
					pos++;
					break;
				default:
					printf("Wrong CTRL: %d ", ctrl);
					pos++;
					break;
			}
		}
	}
	else if (update->type == BitmapUpdate::DIF) {

		if ((update->width != width) || (update->height != height)) {
			reset();
			init(update->width, update->height);
		}

		uint32* src = (uint32*)update->data;
		uint32* dst = (uint32*)data;
		int ctrl;
		int len = 0;
		int totallen = 0;
		int pos = 0;
		int tsize = update->size/4;
		while (pos < tsize) {
			ctrl = (*src >> 24);
			len = (*src & 0xFFFFFF);
			src++;
			pos++;
			switch(ctrl) {
				case DATACTRL:
					memcpy(dst, src, len*4);
					src += len;
					dst += len;
					totallen += len;
					pos += len;
					break;
				case DIFCTRL:
					dst += len;
					// dst += len;
					totallen += len;
					// pos++;
					break;
				default:
					printf("Wrong CTRL: %d ", ctrl);
					pos++;
					break;
			}
		}
	}
	else if (update->type == BitmapUpdate::DIFRUNLENGTH) {

		if ((update->width != width) || (update->height != height)) {
			reset();
			init(update->width, update->height);
		}

		uint32* src = (uint32*)update->data;
		uint32* dst = (uint32*)data;
		int ctrl;
		int len = 0;
		int totallen = 0;
		int pos = 0;
		int tsize = update->size/4;
		while (pos < tsize) {
			ctrl = (*src >> 24);
			len = (*src & 0xFFFFFF);
			src++;
			pos++;
			int n;
			switch(ctrl) {
				case DATACTRL:
					memcpy(dst, src, len*4);
					src += len;
					dst += len;
					totallen += len;
					pos += len;
					break;
				case RLECTRL:
					for (n=0; n<len; n++)
						*dst++ = src[0];
					src++;
					// dst += len;
					totallen += len;
					pos++;
					break;
				case DIFCTRL:
					dst += len;
					// dst += len;
					totallen += len;
					// pos++;
					break;
				default:
					printf("Wrong CTRL: %d ", ctrl);
					pos++;
					break;
			}
		}
	}

	setBitmapUpdated();
	return true;
}




//////////////////////////////////////////////////////////////////////
// Basic Functions
//////////////////////////////////////////////////////////////////////

bool Bitmap::setPixel(uint32 x, uint32 y, uint8 red, uint8 green, uint8 blue, uint8 alpha) {
	if (data == NULL)
		return false;

	if ((y < 0) || (y >= height) || (x < 0) || (x >= width))
		return false;

	uint32* dst = ((uint32*) data) + y*width + x;
	*dst = red + (((uint32)green) << 8) + (((uint32)blue) << 16) + (((uint32)alpha) << 24);
	return true;
}

bool Bitmap::setPixel(uint32 pos, uint8 red, uint8 green, uint8 blue, uint8 alpha) {
	if (data == NULL)
		return false;
	if (pos >= size)
		return false;
	*(data+pos) = red;
	*(data+pos+1) = green;
	*(data+pos+2) = blue;
	*(data+pos+3) = alpha;
	setBitmapUpdated();
	return true;
}

bool Bitmap::setPixelXOR(uint32 x, uint32 y, uint8 red, uint8 green, uint8 blue, uint8 alpha) {
	if (data == NULL)
		return false;

	if ((y < 0) || (y >= height) || (x < 0) || (x >= width))
		return false;

	unsigned int* dst = ((unsigned int*) data) + y*width + x;
	*dst |= red + (((uint32)green) << 8) + (((uint32)blue) << 16) + (((uint32)alpha) << 24);
	return true;
}

bool Bitmap::setPixelXOR(uint32 pos, uint8 red, uint8 green, uint8 blue, uint8 alpha) {
	if (data == NULL)
		return false;
	if (pos >= size)
		return false;
	*(data+pos) |= red;
	*(data+pos+1) |= green;
	*(data+pos+2) |= blue;
	*(data+pos+3) |= alpha;
	setBitmapUpdated();
	return true;
}

Pixel Bitmap::getPixel(uint32 x, uint32 y) {
	Pixel p;
	p.x = x;
	p.y = y;
	p.value = NULL;
	getPixel(&p);
	return p;
}

bool Bitmap::getPixel(Pixel* pixel) {
	if (pixel == NULL)
		return false;
	if ( (pixel->x < 0) || (pixel->y < 0) || (pixel->x >= width) || (pixel->y >= height) )
		return false;
	pixel->value = (unsigned int*)(data + (pixel->y*width*4) + (pixel->x * 4));
	return true;
}


uint32 Bitmap::getPos(uint32 x, uint32 y) {
	if ( (x < 0) || (y < 0) || (x >= width) || (y >= height) )
		return false;
	return (y * width*4) + (x * 4);
}

double Bitmap::getDPos(double x, double y) {
	if ( (x < 0) || (y < 0) || (x >= width) || (y >= height) )
		return false;
	return (y * width*4) + (x * 4);
}

Pixel Bitmap::getXY(uint32 pos) {
	Pixel p;
	ldiv_t d;
	d = ldiv(pos, width);
	p.y = d.quot;
	p.x = d.rem;
	p.value = (unsigned int*)(data + (p.y*width*4) + (p.x * 4));
	return p;
}







//////////////////////////////////////////////////////////////////////
// Bitmap Functions
//////////////////////////////////////////////////////////////////////

bool Bitmap::eraseBitmap() {
	return eraseBitmap(255, 255, 255);
}

bool Bitmap::eraseBitmap(uint8 r, uint8 g, uint8 b, uint8 a) {
	uint32 n;

	CMBYTE c[4];
	// if (eraseLine == NULL)
	uint32 byteWidth = width*4;
	char* eraseLine = new char[byteWidth];
	c[0] = (CMBYTE) r;
	c[1] = (CMBYTE) g;
	c[2] = (CMBYTE) b;
	c[3] = (CMBYTE) a;

/*	if (r == 255)
		c[0] = (CMBYTE) 254;
	if (g == 255)
		c[1] = (CMBYTE) 254;
	if (b == 255)
		c[2] = (CMBYTE) 254;*/

	for (n=0; n<byteWidth; n+=4)
		memcpy(eraseLine+n, c, 4);

//	for (n=0; n < size; n+=ymax)
	for (n=0; n < size; n+=(byteWidth))
		memcpy(data+n, eraseLine, byteWidth);

	delete [] eraseLine;
	setBitmapUpdated();
	return true;
}

bool Bitmap::eraseBitmap(const Color& color) {
	return eraseBitmap(color.r, color.g, color.b);
}

bool Bitmap::replaceColor(const Color& oldColor, const Color& newColor) {
	if (data == NULL)
		return false;

	unsigned int oldcolval = oldColor.r + (oldColor.g << 8) + (oldColor.b << 16);
	unsigned int newcolval = newColor.r + (newColor.g << 8) + (newColor.b << 16);

	unsigned int* src = (unsigned int*) data;
	int len = width * height;
	for (int n = 0; n < len; n++) {
		unsigned int pixel = *src;
		if ((pixel & 0xffffff) == oldcolval)
			*src = (pixel & 0xff000000) + newcolval;
		src++;
	}
	return true;
}


bool Bitmap::fillBox(const Box& box, const Color& color) {
	return fillBox((int) box.getLeftX(), (int) box.getUpperY(), (int) box.getWidth(), (int) box.getHeight(), color);
}

bool Bitmap::fillBox(uint32 x, uint32 y, uint32 w, uint32 h, const Color& color) {
	
	uint32 byteWidth = width*4;

	if (x < 0) {
		w += x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		y = 0;
	}

	if (w + x > width)
		w = width - x;
	if (h + y > height)
		h = height - y;
	if ( (w <= 0) || (h <= 0) )
		return false;

	uint32 n;
	uint32 copybytes = w * 4;
	char* src = new char[copybytes];

	for (n=0; n < copybytes; n+=4) {
		src[n] = color.r;
		src[n+1] = color.g;
		src[n+2] = color.b;
		src[n+3] = 0;
	}

	char* dst = data + (y*byteWidth + x*4);
	for (n=0; n < h; n++) {
		memcpy(dst, src, copybytes);
		dst += byteWidth;
	}
	delete [] src;
	addBitmapUpdateRegion(Box(x, y, w, h));
	return true;
}

Color Bitmap::getPixelColor(uint32 x, uint32 y) {
	Color resColor;
	if ( (x < 0) || (y < 0) || (x >= width) || (y >= height) )
		return Color();
	long pos = getPos(x, y);
	resColor.r = *(unsigned char*)(data + pos);
	resColor.g = *(unsigned char*)(data + pos + 1);
	resColor.b = *(unsigned char*)(data + pos + 2);
	return resColor;
}




bool Bitmap::putTransPixel(uint32 x, uint32 y, const Color& color, double weight) {
	if ( (x < 0) || (y < 0) || (x >= width) || (y >= height) )
		return false;
	unsigned char* ppix = (unsigned char*)data + (y*width*4) + (x*4);
	ppix[0]=(unsigned char)((weight*color.r)+(1-weight)*ppix[0]);
	ppix[1]=(unsigned char)((weight*color.g)+(1-weight)*ppix[1]);
	ppix[2]=(unsigned char)((weight*color.b)+(1-weight)*ppix[2]);
	return true;
}

bool Bitmap::drawLine(const Line& line, const Color& color) {

	int x1 = (int) line.startPoint.x;
	int y1 = (int) line.startPoint.y;
	int x2 = (int) line.endPoint.x;
	int y2 = (int) line.endPoint.y;

	// some useful constants first
	double const dw=x2-x1;
	double const dh=y2-y1;
	double const slx=dh/(dw ? dw : 0.000001);
	double const sly=dw/(dh ? dh : 0.000001);

	int extras = 0;
	double alpha;

	// determine wichever raster scanning behaviour to use
	if(fabs(slx)<1.0)
	{
		if (line.lineWidth <= 1)
			extras = 0;
		else if ((dh != 0) && (dw != 0)) {
			alpha = atan((double)dw/dh);
			extras = (int)(line.lineWidth / cos((clPI*0.5) - alpha));
		}
		else
			extras = (int)line.lineWidth;

		// x scan
		int tx1=x1;
		int tx2=x2;
		double raster=y1;

		if(x1>x2)
		{
			tx1=x2;
			tx2=x1;
			raster=y2;
		}

		for(int x=tx1;x<=tx2;x++)
		{
			int const ri=int(raster);

			double const in_y0=1.0-(raster-ri);
			double const in_y1=1.0-(ri+1-raster);

			putTransPixel(x, ri+0 ,color, in_y0);
			for (int n=0; n<extras; n++)
				putTransPixel(x, ri+n+1 ,color, 1.0);
			putTransPixel(x, ri+extras+1 ,color, in_y1);

			raster+=slx;
		}
	}
	else
	{
		if (line.lineWidth <= 1)
			extras = 0;
		else if ((dh != 0) && (dw != 0)) {
			alpha = atan((double)dw/dh);
			extras = (int)(line.lineWidth / sin((clPI*0.5) - alpha));
		}
		else
			extras = (int)line.lineWidth;

		// y scan
		int ty1=y1;
		int ty2=y2;
		double raster=x1;

		if(y1>y2)
		{
			ty1=y2;
			ty2=y1;
			raster=x2;
		}

		for(int y=ty1;y<=ty2;y++)
		{
			int const ri=int(raster);

			double const in_x0=1.0-(raster-ri);
			double const in_x1=1.0-(ri+1-raster);

			putTransPixel(ri+0, y, color, in_x0);
			for (int n=0; n<extras; n++)
				putTransPixel(ri+n+1, y ,color, 1.0);
			putTransPixel(ri+extras+1, y, color, in_x1);

			raster+=sly;
		}
	}
	return true;
}



bool Bitmap::canBeResizedTo(uint32 w, uint32 h) {
	double factor = 1.0;
	// Scale down

	if (w > 0) {
		// Scale down
		if (w < this->width) {
			factor = getBestResizeFactor((double)w/this->width);
		}
		// Scale up
		else if (w > this->width) {
			factor = getBestResizeFactor((double)w/this->width);
		}
		else
			return false;
	}
	else {
		// Scale down
		if (h < this->height) {
			factor = getBestResizeFactor((double)h/this->height);
		}
		// Scale up
		else if (h > this->height) {
			factor = getBestResizeFactor((double)h/this->height);
		}
		else
			return false;
	}

	int newWidth = (int)(this->width * factor);
	int newHeight = (int)(this->height * factor);

	if ((newWidth == 0) || (newHeight == 0) )
		return false;
	else if ((newWidth == this->width) || (newHeight == this->height) )
		return false;
	else
		return true;
}

Bitmap* Bitmap::getResizedCopy(double scale) {

	if ( (scale <= 0) || (this->width == 0) || (this->height == 0) )
		return NULL;
	int newWidth, newHeight;
	double factor = 1.0;

	int scaleUp = 0;
	int scaleDown = 0;
	int scale66 = 0;
	factor = getBestResizeFactors(scale, scaleUp, scaleDown, scale66);

	newWidth = (int)(this->width * factor);
	if ((double)newWidth != (this->width * factor))
		newWidth++;
	newHeight = (int)(this->height * factor);
	if ((double)newHeight != (this->height * factor))
		newHeight++;

	if ((newWidth == 0) || (newHeight == 0) )
		return NULL;
	char* newData = this->createResizedData(newWidth, newHeight, scaleUp, scaleDown, scale66);
	if (newData == NULL)
		return NULL;

	Bitmap* bitmap = new Bitmap(newData, newWidth, newHeight, true);
	return bitmap;
}

Bitmap* Bitmap::getResizedCopy(uint32 w, uint32 h, bool proportional) {

	if ( (w == 0) || (h == 0) || (this->width == 0) || (this->height == 0) )
		return NULL;

	// Not yet...
	if (!proportional)
		return NULL;

	return getResizedCopy((double)w/this->width);
}

bool Bitmap::resizeTo(uint32 w, uint32 h, bool proportional) {
	Bitmap* bitmap = getResizedCopy(w, h, proportional);
	if (bitmap == NULL)
		return false;
	takeDataFromBitmap(bitmap);
	delete(bitmap);
	return true;
}

double Bitmap::getBestResizeFactors(double factor, int &scaleUp, int &scaleDown, int &scale66) {
	// in search of best 2^a * (3/2)^b = factor
	// penalties: a > 0 : 0
	// penalties: b > 0 : a+b
	// penalties: c > 0 : 0.5*c
	// penalties: c < 0 : c
	// penalties: err * max

	double penalty;
	double err;
	double f;
	int max = (int)fabs(factor) + 5;

	double best_penalty = 9999;
	double best_factor = 0;
	int best_a = 0;
	int best_b = 0;
	int best_c = 0;

	for (int a=1; a<max; a++) {
		for (int b=1; b<max; b++) {
			for (int c=-1; c<=1; c++) {
				f = ((double)a/b) * pow(1.5, (double)c);
				err = fabs(f - factor);

				penalty = err * max * max;
				if (c > 0)
					penalty += 0.5*c;
				else if (c < 0)
					penalty += abs(c);
				if (b > 0)
					penalty += a+b-2;
				if (penalty < best_penalty) {
					best_a = (a-1);
					best_b = b;
					best_c = c;
					best_penalty = penalty;
					best_factor = f;
				}
			}
		}
	}
	if (best_penalty < 9999) {
		scaleUp = best_a;
		scaleDown = best_b;
		scale66 = best_c;
		return best_factor;
	}
	else
		return 0.0;
}

double Bitmap::getBestResizeFactor(double factor) {
	int scaleUp = 0;
	int scaleDown = 0;
	int scale66 = 0;
	double answer = getBestResizeFactors(factor, scaleUp, scaleDown, scale66);
	if (answer == 0)
		return factor;
	else
		return answer;
}

char* Bitmap::createResizedData(uint32 newWidth, uint32 newHeight, int32 scaleUp, int32 scaleDown, int32 scale66) {
	
	uint32 w = this->width;
	uint32 h = this->height;
	if ((scaleUp == 0) && (scaleDown == 0) && (scale66 == 0))
		return ImageResizeUp(this->data, w, h, 1);

	char* newData = NULL;
	char* tempData = NULL;

	if (scaleUp > 0) {
		newData = ImageResizeUp(this->data, w, h, scaleUp+1);
		if (scale66 == 1) {
			tempData = newData;
			newData = ImageResizeUp66(tempData, w, h);
			delete [] tempData;
		}
		else if (scale66 == -1) {
			tempData = newData;
			newData = ImageResizeDown66(tempData, w, h);
			delete [] tempData;
		}
		if (scaleDown > 1) {
			tempData = newData;
			newData = ImageResizeDown(tempData, w, h, scaleDown);
			delete [] tempData;
		}
		return newData;
	}
	else if (scale66 == 1) {
		newData = ImageResizeUp66(this->data, w, h);
		if (scaleDown > 1) {
			tempData = newData;
			newData = ImageResizeDown(tempData, w, h, scaleDown);
			delete [] tempData;
		}
		return newData;
	}
	else if (scale66 == -1) {
		newData = ImageResizeDown66(this->data, w, h);
		if (scaleDown > 1) {
			tempData = newData;
			newData = ImageResizeDown(tempData, w, h, scaleDown);
			delete [] tempData;
		}
		return newData;
	}
	else if (scaleDown > 1) {
		newData = ImageResizeDown(this->data, w, h, scaleDown);
		return newData;
	}
	else
		return NULL;
}


bool Bitmap::copyDataFromBitmap(Bitmap* bitmap, bool shouldResize) {
	if (bitmap == NULL)
		return false;

	if ( (bitmap->width == this->width) && (bitmap->height == this->height) && (bitmap->size == this->size) ) {
		memcpy(this->data, bitmap->data, size);
		return true;
	}
	else if ( (bitmap->width == this->width) && (bitmap->height == this->height) ) {
		delete [] this->data;
		this->size = bitmap->size;
		this->data = new char[size];
		memcpy(this->data, bitmap->data, size);
		return true;
	}
	else if (!shouldResize) {
		delete [] this->data;
		this->size = bitmap->size;
		this->data = new char[size];
		memcpy(this->data, bitmap->data, size);
		return true;
	}
	else {
		Bitmap* bitmap2 = bitmap->getResizedCopy(this->width, this->height, true);
		if (bitmap2 == NULL)
			return false;
		takeDataFromBitmap(bitmap2);
		delete(bitmap2);
		return true;
	}
}

bool Bitmap::takeDataFromBitmap(Bitmap* bitmap) {
	if (bitmap == NULL)
		return false;
	delete [] this->data;
	this->size = bitmap->size;
	this->width = bitmap->width;
	this->height = bitmap->height;
	this->data = bitmap->data;
	bitmap->data = NULL;
	bitmap->size = 0;
	bitmap->width = 0;
	bitmap->height = 0;
	return true;
}




/*
bool Bitmap::drawLine(Line line, Color color) {

	int x0 = (int) line.startPoint.x;
	int y0 = (int) line.startPoint.y;
	int x1 = (int) line.endPoint.x;
	int y1 = (int) line.endPoint.y;

//	int pix = color.getRGB();
	int dy = y1 - y0;
	int dx = x1 - x0;
	int stepx, stepy;
	double invD = ((dx==0)&&(dy==0)) ? 0.0 : 1.0/(2.0*sqrt((double)dx*dx+dy*dy));

	if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
	if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }

	dy <<= 1;     // dy is now 2*dy
	dx <<= 1;     // dx is now 2*dx

	int fraction;
	int v2dx, v2dy;
	double dd, invD2dx, invD2dy;
	Color paintColor;
	Color backColor;
	
	if (dx > dy) {
		fraction = dy - (dx >> 1);
		v2dx = 0;
		invD2dx = dx * invD;
		backColor = getPixelColor(x0, y0);
		paintColor = color.mixOn(backColor, 1);
		setPixel(x0, y0, paintColor.r, paintColor.g, paintColor.b);
		backColor = getPixelColor(x0, y0 + stepy);
		paintColor = color.mixOn(backColor, 1 - invD2dx);
		setPixel(x0, y0 + stepy, paintColor.r, paintColor.g, paintColor.b);
		backColor = getPixelColor(x0, y0 - stepy);
		paintColor = color.mixOn(backColor, 1 - invD2dx);
		setPixel(x0, y0 - stepy, paintColor.r, paintColor.g, paintColor.b);
		while (x0 != x1) {
			v2dx = fraction;
			if (fraction >= 0) {
				y0 += stepy;
				v2dx -= dx;
				fraction -= dx;
			}
			x0 += stepx;
			v2dx += (dx >> 1);
			fraction += dy;
			dd = v2dx * invD;

			backColor = getPixelColor(x0, y0);
			paintColor = color.mixOn(backColor, 1 - dd);
			setPixel(x0, y0, paintColor.r, paintColor.g, paintColor.b);
			backColor = getPixelColor(x0, y0 + stepy);
			paintColor = color.mixOn(backColor, 1 - (invD2dx - dd));
			setPixel(x0, y0 + stepy, paintColor.r, paintColor.g, paintColor.b);
			backColor = getPixelColor(x0, y0 - stepy);
			paintColor = color.mixOn(backColor, 1 - (invD2dx + dd));
			setPixel(x0, y0 - stepy, paintColor.r, paintColor.g, paintColor.b);
		}
	}
	else {
		fraction = dx - (dy >> 1);
		v2dy = 0;
		invD2dy = dy * invD;
		setPixel(x0, y0, 0, 0, 0);
		setPixel(x0, y0 + stepy, (int) invD2dy, (int) invD2dy, (int) invD2dy);
		setPixel(x0, y0 - stepy, (int) invD2dy, (int) invD2dy, (int) invD2dy);
		while (y0 != y1) {
			v2dy = fraction;
			if (fraction >= 0) {
				x0 += stepx;
				v2dy -= dy;
				fraction -= dy;
			}
			y0 += stepy;
			v2dy += (dy >> 1);
			fraction += dx;
			dd = v2dy * invD;
			setPixel(x0, y0, (int) dd, (int) dd, (int) dd);
			setPixel(x0 + stepx, y0, (int) (invD2dy - dd), (int) (invD2dy - dd), (int) (invD2dy - dd));
			setPixel(x0 - stepx, y0, (int) (invD2dy + dd), (int) (invD2dy + dd), (int) (invD2dy + dd));
		//	setPixel(x0, y0, 0, 0, 0);
		//	setPixel(x0 + stepx, y0, 0, 0, 0);
		//	setPixel(x0 - stepx, y0, 0, 0, 0);
		}
	}

	setBitmapChanged();
	return true;
}
*/

bool Bitmap::drawLine(const PolyLine& polyline, const Color& color) {
	for (uint32 n=0; n<polyline.getLineCount(); n++) {
		drawLine(polyline.getLine(n), color);
	}
	setBitmapUpdated();
	return true;
}

bool Bitmap::drawBox(const Box& box, const Color& color) {
	Line line = Line(box.getUpperLeft(), box.getUpperRight(), box.getLineWidth());
	drawLine(line, color);
	line = Line(box.getUpperRight(), box.getLowerRight(), box.getLineWidth());
	drawLine(line, color);
	line = Line(box.getLowerRight(), box.getLowerLeft(), box.getLineWidth());
	drawLine(line, color);
	line = Line(box.getLowerLeft(), box.getUpperLeft(), box.getLineWidth());
	drawLine(line, color);
	addBitmapUpdateRegion(box);
	return true;
}

// static void WUCircle(int cx,int cy, int R, int filled, int c)

bool Bitmap::putQuadPixel(uint32 xCenter, uint32 yCenter, uint32 x, uint32 y, const Color& color, double weight) {
	putTransPixel(xCenter + y, yCenter + x, color, weight);
	putTransPixel(xCenter - y, yCenter + x, color, weight);
	putTransPixel(xCenter + y, yCenter - x, color, weight);
	putTransPixel(xCenter - y, yCenter - x, color, weight);
	putTransPixel(xCenter + x, yCenter + y, color, weight);
	putTransPixel(xCenter - x, yCenter + y, color, weight);
	putTransPixel(xCenter + x, yCenter - y, color, weight);
	putTransPixel(xCenter - x, yCenter - y, color, weight);
	return true;
}


bool Bitmap::drawCircle(uint32 xCenter, uint32 yCenter, uint32 radius, const Color& color, double weight, double lineWidth, bool filled) {

	if ((((int)lineWidth) > 1) && (!filled)) {
		for (int n=0-(round(lineWidth/2)); n<round(lineWidth/2); n++) {
			drawCircle(xCenter, yCenter, radius+n, color, weight, 1);
		}
		return true;
	}

	#define D(r,y) (ceil(sqrt((double)r*r - y*y)) - sqrt((double)r*r - y*y))

	int x = radius;
	int y = 0;
	int ax;
	double T = 0;
	double I = 0.93;
	if (filled)
		for(ax=0; ax<x; ++ax)
			putQuadPixel(xCenter, yCenter, ax, y, color, 1.0);

	putQuadPixel(xCenter, yCenter, x, y, color, I);

	while (x>y)
	{
   		++y;
		if (D(radius,y) < T) --x;
		putQuadPixel(xCenter, yCenter, x, y, color, I*(1-D(radius,y)));
		if (!filled) {
			putQuadPixel(xCenter, yCenter, x-1, y, color, I*(1-D(radius,y)));
		}
		if (filled) {
			for(ax=0; ax<x; ++ax)
				putQuadPixel(xCenter, yCenter, ax, y, color, 1.0);
		}
		T = D(radius,y);
	}

	return true;
/*
	int x=0;
	int y=radius;
	int p=(5-(radius<<2))>>2; //Midpoint Algorithm

	putTransPixel(xCenter  , yCenter+y, color, weight);
	putTransPixel(xCenter  , yCenter-y, color, weight);
	putTransPixel(xCenter+y, yCenter  , color, weight);
	putTransPixel(xCenter-y, yCenter  , color, weight);
	
	while(x++ < y) {
		if(p<0)
			p+=1+(x<<1);
		else
			p+=1+((x- --y)<<1);

		if(x<y) {
			putTransPixel(xCenter+x, yCenter+y, color, weight);
			putTransPixel(xCenter-x, yCenter+y, color, weight);
			putTransPixel(xCenter+x, yCenter-y, color, weight);
			putTransPixel(xCenter-x, yCenter-y, color, weight);
			putTransPixel(xCenter+y, yCenter+x, color, weight);
			putTransPixel(xCenter-y, yCenter+x, color, weight);
			putTransPixel(xCenter+y, yCenter-x, color, weight);
			putTransPixel(xCenter-y, yCenter-x, color, weight);
		}
		else if(x==y) {
			putTransPixel(xCenter+x, yCenter+y, color, weight);
			putTransPixel(xCenter-x, yCenter+y, color, weight);
			putTransPixel(xCenter+x, yCenter-y, color, weight);
			putTransPixel(xCenter-x, yCenter-y, color, weight);
		}
	}
	return true;*/
}

bool Bitmap::mirror(bool horizontal, bool vertical) {
	if (!data || !size || !width || !height)
		return false;

	if (!horizontal && !vertical)
		return true;

	char* newData = new char[size];
	int32* src;
	int32* dst = (int32*)newData;
	int32 x, y;

	if (vertical && !horizontal) {
		for (y = height-1; y >= 0; y--) {
			src = ((int32*)data) + (y*width);
			for (x = 0; x < (int32)width; x++) {
				*dst = *src;
				dst++;
				src++;
			}
		}
	}
	else if (vertical && horizontal) {
		for (y = height; y > 0; y--) {
			src = ((int32*)data) + (y*width);
			for (x = 0; x < (int32)width; x++) {
				src--;
				*dst = *src;
				dst++;
			}
		}
	}
	else { // if only horizontal
		for (y = 1; y <= (int32)height; y++) {
			src = ((int32*)data) + (y*width);
			for (x = 0; x < (int32)width; x++) {
				src--;
				*dst = *src;
				dst++;
			}
		}
	}

	delete[] data;
	data = newData;

	return true;
}


bool Bitmap::drawBitmap(Bitmap* bitmap, uint32 srcx, uint32 srcy, uint32 dstx, uint32 dsty, uint32 w, uint32 h) {
	if ( (bitmap == NULL) || (bitmap->data == NULL) || (bitmap == this) )
		return false;

	if (srcx < 0)
		srcx = 0;
	if (srcy < 0)
		srcy = 0;

	int byteWidth = width*4;
	int srcByteWidth = bitmap->width * 4;

	int srcStartX = srcx;
	if (dstx < 0) {
		srcStartX -= dstx;
		dstx = 0;
	}
	int srcStartY = srcy;
	if (dsty < 0) {
		srcStartY -= dsty;
		dsty = 0;
	}

	if (w > bitmap->width - srcStartX)
		w = bitmap->width - srcStartX;
 	if (h > bitmap->height - srcStartY)
		h = bitmap->height - srcStartY;
	
	if (w + dstx > width)
		w = width - dstx;
	if (h + dsty > height)
		h = height - dsty;
	if ( (w < 0) || (h < 0) )
		return false;

//	int copybytes = (w - srcStartX) * 4;
	int copybytes = w * 4;
	char* src = bitmap->data + (srcStartY*srcByteWidth + (srcStartX*4));
	char* dst = data + (dsty*byteWidth + (dstx*4));
//	for (int n=0; n < h - srcStartY; n++) {
	for (uint32 n=0; n < h; n++) {
		memcpy(dst, src, copybytes);
		src += srcByteWidth;
		dst += byteWidth;
	}
	addBitmapUpdateRegion(Box(dstx, dsty, w, h));
	return true;
}





bool Bitmap::drawBitmap(Bitmap* bitmap, uint32 x, uint32 y) {
	if (bitmap == NULL)
		return false;
	bitmap->addBitmapUpdateRegion(Box(x, y, bitmap->width, bitmap->height));
	return drawBitmap(bitmap, x, y, bitmap->width, bitmap->height);
}

bool Bitmap::drawBitmap(Bitmap* bitmap, uint32 x, uint32 y, uint32 w, uint32 h) {
	if ( (bitmap == NULL) || (bitmap->data == NULL) || (bitmap == this) )
		return false;

	return drawBitmap(bitmap, 0, 0, x, y, w, h);
/*

	int byteWidth = width*4;
	int srcByteWidth = bitmap->width * 4;

	int srcStartX = 0;
	if (x < 0) {
		srcStartX -= x;
		x = 0;
	}
	int srcStartY = 0;
	if (y < 0) {
		srcStartY -= y;
		y = 0;
	}

	if (w > bitmap->width + srcStartX)
		w = bitmap->width - srcStartX;
 	if (h > bitmap->height + srcStartY)
		h = bitmap->height - srcStartY;
	
	if (w + x > width)
		w = width - x;
	if (h + y > height)
		h = height - y;
	if ( (w < 0) || (h < 0) )
		return false;

	int copybytes = (w - srcStartX) * 4;
	char* src = bitmap->data + (srcStartY*srcByteWidth + (srcStartX*4));;
	char* dst = data + (y*byteWidth + (x*4));
	for (int n=0; n < h - srcStartY; n++) {
		memcpy(dst, src, copybytes);
		src += srcByteWidth;
		dst += byteWidth;
	}
	bitmap->addBitmapUpdateRegion(Box(x, y, w, h));
	return true;*/
}

bool Bitmap::drawBitmap(Bitmap* bitmap, const Point& p) {
	return drawBitmap(bitmap, p.x, p.y);
}

bool Bitmap::drawBitmap(Bitmap* bitmap, const Box& box) {
	return drawBitmap(bitmap, (int) box.getLeftX(), (int) box.getUpperY(), (int) box.getWidth(), (int) box.getHeight());
}















//////////////////////////////////////////////////////////////////////
// Advanced Functions
//////////////////////////////////////////////////////////////////////

/*
// Rotate around the center point
bool Bitmap::rotate(double angle) {

	double a, b, c;
	int x1, y1, x2, y2, x3, y3;

	long tdiff = 0;
	
	double r = sqrt((double)width*width + height*height);
	int cr = (int) ceil(r) + 8;

	if (altBitmap == NULL) 
		altBitmap = new Bitmap(cr, cr);

	if ( (altBitmap->width != cr) ||
		 (altBitmap->height != cr) ) {
		delete(altBitmap);
		altBitmap = new Bitmap(cr, cr);
	}
	altBitmap->eraseBitmap(bgColor.r, bgColor.g, bgColor.b);
//	altBitmap->eraseBitmap(200, 200, 200);
//	altBitmap->eraseBitmap(255, 0, 0);

	r = (r/2); // - (r/8);
//	r = (r/3); // - (r/8);
//	angle = (clPI/4.0);

	double alpha = asin(width/(2.0*r));
	double beta = 2.0*acos(width/(2.0*r));

	a = angle + (clPI/2) + alpha;
	if (a > clPI) a = a - (2*clPI);

	b = angle + (clPI/2) - alpha;
	if (b > clPI) b = b - (2*clPI);
	
	c = angle + (clPI/2) + alpha + beta;
	if (c > clPI) c = c - (2*clPI);

	x1 = round(r * (1.0 + cos(a))) + 4;
	y1 = round(r * (1.0 - sin(a))) + 4;
	x2 = round(r * (1.0 + cos(b))) + 4;
	y2 = round(r * (1.0 - sin(b))) + 4;
	x3 = round(r * (1.0 + cos(c))) + 4;
	y3 = round(r * (1.0 - sin(c))) + 4;

	//	shapeSlow(x1, y1, x2, y2, x3, y3);
	shape(x1, y1, x2, y2, x3, y3);

	return true;
}


// Rotate around the center point
bool Bitmap::rotateSlow(double angle) {

	double a, b, c;
	int x1, y1, x2, y2, x3, y3;

	long tdiff = 0;
	
	double r = sqrt((double)width*width + height*height);
	int cr = (int) ceil(r) + 8;

	if (altBitmap == NULL) 
		altBitmap = new Bitmap(cr, cr);

	if ( (altBitmap->width != cr) ||
		 (altBitmap->height != cr) ) {
		delete(altBitmap);
		altBitmap = new Bitmap(cr, cr);
	}
	altBitmap->eraseBitmap(bgColor.r, bgColor.g, bgColor.b);
//	altBitmap->eraseBitmap(200, 200, 200);
//	altBitmap->eraseBitmap(255, 0, 0);

	r = (r/2); // - (r/8);
//	r = (r/3); // - (r/8);
//	angle = (clPI/4.0);

	double alpha = asin(width/(2.0*r));
	double beta = 2.0*acos(width/(2.0*r));

	a = angle + (clPI/2) + alpha;
	if (a > clPI) a = a - (2*clPI);

	b = angle + (clPI/2) - alpha;
	if (b > clPI) b = b - (2*clPI);
	
	c = angle + (clPI/2) + alpha + beta;
	if (c > clPI) c = c - (2*clPI);

	x1 = round(r * (1.0 + cos(a))) + 4;
	y1 = round(r * (1.0 - sin(a))) + 4;
	x2 = round(r * (1.0 + cos(b))) + 4;
	y2 = round(r * (1.0 - sin(b))) + 4;
	x3 = round(r * (1.0 + cos(c))) + 4;
	y3 = round(r * (1.0 - sin(c))) + 4;

	shapeSlow(x1, y1, x2, y2, x3, y3);
	// shape(x1, y1, x2, y2, x3, y3);

	return true;
}


bool Bitmap::shapeSlow(int x1, int y1, int x2, int y2, int x3, int y3) {
	
	double px, py;
	long pos, n, altbyteWidth;

//	byteWidth = width * byteDepth + padding;
	altbyteWidth = (altBitmap->width*altBitmap->byteDepth);

	CMBYTE c[6];
	c[0] = (CMBYTE) 0;
	c[1] = (CMBYTE) 0;
	c[2] = (CMBYTE) 0;
	c[3] = (CMBYTE) 0;
	c[4] = (CMBYTE) 0;
	c[5] = (CMBYTE) 0;
	
	double d1x = ((double)(x2 - x1)) / width;
	double d1y = ((double)(y2 - y1)) / width;

	double d2x = ((double)(x3 - x1)) / height;
	double d2y = ((double)(y3 - y1)) / height;
	
	double ppx, ppy;

	long startLine = size;

	px = ppx = x1;// + (y * d2x) + (x * d1x);
	py = ppy = y1;// + (y * d2y) + (x * d1y);
	for (int y=0; y < height; y++) {
		n = (startLine -= byteWidth); // y * byteWidth;
		for (int x=0; x < width; x++) {
			if ((n == 0) || (x == 0)) {
				pos = ((altBitmap->height-(long)py) * altbyteWidth) + ((long)px * byteDepth);
				memcpy(altBitmap->data+pos, data+n, 3);
			}
			else if ((n > 0) && (n < size)) {
				pos = ((altBitmap->height-(long)py) * altbyteWidth) + ((long)px * byteDepth);
				memcpy(altBitmap->data+pos-3, data+n-3, 6);
			//	memcpy(altBitmap->data+pos-3, c, 6);
			}
			n += 3;
			px += d1x; // + (x * d1x);
			py += d1y; // + (x * d1y);
		}
		px = (ppx += d2x);
		py = (ppy += d2y);
	}

//	int x4 = x3 + (x2-x1);
//	int y4 = y3 - (y1-y2);
//	for (int k=-5; k<5; k++) {
//		altBitmap->setPixel(x1+k, y1+k, 0, 255, 0);
//		altBitmap->setPixel(x2+k, y2+k, 255, 0, 0);
//		altBitmap->setPixel(x3+k, y3+k, 255, 0, 0);
//		altBitmap->setPixel(x4+k, y4+k, 255, 0, 0);
//		altBitmap->setPixel(x1+k, y1-k, 0, 255, 0);
//		altBitmap->setPixel(x2+k, y2-k, 255, 0, 0);
//		altBitmap->setPixel(x3+k, y3-k, 255, 0, 0);
//		altBitmap->setPixel(x4+k, y4-k, 255, 0, 0);
//	}
	return true;
}




bool Bitmap::shape(int x1, int y1, int x2, int y2, int x3, int y3) {
	double px, py;
//	double ppx, ppy;
	long pos, oldpy, n;

//	long byteWidth = width * byteDepth + padding;
	long altbyteWidth = (altBitmap->width*altBitmap->byteDepth);

//	double d1x = ((double)(x2 - x1)) / width;
//	double d1y = ((double)(y2 - y1)) / height;

//	double d2x = ((double)(x3 - x1)) / width;
//	double d2y = ((double)(y3 - y1)) / height;

	double d1x = ((double)(x2 - x1)) / width;
	double d1y = ((double)(y2 - y1)) / width;

	double d2x = ((double)(x3 - x1)) / height;
	double d2y = ((double)(y3 - y1)) / height;

	long startLine = size;
	long nStart, posStart;
	int length;
	bool inverted = false;

//	altBitmap->eraseBitmap(200, 200, 200);
	
	// nStart is upper left corner of original bitmap
	nStart = size - byteWidth;
	// posStart is the equivalent rotated upper left corner = (x1,y1)
	posStart = ((altBitmap->height-y1) * altbyteWidth) + (x1 * byteDepth);

	length = 0;
	px = x1 + (0 * d2x);// + (y * d2x) + (x * d1x);
	py = y1 + (0 * d2y);// + (y * d2y) + (x * d1y);
	n = nStart;
	for (int x=0; x < width; x++) {
		if ((n >= 0) && (n < size)) {
			pos = ((altBitmap->height-(long)py) * altbyteWidth) + ((long)px * byteDepth);
			if ((oldpy == (long)py) && ((y3 - y1) > 0)) {
				length--;
				L[length] += 3;
			}
			else {
				N[length] = n - nStart;
				P[length] = pos - posStart;
				L[length] = 3;
			}
			length++;
		}
		n += 3;
		oldpy = (long)py;
		px += d1x; // + (x * d1x);
		py += d1y; // + (x * d1y);
	}
	// Make sure first pixel does not get black from outside
	if (length > 0) {
		N[0] += 3;
		L[0] -= 3;
	//	P[0] += 3;
	}

	long posLineStart = posStart;
	long nLineStart = nStart;
	px = x1; // + d2x; // + (0 * d2x);
	py = y1; // + d2y; // + (0 * d2y);
	for (int y=0; y < height; y++) {
		for (n=0; n<length; n++) {
			memcpy(altBitmap->data+P[n]+posLineStart-3, data+N[n]+nLineStart-3, L[n]+3);
		}
		nLineStart -= byteWidth;
		px += d2x;
		py += d2y;
		posLineStart = ((altBitmap->height-(long)py) * altbyteWidth) + ((long)px * byteDepth);
	}

//	int x4 = x3 + (x2-x1);
//	int y4 = y3 - (y1-y2);
//	for (int k=-5; k<5; k++) {
//		altBitmap->setPixel(x1+k, y1+k, 0, 0, 255);
//		altBitmap->setPixel(x2+k, y2+k, 255, 0, 0);
//		altBitmap->setPixel(x3+k, y3+k, 255, 0, 0);
//		altBitmap->setPixel(x4+k, y4+k, 255, 0, 0);
//		altBitmap->setPixel(x1+k, y1-k, 0, 0, 255);
//		altBitmap->setPixel(x2+k, y2-k, 255, 0, 0);
//		altBitmap->setPixel(x3+k, y3-k, 255, 0, 0);
//		altBitmap->setPixel(x4+k, y4-k, 255, 0, 0);
//	}
	return true;
}
*/


//////////////////////////////////////////////////////////////////////
// File Routines
//////////////////////////////////////////////////////////////////////

bool Bitmap::readFromMemory(const char* dat, uint32 len) {

	BITMAPFILEHEADER_CM bmfh = { 0 };
	BITMAPINFOHEADER_CM bmih = { 0 };
	uint32 count = 0;

	if (!dat || !len) {
		//	printf("No file data\n");
		return false;
	}

	const char* src = dat;

	//	bmfh = (BITMAPFILEHEADER_CM*) src;
	memcpy(&bmfh, src, sizeof(bmfh)); /* read bitmap file header */
	src += sizeof(BITMAPFILEHEADER_CM);
	//	bmih = (BITMAPINFOHEADER_CM*) src;
	memcpy(&bmih, src, sizeof(bmih)); /* read bitmap info header */
	src += sizeof(BITMAPINFOHEADER_CM);

	//	MessageBox(NULL, JString::format("Read binary file len %d [%d %d]", len, (int)bmfh, (int)bmih), JString("WinClient"), MB_OK);
	//	MessageBox(NULL, JString::format("Bitmap: %d %d %d %d \n", sizeof(BITMAPFILEHEADER_CM), sizeof(BITMAPINFOHEADER_CM), bmfh.bfSize, bmfh.bfOffBits), JString("WinClient"), MB_OK);
	//	printf("Bitmap: %d %d %d %d %d \n", sizeof(BITMAPFILEHEADER_CM), sizeof(BITMAPINFOHEADER_CM), bmfh->bfSize, bmfh->bfType, bmfh->bfOffBits);

	//	MessageBox(NULL, JString("Analysing binary file "), JString("WinClient"), MB_OK);
	if (bmih.biCompression != 0) {
		//		MessageBox(NULL, JString("auch, compression"), JString("WinClient"), MB_OK);
		delete[] dat;
		//	printf("Compression\n");
		return false;
	}

	//	MessageBox(NULL, JString("Is it 24 bit?"), JString("WinClient"), MB_OK);
	if (bmih.biBitCount != 24) {
		//		MessageBox(NULL, JString("auch, !24"), JString("WinClient"), MB_OK);
		delete[] dat;
		//	printf("Not 24 bits: %d\n", bmih.biBitCount);
		return false;
	}

	/*	if (bmih.biBitCount <= 8) {
	// determine type of window bitmaps
	if(bmih.biBitCount==8) bmpcolour=256;
	if(bmih.biBitCount==4) bmpcolour=16;
	if(bmih.biBitCount==1) bmpcolour=2;

	// * * * * * * * * * * * * * * * * * *
	read the palette information
	from MSB b,g,r,i (i=ignore)
	// * * * * * * * * * * * * * * * * * * *

	for(index=0;index<bmpcolour;index++)
	{
	pal3[index].b =	fgetc(fp) >> 2;
	pal3[index].g = fgetc(fp) >> 2;
	pal3[index].r = fgetc(fp) >> 2;
	fgetc(fp);
	}
	}*/

	//	MessageBox(NULL, JString("24 bit OK"), JString("WinClient"), MB_OK);
	int depth = bmih.biBitCount;
	int planes = bmih.biPlanes;
	int width = bmih.biWidth;
	int height = bmih.biHeight;
	//	MessageBox(NULL, JString::format("D: %d, P: %d, W: %d, H: %d", depth, planes, width, height), JString("WinClient"), MB_OK);
	//	byteDepth = (int) (depth/8);
	//	byteWidth = width * byteDepth + padding;

	if (bmih.biSizeImage == 0)
		bmih.biSizeImage = bmfh.bfSize - bmfh.bfOffBits;

	//	div_t d = div(width, 4);
	//	int padding = d.rem;

	//	MessageBox(NULL, JString("Reinit bitmap "), JString("WinClient"), MB_OK);
	init(width, height);
	if (data == NULL) {
		//	printf("No data\n");
		return false;
	}

	//	MessageBox(NULL, JString("Converting data "), JString("WinClient"), MB_OK);
	convertBitmapFileData(src, width, height, (int)(bmih.biBitCount / 8), data, size);
	//	MessageBox(NULL, JString("Converted data "), JString("WinClient"), MB_OK);

	setBitmapUpdated();
	return true;
}


bool Bitmap::readFromFile(const char* filename) {

	uint32 len;
	char* dat = utils::ReadAFile(filename, len, true);
	if (!dat || !len) {
		delete[] dat;
	//	printf("No file data\n");
		return false;
	}
	bool res = readFromMemory(dat, len);
	delete[] dat;
	return res;
}

bool Bitmap::saveToFile(const char* filename) {

	// Open file
	//if((fp=fopen(filename,"wb"))==NULL)
	//	return false;

	uint32 len = 0;
	char* buffer = toBitmapFileFormat(len);
	if ( (len <= 0) && (buffer == NULL) ) {
		delete [] buffer;
		//fclose(fp);
		return false;
	}

	if (!utils::WriteAFile(filename, buffer, len, true)) {
		delete [] buffer;
		return false;
	}

	delete [] buffer;
	return true;

/*
	BITMAPFILEHEADER_CM bmfh={0};
	// BITMAPINFOHEADER_CM bmih={0};
	BITMAPINFOHEADER_CM* info;
//	int infosize = sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM) + 2;  // + 2 for rounding
	int infosize = sizeof(BITMAPINFOHEADER_CM) + 2;  // + 2 for rounding

//	CMDWORD index;
//	CMBYTE ch;
	long count = 0;

	int byteWidth = width*4;

	// Fill out structures
	bmfh.bfType = 19778; // 'BM'
	bmfh.bfSize = size + sizeof(BITMAPFILEHEADER_CM) + infosize; // might need padding...
	bmfh.bfReserved1 = 0;
	bmfh.bfReserved2 = 0;
	bmfh.bfOffBits = sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM);

	info = (BITMAPINFOHEADER_CM*) malloc(infosize);
	memset(info, 0, infosize);

	info->biSize = sizeof(BITMAPINFOHEADER_CM);
	info->biWidth = width;
	info->biHeight = height;
	info->biPlanes = 1;
	info->biCompression = 0;
	info->biBitCount = 24;
	info->biSizeImage = 0; // size;
	info->biXPelsPerMeter = 2834;
	info->biYPelsPerMeter = 2834;
	info->biClrUsed = 0;
	info->biClrImportant = 0;

	// div_t d = div(width, 4);
	// int padding = d.rem;
	int padding = 4 - ((width*3)%4);
	if (padding == 4) padding = 0;

	// write bitmap file header
	if (fwrite(&bmfh,sizeof(BITMAPFILEHEADER_CM),1,fp) != 1) { 
		fclose(fp);
		free(info);
		return false;
	}

	// write bitmap info header
//	if (fwrite(&bmih,sizeof(bmih),1,fp) != 1) { 
	if (fwrite(info, sizeof(BITMAPINFOHEADER_CM),1,fp) != 1) { 
		fclose(fp);
		free(info);
		return false;
	}

	free(info);

//	char* binline = new char[byteWidth];
//	memset(binline, 0, byteWidth);

	// Write bitmap data
	int c, x;
	char* src = data + (height-1) * byteWidth;
	for (int l = 0; l < height; l++)
	{
	//	fwrite(binline, 1, byteWidth, fp);
		x = 0;
		for (c = 0; c < width; c++) {
			fputc(src[x+2], fp);
			fputc(src[x+1], fp);
			fputc(src[x], fp);
			x += 4;
		}
		for (c=0; c < padding; c++) {
			fputc(0, fp);
		}
		src -= byteWidth;
	}

	// Close file
	fclose(fp);
	return true; */
}

char* Bitmap::toBitmapFileFormat(uint32& len) {
	return DataToBitmapFileFormat(data, size, width, height, len);
}

char* Bitmap::DataToBitmapFileFormat(const char* data, uint32 size, uint32 width, uint32 height, uint32& len) {
	return DataToBitmapFileFormat(data, size, width, height, "RGBA", width * 4, len);
}

char* Bitmap::DataToBitmapFileFormat(const char* data, uint32 size, uint32 width, uint32 height, uint32 linestep, uint32& len) {
	uint32 srcPixelSize = (uint32)(linestep / width);
	if ((srcPixelSize > 4) || (srcPixelSize < 1))
		return NULL;
	else if (srcPixelSize == 4)
		return DataToBitmapFileFormat(data, size, width, height, "RGBA", linestep, len);
	else if (srcPixelSize == 3)
		return DataToBitmapFileFormat(data, size, width, height, "rgb8", linestep, len);
	else if (srcPixelSize == 1)
		return DataToBitmapFileFormat(data, size, width, height, "grey", linestep, len);
	else
		return NULL;
}

char* Bitmap::DataToBitmapFileFormat(const char* data, uint32 size, uint32 width, uint32 height, const char* encoding, uint32& len) {
	return DataToBitmapFileFormat(data, size, width, height, encoding, 0, len);
}

char* Bitmap::DataToBitmapFileFormat(const char* data, uint32 size, uint32 width, uint32 height, const char* encoding, uint32 linestep, uint32& len) {

	uint32 infosize = sizeof(BITMAPINFOHEADER_CM) + 2;  // + 2 for rounding
	uint32 headersize = sizeof(BITMAPFILEHEADER_CM) + infosize;

	uint32 count = 0;

	uint32 padding = 4 - ((width * 3) % 4);
	if (padding == 4) padding = 0;

	len = headersize + (height * width * 3) + (padding * height);
	char* buffer = new char[len];
	char* dst = buffer;
	memset(dst, 0, headersize);

	BITMAPFILEHEADER_CM* bmfh = (BITMAPFILEHEADER_CM*) dst;
	BITMAPINFOHEADER_CM* info = (BITMAPINFOHEADER_CM*)(dst+sizeof(BITMAPFILEHEADER_CM));

	// Fill out structures
	bmfh->bfType = 19778; // 'BM'
						 //	bmfh.bfSize = size + sizeof(BITMAPFILEHEADER_CM) + infosize; // might need padding...
	bmfh->bfSize = len;
	bmfh->bfReserved1 = 0;
	bmfh->bfReserved2 = 0;
	bmfh->bfOffBits = sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM);

	info->biSize = sizeof(BITMAPINFOHEADER_CM);
	info->biWidth = width;
	info->biHeight = height;
	info->biPlanes = 1;
	info->biCompression = 0;
	info->biBitCount = 24;
	info->biSizeImage = 0; // size;
	info->biXPelsPerMeter = 2834;
	info->biYPelsPerMeter = 2834;
	info->biClrUsed = 0;
	info->biClrImportant = 0;

	dst += headersize;
	const char* src;
	uint32 srcPadding = 0;
	double scaleFactor;

	uint32 c, l, x;

	// Write bitmap data
	uint32 srcPixelSize;
	bool isUnsigned = true;
	uint8 floatSize = 0;
	double minVal = 0, maxVal = 0;
	if (stricmp(encoding, "rgba") == 0)
		srcPixelSize = 4;
	else if (stricmp(encoding, "rgb8") == 0)
		srcPixelSize = 3;
	else if (stricmp(encoding, "bgr8") == 0)
		srcPixelSize = 3;
	else if (stricmp(encoding, "16UC1") == 0) {
		srcPixelSize = 2;
		if (linestep)
			srcPadding = linestep - (srcPixelSize * width);
		src = data;
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(uint16*)src) < minVal)
					minVal = *(uint16*)src;
				else if ((*(uint16*)src) > maxVal)
					maxVal = *(uint16*)src;
				src += 2;
			}
			src += srcPadding;
		}
	}
	else if (stricmp(encoding, "32FC1") == 0) {
		srcPixelSize = 4;
		if (linestep)
			srcPadding = linestep - (srcPixelSize * width);
		floatSize = 4;
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		src = data;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(float32*)src) < minVal)
					minVal = *(float32*)src;
				else if ((*(float32*)src) > maxVal)
					maxVal = *(float32*)src;
				src += 4;
			}
			src += srcPadding;
		}
	}
	else if ((stricmp(encoding, "8PC1") == 0) || (stricmp(encoding, "8PC1R") == 0)) {
		srcPixelSize = 1;
		if (linestep)
			srcPadding = linestep - (srcPixelSize * width);
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		src = data;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(int8*)src) < minVal)
					minVal = *(int8*)src;
				else if ((*(int8*)src) > maxVal)
					maxVal = *(int8*)src;
				src += 1;
			}
			src += srcPadding;
		}
	}
	else if (linestep)
		srcPixelSize = (uint32)(linestep / width);
	else {
		delete [] buffer;
		return NULL;
	}

	if (!linestep)
		linestep = width * srcPixelSize;
	srcPadding = linestep - (srcPixelSize * width);
	src = data + ((height - 1) * linestep) - srcPadding;


	if (stricmp(encoding, "16UC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - minVal)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				*dst = dst[1] = dst[2] = (uint8)(((*(uint16*)src) - minVal) * scaleFactor);
				dst += 3;
				src += 2;
			}
			dst += padding;
			src -= linestep + linestep - srcPadding;
		}
	}
	else if (stricmp(encoding, "32FC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - minVal)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
				dst += 3;
				src += 4;
			}
			dst += padding;
			src -= linestep + linestep - srcPadding;
		}
	}
	else if (stricmp(encoding, "8PC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - 0)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
				if (*(int8*)src >= 0) {
					dst[0] = 0;
					dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
				}
				else {
					dst[0] = 50;
					dst[1] = 0;
				}
				dst[2] = 0;
				dst += 3;
				src += 1;
			}
			dst += padding;
			src -= linestep + linestep - srcPadding;
		}
	}
	else if (stricmp(encoding, "8PC1R") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - 0)*255.0;
		else
			scaleFactor = 1;
		// read vertically from the bottom up, just like the bitmap
		src = data;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
				if (*(int8*)src >= 0) {
					dst[0] = 0;
					dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
				}
				else {
					dst[0] = 50;
					dst[1] = 0;
				}
				dst[2] = 0;
				dst += 3;
				src += 1;
			}
			dst += padding;
			src += srcPadding;
		}
	}
	else if (srcPixelSize == 4) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				*dst++ = src[x];
				*dst++ = src[x + 2];
				*dst++ = src[x + 1];
				x += srcPixelSize;
			}
			dst += padding;
			src -= linestep;
		}
	}
	else if (stricmp(encoding, "bgr8") == 0) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				*dst++ = src[x + 2];
				*dst++ = src[x + 0];
				*dst++ = src[x + 1];
				x += srcPixelSize;
			}
			dst += padding;
			src -= linestep;
		}
	}
	else if (srcPixelSize == 3) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				*dst++ = src[x];
				*dst++ = src[x + 2];
				*dst++ = src[x + 1];
				x += srcPixelSize;
			}
			dst += padding;
			src -= linestep;
		}
	}
	else if (srcPixelSize == 2) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				*dst++ = 0;
				*dst++ = src[x + 1];
				*dst++ = src[x];
				x += srcPixelSize;
			}
			dst += padding;
			src -= linestep;
		}
	}
	else if (srcPixelSize == 1) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				*dst++ = src[x];
				*dst++ = src[x];
				*dst++ = src[x];
				x += srcPixelSize;
			}
			dst += padding;
			src -= linestep;
		}
	}

	return buffer;
}


char* Bitmap::DataToEncodingFormat(const char* data, uint32 size, uint32 width,
	uint32 height, const char* srcEncoding, uint32 linestep, const char* dstEncoding, uint32& len) {

	if (!data || !size || !width || !height) {
		len = 0;
		return NULL;
	}
	
	uint32 count = 0;

	uint32 dstPixelSize;
	if (stricmp(dstEncoding, "rgba") == 0)
		dstPixelSize = 4;
	else if (stricmp(dstEncoding, "rgb8") == 0)
		dstPixelSize = 3;
	else if (stricmp(dstEncoding, "bgr8") == 0)
		dstPixelSize = 3;
	else if (stricmp(dstEncoding, "grey") == 0)
		dstPixelSize = 1;
	else {
		len = 0;
		return NULL;
	}

	len = (height * width * dstPixelSize);
	char* buffer = new char[len];
	char* dst = buffer;

	const char* src;
	uint32 srcPadding;
	double scaleFactor;

	uint32 c, l, x;

	uint32 srcPixelSize;
	bool isUnsigned = true;
	uint8 floatSize = 0;
	double minVal = 0, maxVal = 0;
	if (stricmp(srcEncoding, "rgba") == 0)
		srcPixelSize = 4;
	else if (stricmp(srcEncoding, "rgb8") == 0)
		srcPixelSize = 3;
	else if (stricmp(srcEncoding, "bgr8") == 0)
		srcPixelSize = 3;
	else if (stricmp(srcEncoding, "16UC1") == 0) {
		srcPixelSize = 2;
		srcPadding = linestep - (srcPixelSize * width);
		src = data;
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(uint16*)src) < minVal)
					minVal = *(uint16*)src;
				else if ((*(uint16*)src) > maxVal)
					maxVal = *(uint16*)src;
				src += 2;
			}
			src += srcPadding;
		}
	}
	else if (stricmp(srcEncoding, "32FC1") == 0) {
		srcPixelSize = 4;
		srcPadding = linestep - (srcPixelSize * width);
		floatSize = 4;
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		src = data;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(float32*)src) < minVal)
					minVal = *(float32*)src;
				else if ((*(float32*)src) > maxVal)
					maxVal = *(float32*)src;
				src += 4;
			}
			src += srcPadding;
		}
	}
	else if ((stricmp(srcEncoding, "8PC1") == 0) || (stricmp(srcEncoding, "8PC1R") == 0)) {
		srcPixelSize = 1;
		srcPadding = linestep - (srcPixelSize * width);
		maxVal = -1000000000.0;
		minVal = 1000000000.0;
		src = data;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if ((*(int8*)src) < minVal)
					minVal = *(int8*)src;
				else if ((*(int8*)src) > maxVal)
					maxVal = *(int8*)src;
				src += 1;
			}
			src += srcPadding;
		}
	}
	else
		srcPixelSize = (uint32)(linestep / width);

	srcPadding = linestep - (srcPixelSize * width);
	//src = data + ((height - 1) * linestep) - srcPadding;
	src = data;

	if (stricmp(srcEncoding, "16UC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - minVal)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst = dst[1] = dst[2] = (uint8)(((*(uint16*)src) - minVal) * scaleFactor);
					dst[3] = 0;
					dst += 4;
				}
				else if (dstPixelSize == 3) {
					*dst = dst[1] = dst[2] = (uint8)(((*(uint16*)src) - minVal) * scaleFactor);
					dst += 3;
				}
				else if (dstPixelSize == 1) {
					*dst = (uint8)(((*(uint16*)src) - minVal) * scaleFactor);
					dst += 1;
				}
				src += 2;
			}
			//*dst += padding;
			src += srcPadding;
		}
	}
	else if (stricmp(srcEncoding, "32FC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - minVal)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					dst[3] = 0;
					dst += 4;
				}
				else if (dstPixelSize == 3) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					dst += 3;
				}
				else if (dstPixelSize == 1) {
					*dst = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					dst += 1;
				}
				src += 4;
			}
			//*dst += padding;
			src += srcPadding;
		}
	}
	else if (stricmp(srcEncoding, "8PC1") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - 0)*255.0;
		else
			scaleFactor = 1;
		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {

				if (dstPixelSize == 4) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						dst[0] = 0;
						dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						dst[0] = 50;
						dst[1] = 0;
					}
					dst[2] = 0;
					dst[3] = 0;
					dst += 4;
				}
				else if (dstPixelSize == 3) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						dst[0] = 0;
						dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						dst[0] = 50;
						dst[1] = 0;
					}
					dst[2] = 0;
					dst += 3;
				}
				else if (dstPixelSize == 1) {
					*dst = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						*dst = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						*dst = 50;
					}
					dst += 1;
				}
				src += 1;
			}
			//*dst += padding;
			src += srcPadding;
		}
	}
	else if (stricmp(srcEncoding, "8PC1R") == 0) {
		if (minVal < maxVal)
			scaleFactor = 1 / (maxVal - 0)*255.0;
		else
			scaleFactor = 1;
		// read src vertically bottom up
		src = data + ((height - 1) * linestep) - srcPadding;

		for (l = 0; l < height; l++) {
			for (c = 0; c < width; c++) {

				if (dstPixelSize == 4) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						dst[0] = 0;
						dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						dst[0] = 50;
						dst[1] = 0;
					}
					dst[2] = 0;
					dst[3] = 0;
					dst += 4;
				}
				else if (dstPixelSize == 3) {
					*dst = dst[1] = dst[2] = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						dst[0] = 0;
						dst[1] = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						dst[0] = 50;
						dst[1] = 0;
					}
					dst[2] = 0;
					dst += 3;
				}
				else if (dstPixelSize == 1) {
					*dst = (uint8)(((*(float32*)src) - minVal) * scaleFactor);
					if (*(int8*)src >= 0) {
						*dst = (uint8)(((*(int8*)src)) * scaleFactor);
					}
					else {
						*dst = 50;
					}
					dst += 1;
				}
				src += 1;
			}
			//*dst += padding;
			//src += srcPadding;
			src -= linestep + linestep - srcPadding;
		}
	}
	else if (stricmp(srcEncoding, "bgr8") == 0) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst++ = src[x + 2];
					*dst++ = src[x + 1];
					*dst++ = src[x];
					*dst++ = 0;
				}
				else if (dstPixelSize == 3) {
					*dst++ = src[x + 2];
					*dst++ = src[x + 1];
					*dst++ = src[x];
				}
				else if (dstPixelSize == 1) {
					*dst++ = (uint8)(((uint32)src[x] + (uint32)src[x + 1] + (uint32)src[x + 2]) / 3);
				}
				x += srcPixelSize;
			}
			//*dst += padding;
			src += linestep;
		}
	}
	else if (srcPixelSize >= 3) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst++ = src[x];
					*dst++ = src[x + 1];
					*dst++ = src[x + 2];
					*dst++ = 0;
				}
				else if (dstPixelSize == 3) {
					*dst++ = src[x];
					*dst++ = src[x + 1];
					*dst++ = src[x + 2];
				}
				else if (dstPixelSize == 1) {
					*dst++ = (uint8)(((uint32)src[x] + (uint32)src[x + 1] + (uint32)src[x + 2])/3);
				}
				x += srcPixelSize;
			}
			//*dst += padding;
			src += linestep;
		}
	}
	else if (srcPixelSize == 2) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst++ = 0;
					*dst++ = src[x + 1];
					*dst++ = src[x];
					*dst++ = 0;
				}
				else if (dstPixelSize == 3) {
					*dst++ = 0;
					*dst++ = src[x + 1];
					*dst++ = src[x];
				}
				else if (dstPixelSize == 1) {
					*dst++ = (uint8)(((uint32)src[x] + (uint32)src[x + 1]) / 2);
				}
				x += srcPixelSize;
			}
			//*dst += padding;
			src += linestep;
		}
	}
	else if (srcPixelSize == 1) {
		for (l = 0; l < height; l++) {
			x = 0;
			for (c = 0; c < width; c++) {
				if (dstPixelSize == 4) {
					*dst++ = src[x];
					*dst++ = src[x];
					*dst++ = src[x];
					*dst++ = 0;
				}
				else if (dstPixelSize == 3) {
					*dst++ = src[x];
					*dst++ = src[x];
					*dst++ = src[x];
				}
				else if (dstPixelSize == 1) {
					*dst++ = src[x];
				}
				x += srcPixelSize;
			}
			//*dst += padding;
			src += linestep;
		}
	}

	return buffer;
}


//////////////////////////////////////////////////////////////////////
// Math Helper Routines
//////////////////////////////////////////////////////////////////////

int Bitmap::round(double d) {

	double f = floor(d);

	if ((d - f) < 0.5000)
		return (int) f;
	else
		return (int) (f+1);
}























//HTMLPage* Bitmap::toHTMLBitmap() {
//	return new HTMLPage(this);
//}
//
//JString Bitmap::toHTML() {
//	return JString::format("Bitmap size %d bytes, time %s", size, (char*) timestamp.print());
//}
//
//
//JString Bitmap::toXML() {
//	return "<bitmap />";
//}
//
//bool Bitmap::fromXML(XMLNode* node) {
//
//	if (node == NULL)
//		return false;
//
//	if (!node->getTag().equalsIgnoreCase("bitmap"))
//		return false;
//
//	return true;	
//}
//



















bool Bitmap::convertBitmapFileData(const char* src, uint32 width, uint32 height, uint32 depth, char* dst, uint32 dstlen) {

	uint32 c;
	uint32 len = width*depth;
	uint32 padding = 4 - (len%4);
	if (padding == 4) padding = 0;
	uint32 lineWidth = len + padding;
	int32 offset = ((int32)padding) - (2*lineWidth);

	unsigned char* newsrc = (unsigned char*) (src + ((height-1) * lineWidth));
	unsigned int* newdst = (unsigned int*) dst;

	for (uint32 l = 0; l < height; l++)
	{
		for (c = 0; c < width; c++) {
			*(newdst++) = ((uint32)((newsrc[0] << 16) | (newsrc[1] << 8) | (newsrc[2] << 0)));
			newsrc += depth;
		}
		newsrc += offset;
	}

	return true;
}

char* Bitmap::convertBitmapFileDataRunLength(const char* src, uint32 width, uint32 height, uint32 depth, uint32 &dstlen) {

//	src = "012345678900000000001234567890";
//	height = 1;
//	width = 30;

	char* dst = new char[width*height*4 + 100];

	uint32 c;
	uint32 padding = 4 - ((width*depth)%4);
	if (padding == 4) padding = 0;
	uint32 lineWidth = width*depth + padding;
	uint32 offset = padding - (2*lineWidth);

	unsigned char* newsrc = (unsigned char*) (src + ((height-1) * lineWidth));
	uint32* newdst = (unsigned int*) dst;
	uint32* lastCtrl = newdst;
	
	uint32 pos = 1;
	uint32 length = 0;

	bool isInRL = false;

//	int n;
/*	for (n=0; n<30; n++) {
		printf("[ %c] ", (char)src[n]);
	}
	printf("\n");*/

	//int len = 0;

	for (uint32 l = 0; l < height; l++)
	{
		for (c = 0; c < width; c++) {
			newdst[pos] = ((uint32)((newsrc[0] << 16) | (newsrc[1] << 8) | (newsrc[2] << 0)));

			if (isInRL) {
				if (newdst[pos-1] == newdst[pos]) {
					// Continue RL
					pos--;
				}
				else {
					// End RL
					*lastCtrl = (uint32)((RLECTRL << 24) | length);
				//	len += length;
					length = 0;
					lastCtrl += 2;
					pos++;
					newdst[pos] = newdst[pos-1];
					isInRL = false;
				}
			}
			else {
				if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
					if (length == 2) {
						// No data series, switch to RL
						pos -= 2;
					}
					else {
						// Close previous series
						*lastCtrl = (uint32)((DATACTRL << 24) | (length - 2));
					//	len += length - 2;
						lastCtrl += length - 1;
						length = 2;
						pos--;
					}
					isInRL = true;
				}
				else {
					// Just continue
				}
			}
			pos++;
			length++;
			newsrc += depth;
		}
		newsrc += offset;
	}
	if (isInRL) {
		*lastCtrl = (uint32)((RLECTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (uint32)((DATACTRL << 24) | length);
	//	len += length;
	}
	dstlen = pos * 4;

/*	for (n=0; n<30; n++) {
		printf("[ %c] ", (char)dst[n]);
	}
	printf("\n");*/

/*	int ctrl;
	len = 0;
	int totallen = 0;
	pos = 0;
	while (pos < (dstlen/4)) {
		ctrl = (newdst[pos] >> 24);
		len = (newdst[pos] & 0xFFFFFF);
		pos++;
		if (ctrl == 1) {
		//	printf("Data[%d] ", len);
		//	for (int k=0; k<len; k++)
		//		printf("%c ", (char) newdst[pos+k]);
			totallen += len;
			pos += len;
		}
		else if (ctrl == 2) {
//			printf("RLE[%d] %d ", len, (int) newdst[pos]);
			totallen += len;
			pos++;
		}
		else {
			printf("Wrong CTRL: %d ", ctrl);
			pos++;
		}
	}
*/

/*	pos = 0;
	totallen = 0;
	while (pos < dstlen) {
		ctrl = (newdst[pos] >> 24);
		len = (newdst[pos] & 0xFFFFFF);
		pos++;
		if (ctrl == 1) {
			totallen += len;
			printf("Data[%d] (%d) ", len, totallen);
		//	for (int k=0; k<len; k++)
		//		printf("%c ", (char) newdst[pos+k]);
			pos += len;
		}
		else if (ctrl == 2) {
			totallen += len;
			printf("RLE[%d] (%d) ", len, totallen);
			pos++;
		}
		else {
			printf("Wrong CTRL: %d ", ctrl);
			pos++;
		}
	}
*/

	return dst;
}

char* Bitmap::differenceBitmapFileData(char* orig, char* src, uint32 width, uint32 height, uint32 depth, uint32 &dstlen) {

/*	orig = "987654321100000000009876543211";
	src  = "012345678900000000001234567890";
	height = 1;
	width = 30;
*/
	char* dst = new char[width*height*4 + 100];

	uint32 c;
	uint32 padding = 4 - ((width*depth)%4);
	if (padding == 4) padding = 0;
	uint32 lineWidth = width*depth + padding;
	uint32 offset = padding - (2*lineWidth);

	unsigned char* newsrc = (unsigned char*) (src + ((height-1) * lineWidth));
	uint32* newdst = (uint32*) dst;
	uint32* org = (uint32*) orig;
	// unsigned char* org = (unsigned char*) orig;
	uint32* lastCtrl = newdst;
	
	uint32 pos = 1;
	uint32 length = 0;
	uint32 dlength = 0;

	bool isInDif = false;

/*	int n;
	for (n=0; n<30; n++) {
		newdst[n] = 0;
	}*/

	//int len = 0;
	for (uint32 l = 0; l < height; l++)
	{
		for (c = 0; c < width; c++) {
			newdst[pos] = ((uint32)((newsrc[0] << 16) | (newsrc[1] << 8) | (newsrc[2] << 0)));
		//	newdst[pos] = src[c];

		/*	for (n=0; n<30; n++) {
				printf("[%.2d] ", newdst[n]);
			}
			printf("\n");*/

			if (isInDif) {
				if (*org == newdst[pos]) {
					// Continue Dif
					pos--;
				}
				else {
					// End Dif
					*lastCtrl = (uint32)((DIFCTRL << 24) | length);
				//	len += length;
					length = 0;
					lastCtrl++;
					pos++;
					newdst[pos] = newdst[pos-1];
					isInDif = false;
				}
			}
			else {
				if (*org == newdst[pos]) {
					if (pos == 1) {
						pos--;
						isInDif = true;
					}
					else {
						// Close previous series
						*lastCtrl = (uint32)((DATACTRL << 24) | (length));
					//	len += length;
						lastCtrl += length + 1;
						length = 0;
						// pos--;
						isInDif = true;
					}
				}
				else {
					// Just continue
				}
			}
			pos++;
			length++;
			newsrc += depth;
			org++;
		}
		newsrc += offset;
	}
	if (isInDif) {
		*lastCtrl = (uint32)((DIFCTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (uint32)((DATACTRL << 24) | length);
	//	len += length;
	}
	dstlen = pos * 4;


/*	pos = 0;
	int totallen = 0;
	int ctrl;
	while (pos < (dstlen/4)) {
		ctrl = (newdst[pos] >> 24);
		len = (newdst[pos] & 0xFFFFFF);
		pos++;
		if (ctrl == 1) {
			totallen += len;
			printf("Data[%d] (%d) ", len, totallen);
		//	for (int k=0; k<len; k++)
		//		printf("%c ", (char) newdst[pos+k]);
			pos += len;
		}
		else if (ctrl == 2) {
			totallen += len;
			printf("RLE[%d] (%d) ", len, totallen);
			pos++;
		}
		else if (ctrl == 3) {
			totallen += len;
			printf("RLE[%d] (%d) ", len, totallen);
			// pos++;
		}
		else {
			printf("Wrong CTRL: %d ", ctrl);
			pos++;
		}
	}*/

	return dst;
}

char* Bitmap::differenceBitmapFileDataRunLength(char* orig, char* src, uint32 width, uint32 height, uint32 depth, uint32 &dstlen) {

	char* dst = new char[width*height*4 + 100];

	uint32 c;
	uint32 padding = 4 - ((width*depth)%4);
	if (padding == 4) padding = 0;
	uint32 lineWidth = width*depth + padding;
	uint32 offset = padding - (2*lineWidth);

	unsigned char* newsrc = (unsigned char*) (src + ((height-1) * lineWidth));
	uint32* newdst = (uint32*) dst;
	uint32* org = (uint32*) orig;
	uint32* lastCtrl = newdst;
	
	uint32 pos = 1;
	uint32 length = 0;
	uint32 dlength = 0;

	bool isInRL = false;
	bool isInDif = false;

//	int len = 0;
	for (uint32 l = 0; l < height; l++)
	{
		for (c = 0; c < width; c++) {
			newdst[pos] = ((uint32)((newsrc[0] << 16) | (newsrc[1] << 8) | (newsrc[2] << 0)));

			if (isInDif) {
				if (*org == newdst[pos]) {
					// Continue Dif
					pos--;
				}
				else {
					// End Dif
					*lastCtrl = (uint32)((DIFCTRL << 24) | length);
					//len += length;
					length = 0;
					lastCtrl++;
					pos++;
					newdst[pos] = newdst[pos-1];
					isInDif = false;
				}
			}
			else if (isInRL) {
				if (newdst[pos-1] == newdst[pos]) {
					// Continue RL
					pos--;
				}
				else {
					// End RL
					*lastCtrl = (uint32)((RLECTRL << 24) | length);
					//len += length;
					length = 0;
					lastCtrl += 2;
					pos++;
					newdst[pos] = newdst[pos-1];
					isInRL = false;
				}
			}
			else {
				if (*org == newdst[pos]) {
					if (pos == 1) {
						pos--;
					}
					else {
						// Close previous series
						*lastCtrl = (uint32)((DATACTRL << 24) | (length));
					//	len += length;
						lastCtrl += length + 1;
						length = 0;
						// pos--;
					}
					isInDif = true;
				}
				else if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
					if (length == 2) {
						// No data series, switch to RL
						pos -= 2;
					}
					else {
						// Close previous series
						*lastCtrl = (uint32)((DATACTRL << 24) | (length - 2));
					//	len += length - 2;
						lastCtrl += length - 1;
						length = 2;
						pos--;
					}
					isInRL = true;
				}
				else {
					// Just continue
				}
			}
			pos++;
			length++;
			org++;
			newsrc += depth;
		}
		newsrc += offset;
	}
	if (isInDif) {
		*lastCtrl = (uint32)((DIFCTRL << 24) | length);
	//	len += length;
	}
	else if (isInRL) {
		*lastCtrl = (uint32)((RLECTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (uint32)((DATACTRL << 24) | length);
	//	len += length;
	}
	dstlen = pos * 4;

	return dst;
}

BitmapUpdate* Bitmap::getCompressedUpdate(bool destructive) const {
	if (!destructive)
		return runLengthEncode();
	else
		return runLengthDestructiveEncode();
}


BitmapUpdate* Bitmap::operator -( const Bitmap &bitmap ) const {

	if ( (this->width != bitmap.width) || (this->height != bitmap.height) )
		return NULL;

	int byteWidth = width*4;

	char* dst = new char[width*height*4 + 100];

	unsigned int* newsrc = (unsigned int*) this->data;
	unsigned int* newdst = (unsigned int*) dst;
	unsigned int* org = (unsigned int*) bitmap.data;
	unsigned int* lastCtrl = newdst;
	
	int pos = 1;
//	int pos = 0;
	int length = 0;

	bool isInRL = false;
	bool isInDif = false;

	int maxpos = (int)(width*height*0.9);
	unsigned int* endsrc = newsrc + (width*height);
	while ((newsrc < endsrc) && (pos <= maxpos)) {

		newdst[pos] = *newsrc;

		if (isInDif) {
			if (*org == newdst[pos]) {
				// Continue Dif
				pos--;
			}
			else {
				// End Dif
				*lastCtrl = (unsigned int)((DIFCTRL << 24) | length);
				//len += length;
				length = 0;
				lastCtrl++;
				pos++;
				newdst[pos] = newdst[pos-1];
				isInDif = false;
			}
		}
		else if (isInRL) {
			if (newdst[pos-1] == newdst[pos]) {
				// Continue RL
				pos--;
			}
			else {
				// End RL
				*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
				//len += length;
				length = 0;
				lastCtrl += 2;
				pos++;
				newdst[pos] = newdst[pos-1];
				isInRL = false;
			}
		}
		else {
			if (*org == newdst[pos]) {
				if (pos == 1) {
					pos--;
				}
				else {
					// Close previous series
					*lastCtrl = (unsigned int)((DATACTRL << 24) | (length));
				//	len += length;
					lastCtrl += length + 1;
					length = 0;
					// pos--;
				}
				isInDif = true;
			}
			else if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
				if (length == 2) {
					// No data series, switch to RL
					pos -= 2;
				}
				else {
					// Close previous series
					*lastCtrl = (unsigned int)((DATACTRL << 24) | (length - 2));
				//	len += length - 2;
					lastCtrl += length - 1;
					length = 2;
					pos--;
				}
				isInRL = true;
			}
			else {
				// Just continue
			}
		}
		pos++;
		length++;
		org++;
		newsrc++;
	}
	
	// Forget it, difference is too big anyway...
	if (pos > maxpos) {
		delete [] dst;
		return this->getCompressedUpdate(false);
	}


	if (isInDif) {
		if (pos <= 1) {
			delete [] dst;
			return NULL;
		}
		*lastCtrl = (unsigned int)((DIFCTRL << 24) | length);
	//	len += length;
	}
	else if (isInRL) {
		*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (unsigned int)((DATACTRL << 24) | length);
	//	len += length;
	}

	BitmapUpdate* update = new BitmapUpdate(width, height);
	update->data = dst;
	update->size = pos * 4;
	update->setUpdateType(BitmapUpdate::DIFRUNLENGTH);
	return update;
}





BitmapUpdate* Bitmap::runLengthEncode() const {

	if ( (this->width <= 0) || (this->height <= 0) )
		return NULL;

	int byteWidth = width*4;

	char* dst = new char[width*height*4 + 100];

	unsigned int* newsrc = (unsigned int*) this->data;
	unsigned int* newdst = (unsigned int*) dst;
	unsigned int* lastCtrl = newdst;
	
	int pos = 1;
//	int pos = 0;
	int length = 0;

	bool isInRL = false;

	unsigned int* endsrc = newsrc + (width*height);

	while (newsrc < endsrc) {
		newdst[pos] = *newsrc;

		if (isInRL) {
			if (newdst[pos-1] == newdst[pos]) {
				// Continue RL
				pos--;
			}
			else {
				// End RL
				*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
				//len += length;
				length = 0;
				lastCtrl += 2;
				pos++;
				newdst[pos] = newdst[pos-1];
				isInRL = false;
			}
		}
		else {
			if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
				if (length == 2) {
					// No data series, switch to RL
					pos -= 2;
				}
				else {
					// Close previous series
					*lastCtrl = (unsigned int)((DATACTRL << 24) | (length - 2));
				//	len += length - 2;
					lastCtrl += length - 1;
					length = 2;
					pos--;
				}
				isInRL = true;
			}
			else {
				// Just continue
			}
		}
		pos++;
		length++;
		newsrc++;
	}

	if (isInRL) {
		*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (unsigned int)((DATACTRL << 24) | length);
	//	len += length;
	}

	BitmapUpdate* update = new BitmapUpdate(width, height);
	update->data = dst;
	update->size = pos * 4;
	update->setUpdateType(BitmapUpdate::RUNLENGTH);
	return update;
}

BitmapUpdate* Bitmap::runLengthDestructiveEncode() const {

	if ( (this->width <= 0) || (this->height <= 0) )
		return NULL;

	int byteWidth = width*4;

	char* dst = new char[width*height*2 + 100];

	unsigned int* newsrc = (unsigned int*) this->data;
	unsigned int* newdst = (unsigned int*) dst;
	unsigned int* lastCtrl = newdst;
	
	int pos = 1;
//	int pos = 0;
	int length = 0;

	bool isInRL = false;

	unsigned int* endsrc = newsrc + (width*height);
	unsigned int* endsrc1 = endsrc - 2;
	
	while (newsrc < endsrc1) {
		newdst[pos] = GET_DRGB_FROM_2RGBS(*newsrc,*(newsrc+1));
		newsrc+=2;
		if (isInRL) {
			if (newdst[pos-1] == newdst[pos]) {
				// Continue RL
				pos--;
			}
			else {
				// End RL
				*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
				//len += length;
				length = 0;
				lastCtrl += 2;
				pos++;
				newdst[pos] = newdst[pos-1];
				isInRL = false;
			}
		}
		else {
			if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
				if (length == 2) {
					// No data series, switch to RL
					pos -= 2;
				}
				else {
					// Close previous series
					*lastCtrl = (unsigned int)((DATACTRL << 24) | (length - 2));
				//	len += length - 2;
					lastCtrl += length - 1;
					length = 2;
					pos--;
				}
				isInRL = true;
			}
			else {
				// Just continue
			}
		}
		pos++;
		length++;
		// newsrc+=2;
	}
	if (endsrc-newsrc == 2)
		newdst[pos] = GET_DRGB_FROM_2RGBS(*newsrc,*(newsrc+1));
	else
		newdst[pos] = GET_DRGB_FROM_2RGBS(*newsrc,0);

	if (isInRL) {
		if (newdst[pos-1] == newdst[pos]) {
			// Continue RL
			pos--;
		}
		else {
			// End RL
			*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
			//len += length;
			length = 0;
			lastCtrl += 2;
			pos++;
			newdst[pos] = newdst[pos-1];
			isInRL = false;
		}
	}
	else {
		if ( (length > 1) && (newdst[pos-2] == newdst[pos-1]) && (newdst[pos-1] == newdst[pos]) ) {
			if (length == 2) {
				// No data series, switch to RL
				pos -= 2;
			}
			else {
				// Close previous series
				*lastCtrl = (unsigned int)((DATACTRL << 24) | (length - 2));
			//	len += length - 2;
				lastCtrl += length - 1;
				length = 2;
				pos--;
			}
			isInRL = true;
		}
		else {
			// Just continue
		}
	}
	pos++;
	length++;
	newsrc+=2;

	if (isInRL) {
		*lastCtrl = (unsigned int)((RLECTRL << 24) | length);
	//	len += length;
	}
	else {
		*lastCtrl = (unsigned int)((DATACTRL << 24) | length);
	//	len += length;
	}

	BitmapUpdate* update = new BitmapUpdate(width, height);
	update->data = dst;
	update->size = pos * 4;
	update->setUpdateType(BitmapUpdate::RL16);
	return update;
}



char* ImageResizeDown66(char* bytesource, uint32 &cols, uint32 &rows)
{

	// This algorithm was written for single byte images,
	// we need this for 4 byte images, or integers

	char* bytedest = new char[cols*rows*4];

	unsigned int* source = (unsigned int*) bytesource;
	unsigned int* dest = (unsigned int*) bytedest;

	// Pointer arrangment
	//       p q r    ->   a b
	//       s t u         c d
	//       v w x

	unsigned int *p;
	unsigned int *q;
	unsigned int *r;
	unsigned int *s;
	unsigned int *t;
	unsigned int *u;
	unsigned int *v;
	unsigned int *w;
	unsigned int *x;
	unsigned int *a;
	unsigned int *b;
	unsigned int *c;
	unsigned int *d;

	unsigned int** shrows = new unsigned int*[rows];

	shrows[0] = source;
	for (uint32 n=1; n<rows; n++) {
		shrows[n] = shrows[n-1] + cols;
	}

	q = shrows[0] + 1;
	s = shrows[1];
	t = s + 1;
	u = t + 1;
	w = shrows[2] + 1;

	// Smooth - skip first and last rows, first and last cols
	// This is a bit of a cheat since the left pixel of each group
	// is smoothed with an already-smoothed pixel
	// ------------------------------------------------------

	/*        for(int i = 1; i < rows-1; i++)
			{
			q = shrows[i-1] + 1;
			s = shrows[i];
			t = s + 1;
			u = t + 1;
			w = shrows[i+1] + 1;

			for(int j = 0; j < cols; j++, q++, s++, t++, u++, w++)
					*t = (AGPIXEL)(((int)(*q) + (int)(*w) + 4 * (int)(*t)) / 6);
			}
	*/

	// Now for the resizing
	// --------------------

	int newcols = (int)((2.0*cols)/3.0);
	int newrows = (int)((2.0*rows)/3.0);

	if (newcols != (2.0*cols/3.0))
		newcols++;
	if (newrows != (2.0*cols/3.0))
		newrows++;

	unsigned int *r1 = dest;
	unsigned int *r2 = dest + newcols;

	//        printf("RESIZE\n Rows: %-3d->%-3d\n Cols %-3d->%-3d\n", rows, newrows, cols, newcols);

	uint32 j;
	for(uint32 i = 0, k = 0; i < rows; i += 3, k += 2) {
		p = shrows[i];
		q = p+1;
		r = q+1;
		if (rows - i > 2)
			s = shrows[i+1];
		else
			s = p;
		t = s+1;
		u = t+1;
		if (rows - i > 2)
			v = shrows[i+2];
		else
			v = p;
		w = v+1;
		x = w+1;

		// r1 = PIXEL[k];
		r1 = dest + (newcols * k);
		// r2 = r1+ cols64;
		r2 = r1 + newcols;

		a = r1;
		b = a+1;
		if (newrows - k < 1)
			c = a;
		else
			c = r2;
		d = c+1;

		for(j = 0; j < cols-3; j += 3) {
			*a = GET_RGB_FROM_RGBA(*p);
			*b = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*q) + (double)GET_R_FROM_RGBA(*r))/2)) << 0) +
				 (((unsigned int)((1+(double)GET_G_FROM_RGBA(*q) + (double)GET_G_FROM_RGBA(*r))/2)) << 8) +
				 (((unsigned int)((1+(double)GET_B_FROM_RGBA(*q) + (double)GET_B_FROM_RGBA(*r))/2)) << 16);
			*c = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*s) + (double)GET_R_FROM_RGBA(*v))/2)) << 0) +
				 (((unsigned int)((1+(double)GET_G_FROM_RGBA(*s) + (double)GET_G_FROM_RGBA(*v))/2)) << 8) +
				 (((unsigned int)((1+(double)GET_B_FROM_RGBA(*s) + (double)GET_B_FROM_RGBA(*v))/2)) << 16);
			*d = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*t) + (double)GET_R_FROM_RGBA(*u) + (double)GET_R_FROM_RGBA(*w) + (double)GET_R_FROM_RGBA(*x))/4)) << 0) +
				 (((unsigned int)((1+(double)GET_G_FROM_RGBA(*t) + (double)GET_G_FROM_RGBA(*u) + (double)GET_G_FROM_RGBA(*w) + (double)GET_G_FROM_RGBA(*x))/4)) << 8) +
				 (((unsigned int)((1+(double)GET_B_FROM_RGBA(*t) + (double)GET_B_FROM_RGBA(*u) + (double)GET_B_FROM_RGBA(*w) + (double)GET_B_FROM_RGBA(*x))/4)) << 16);

			p += 3;
			q += 3;
			r += 3;
			s += 3;
			t += 3;
			u += 3;
			v += 3;
			w += 3;
			x += 3;
			a += 2;
			b += 2;
			c += 2;
			d += 2;
		}
		if (cols - j > 0)
			*a = GET_RGB_FROM_RGBA(*p);
		else
			*a = *(a-2);
		if (cols - j > 1)
			*b = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*q) + (double)GET_R_FROM_RGBA(*r))/2)) << 0) +
					(((unsigned int)((1+(double)GET_G_FROM_RGBA(*q) + (double)GET_G_FROM_RGBA(*r))/2)) << 8) +
					(((unsigned int)((1+(double)GET_B_FROM_RGBA(*q) + (double)GET_B_FROM_RGBA(*r))/2)) << 16);
		else
			*b = *(b-2);
		if (cols - j > 2)
			*c = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*s) + (double)GET_R_FROM_RGBA(*v))/2)) << 0) +
					(((unsigned int)((1+(double)GET_G_FROM_RGBA(*s) + (double)GET_G_FROM_RGBA(*v))/2)) << 8) +
					(((unsigned int)((1+(double)GET_B_FROM_RGBA(*s) + (double)GET_B_FROM_RGBA(*v))/2)) << 16);
		else
			*c = *(c-2);
		if (cols - j > 3)
			*d = (((unsigned int)((1+(double)GET_R_FROM_RGBA(*t) + (double)GET_R_FROM_RGBA(*u) + (double)GET_R_FROM_RGBA(*w) + (double)GET_R_FROM_RGBA(*x))/4)) << 0) +
					(((unsigned int)((1+(double)GET_G_FROM_RGBA(*t) + (double)GET_G_FROM_RGBA(*u) + (double)GET_G_FROM_RGBA(*w) + (double)GET_G_FROM_RGBA(*x))/4)) << 8) +
					(((unsigned int)((1+(double)GET_B_FROM_RGBA(*t) + (double)GET_B_FROM_RGBA(*u) + (double)GET_B_FROM_RGBA(*w) + (double)GET_B_FROM_RGBA(*x))/4)) << 16);
		else
			*d = *(d-2);

	}
//	printf("Resize done");
	delete [] shrows;
	cols = newcols;
	rows = newrows;
	return bytedest;
}

char* ImageResizeUp66(char* bytesource, uint32 &cols, uint32 &rows) {

	// This algorithm was written for single byte images,
	// we need this for 4 byte images, or integers

	// Pointer arrangment
	//       p q r    ->   a b
	//       s t u         c d
	//       v w x

	unsigned int *p;
	unsigned int *q;
	unsigned int *r;
	unsigned int *s;
	unsigned int *t;
	unsigned int *u;
	unsigned int *v;
	unsigned int *w;
	unsigned int *x;
	unsigned int *a;
	unsigned int *b;
	unsigned int *c;
	unsigned int *d;

	int newcols = (int)((3.0*(double)cols)/2.0);
	int newrows = (int)((3.0*(double)rows)/2.0);
	if ((double)newcols != (3.0*cols)/2.0)
		newcols++;
	if ((double)newrows != (3.0*rows)/2.0)
		newrows++;

	char* bytedest = new char[newcols*newrows*4];
	unsigned int* source = (unsigned int*) bytesource;
	unsigned int* dest = (unsigned int*) bytedest;

	unsigned int** dhrows = new unsigned int*[newrows];

	dhrows[0] = dest;
	for (int n=1; n<newrows; n++) {
		dhrows[n] = dhrows[n-1] + newcols;
	}

	q = dhrows[0] + 1;
	s = dhrows[1];
	t = s + 1;
	u = t + 1;
	w = dhrows[2] + 1;

	// Now for the resizing
	// --------------------


	unsigned int *r1 = source;
	unsigned int *r2 = source + cols;

	//        printf("RESIZE\n Rows: %-3d->%-3d\n Cols %-3d->%-3d\n", rows, newrows, cols, newcols);

	int realrows = newrows-3;
	int realcols = newcols-3;
//	int ii, kk, jj;
	int i, j, k;
	for(i = 0, k = 0; i <= realrows; i += 3, k += 2) {
		p = dhrows[i];
		q = p+1;
		r = q+1;
		s = dhrows[i+1];
		t = s+1;
		u = t+1;
		v = dhrows[i+2];
		w = v+1;
		x = w+1;

		// r1 = PIXEL[k];
		r1 = source + (cols * k);
		// r2 = r1+ cols64;
		r2 = r1 + cols;

		a = r1;
		b = a+1;
		c = r2;
		d = c+1;

		for(j = 0; j <= realcols; j += 3) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q = *r = GET_RGB_FROM_RGBA(*b);
			*s = *v = GET_RGB_FROM_RGBA(*c);
			*t = *u = *w = *x = GET_RGB_FROM_RGBA(*d);;
			p += 3;
			q += 3;
			r += 3;
			s += 3;
			t += 3;
			u += 3;
			v += 3;
			w += 3;
			x += 3;
			a += 2;
			b += 2;
			c += 2;
			d += 2;
		}
		if (newcols - j == 2) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q= GET_RGB_FROM_RGBA(*b);
			*s = *v = GET_RGB_FROM_RGBA(*c);
			*t= *w = GET_RGB_FROM_RGBA(*d);;
		}
		else if (newcols - j == 1) {
			*p = GET_RGB_FROM_RGBA(*a);
			*s = *v = GET_RGB_FROM_RGBA(*c);
		}
	}
	if (newrows - i == 2) {
		p = dhrows[i];
		q = p+1;
		r = q+1;
		s = dhrows[i+1];
		t = s+1;
		u = t+1;

		// r1 = PIXEL[k];
		r1 = source + (cols * k);
		// r2 = r1+ cols64;
		r2 = r1 + cols;

		a = r1;
		b = a+1;
		c = r2;
		d = c+1;

		for(j = 0; j <= realcols; j += 3) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q = *r = GET_RGB_FROM_RGBA(*b);
			*s = GET_RGB_FROM_RGBA(*c);
			*t = *u = GET_RGB_FROM_RGBA(*d);;
			p += 3;
			q += 3;
			r += 3;
			s += 3;
			t += 3;
			u += 3;
			v += 3;
			w += 3;
			x += 3;
			a += 2;
			b += 2;
			c += 2;
			d += 2;
		}
		if (newcols - j == 2) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q= GET_RGB_FROM_RGBA(*b);
			*s =GET_RGB_FROM_RGBA(*c);
			*t= GET_RGB_FROM_RGBA(*d);;
		}
		else if (newcols - j == 1) {
			*p = GET_RGB_FROM_RGBA(*a);
			*s = GET_RGB_FROM_RGBA(*c);
		}
	}
	else if (newrows - i == 1) {
		p = dhrows[i];
		q = p+1;
		r = q+1;

		// r1 = PIXEL[k];
		r1 = source + (cols * k);
		// r2 = r1+ cols64;
		r2 = r1 + cols;

		a = r1;
		b = a+1;
		c = r2;
		d = c+1;

		for(j = 0; j <= realcols; j += 3) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q = *r = GET_RGB_FROM_RGBA(*b);
			p += 3;
			q += 3;
			r += 3;
			s += 3;
			t += 3;
			u += 3;
			v += 3;
			w += 3;
			x += 3;
			a += 2;
			b += 2;
			c += 2;
			d += 2;
		}
		if (newcols - j == 2) {
			*p = GET_RGB_FROM_RGBA(*a);
			*q= GET_RGB_FROM_RGBA(*b);
		}
		else if (newcols - j == 1) {
			*p = GET_RGB_FROM_RGBA(*a);
		}
	}
//	printf("Resize done");
	delete [] dhrows;
	cols = newcols;
	rows = newrows;
	return bytedest;
}

char* ImageResizeDown(char* bytesource, uint32 &cols, uint32 &rows, uint32 factor) {
	if (factor <= 0) {
		return NULL;
	}
	int factor2 = factor*factor;

	int newcols = (int)((double)cols/factor);
	int newrows = (int)((double)rows/factor);
	if ((double)newcols != ((double)cols/factor))
		newcols++;
	if ((double)newrows != ((double)rows/factor))
		newrows++;

	char* bytedest = new char[newcols*newrows*4];

	if (factor == 1) {
		memcpy(bytedest, bytesource, cols*rows*4);
		return bytedest;
	}

	unsigned int* source = (unsigned int*) bytesource;
	unsigned int* dest = (unsigned int*) bytedest;

	int n, m;
//	unsigned int* destPixel = dest;
	unsigned int* srcStartPixel = source;
//	unsigned int* srcPixel;
	unsigned char* srcPixelByte;

	double r;
	double g;
	double b;
	int step = (cols - factor)*4;
	int linestep = (factor-1)*cols;

	int rowmax = rows-factor;
	int colmax = cols-factor;
//	int rowmax = rows;
//	int colmax = cols;

	int factorX, factorY;

	double dfactor2 = 1.0/((double)factor2);

	unsigned char* destPixelByte = (unsigned char*) dest;
	// Take a square of f*f pixels and average to produce 1 pixel
	for (int y=0; y<=rowmax; y+=factor) {
		factorY = clmin(factor, rows-y);
		for (int x=0; x<=colmax; x+=factor) {
			factorX = clmin(factor, cols-x);
//			factorX = factor;
			r = g = b = 0;
			factor2 = factorY*factorX;
			srcPixelByte = (unsigned char*)srcStartPixel;
			for (n=0; n<factorY; n++) {
				for (m=0; m<factorX; m++) {
					r += *srcPixelByte++;
					g += *srcPixelByte++;
					b += *srcPixelByte++;
					srcPixelByte++;
				}
				srcPixelByte += step;
			}
			*destPixelByte++ = (unsigned char) (r*dfactor2);
			*destPixelByte++ = (unsigned char) (g*dfactor2);
			*destPixelByte++ = (unsigned char) (b*dfactor2);
			*destPixelByte++ = 0;
		//	((unsigned char*)destPixel)[0] = (int) r/factor2;
		//	((unsigned char*)destPixel)[1] = (int) g/factor2;
		//	((unsigned char*)destPixel)[2] = (int) b/factor2;
			// *destPixel = (int) val/(factor*factor);
			srcStartPixel += factorX;
		}
		srcStartPixel += linestep;
/*		if ( (x-colmax > 0) && (x-colmax <= (factor/2)) ) {
			*destPixelByte++ = (unsigned char) (r/factor2);
			*destPixelByte++ = (unsigned char) (g/factor2);
			*destPixelByte++ = (unsigned char) (b/factor2);
			*destPixelByte++ = 0;
		}*/
	}
	cols = newcols;
	rows = newrows;
	return bytedest;
}

char* ImageResizeUp(char* bytesource, uint32 &cols, uint32 &rows, uint32 factor) {

	if (factor <= 0) {
		return NULL;
	}
	int factor2 = factor*factor;
	int newcols = cols*factor;
	int newrows = rows*factor;

	char* bytedest = new char[newcols*newrows*4];
	if (factor == 1) {
		memcpy(bytedest, bytesource, cols*rows*4);
		return bytedest;
	}

	unsigned int* source = (unsigned int*) bytesource;
	unsigned int* dest = (unsigned int*) bytedest;

	int destCols = cols*factor;
	uint32 n, m, pixel;
	unsigned int* destStartPixel = dest;
	unsigned int* destPixel;
	unsigned int* srcPixel = source;
	int step = destCols - factor;
	int linestep = (factor-1)*destCols;
	// int linestep = factor*cols;

	// Take a pixel and make it a square of f*f pixels
	for (uint32 x=0; x<rows; x++) {
		for (uint32 y=0; y<cols; y++) {
			destPixel = destStartPixel;
			pixel = GET_RGB_FROM_RGBA(*srcPixel++);
			for (n=0; n<factor; n++) {
				for (m=0; m<factor; m++) {
					*destPixel++ = pixel;
				}
				destPixel += step;
			}
			destStartPixel += factor;
		}
		destStartPixel += linestep;
	}
	// Take a square of f*f pixels and average to produce 1 pixel
	cols = newcols;
	rows = newrows;
	return bytedest;
}





//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

BitmapUpdate::BitmapUpdate() {
	init(0, 0);
}

BitmapUpdate::BitmapUpdate(uint32 w, uint32 h) {
	init(w, h);
}

BitmapUpdate::BitmapUpdate(const char* filename, bool compress) {
	init(0, 0);
	if (compress) {
		if (!readFromFileRLE(filename)) {
		}
	}
	else {
		if (!readFromFile(filename)) {
		}
	}
}

BitmapUpdate::~BitmapUpdate() {
	reset();
}

bool BitmapUpdate::init(uint32 w, uint32 h) {
	//	setWidth(w);
	width = w;
	height = h;
	//	setHeight(h);
	return true;
}

bool BitmapUpdate::reset() {
	//	setWidth(0);
	//	setHeight(0);
	width = height = 0;
	setUpdateType(NONE);
	delete(data);
	data = NULL;
	size = 0;
	return true;
}
/*
bool BitmapUpdate::setWidth(int width) {
return this->params.put("Width", JString(width));
}

int BitmapUpdate::getWidth() {
return this->params.get("Width").toInt();
}

bool BitmapUpdate::setHeight(int height) {
return this->params.put("Height", JString(height));
}

int BitmapUpdate::getHeight() {
return this->params.get("Height").toInt();
}
*/
//bool BitmapUpdate::setUpdateType(JString updateType) {
//	return this->params.put("Update", updateType);
//}
//
//JString BitmapUpdate::getUpdateType() {
//	return this->params.get("Update");
//}

Bitmap* BitmapUpdate::toBitmap() {
	return new Bitmap(this);
}

bool BitmapUpdate::setUpdateType(BitmapUpdate::Type updateType) {
	type = updateType;
	return true;
}

BitmapUpdate::Type BitmapUpdate::getUpdateType() {
	return type;
}

//HTMLPage* BitmapUpdate::toHTMLBitmap() {
//	Bitmap* bitmap = this->toBitmap();
//	HTMLPage* page = NULL;
//	if (bitmap != NULL) {
//		page = new HTMLPage(bitmap);
//		delete(bitmap);
//	}
//	return page;
//}


bool BitmapUpdate::setFullUpdate(Bitmap* bitmap) {
	if ((bitmap == NULL) || (bitmap->data == NULL))
		return false;
	reset();
	setUpdateType(FULL);
	init(bitmap->width, bitmap->height);
	size = bitmap->size;
	data = new char[size];
	memcpy(data, bitmap->data, size);
	return true;
}

bool BitmapUpdate::readFromFile(const char* filename) {

	//	JTime t1;

	BITMAPFILEHEADER_CM* bmfh; // ={0};
	BITMAPINFOHEADER_CM* bmih; // ={0};
	uint32 count = 0;

	uint32 len;
	char* dat = utils::ReadAFile(filename, len, true);
	if (dat == NULL)
		return false;

	//	JTime t2;

	char* src = (char*)dat;
	bmfh = (BITMAPFILEHEADER_CM*)src;
	src += sizeof(BITMAPFILEHEADER_CM);
	bmih = (BITMAPINFOHEADER_CM*)src;
	src += sizeof(BITMAPINFOHEADER_CM);

	if (bmih->biCompression != 0) {
		delete[] dat;
		return false;
	}

	if (bmih->biBitCount != 24) {
		delete[] dat;
		return false;
	}

	//	JTime t3;

	int depth = bmih->biBitCount;
	int planes = bmih->biPlanes;
	width = bmih->biWidth;
	height = bmih->biHeight;

	if (bmih->biSizeImage == 0)
		bmih->biSizeImage = bmfh->bfSize - bmfh->bfOffBits;

	if (len < (int)(sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM) + (width * height * ((int)(depth / 8))))) {
		delete[] dat;
		return false;
	}

	//	JTime t4;

	//	setWidth(width);
	//	setHeight(height);

	//	JTime t5;

	this->size = width * height * 4;
	data = new char[this->size];
	if (data == NULL) {
		this->size = 0;
		delete[] dat;
		return false;
	}

	//	JTime t6;
	Bitmap::convertBitmapFileData(src, width, height, (int)(bmih->biBitCount / 8), data, this->size);
	//	JTime t7;

	delete[] dat;
	setUpdateType(FULL);
	//	JTime t8;
	//		printf("                                               Time: %s\n", (char*) JString::format("%d  %d  %d  %d  %d  %d  %d  =  %d",
	//			t2.microDifference(t1), t3.microDifference(t2), t4.microDifference(t3), t5.microDifference(t4), t6.microDifference(t5), t7.microDifference(t6), t8.microDifference(t7), t8.microDifference(t1)));
	return true;
}

bool BitmapUpdate::readFromFileRLE(const char* filename) {

	BITMAPFILEHEADER_CM* bmfh; // ={0};
	BITMAPINFOHEADER_CM* bmih; // ={0};
	uint32 count = 0;

	uint32 len;
	char* dat = utils::ReadAFile(filename, len, true);
	if (dat == NULL)
		return false;

	char* src = (char*)dat;
	bmfh = (BITMAPFILEHEADER_CM*)src;
	src += sizeof(BITMAPFILEHEADER_CM);
	bmih = (BITMAPINFOHEADER_CM*)src;
	src += sizeof(BITMAPINFOHEADER_CM);

	if (bmih->biCompression != 0) {
		delete[] dat;
		return false;
	}

	if (bmih->biBitCount != 24) {
		delete[] dat;
		return false;
	}

	int depth = bmih->biBitCount;
	int planes = bmih->biPlanes;
	width = bmih->biWidth;
	height = bmih->biHeight;

	if (bmih->biSizeImage == 0)
		bmih->biSizeImage = bmfh->bfSize - bmfh->bfOffBits;

	if (len < (int)(sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM) + (width * height * ((int)(depth / 8))))) {
		delete[] dat;
		return false;
	}

	//	setWidth(width);
	//	setHeight(height);

	/*	this->size = width * height * 4;
	data = new char[this->size];
	if (data == NULL) {
	this->size = 0;
	delete [] dat;
	return false;
	}*/

	data = Bitmap::convertBitmapFileDataRunLength(src, width, height, (int)(bmih->biBitCount / 8), this->size);

	delete[] dat;
	if (data == NULL)
		return false;

	setUpdateType(RUNLENGTH);
	return true;
}

bool BitmapUpdate::readFromFileDiff(const char* filename, Bitmap* oldBitmap) {
	BITMAPFILEHEADER_CM* bmfh; // ={0};
	BITMAPINFOHEADER_CM* bmih; // ={0};
	uint32 count = 0;

	uint32 len;
	char* dat = utils::ReadAFile(filename, len, true);
	if (dat == NULL)
		return false;

	char* src = (char*)dat;
	bmfh = (BITMAPFILEHEADER_CM*)src;
	src += sizeof(BITMAPFILEHEADER_CM);
	bmih = (BITMAPINFOHEADER_CM*)src;
	src += sizeof(BITMAPINFOHEADER_CM);

	if (bmih->biCompression != 0) {
		delete[] dat;
		return false;
	}

	if (bmih->biBitCount != 24) {
		delete[] dat;
		return false;
	}

	int depth = bmih->biBitCount;
	int planes = bmih->biPlanes;
	width = bmih->biWidth;
	height = bmih->biHeight;

	if (bmih->biSizeImage == 0)
		bmih->biSizeImage = bmfh->bfSize - bmfh->bfOffBits;

	if (len < (int)(sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM) + (width * height * ((int)(depth / 8))))) {
		delete[] dat;
		return false;
	}

	//	setWidth(width);
	//	setHeight(height);

	data = Bitmap::differenceBitmapFileData(oldBitmap->data, src, width, height, (int)(bmih->biBitCount / 8), this->size);

	delete[] dat;
	if (data == NULL)
		return false;

	setUpdateType(DIF);
	return true;
}

bool BitmapUpdate::readFromFileRLEDiff(const char* filename, Bitmap* oldBitmap) {
	BITMAPFILEHEADER_CM* bmfh; // ={0};
	BITMAPINFOHEADER_CM* bmih; // ={0};
	uint32 count = 0;

	uint32 len;
	char* dat = utils::ReadAFile(filename, len, true);
	if (dat == NULL)
		return false;

	char* src = (char*)dat;
	bmfh = (BITMAPFILEHEADER_CM*)src;
	src += sizeof(BITMAPFILEHEADER_CM);
	bmih = (BITMAPINFOHEADER_CM*)src;
	src += sizeof(BITMAPINFOHEADER_CM);

	if (bmih->biCompression != 0) {
		delete[] dat;
		return false;
	}

	if (bmih->biBitCount != 24) {
		delete[] dat;
		return false;
	}

	int depth = bmih->biBitCount;
	int planes = bmih->biPlanes;
	width = bmih->biWidth;
	height = bmih->biHeight;

	if (bmih->biSizeImage == 0)
		bmih->biSizeImage = bmfh->bfSize - bmfh->bfOffBits;

	if (len < (int)(sizeof(BITMAPFILEHEADER_CM) + sizeof(BITMAPINFOHEADER_CM) + (width * height * ((int)(depth / 8))))) {
		delete[] dat;
		return false;
	}

	//	setWidth(width);
	//	setHeight(height);

	data = Bitmap::differenceBitmapFileDataRunLength(oldBitmap->data, src, width, height, (int)(bmih->biBitCount / 8), this->size);

	delete[] dat;
	if (data == NULL)
		return false;

	setUpdateType(DIFRUNLENGTH);
	return true;
}


bool BitmapUpdate::update(BitmapUpdate* update) {
	return false;
}

















// ################# Unit Test #################

bool Bitmap::UnitTest() {
	const uint32 W = 64;
	const uint32 H = 48;

	unittest::progress(0, "construct");

	// --- Construction and dimensions ---
	Bitmap bmp(W, H);
	if (bmp.width != W || bmp.height != H) {
		unittest::fail("constructed dimensions wrong: got %ux%u expected %ux%u", bmp.width, bmp.height, W, H);
		return false;
	}
	if (bmp.size != W * H * 4) {
		unittest::fail("size wrong: got %u expected %u", bmp.size, W * H * 4);
		return false;
	}
	if (bmp.data == NULL) {
		unittest::fail("data buffer was not allocated");
		return false;
	}

	unittest::progress(15, "erase");

	// --- Erase whole bitmap to a known colour, verify via getPixelColor ---
	if (!bmp.eraseBitmap(10, 20, 30)) {
		unittest::fail("eraseBitmap returned false");
		return false;
	}
	Color c = bmp.getPixelColor(0, 0);
	if (c.r != 10 || c.g != 20 || c.b != 30) {
		unittest::fail("erase colour mismatch at (0,0): got %u,%u,%u expected 10,20,30", c.r, c.g, c.b);
		return false;
	}
	c = bmp.getPixelColor(W - 1, H - 1);
	if (c.r != 10 || c.g != 20 || c.b != 30) {
		unittest::fail("erase colour mismatch at far corner: got %u,%u,%u expected 10,20,30", c.r, c.g, c.b);
		return false;
	}

	unittest::progress(30, "setPixel/getPixelColor");

	// --- setPixel then read back via getPixelColor ---
	if (!bmp.setPixel((uint32)5, (uint32)7, (uint8)200, (uint8)100, (uint8)50, (uint8)0)) {
		unittest::fail("setPixel(5,7) returned false");
		return false;
	}
	c = bmp.getPixelColor(5, 7);
	if (c.r != 200 || c.g != 100 || c.b != 50) {
		unittest::fail("setPixel readback mismatch: got %u,%u,%u expected 200,100,50", c.r, c.g, c.b);
		return false;
	}
	// neighbour pixel must be untouched (still the erase colour)
	c = bmp.getPixelColor(6, 7);
	if (c.r != 10 || c.g != 20 || c.b != 30) {
		unittest::fail("neighbour pixel was disturbed: got %u,%u,%u expected 10,20,30", c.r, c.g, c.b);
		return false;
	}

	// --- Out-of-range setPixel must be rejected ---
	if (bmp.setPixel((uint32)(W + 10), (uint32)0, (uint8)1, (uint8)2, (uint8)3, (uint8)0)) {
		unittest::fail("setPixel accepted an out-of-range x coordinate");
		return false;
	}

	unittest::progress(45, "getPos");

	// --- getPos addressing sanity ---
	if (bmp.getPos(0, 0) != 0) {
		unittest::fail("getPos(0,0) should be 0, got %u", bmp.getPos(0, 0));
		return false;
	}
	if (bmp.getPos(3, 2) != (2 * W + 3) * 4) {
		unittest::fail("getPos(3,2) wrong: got %u expected %u", bmp.getPos(3, 2), (2 * W + 3) * 4);
		return false;
	}

	unittest::progress(55, "fillBox");

	// --- fillBox fills a rectangle; corners inside set, outside untouched ---
	bmp.eraseBitmap(0, 0, 0);
	Color fillCol(123, 45, 67);
	if (!bmp.fillBox((uint32)10, (uint32)10, (uint32)20, (uint32)15, fillCol)) {
		unittest::fail("fillBox returned false");
		return false;
	}
	c = bmp.getPixelColor(10, 10);
	if (c.r != 123 || c.g != 45 || c.b != 67) {
		unittest::fail("fillBox top-left mismatch: got %u,%u,%u expected 123,45,67", c.r, c.g, c.b);
		return false;
	}
	c = bmp.getPixelColor(29, 24);
	if (c.r != 123 || c.g != 45 || c.b != 67) {
		unittest::fail("fillBox bottom-right mismatch: got %u,%u,%u expected 123,45,67", c.r, c.g, c.b);
		return false;
	}
	// just outside the box must still be black
	c = bmp.getPixelColor(9, 10);
	if (c.r != 0 || c.g != 0 || c.b != 0) {
		unittest::fail("fillBox leaked outside box at (9,10): got %u,%u,%u", c.r, c.g, c.b);
		return false;
	}
	c = bmp.getPixelColor(30, 25);
	if (c.r != 0 || c.g != 0 || c.b != 0) {
		unittest::fail("fillBox leaked outside box at (30,25): got %u,%u,%u", c.r, c.g, c.b);
		return false;
	}

	unittest::progress(65, "replaceColor");

	// --- replaceColor swaps the filled colour for another ---
	if (!bmp.replaceColor(fillCol, Color(9, 9, 9))) {
		unittest::fail("replaceColor returned false");
		return false;
	}
	c = bmp.getPixelColor(15, 15);
	if (c.r != 9 || c.g != 9 || c.b != 9) {
		unittest::fail("replaceColor mismatch: got %u,%u,%u expected 9,9,9", c.r, c.g, c.b);
		return false;
	}

	unittest::progress(72, "getCopy");

	// --- getCopy extracts a sub-region with matching pixels ---
	bmp.eraseBitmap(0, 0, 0);
	bmp.setPixel((uint32)12, (uint32)12, (uint8)77, (uint8)88, (uint8)99, (uint8)0);
	Bitmap* sub = bmp.getCopy(Box(10, 10, 8, 8));
	if (!sub) {
		unittest::fail("getCopy returned NULL");
		return false;
	}
	if (sub->width != 8 || sub->height != 8) {
		unittest::fail("getCopy dimensions wrong: got %ux%u expected 8x8", sub->width, sub->height);
		delete sub;
		return false;
	}
	// the pixel at bmp(12,12) maps to sub(2,2)
	c = sub->getPixelColor(2, 2);
	if (c.r != 77 || c.g != 88 || c.b != 99) {
		unittest::fail("getCopy pixel mismatch at (2,2): got %u,%u,%u expected 77,88,99", c.r, c.g, c.b);
		delete sub;
		return false;
	}
	delete sub;

	unittest::progress(80, "DataMessage roundtrip");

	// --- toMessage / Bitmap(DataMessage*) roundtrip ---
	bmp.eraseBitmap(40, 41, 42);
	bmp.setPixel((uint32)1, (uint32)1, (uint8)111, (uint8)112, (uint8)113, (uint8)0);
	DataMessage* msg = bmp.toMessage();
	if (!msg) {
		unittest::fail("toMessage returned NULL");
		return false;
	}
	Bitmap restored(msg);
	delete msg;
	if (restored.width != W || restored.height != H || restored.size != bmp.size) {
		unittest::fail("roundtrip dimensions/size mismatch: %ux%u size %u vs %ux%u size %u",
			restored.width, restored.height, restored.size, W, H, bmp.size);
		return false;
	}
	c = restored.getPixelColor(1, 1);
	if (c.r != 111 || c.g != 112 || c.b != 113) {
		unittest::fail("roundtrip pixel mismatch at (1,1): got %u,%u,%u expected 111,112,113", c.r, c.g, c.b);
		return false;
	}
	c = restored.getPixelColor(0, 0);
	if (c.r != 40 || c.g != 41 || c.b != 42) {
		unittest::fail("roundtrip background mismatch at (0,0): got %u,%u,%u expected 40,41,42", c.r, c.g, c.b);
		return false;
	}

	unittest::progress(88, "throughput");

	// --- Throughput metric: measure setPixel rate over a full sweep ---
	Bitmap perf(W, H);
	perf.eraseBitmap(0, 0, 0);
	const uint32 sweeps = 50;
	uint64 t0 = GetTimeNow();
	uint64 ops = 0;
	for (uint32 s = 0; s < sweeps; s++) {
		for (uint32 yy = 0; yy < H; yy++) {
			for (uint32 xx = 0; xx < W; xx++) {
				perf.setPixel(xx, yy, (uint8)(xx & 0xFF), (uint8)(yy & 0xFF), (uint8)(s & 0xFF), (uint8)0);
				ops++;
			}
		}
	}
	double us = (double)(GetTimeNow() - t0);
	if (us > 0)
		unittest::metric("setpixel_throughput", (double)ops / us * 1e6, "px/s", true);
	unittest::detail("setPixel sweep: %llu ops in %.0f us", (unsigned long long)ops, us);

	// verify a representative pixel from the last sweep
	c = perf.getPixelColor(3, 4);
	if (c.r != (3 & 0xFF) || c.g != (4 & 0xFF) || c.b != ((sweeps - 1) & 0xFF)) {
		unittest::fail("throughput sweep produced wrong final pixel at (3,4): got %u,%u,%u", c.r, c.g, c.b);
		return false;
	}

	unittest::progress(100, "done");
	return true;
}

void Register_Bitmap_Tests() {
	UnitTestRunner::instance().registerTest(
		"bitmap", Bitmap::UnitTest,
		"Bitmap image: construct, set/get pixels, erase, fillBox, replaceColor, getCopy, DataMessage roundtrip", "util");
}


} // namespace cmlabs
