// Object_Image

#include "ascript/Object_Image.h"
#include "ascript/Object_Matrix.h"
#include "ascript/Object_List.h"
#include "ascript/Object_Stream.h"
#include "ascript/Expr.h"
#include "ascript/PackedNumber.h"
#include "ascript/Directory.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Image (common)
//-----------------------------------------------------------------------------
Value Object_Image::DoPropGet(Signal sig, const Symbol *pSymbol, bool &evaluatedFlag)
{
	evaluatedFlag = true;
	if (pSymbol->IsIdentical(AScript_Symbol(format))) {
		return Value(FormatToSymbol(_format));
	} else if (pSymbol->IsIdentical(AScript_Symbol(width))) {
		return Value(static_cast<unsigned int>(_width));
	} else if (pSymbol->IsIdentical(AScript_Symbol(height))) {
		return Value(static_cast<unsigned int>(_height));
	} else if (pSymbol->IsIdentical(AScript_Symbol(palette))) {
		if (_pObjPalette == NULL) return Value::Null;
		return Value(Object_Palette::Reference(_pObjPalette));
	}
	evaluatedFlag = false;
	return Value::Null;
}

Value Object_Image::DoPropSet(Signal sig, const Symbol *pSymbol,
									const Value &value, bool &evaluatedFlag)
{
	if (pSymbol->IsIdentical(AScript_Symbol(palette))) {
		if (value.IsPalette()) {
			Object::Delete(_pObjPalette);
			_pObjPalette = Object_Palette::Reference(value.GetPaletteObj());
			return Value(Object_Palette::Reference(_pObjPalette));
		} else if (value.IsInvalid()) {
			Object::Delete(_pObjPalette);
			_pObjPalette = NULL;
			return Value::Null;
		} else {
			sig.SetError(ERR_ValueError, "palette object must be specified");
			return Value::Null;
		}
	}
	return DoPropGet(sig, pSymbol, evaluatedFlag);
}

String Object_Image::ToString(Signal sig, bool exprFlag)
{
	String rtn;
	rtn = "<image:";
	rtn += FormatToSymbol(_format)->GetName();
	rtn += ":";
	if (IsValid()) {
		char buff[64];
		::sprintf(buff, "%dx%d", GetWidth(), GetHeight());
		rtn += buff;
	} else {
		rtn += "invalid";
	}
	rtn += ">";
	return rtn;
}

bool Object_Image::CheckEmpty(Signal sig) const
{
	if (!IsValid()) return true;
	sig.SetError(ERR_ValueError, "image has already been initialized with a buffer");
	return false;
}

bool Object_Image::CheckValid(Signal sig) const
{
	if (IsValid()) return true;
	sig.SetError(ERR_ValueError, "image does not have a buffer");
	return false;
}

size_t Object_Image::SymbolToPixelOffset(Signal sig, const Symbol *pSymbol) const
{
	if (pSymbol->IsIdentical(AScript_Symbol(red))) {
		return OffsetRed;
	} else if (pSymbol->IsIdentical(AScript_Symbol(green))) {
		return OffsetGreen;
	} else if (pSymbol->IsIdentical(AScript_Symbol(blue))) {
		return OffsetBlue;
	} else if (pSymbol->IsIdentical(AScript_Symbol(alpha))) {
		if (GetFormat() != FORMAT_RGBA) {
			sig.SetError(ERR_FormatError, "only RGBA format contains alpha element");
			return 0;
		}
		return OffsetAlpha;
	}
	sig.SetError(ERR_FormatError, "unsupported image element: %s", pSymbol->GetName());
	return 0;
}

Object_Image::Format Object_Image::SymbolToFormat(Signal sig, const Symbol *pSymbol)
{
	if (pSymbol->IsIdentical(AScript_Symbol(rgb))) {
		return FORMAT_RGB;
	} else if (pSymbol->IsIdentical(AScript_Symbol(rgba))) {
		return FORMAT_RGBA;
	} else {
		sig.SetError(ERR_FormatError, "specify `rgb or `rgba for image format");
		return FORMAT_None;
	}
}

const Symbol *Object_Image::FormatToSymbol(Format format)
{
	if (format == FORMAT_RGB) {
		return AScript_Symbol(rgb);
	} else if (format == FORMAT_RGBA) {
		return AScript_Symbol(rgba);
	} else {
		return AScript_Symbol(nil);
	}
}

void Object_Image::InitMetrics()
{
	_metrics.bitsPerPixel =
			(_format == FORMAT_RGB)? 24 :
			(_format == FORMAT_RGBA)? 32 : 0;
	_metrics.bytesPerPixel = _metrics.bitsPerPixel / 8;
	_metrics.bytesPerLine =
			(_format == FORMAT_RGB)?	((_width * 3 + 3) / 4) * 4 :
			(_format == FORMAT_RGBA)?	_width * 4 : 0;
}

Object_Image *Object_Image::CreateDerivation(Signal sig,
						size_t width, size_t height, Object_Palette *pObjPalette)
{
	Environment &env = *this;
	Object_Image *pObj = new Object_Image(env, _format);
	if (!pObj->AllocBuffer(sig, width, height, 0x00)) {
		delete pObj;
		return NULL;
	}
	if (pObjPalette != NULL) {
		pObj->_pObjPalette = pObjPalette;
	} else if (_pObjPalette != NULL) {
		pObj->_pObjPalette = Object_Palette::Reference(_pObjPalette);
	}
	return pObj;
}

bool Object_Image::CheckCoord(Signal sig, size_t x, size_t y) const
{
	if (CheckCoord(static_cast<int>(x), static_cast<int>(y))) return true;
	sig.SetError(ERR_ValueError, "coordinate is out of range");
	return false;
}

bool Object_Image::AdjustCoord(int &x, int &y, int &width, int &height) const
{
	int wdImage = static_cast<int>(GetWidth());
	int htImage = static_cast<int>(GetHeight());
	if (x >= wdImage) return false;
	if (y >= htImage) return false;
	if (x + width <= 0) return false;
	if (y + height <= 0) return false;
	if (x < 0) {
		width += x, x = 0;
	}
	if (y < 0) {
		height += y, y = 0;
	}
	if (x + width > wdImage) {
		width = wdImage - x;
	}
	if (y + height > htImage) {
		height = htImage - y;
	}
	return true;
}

void Object_Image::PutPixel(unsigned char *buff, const Object_Color *pObjColor)
{
	if (_format == FORMAT_RGB) {
		*(buff + OffsetRed)		= pObjColor->GetRed();
		*(buff + OffsetGreen)	= pObjColor->GetGreen();
		*(buff + OffsetBlue)	= pObjColor->GetBlue();
	} else if (_format == FORMAT_RGBA) {
		*(buff + OffsetRed)		= pObjColor->GetRed();
		*(buff + OffsetGreen)	= pObjColor->GetGreen();
		*(buff + OffsetBlue)	= pObjColor->GetBlue();
		*(buff + OffsetAlpha)	= pObjColor->GetAlpha();
	}
}

void Object_Image::GetPixel(const unsigned char *buff, Object_Color *pObjColor)
{
	if (_format == FORMAT_RGB) {
		pObjColor->Set(GetPixelR(buff), GetPixelG(buff), GetPixelB(buff), 255);
	} else if (_format == FORMAT_RGBA) {
		pObjColor->Set(GetPixelR(buff), GetPixelG(buff), GetPixelB(buff), GetPixelA(buff));
	}
}

bool Object_Image::Store(Signal sig, size_t x, size_t y, size_t width, size_t height,
					const Symbol *pSymbol, const Object_Matrix *pObjMat)
{
	if (pObjMat->RowSize() < height || pObjMat->ColSize() < width) {
		sig.SetError(ERR_ValueError, "matrix size is too small");
		return false;
	}
	size_t bytesPerLine = GetBytesPerLine();
	size_t bytesPerPixel = GetBytesPerPixel();
	unsigned char *pLine = GetPointer(x, y);
	size_t offPixel = SymbolToPixelOffset(sig, pSymbol);
	if (sig.IsSignalled()) return false;
	for (size_t iLine = 0; iLine < height; iLine++, pLine += bytesPerLine) {
		unsigned char *pPixel = pLine + offPixel;
		ValueList::const_iterator pValueElem = pObjMat->GetPointer(iLine, 0);
		for (size_t iPixel = 0; iPixel < width;
							iPixel++, pPixel += bytesPerPixel, pValueElem++) {
			*pPixel = pValueElem->GetUChar();
		}
	}
	return true;
}

bool Object_Image::Store(Signal sig, size_t x, size_t y, size_t width, size_t height,
					const Symbol *pSymbol, Iterator *pIterator)
{
	Environment &env = *this;
	size_t bytesPerLine = GetBytesPerLine();
	size_t bytesPerPixel = GetBytesPerPixel();
	unsigned char *pLine = GetPointer(x, y);
	size_t offPixel = SymbolToPixelOffset(sig, pSymbol);
	if (sig.IsSignalled()) return false;
	for (size_t iLine = 0; iLine < height; iLine++, pLine += bytesPerLine) {
		unsigned char *pPixel = pLine + offPixel;
		for (size_t iPixel = 0; iPixel < width; iPixel++, pPixel += bytesPerPixel) {
			Value value;
			if (!pIterator->Next(env, sig, value)) return false;
			*pPixel = value.GetUChar();
		}
	}
	return true;
}

bool Object_Image::Extract(Signal sig, size_t x, size_t y, size_t width, size_t height,
					const Symbol *pSymbol, Object_Matrix *pObjMat)
{
	if (pObjMat->RowSize() < height || pObjMat->ColSize() < width) {
		sig.SetError(ERR_ValueError, "matrix size is too small");
		return false;
	}
	size_t bytesPerLine = GetBytesPerLine();
	size_t bytesPerPixel = GetBytesPerPixel();
	const unsigned char *pLine = GetPointer(x, y);
	size_t offPixel = SymbolToPixelOffset(sig, pSymbol);
	if (sig.IsSignalled()) return false;
	for (size_t iLine = 0; iLine < height; iLine++, pLine += bytesPerLine) {
		const unsigned char *pPixel = pLine + offPixel;
		ValueList::iterator pValueElem = pObjMat->GetPointer(iLine, 0);
		for (size_t iPixel = 0; iPixel < width;
							iPixel++, pPixel += bytesPerPixel, pValueElem++) {
			*pValueElem = Value(*pPixel);
		}
	}
	return true;
}

bool Object_Image::Extract(Signal sig, size_t x, size_t y, size_t width, size_t height,
					const Symbol *pSymbol, Object_List *pObjList)
{
	if (pObjList->GetList().size() < height * width) {
		sig.SetError(ERR_ValueError, "list size is too small");
		return false;
	}
	size_t bytesPerLine = GetBytesPerLine();
	size_t bytesPerPixel = GetBytesPerPixel();
	const unsigned char *pLine = GetPointer(x, y);
	size_t offPixel = SymbolToPixelOffset(sig, pSymbol);
	if (sig.IsSignalled()) return false;
	ValueList::iterator pValueElem = pObjList->GetList().begin();
	for (size_t iLine = 0; iLine < height; iLine++, pLine += bytesPerLine) {
		const unsigned char *pPixel = pLine + offPixel;
		for (size_t iPixel = 0; iPixel < width;
							iPixel++, pPixel += bytesPerPixel, pValueElem++) {
			*pValueElem = Value(*pPixel);
		}
	}
	return true;
}

void Object_Image::FillRect(size_t x, size_t y, size_t width, size_t height,
												const Object_Color *pObjColor)
{
	unsigned char buff[8];
	PutPixel(buff, pObjColor);
	std::auto_ptr<Scanner> pScanner(CreateScanner(x, y, width, height));
	if (_format == FORMAT_RGBA) {
		do {
			StorePixelRGBA(pScanner->GetPointer(), buff);
		} while (pScanner->Next());
	} else {
		do {
			StorePixelRGB(pScanner->GetPointer(), buff);
		} while (pScanner->Next());
	}
}

void Object_Image::FillRectAlpha(size_t x, size_t y, size_t width, size_t height,
		const Object_Color *pObjColor, unsigned char alphaKey, unsigned char alphaRest)
{
	unsigned char buff[8];
	PutPixel(buff, pObjColor);
	std::auto_ptr<Scanner> pScanner(CreateScanner(x, y, width, height));
	do {
		unsigned char *pPixel = pScanner->GetPointer();
		if (::memcmp(pPixel, buff, 3) == 0) {
			*(pPixel + OffsetAlpha) = alphaKey;
		} else {
			*(pPixel + OffsetAlpha) = alphaRest;
		}
	} while (pScanner->Next());
}

Object_Image *Object_Image::ReduceColor(Signal sig, const Object_Palette *pObjPalette)
{
	Object_Image *pObj = CreateDerivation(sig, _width, _height,
										Object_Palette::Reference(pObjPalette));
	if (sig.IsSignalled()) return NULL;
	std::auto_ptr<Scanner> pScannerSrc(CreateScanner());
	std::auto_ptr<Scanner> pScannerDst(pObj->CreateScanner());
	if (_format == FORMAT_RGBA) {
		do {
			size_t idx = pObjPalette->LookupNearest(pScannerSrc->GetPointer());
			StorePixelRGBA(pScannerDst->GetPointer(), pObjPalette->GetEntry(idx));
		} while (pScannerSrc->Next(*pScannerDst));
	} else {
		do {
			size_t idx = pObjPalette->LookupNearest(pScannerSrc->GetPointer());
			StorePixelRGB(pScannerDst->GetPointer(), pObjPalette->GetEntry(idx));
		} while (pScannerSrc->Next(*pScannerDst));
	}
	return pObj;
}

Object_Image *Object_Image::Blur(Signal sig, int radius)
{
	//int kernel[17];
	
	return NULL;
}

Object_Image *Object_Image::Flip(Signal sig, bool horzFlag, bool vertFlag)
{
	Object_Image *pObj = CreateDerivation(sig, _width, _height);
	if (sig.IsSignalled()) return NULL;
	if (horzFlag) {
		std::auto_ptr<Scanner> pScannerSrc(CreateScanner());
		std::auto_ptr<Scanner> pScannerDst(pObj->CreateScanner(
						vertFlag? SCAN_RightBottomHorz : SCAN_RightTopHorz));
		if (_format == FORMAT_RGBA) {
			do {
				StorePixelRGBA(pScannerDst->GetPointer(), pScannerSrc->GetPointer());
			} while (pScannerSrc->Next(*pScannerDst));
		} else {
			do {
				StorePixelRGB(pScannerDst->GetPointer(), pScannerSrc->GetPointer());
			} while (pScannerSrc->Next(*pScannerDst));
		}
	} else if (vertFlag) {
		const unsigned char *pLineSrc = GetPointer(0);
		unsigned char *pLineDst = pObj->GetPointer(_height);
		size_t bytesPerLine = GetBytesPerLine();
		for (size_t y = 0; y < _height; y++) {
			pLineDst -= bytesPerLine;
			::memcpy(pLineDst, pLineSrc, bytesPerLine);
			pLineSrc += bytesPerLine;
		}
	}
	return pObj;
}

Object_Image *Object_Image::Rotate90(Signal sig, bool clockwiseFlag)
{
	size_t width = _height, height = _width;
	Object_Image *pObj = CreateDerivation(sig, width, height);
	if (sig.IsSignalled()) return NULL;
	std::auto_ptr<Scanner> pScannerSrc(CreateScanner());
	std::auto_ptr<Scanner> pScannerDst(pObj->CreateScanner(
					clockwiseFlag? SCAN_RightTopVert : SCAN_LeftBottomVert));
	if (_format == FORMAT_RGBA) {
		do {
			StorePixelRGBA(pScannerDst->GetPointer(), pScannerSrc->GetPointer());
		} while (pScannerSrc->Next(*pScannerDst));
	} else {
		do {
			StorePixelRGB(pScannerDst->GetPointer(), pScannerSrc->GetPointer());
		} while (pScannerSrc->Next(*pScannerDst));
	}
	return pObj;
}

Object_Image *Object_Image::Rotate(Signal sig,
							double angle, const Object_Color *pObjColor)
{
	int angleInt = static_cast<int>(angle);
	if (static_cast<double>(angleInt) != angle) {
		// nothing to do
	} else if (angleInt == 180) {
		return Flip(sig, true, true);
	} else if ((angleInt + 270) % 360 == 0) {
		return Rotate90(sig, true);
	} else if ((angleInt + 90) % 360 == 0) {
		return Rotate90(sig, false);
	}
	double rad = angle * 3.14159265358979323846 / 180;
	int cos1024 = static_cast<int>(::cos(rad) * 1024);
	int sin1024 = -static_cast<int>(::sin(rad) * 1024);
	int width, height;
	int xCenter = static_cast<int>(_width / 2);
	int yCenter = static_cast<int>(_height / 2);
	int xCenterNew, yCenterNew;
	do {
		int left = -xCenter;
		int right = left + static_cast<int>(_width);
		int bottom = -yCenter;
		int top = bottom + static_cast<int>(_height);
		int xs[4], ys[4];
		RotateCoord(xs[0], ys[0], left, top, cos1024, sin1024);
		RotateCoord(xs[1], ys[1], left, bottom, cos1024, sin1024);
		RotateCoord(xs[2], ys[2], right, top, cos1024, sin1024);
		RotateCoord(xs[3], ys[3], right, bottom, cos1024, sin1024);
		int xMin = *std::min_element(xs, xs + 4);
		int xMax = *std::max_element(xs, xs + 4);
		int yMin = *std::min_element(ys, ys + 4);
		int yMax = *std::max_element(ys, ys + 4);
		width = xMax - xMin;
		height = yMax - yMin;
		xCenterNew = width / 2;
		yCenterNew = height / 2;
	} while (0);
	Object_Image *pObj = CreateDerivation(sig, width, height);
	if (sig.IsSignalled()) return NULL;
	unsigned char *pLineDst = pObj->GetPointer(0);
	size_t bytesPerLineDst = pObj->GetBytesPerLine();
	size_t bytesPerPixel = GetBytesPerPixel();
	unsigned char buffBlank[8];
	PutPixel(buffBlank, pObjColor);
	for (int y = 0; y < height; y++) {
		unsigned char *pPixelDst = pLineDst;
		for (int x = 0; x < width; x++) {
			int xm, ym;
			RotateCoord(xm, ym, x - xCenterNew, y - yCenterNew, cos1024, sin1024);
			xm += xCenter, ym += yCenter;
			if (xm >= 0 && xm < static_cast<int>(_width) &&
								ym >= 0 && ym < static_cast<int>(_height)) {
				unsigned char *pPixelSrc = GetPointer(xm, ym);
				StorePixel(pPixelDst, pPixelSrc, _format == FORMAT_RGBA);
			} else {
				StorePixel(pPixelDst, buffBlank, _format == FORMAT_RGBA);
			}
			pPixelDst += bytesPerPixel;
		}
		pLineDst += bytesPerLineDst;
	}
	return pObj;
}

Object_Image *Object_Image::Crop(Signal sig, size_t x, size_t y, size_t width, size_t height)
{
	Object_Image *pObj = CreateDerivation(sig, width, height);
	if (sig.IsSignalled()) return NULL;
	const unsigned char *pLineSrc = GetPointer(x, y);
	unsigned char *pLineDst = pObj->GetPointer(0);
	size_t bytesPerLineSrc = GetBytesPerLine();
	size_t bytesPerLineDst = pObj->GetBytesPerLine();
	for (size_t y = 0; y < height; y++) {
		::memcpy(pLineDst, pLineSrc, bytesPerLineDst);
		pLineSrc += bytesPerLineSrc;
		pLineDst += bytesPerLineDst;
	}
	return pObj;
}


Object_Image *Object_Image::Resize(Signal sig, size_t width, size_t height)
{
	Object_Image *pObj = CreateDerivation(sig, width, height);
	if (sig.IsSignalled()) return NULL;
	const unsigned char *pLineSrc = GetPointer(0);
	unsigned char *pLineDst = pObj->GetPointer(0);
	size_t bytesPerPixel = GetBytesPerPixel();
	size_t bytesPerLineSrc = GetBytesPerLine();
	size_t bytesPerLineDst = pObj->GetBytesPerLine();
	OAL::Memory memory;
	size_t accumsSize = width * sizeof(Accum);
	Accum *accums = reinterpret_cast<Accum *>(memory.Allocate(accumsSize));
	::memset(accums, 0x00, accumsSize);
	size_t numerY = 0;
	if (_format == FORMAT_RGB) {
		for (size_t ySrc = 0; ySrc < _height; ySrc++) {
			if (ySrc < _height) {
				const unsigned char *pPixelSrc = pLineSrc;
				unsigned char *pPixelDst = pLineDst;
				Accum *pAccum = accums;
				size_t numerX = 0;
				for (size_t xSrc = 0; xSrc < _width; xSrc++) {
					pAccum->AddRGB(pPixelSrc);
					pPixelSrc += bytesPerPixel;
					numerX += width;
					for ( ; numerX > _width; numerX -= _width) {
						pAccum++;
					}
				}
				pLineSrc += bytesPerLineSrc;
			}
			numerY += height;
			if (numerY > _height) {
				if (accums[0].cnt == 0) accums[0].cnt = 0; // this must not happen
				Accum *pAccum = accums;
				Accum *pAccumPrev = accums;
				for (size_t xDst = 0; xDst < width; xDst++, pAccum++) {
					size_t cnt = pAccum->cnt;
					if (cnt == 0) {
						*pAccum = *pAccumPrev;
					} else if (cnt == 1) {
						pAccumPrev = pAccum;
					} else if (cnt == 2) {
						pAccum->red >>= 1;
						pAccum->green >>= 1;
						pAccum->blue >>= 1;
						pAccumPrev = pAccum;
					} else if (cnt == 4) {
						pAccum->red >>= 2;
						pAccum->green >>= 2;
						pAccum->blue >>= 2;
						pAccumPrev = pAccum;
					} else {
						pAccum->red /= cnt;
						pAccum->green /= cnt;
						pAccum->blue /= cnt;
						pAccumPrev = pAccum;
					}
				}
				for ( ; numerY > _height; numerY -= _height) {
					Accum *pAccum = accums;
					unsigned char *pPixelDst = pLineDst;
					for (size_t xDst = 0; xDst < width;
									xDst++, pAccum++, pPixelDst += bytesPerPixel) {
						pAccum->StoreRGB(pPixelDst);
					}
					pLineDst += bytesPerLineDst;
				}
				::memset(accums, 0x00, accumsSize);
			}
		}
	} else if (_format == FORMAT_RGBA) {
		for (size_t ySrc = 0; ySrc < _height; ySrc++) {
			if (ySrc < _height) {
				const unsigned char *pPixelSrc = pLineSrc;
				unsigned char *pPixelDst = pLineDst;
				Accum *pAccum = accums;
				size_t numerX = 0;
				for (size_t xSrc = 0; xSrc < _width; xSrc++) {
					pAccum->AddRGBA(pPixelSrc);
					pPixelSrc += bytesPerPixel;
					numerX += width;
					for ( ; numerX > _width; numerX -= _width) {
						pAccum++;
					}
				}
				pLineSrc += bytesPerLineSrc;
			}
			numerY += height;
			if (numerY > _height) {
				if (accums[0].cnt == 0) accums[0].cnt = 0; // this must not happen
				Accum *pAccum = accums;
				Accum *pAccumPrev = accums;
				for (size_t xDst = 0; xDst < width; xDst++, pAccum++) {
					size_t cnt = pAccum->cnt;
					if (cnt == 0) {
						*pAccum = *pAccumPrev;
					} else if (cnt == 1) {
						pAccumPrev = pAccum;
					} else if (cnt == 2) {
						pAccum->red >>= 1;
						pAccum->green >>= 1;
						pAccum->blue >>= 1;
						pAccum->alpha >>= 1;
						pAccumPrev = pAccum;
					} else if (cnt == 4) {
						pAccum->red >>= 2;
						pAccum->green >>= 2;
						pAccum->blue >>= 2;
						pAccum->alpha >>= 2;
						pAccumPrev = pAccum;
					} else {
						pAccum->red /= cnt;
						pAccum->green /= cnt;
						pAccum->blue /= cnt;
						pAccum->alpha /= cnt;
						pAccumPrev = pAccum;
					}
				}
				for ( ; numerY > _height; numerY -= _height) {
					Accum *pAccum = accums;
					unsigned char *pPixelDst = pLineDst;
					for (size_t xDst = 0; xDst < width;
								xDst++, pAccum++, pPixelDst += bytesPerPixel) {
						pAccum->StoreRGBA(pPixelDst);
					}
					pLineDst += bytesPerLineDst;
				}
				::memset(accums, 0x00, accumsSize);
			}
		}
	}
	return pObj;
}

void Object_Image::Paste(size_t x, size_t y, Object_Image *pObjImg,
	size_t width, size_t height, size_t xOffset, size_t yOffset, unsigned char alpha)
{
	const unsigned char *pLineSrc = pObjImg->GetPointer(xOffset, yOffset);
	unsigned char *pLineDst = GetPointer(x, y);
	size_t bytesPerLineSrc = pObjImg->GetBytesPerLine();
	size_t bytesPerLineDst = GetBytesPerLine();
	if (_format == pObjImg->GetFormat()) {
		size_t bytesToCopy = width * GetBytesPerPixel();
		for (size_t y = 0; y < height; y++) {
			::memcpy(pLineDst, pLineSrc, bytesToCopy);
			pLineSrc += bytesPerLineSrc;
			pLineDst += bytesPerLineDst;
		}
	} else if (_format == FORMAT_RGB && pObjImg->GetFormat() == FORMAT_RGBA) {
		for (size_t y = 0; y < height; y++) {
			const unsigned char *pPixelSrc = pLineSrc;
			unsigned char *pPixelDst = pLineDst;
			for (size_t x = 0; x < width; x++) {
				*pPixelDst++ = *pPixelSrc++;
				*pPixelDst++ = *pPixelSrc++;
				*pPixelDst++ = *pPixelSrc++;
				pPixelSrc++;
			}
			pLineSrc += bytesPerLineSrc;
			pLineDst += bytesPerLineDst;
		}
	} else if (_format == FORMAT_RGBA && pObjImg->GetFormat() == FORMAT_RGB) {
		for (size_t y = 0; y < height; y++) {
			const unsigned char *pPixelSrc = pLineSrc;
			unsigned char *pPixelDst = pLineDst;
			for (size_t x = 0; x < width; x++) {
				*pPixelDst++ = *pPixelSrc++;
				*pPixelDst++ = *pPixelSrc++;
				*pPixelDst++ = *pPixelSrc++;
				*pPixelDst++ = alpha;
			}
			pLineSrc += bytesPerLineSrc;
			pLineDst += bytesPerLineDst;
		}
	}
}

Object_Palette *Object_Image::CreateEmptyPalette(size_t nEntries)
{
	Object::Delete(_pObjPalette);
	_pObjPalette = new Object_Palette(*this, nEntries);
	return _pObjPalette;
}

void Object_Image::SetPaletteObj(Object_Palette *pObjPalette)
{
	Object::Delete(_pObjPalette);
	_pObjPalette = pObjPalette;
}

bool Object_Image::Read(Signal sig, Stream &stream, const char *imgType)
{
	Environment &env = *this;
	ImageStreamer *pImageStreamer = NULL;
	pImageStreamer = ImageStreamer::FindResponsible(sig, stream, imgType);
	if (sig.IsSignalled()) return false;
	if (pImageStreamer == NULL) {
		sig.SetError(ERR_FormatError, "unsupported image type");
		return false;
	}
	return pImageStreamer->Read(env, sig, this, stream);
}

bool Object_Image::Write(Signal sig, Stream &stream, const char *imgType)
{
	Environment &env = *this;
	ImageStreamer *pImageStreamer = NULL;
	pImageStreamer = ImageStreamer::FindResponsible(sig, stream, imgType);
	if (sig.IsSignalled()) return false;
	if (pImageStreamer == NULL) {
		sig.SetError(ERR_FormatError, "unsupported image type");
		return false;
	}
	return pImageStreamer->Write(env, sig, this, stream);
}

int Object_Image::CalcDIBBitCount() const
{
	if (GetPaletteObj() == NULL) return static_cast<int>(GetBitsPerPixel());
	size_t nEntries = GetPaletteObj()->CountEntries();
	size_t nBits = 1;
	for ( ; nEntries > static_cast<size_t>(1 << nBits); nBits++) ;
	nBits =
		(nBits == 2 || nBits == 3)? 4 :
		(nBits == 5 || nBits == 6 || nBits == 7)? 8 :
		(nBits > 8)? 8 : nBits;
	return static_cast<int>(nBits);
}

size_t Object_Image::CalcDIBImageSize(int biBitCount, bool maskFlag) const
{
	size_t bytesPerLine = 0;
	if (biBitCount == 1) {
		bytesPerLine = (_width + 7) / 8;
	} else if (biBitCount == 4) {
		bytesPerLine = (_width + 1) / 2;
	} else if (biBitCount == 8) {
		bytesPerLine = _width;
	} else if (biBitCount == 24) {
		bytesPerLine = _width * 3;
	} else if (biBitCount == 32) {
		bytesPerLine = _width * 4;
	}
	size_t bytes = ((bytesPerLine + 3) / 4 * 4) * _height;
	if (maskFlag) {
		size_t bytesPerLine = (_width + 7) / 8;
		bytes += ((bytesPerLine + 3) / 4 * 4) * _height;
	}
	return bytes;
}

bool Object_Image::ReadDIBPalette(Signal sig, Stream &stream, int biBitCount)
{
	if (biBitCount == 24 || biBitCount == 32) return true;
	if (!(biBitCount == 1 || biBitCount == 4 || biBitCount == 8)) {
		sig.SetError(ERR_FormatError, "unsupported pixel depth %d", biBitCount);
		return false;
	}
	size_t nEntries = 1 << biBitCount;
	CreateEmptyPalette(nEntries);
	for (size_t idx = 0; idx < nEntries; idx++) {
		unsigned char buff[4];
		if (stream.Read(sig, buff, 4) < 4) {
			sig.SetError(ERR_FormatError, "failed to read DIB palette");
			return false;
		}
		_pObjPalette->SetEntry(idx, buff[2], buff[1], buff[0]);
	}
	return true;
}

bool Object_Image::WriteDIBPalette(Signal sig, Stream &stream, int biBitCount)
{
	Environment &env = *this;
	if (biBitCount == 24 || biBitCount == 32) return true;
	if (!(biBitCount == 1 || biBitCount == 4 || biBitCount == 8)) {
		sig.SetError(ERR_FormatError, "unsupported pixel depth %d", biBitCount);
		return false;
	}
	size_t nEntries = 1 << biBitCount;
	if (_pObjPalette == NULL) {
		_pObjPalette = new Object_Palette(env);
		if (biBitCount == 1) {
			_pObjPalette->Prepare(sig, AScript_Symbol(mono));
		} else if (biBitCount == 4) {
			_pObjPalette->Prepare(sig, AScript_Symbol(basic));
		} else { // biBitCount == 8
			_pObjPalette->Prepare(sig, AScript_Symbol(win256));
		}
		if (sig.IsSignalled()) return false;
	}
	size_t idx = 0;
	size_t idxMax = ChooseMin(nEntries, _pObjPalette->CountEntries());
	unsigned char buff[4];
	for ( ; idx < idxMax; idx++) {
		const unsigned char *pEntry = _pObjPalette->GetEntry(idx);
		buff[0] = GetPixelB(pEntry);
		buff[1] = GetPixelG(pEntry);
		buff[2] = GetPixelR(pEntry);
		buff[3] = 0x00;
		stream.Write(sig, buff, 4);
		if (sig.IsSignalled()) return false;
	}
	::memset(buff, 0x00, 4);
	for (; idx < nEntries; idx++) {
		stream.Write(sig, buff, 4);
		if (sig.IsSignalled()) return false;
	}
	return true;
}

bool Object_Image::ReadDIB(Signal sig, Stream &stream,
				int biWidth, int biHeight, int biBitCount, bool maskFlag)
{
	bool vertRevFlag = true;
	if (biHeight < 0) {
		biHeight = -biHeight;
		vertRevFlag = false;
	}
	if (!AllocBuffer(sig, biWidth, biHeight, 0xff)) return false;
	if (biBitCount == 1) {
		size_t bytesPerLine = (biWidth + 7) / 8;
		size_t bytesAlign = (bytesPerLine + 3) / 4 * 4 - bytesPerLine;
		int bitsRest = 0;
		unsigned char ch;
		int iLine = 0, iPixel = 0;
		int bytesPitch = static_cast<int>(GetBytesPerLine());
		if (vertRevFlag) bytesPitch = -bytesPitch;
		unsigned char *pLine = GetPointer(vertRevFlag? biHeight - 1 : 0);
		unsigned char *pPixel = pLine;
		for (;;) {
			if (iPixel >= biWidth) {
				if (++iLine >= biHeight) break;
				iPixel = 0, pLine += bytesPitch, pPixel = pLine;
				stream.Seek(sig, static_cast<long>(bytesAlign), Stream::SeekCur);
				if (sig.IsSignalled()) return false;
				bitsRest = 0;
			}
			if (bitsRest == 0) {
				if (stream.Read(sig, &ch, 1) < 1) break;
				bitsRest = 8;
			}
			unsigned char idx = ch >> 7;
			ch <<= 1, bitsRest--;
			StorePixelRGB(pPixel, _pObjPalette->GetEntry(idx));
			pPixel += GetBytesPerPixel();
			iPixel++;
		}
		if (sig.IsSignalled()) return false;
	} else if (biBitCount == 4) {
		size_t bytesPerLine = (biWidth + 1) / 2;
		size_t bytesAlign = (bytesPerLine + 3) / 4 * 4 - bytesPerLine;
		unsigned char ch;
		std::auto_ptr<Scanner> pScanner(CreateScanner(
				vertRevFlag? SCAN_LeftBottomHorz : SCAN_LeftTopHorz));
		if (stream.Read(sig, &ch, 1) < 1) return false;
		int bitsRest = 8;
		for (;;) {
			unsigned char idx = ch >> 4;
			ch <<= 4, bitsRest -= 4;
			StorePixelRGB(pScanner->GetPointer(), _pObjPalette->GetEntry(idx));
			if (!pScanner->NextPixel()) {
				if (!pScanner->NextLine()) break;
				stream.Seek(sig, static_cast<long>(bytesAlign), Stream::SeekCur);
				if (sig.IsSignalled()) return false;
				bitsRest = 0;
			}
			if (bitsRest == 0) {
				if (stream.Read(sig, &ch, 1) < 1) break;
				bitsRest = 8;
			}
		}
		if (sig.IsSignalled()) return false;
	} else if (biBitCount == 8) {
		size_t bytesAlign = (biWidth + 3) / 4 * 4 - biWidth;
		unsigned char ch;
		std::auto_ptr<Scanner> pScanner(CreateScanner(
				vertRevFlag? SCAN_LeftBottomHorz : SCAN_LeftTopHorz));
		if (stream.Read(sig, &ch, 1) < 1) return false;
		for (;;) {
			StorePixelRGB(pScanner->GetPointer(), _pObjPalette->GetEntry(ch));
			if (!pScanner->NextPixel()) {
				if (!pScanner->NextLine()) break;
				stream.Seek(sig, static_cast<long>(bytesAlign), Stream::SeekCur);
				if (sig.IsSignalled()) return false;
			}
			if (stream.Read(sig, &ch, 1) < 1) break;
		}
		if (sig.IsSignalled()) return false;
	}  else if (biBitCount == 24) {
		size_t bytesAlign = (3 * biWidth + 3) / 4 * 4 - 3 * biWidth;
		unsigned char buff[3];
		std::auto_ptr<Scanner> pScanner(CreateScanner(
					vertRevFlag? SCAN_LeftBottomHorz : SCAN_LeftTopHorz));
		for (;;) {
			if (stream.Read(sig, buff, 3) < 3) break;
			StorePixel(pScanner->GetPointer(), buff[2], buff[1], buff[0]);
			if (!pScanner->NextPixel()) {
				if (!pScanner->NextLine()) break;
				stream.Seek(sig, static_cast<long>(bytesAlign), Stream::SeekCur);
				if (sig.IsSignalled()) return false;
			}
		}
		if (sig.IsSignalled()) return false;
	} else if (biBitCount == 32) {
		unsigned char buff[4];
		std::auto_ptr<Scanner> pScanner(CreateScanner(
					vertRevFlag? SCAN_LeftBottomHorz : SCAN_LeftTopHorz));
		while (stream.Read(sig, buff, 4) == 4) {
			StorePixel(pScanner->GetPointer(), buff[2], buff[1], buff[0]);
			if (!pScanner->Next()) break;
		}
		if (sig.IsSignalled()) return false;
	} else {
		sig.SetError(ERR_FormatError, "invalid DIB format");
		return false;
	}
	if (maskFlag) {
		size_t bytesPerLine = (biWidth + 7) / 8;
		size_t bytesPerLineAligned = (bytesPerLine + 3) / 4 * 4;
		size_t bytesAlign = bytesPerLineAligned - bytesPerLine;
		if (GetFormat() == FORMAT_RGBA) {
			// read AND bitmap
			int bitsRest = 0;
			unsigned char ch;
			int iLine = 0, iPixel = 0;
			int bytesPitch = static_cast<int>(GetBytesPerLine());
			if (vertRevFlag) bytesPitch = -bytesPitch;
			unsigned char *pLine = GetPointer(vertRevFlag? biHeight - 1 : 0);
			unsigned char *pPixel = pLine;
			for (;;) {
				if (iPixel >= biWidth) {
					if (++iLine >= biHeight) break;
					iPixel = 0, pLine += bytesPitch, pPixel = pLine;
					stream.Seek(sig, static_cast<long>(bytesAlign), Stream::SeekCur);
					if (sig.IsSignalled()) return false;
					bitsRest = 0;
				}
				if (bitsRest == 0) {
					if (stream.Read(sig, &ch, 1) < 1) break;
					bitsRest = 8;
				}
				unsigned char idx = ch >> 7;
				ch <<= 1, bitsRest--;
				pPixel[OffsetAlpha] = idx? 0 : 255;
				pPixel += GetBytesPerPixel();
				iPixel++;
			}
		} else {
			// just skip AND bitmap
			long bytes = static_cast<long>((bytesPerLine + 3) / 4 * 4 * GetHeight());
			stream.Seek(sig, bytes, Stream::SeekCur);
		}
		if (sig.IsSignalled()) return false;
	}
	return true;
}

bool Object_Image::WriteDIB(Signal sig, Stream &stream, int biBitCount, bool maskFlag)
{
	Environment &env = *this;
	int biWidth = static_cast<int>(GetWidth());
	int biHeight = static_cast<int>(GetHeight());
	if (biBitCount == 1) {
		if (_pObjPalette == NULL) return false;
		size_t bytesPerLine = (biWidth + 7) / 8;
		size_t bytesAlign = (bytesPerLine + 3) / 4 * 4 - bytesPerLine;
		int bitsAccum = 0;
		unsigned char chAccum = 0x00;
		std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
		for (;;) {
			unsigned char ch = static_cast<unsigned char>(
							_pObjPalette->LookupNearest(pScanner->GetPointer()));
			chAccum |= ch << ((8 - 1) - bitsAccum);
			bitsAccum += 1;
			if (bitsAccum >= 8) {
				stream.Write(sig, &chAccum, 1);
				if (sig.IsSignalled()) return false;
				chAccum = 0x00;
				bitsAccum = 0;
			}
			if (!pScanner->NextPixel()) {
				if (bitsAccum > 0) {
					stream.Write(sig, &chAccum, 1);
					if (sig.IsSignalled()) return false;
					chAccum = 0x00;
					bitsAccum = 0;
				}
				stream.Write(sig, "\x00\x00\x00\x00", bytesAlign);
				if (sig.IsSignalled()) return false;
				if (!pScanner->NextLine()) break;
			}
		}
	} else if (biBitCount == 4) {
		if (_pObjPalette == NULL) return false;
		size_t bytesPerLine = (biWidth + 1) / 2;
		size_t bytesAlign = (bytesPerLine + 3) / 4 * 4 - bytesPerLine;
		int bitsAccum = 0;
		unsigned char chAccum = 0x00;
		std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
		for (;;) {
			unsigned char ch = static_cast<unsigned char>(
							_pObjPalette->LookupNearest(pScanner->GetPointer()));
			chAccum |= ch << ((8 - 4) - bitsAccum);
			bitsAccum += 4;
			if (bitsAccum >= 8) {
				stream.Write(sig, &chAccum, 1);
				if (sig.IsSignalled()) return false;
				chAccum = 0x00;
				bitsAccum = 0;
			}
			if (!pScanner->NextPixel()) {
				if (bitsAccum > 0) {
					stream.Write(sig, &chAccum, 1);
					if (sig.IsSignalled()) return false;
					chAccum = 0x00;
					bitsAccum = 0;
				}
				stream.Write(sig, "\x00\x00\x00\x00", bytesAlign);
				if (sig.IsSignalled()) return false;
				if (!pScanner->NextLine()) break;
			}
		}
	} else if (biBitCount == 8) {
		if (_pObjPalette == NULL) return false;
		size_t bytesAlign = (biWidth + 3) / 4 * 4 - biWidth;
		std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
		for (;;) {
			unsigned char ch = static_cast<unsigned char>(
							_pObjPalette->LookupNearest(pScanner->GetPointer()));
			stream.Write(sig, &ch, 1);
			if (sig.IsSignalled()) return false;
			if (!pScanner->NextPixel()) {
				stream.Write(sig, "\x00\x00\x00\x00", bytesAlign);
				if (sig.IsSignalled()) return false;
				if (!pScanner->NextLine()) break;
			}
		}
	} else if (biBitCount == 24) {
		size_t bytesAlign = ((3 * biWidth) + 3) / 4 * 4 - 3 * biWidth;
		unsigned char buff[3];
		std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
		for (;;) {
			buff[0] = pScanner->GetBlue();
			buff[1] = pScanner->GetGreen();
			buff[2] = pScanner->GetRed();
			stream.Write(sig, buff, 3);
			if (sig.IsSignalled()) return false;
			if (!pScanner->NextPixel()) {
				stream.Write(sig, "\x00\x00\x00\x00", bytesAlign);
				if (sig.IsSignalled()) return false;
				if (!pScanner->NextLine()) break;
			}
		}
	} else if (biBitCount == 32) {
		unsigned char buff[4];
		std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
		for (;;) {
			buff[0] = pScanner->GetBlue();
			buff[1] = pScanner->GetGreen();
			buff[2] = pScanner->GetRed();
			buff[3] = pScanner->GetAlpha();
			stream.Write(sig, buff, 4);
			if (sig.IsSignalled()) return false;
			if (!pScanner->Next()) break;
		}
	} else {
		sig.SetError(ERR_FormatError, "not supported bit depth");
		return false;
	}
	if (maskFlag) {
		size_t bytesPerLine = (biWidth + 7) / 8;
		size_t bytesPerLineAligned = (bytesPerLine + 3) / 4 * 4;
		size_t bytesAlign = bytesPerLineAligned - bytesPerLine;
		if (GetFormat() == FORMAT_RGBA) {
			// write AND bitmap
			int bitsAccum = 0;
			unsigned char chAccum = 0x00;
			std::auto_ptr<Scanner> pScanner(CreateScanner(SCAN_LeftBottomHorz));
			for (;;) {
				unsigned char ch = (pScanner->GetAlpha() < 128)? 1 : 0;
				chAccum |= ch << ((8 - 1) - bitsAccum);
				bitsAccum += 1;
				if (bitsAccum >= 8) {
					stream.Write(sig, &chAccum, 1);
					if (sig.IsSignalled()) return false;
					chAccum = 0x00;
					bitsAccum = 0;
				}
				if (!pScanner->NextPixel()) {
					if (bitsAccum > 0) {
						stream.Write(sig, &chAccum, 1);
						if (sig.IsSignalled()) return false;
						chAccum = 0x00;
						bitsAccum = 0;
					}
					stream.Write(sig, "\x00\x00\x00\x00", bytesAlign);
					if (sig.IsSignalled()) return false;
					if (!pScanner->NextLine()) break;
				}
			}
		} else {
			char *buff = new char [bytesPerLineAligned];
			::memset(buff, 0x00, bytesPerLineAligned);
			for (size_t y = 0; y < GetHeight(); y++) {
				stream.Write(sig, buff, bytesPerLineAligned);
				if (sig.IsSignalled()) break;
			}
			delete[] buff;
			if (sig.IsSignalled()) return false;
		}
	}
	return true;
}

#if USE_WINDOWS_DIB
//-----------------------------------------------------------------------------
// Object_Image (Windows DIB section)
//-----------------------------------------------------------------------------
Object_Image::Object_Image(Environment &env, Format format) :
		Object(env.LookupClass(VTYPE_Image)),
		_format(format), _width(0), _height(0), _buff(NULL),
		_hBmp(NULL), _pObjPalette(NULL)
{
	InitMetrics();
}

Object_Image::Object_Image(Class *pClass, Format format) :
		Object(pClass),
		_format(format), _width(0), _height(0), _buff(NULL),
		_hBmp(NULL), _pObjPalette(NULL)
{
	InitMetrics();
}

Object_Image::Object_Image(const Object_Image &obj) : Object(obj),
		_format(obj._format), _width(0), _height(0), _buff(NULL),
		_hBmp(NULL), _pObjPalette(Object_Palette::Reference(obj._pObjPalette))
{
	Signal sig;
	if (AllocBuffer(sig, obj._width, obj._height, 0x00)) {
		::memcpy(_buff, obj._buff, GetBufferSize());
	}
}

Object_Image::~Object_Image()
{
	FreeBuffer();
	Object::Delete(_pObjPalette);
}

Object *Object_Image::Clone() const
{
	return new Object_Image(*this);
}

bool Object_Image::AllocBuffer(Signal sig,
					size_t width, size_t height, unsigned char fillValue)
{
	FreeBuffer();
	if (width == 0 || height == 0) {
		sig.SetError(ERR_MemoryError, "failed to allocate image buffer");
		return false;
	}
	BITMAPINFO bmi;
	BITMAPINFOHEADER &hdr = bmi.bmiHeader;
	::memset(&bmi, 0, sizeof(BITMAPINFO));
	hdr.biSize = sizeof(BITMAPINFOHEADER);
	hdr.biWidth = static_cast<int>(width);
	hdr.biHeight = -static_cast<int>(height);
	hdr.biPlanes = 1;
	hdr.biBitCount =
		(_format == FORMAT_RGB)? 24 :
		(_format == FORMAT_RGBA)? 32 : 32;
	hdr.biCompression = BI_RGB;
	hdr.biSizeImage = 0;
	hdr.biXPelsPerMeter = 0;
	hdr.biYPelsPerMeter = 0;
	hdr.biClrUsed = 0;
	hdr.biClrImportant = 0;
	void *buff = NULL;
	_hBmp = ::CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &buff, NULL, 0);
	if (_hBmp == NULL || buff == NULL) {
		sig.SetError(ERR_MemoryError, "failed to allocate image buffer");
		return false;
	}
	_width = width, _height = height;
	_buff = reinterpret_cast<unsigned char *>(buff);
	InitMetrics();
	::memset(_buff, fillValue, GetBufferSize());
	return true;
}

void Object_Image::FreeBuffer()
{
	if (_hBmp != NULL) ::DeleteObject(_hBmp);
	_width = 0, _height = 0;
	_buff = NULL;
	_hBmp = NULL;
}
#else // USE_WINDOWS_DIB
//-----------------------------------------------------------------------------
// Object_Image (on memory)
//-----------------------------------------------------------------------------
Object_Image::Object_Image(Environment &env, Format format) :
		Object(env.LookupClass(VTYPE_Image)),
		_format(format), _width(0), _height(0), _buff(NULL), _pObjPalette(NULL)
{
	InitMetrics();
}

Object_Image::Object_Image(Class *pClass, Format format) :
		Object(pClass),
		_format(format), _width(0), _height(0), _buff(NULL), _pObjPalette(NULL)
{
	InitMetrics();
}

Object_Image::Object_Image(const Object_Image &obj) : Object(obj),
		_format(obj._format), _width(0), _height(0), _buff(NULL),
		_pObjPalette(Object_Palette::Reference(obj._pObjPalette))
{
	Signal sig;
	if (AllocBuffer(sig, obj._width, obj._height, 0x00)) {
		::memcpy(_buff, obj._buff, GetBufferSize());
	}
}

Object_Image::~Object_Image()
{
	FreeBuffer();
	Object::Delete(_pObjPalette);
}

Object *Object_Image::Clone() const
{
	return new Object_Image(*this);
}

bool Object_Image::AllocBuffer(Signal sig,
					size_t width, size_t height, unsigned char fillValue)
{
	_memory.Free();
	if (width == 0 || height == 0) {
		sig.SetError(ERR_MemoryError, "failed to allocate image buffer");
		return false;
	}
	_width = width, _height = height;
	InitMetrics();
	_buff = reinterpret_cast<unsigned char *>(_memory.Allocate(GetBufferSize()));
	::memset(_buff, fillValue, GetBufferSize());
	return true;
}

void Object_Image::FreeBuffer()
{
	_memory.Free();
	_width = 0, _height = 0;
	_buff = NULL;
}
#endif

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Image
//-----------------------------------------------------------------------------
// image#size()
AScript_DeclareMethod(Image, size)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Image, size)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.push_back(static_cast<int>(pSelf->GetWidth()));
	valList.push_back(static_cast<int>(pSelf->GetHeight()));
	return result;
}

// image#allocbuff(width:number, height:number, color?:color):void
AScript_DeclareMethod(Image, allocbuff)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareArg(env, "color", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Image, allocbuff)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckEmpty(sig)) return Value::Null;
	pSelf->AllocBuffer(sig, args.GetSizeT(0), args.GetSizeT(1), 0x00);
	if (args.IsColor(2)) pSelf->Fill(args.GetColorObj(2));
	return Value::Null;
}

// image#putpixel(x:number, y:number, color:color):map:void
AScript_DeclareMethod(Image, putpixel)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "color", VTYPE_Color);
}

AScript_ImplementMethod(Image, putpixel)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	int x = args.GetInt(0), y = args.GetInt(1);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	unsigned char *p = pSelf->GetPointer(x, y);
	pSelf->PutPixel(p, args.GetColorObj(2));
	return Value::Null;
}

// image#getpixel(x:number, y:number):map
AScript_DeclareMethod(Image, getpixel)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
}

AScript_ImplementMethod(Image, getpixel)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	int x = args.GetInt(0), y = args.GetInt(1);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	unsigned char *p = pSelf->GetPointer(x, y);
	Object_Color *pObjColor = new Object_Color(env, 0, 0, 0, 0);
	pSelf->GetPixel(p, pObjColor);
	return Value(pObjColor);
}

// image#store(x:number, y:number, width:number, height:number, element:symbol, src):void
AScript_DeclareMethod(Image, store)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareArg(env, "element", VTYPE_Symbol);
	DeclareArg(env, "src", VTYPE_Any);
}

AScript_ImplementMethod(Image, store)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	size_t x = args.GetSizeT(0), y = args.GetSizeT(1);
	size_t width = args.GetSizeT(2), height = args.GetSizeT(3);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	if (!pSelf->CheckCoord(sig, x + width - 1, y + height - 1)) return Value::Null;
	const Symbol *pSymbol = args.GetSymbol(4);
	if (args.IsMatrix(5)) {
		pSelf->Store(sig, x, y, width, height, pSymbol, args.GetMatrixObj(5));
	} else if (args.IsList(5) || args.IsIterator(5)) {
		Iterator *pIterator = args.GetValue(5).CreateIterator(sig);
		pSelf->Store(sig, x, y, width, height, pSymbol, pIterator);
		Iterator::Delete(pIterator);
	} else {
		sig.SetError(ERR_ValueError, "invalid object for image's source");
		return Value::Null;
	}
	return Value::Null;
}

// image#extract(x:number, y:number, width:number, height:number, element:symbol, dst):void
AScript_DeclareMethod(Image, extract)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareArg(env, "element", VTYPE_Symbol);
	DeclareArg(env, "dst", VTYPE_Any);
}

AScript_ImplementMethod(Image, extract)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	size_t x = args.GetSizeT(0), y = args.GetSizeT(1);
	size_t width = args.GetSizeT(2), height = args.GetSizeT(3);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	if (!pSelf->CheckCoord(sig, x + width - 1, y + height - 1)) return Value::Null;
	const Symbol *pSymbol = args.GetSymbol(4);
	if (args.IsMatrix(5)) {
		pSelf->Extract(sig, x, y, width, height, pSymbol, args.GetMatrixObj(5));
	} else if (args.IsList(5)) {
		pSelf->Extract(sig, x, y, width, height, pSymbol, args.GetListObj(5));
	} else {
		sig.SetError(ERR_ValueError, "invalid object for image's destination");
		return Value::Null;
	}
	return Value::Null;
}

// image#fill(color:color):void
AScript_DeclareMethod(Image, fill)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareArg(env, "color", VTYPE_Color);
}

AScript_ImplementMethod(Image, fill)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	pSelf->Fill(args.GetColorObj(0));
	return Value::Null;
}

// image#fillrect(x:number, y:number, width:number, height:number, color:color):map:void
AScript_DeclareMethod(Image, fillrect)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number);
	DeclareArg(env, "height", VTYPE_Number);
	DeclareArg(env, "color", VTYPE_Color);
}

AScript_ImplementMethod(Image, fillrect)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	int x = args.GetInt(0), y = args.GetInt(1);
	int width = args.GetInt(2), height = args.GetInt(3);
	if (!pSelf->AdjustCoord(x, y, width, height)) return Value::Null;
	pSelf->FillRect(x, y, width, height, args.GetColorObj(4));
	return Value::Null;
}

// image#setalpha(colorKey:color, alphaKey:number => 0, alphaRest:number => 255):reduce
AScript_DeclareMethod(Image, setalpha)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "colorKey", VTYPE_Color);
	DeclareArg(env, "alphaKey", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "alphaRest", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(255));
}

AScript_ImplementMethod(Image, setalpha)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	if (pSelf->GetFormat() != Object_Image::FORMAT_RGBA) {
		sig.SetError(ERR_ValueError, "only RGBA format contains alpha element");
		return 0;
	}
	pSelf->FillAlpha(args.GetColorObj(0), args.GetUChar(1), args.GetUChar(2));
	return args.GetSelf();
}

// image#reducecolor(palette?:palette)
AScript_DeclareMethod(Image, reducecolor)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "palette", VTYPE_Palette, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Image, reducecolor)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	const Object_Palette *pObjPalette = pSelf->GetPaletteObj();
	if (args.IsPalette(0)) {
		pObjPalette = Object_Palette::Reference(args.GetPaletteObj(0));
	}
	Object_Image *pObj = pSelf->ReduceColor(sig, pObjPalette);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObj);
}

// image#delpalette():reduce
AScript_DeclareMethod(Image, delpalette)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
}

AScript_ImplementMethod(Image, delpalette)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	pSelf->SetPaletteObj(NULL);
	return args.GetSelf();
}

// image#flip(orient:symbol):map
AScript_DeclareMethod(Image, flip)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "orient", VTYPE_Symbol);
}

AScript_ImplementMethod(Image, flip)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	const Symbol *pSymbol = args.GetSymbol(0);
	bool horzFlag = false, vertFlag = false;
	if (pSymbol->IsIdentical(AScript_Symbol(horz))) {
		horzFlag = true;
	} else if (pSymbol->IsIdentical(AScript_Symbol(vert))) {
		vertFlag = true;
	} else if (pSymbol->IsIdentical(AScript_Symbol(both))) {
		horzFlag = vertFlag = true;
	} else {
		sig.SetError(ERR_ValueError, "orient must be one of `horz or `vert");
		return Value::Null;
	}
	if (!pSelf->CheckValid(sig)) return Value::Null;
	Object_Image *pObj = pSelf->Flip(sig, horzFlag, vertFlag);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObj);
}

// image#rotate(angle:number, background?:color):map
AScript_DeclareMethod(Image, rotate)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "rotate", VTYPE_Number);
	DeclareArg(env, "background", VTYPE_Color, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Image, rotate)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	Value valueBg = args.GetValue(1);
	if (!valueBg.IsColor()) {
		valueBg = Value(new Object_Color(env, 0, 0, 0, 0));
	}
	Object_Image *pObj = pSelf->Rotate(sig,
					args.GetNumber(0), valueBg.GetColorObj());
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObj);
}

// image#crop(x:number, y:number, width?:number, height?:number):map
AScript_DeclareMethod(Image, crop)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "height", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Image, crop)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	size_t x = args.GetSizeT(0);
	size_t y = args.GetSizeT(1);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	size_t width = args.IsNumber(2)? args.GetSizeT(2) : pSelf->GetWidth() - x;
	size_t height = args.IsNumber(3)? args.GetSizeT(3) : pSelf->GetHeight() - y;
	if (!pSelf->CheckCoord(sig, x + width - 1, y + height - 1)) return Value::Null;
	Object_Image *pObj = pSelf->Crop(sig, x, y, width, height);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObj);
}

// image#resize(width?:number, height?:number):map:[box]
AScript_DeclareMethod(Image, resize)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "height", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(box));
}

AScript_ImplementMethod(Image, resize)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	bool boxFlag = args.IsSet(AScript_Symbol(box));
	size_t width, height;
	if (!args.IsNumber(0) && !args.IsNumber(1)) {
		sig.SetError(ERR_ValueError, "width or height must be specified");
		return Value::Null;
	}
	if (args.IsNumber(0) && !args.IsNumber(1)) {
		width = args.GetSizeT(0);
		if (boxFlag) {
			height = width;
		} else {
			height = pSelf->GetHeight() * width / pSelf->GetWidth();
		}
	} else if (!args.IsNumber(0) && args.IsNumber(1)) {
		height = args.GetSizeT(1);
		if (boxFlag) {
			width = height;
		} else {
			width = pSelf->GetWidth() * height / pSelf->GetHeight();
		}
	} else {
		width = args.GetSizeT(0), height = args.GetSizeT(1);
	}
	Object_Image *pObj = pSelf->Resize(sig, width, height);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObj);
}

// image#thumbnail(width?:number, height?:number):map:[box]
AScript_DeclareMethod(Image, thumbnail)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "height", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(box));
}

AScript_ImplementMethod(Image, thumbnail)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	bool boxFlag = args.IsSet(AScript_Symbol(box));
	Object_Image *pObj;
	if (!args.IsNumber(0) && !args.IsNumber(1)) {
		sig.SetError(ERR_ValueError, "width or height must be specified");
		return Value::Null;
	}
	size_t width = 0, height = 0;
	if (args.IsNumber(0) && !args.IsNumber(1)) {
		width = args.GetSizeT(0);
		if (boxFlag) {
			height = width;
		} else {
			if (pSelf->GetWidth() < width) {
				pObj = dynamic_cast<Object_Image *>(pSelf->Clone());
			} else {
				height = pSelf->GetHeight() * width / pSelf->GetWidth();
				pObj = pSelf->Resize(sig, width, height);
				if (sig.IsSignalled()) return Value::Null;
			}
			return Value(pObj);
		}
	} else if (!args.IsNumber(0) && args.IsNumber(1)) {
		height = args.GetSizeT(1);
		if (boxFlag) {
			width = height;
		} else {
			if (pSelf->GetHeight() < height) {
				pObj = dynamic_cast<Object_Image *>(pSelf->Clone());
			} else {
				width = pSelf->GetWidth() * height / pSelf->GetHeight();
				pObj = pSelf->Resize(sig, width, height);
				if (sig.IsSignalled()) return Value::Null;
			}
			return Value(pObj);
		}
	} else {
		width = args.GetSizeT(0);
		height = args.GetSizeT(1);
	}
	if (pSelf->GetWidth() < width && pSelf->GetHeight() < height) {
		pObj = dynamic_cast<Object_Image *>(pSelf->Clone());
	} else {
		size_t widthExp = pSelf->GetWidth() * height / pSelf->GetHeight();
		if (widthExp <= width) {
			width = widthExp;
		} else {
			height = pSelf->GetHeight() * width / pSelf->GetWidth();
		}
		pObj = pSelf->Resize(sig, width, height);
		if (sig.IsSignalled()) return Value::Null;
	}
	return Value(pObj);
}

// image#paste(x:number, y:number, src:image, width?:number, height?:number,
//     xoffset:number => 0, yoffset:number => 0, alpha:number => 255):map:reduce
AScript_DeclareMethod(Image, paste)
{
	SetMode(RSLTMODE_Reduce, FLAG_Map);
	DeclareArg(env, "x", VTYPE_Number);
	DeclareArg(env, "y", VTYPE_Number);
	DeclareArg(env, "src", VTYPE_Image);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "height", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "xoffset", VTYPE_Number, OCCUR_Once,
								false, false, new Expr_Value(0));
	DeclareArg(env, "yoffset", VTYPE_Number, OCCUR_Once,
								false, false, new Expr_Value(0));
	DeclareArg(env, "alpha", VTYPE_Number, OCCUR_Once,
								false, false, new Expr_Value(255));
}

AScript_ImplementMethod(Image, paste)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->CheckValid(sig)) return Value::Null;
	Object_Image *pObjImg = args.GetImageObj(2);
	if (!pObjImg->CheckValid(sig)) return Value::Null;
	size_t x = args.GetSizeT(0);
	size_t y = args.GetSizeT(1);
	if (!pSelf->CheckCoord(sig, x, y)) return Value::Null;
	size_t xOffset = args.GetSizeT(5);
	size_t yOffset = args.GetSizeT(6);
	if (!pObjImg->CheckCoord(sig, xOffset, yOffset)) return Value::Null;
	size_t width = args.IsNumber(3)?
				args.GetSizeT(3) : pObjImg->GetWidth() - xOffset;
	size_t height = args.IsNumber(4)?
				args.GetSizeT(4) : pObjImg->GetHeight() - yOffset;
	if (!pObjImg->CheckCoord(sig, xOffset + width - 1, yOffset + height - 1)) {
		return Value::Null;
	}
	if (x + width > pSelf->GetWidth()) width = pSelf->GetWidth() - x;
	if (y + height > pSelf->GetHeight()) height = pSelf->GetHeight() - y;
	unsigned char alpha = args.GetUChar(7);
	pSelf->Paste(x, y, pObjImg, width, height, xOffset, yOffset, alpha);
	if (sig.IsSignalled()) return Value::Null;
	return args.GetSelf();
}

// image#read(stream:stream, imgtype?:string):map:reduce
AScript_DeclareMethod(Image, read)
{
	SetMode(RSLTMODE_Reduce, FLAG_Map);
	DeclareArg(env, "stream", VTYPE_Stream);
	DeclareArg(env, "imgtype", VTYPE_String, OCCUR_ZeroOrOnce);
	SetHelp("Reads image data from a stream.");
}

AScript_ImplementMethod(Image, read)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->Read(sig, args.GetStream(0),
			args.IsString(1)? args.GetString(1) : NULL)) return Value::Null;
	return args.GetSelf();
}

// image#write(stream:stream, imgtype?:string):map:reduce
AScript_DeclareMethod(Image, write)
{
	SetMode(RSLTMODE_Reduce, FLAG_Map);
	DeclareArg(env, "stream", VTYPE_Stream);
	DeclareArg(env, "imgtype", VTYPE_String, OCCUR_ZeroOrOnce);
	SetHelp("Writes image data to a stream.");
}

AScript_ImplementMethod(Image, write)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	if (!pSelf->Write(sig, args.GetStream(0),
			args.IsString(1)? args.GetString(1) : NULL)) return Value::Null;
	return args.GetSelf();
}

// image#each(x:number, y:number, width:number, height:number, scandir:symbol) {block?}
AScript_DeclareMethod(Image, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "x", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "y", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "width", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "height", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "scandir", VTYPE_Symbol, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp("Returns an iterator that scans pixels in the image.");
}

AScript_ImplementMethod(Image, each)
{
	Object_Image *pSelf = Object_Image::GetSelfObj(args);
	size_t x = args.IsNumber(0)? args.GetSizeT(0) : 0;
	size_t y = args.IsNumber(1)? args.GetSizeT(1) : 0;
	size_t width = args.IsNumber(2)? args.GetSizeT(2) : pSelf->GetWidth();
	size_t height = args.IsNumber(3)? args.GetSizeT(3) : pSelf->GetHeight();
	Object_Image::ScanDir scanDir = Object_Image::SCAN_LeftTopHorz;
	if (args.IsSymbol(4)) {
		const Symbol *pSymbol = args.GetSymbol(4);
	}
	Iterator *pIterator = new Object_Image::IteratorEach(
					Object_Image::Reference(pSelf), x, y, width, height, scanDir);
	return ReturnIterator(env, sig, args, pIterator);
}

// assignment
Class_Image::Class_Image(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Image, size);
	AScript_AssignMethod(Image, allocbuff);
	AScript_AssignMethod(Image, putpixel);
	AScript_AssignMethod(Image, getpixel);
	AScript_AssignMethod(Image, store);
	AScript_AssignMethod(Image, extract);
	AScript_AssignMethod(Image, fill);
	AScript_AssignMethod(Image, fillrect);
	AScript_AssignMethod(Image, setalpha);
	AScript_AssignMethod(Image, reducecolor);
	AScript_AssignMethod(Image, delpalette);
	AScript_AssignMethod(Image, flip);
	AScript_AssignMethod(Image, rotate);
	AScript_AssignMethod(Image, crop);
	AScript_AssignMethod(Image, resize);
	AScript_AssignMethod(Image, thumbnail);
	AScript_AssignMethod(Image, paste);
	AScript_AssignMethod(Image, read);
	AScript_AssignMethod(Image, write);
	AScript_AssignMethod(Image, each);
}

bool Class_Image::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsString()) {
		Stream *pStream = Directory::OpenStream(env, sig,
								value.GetString(), Stream::ATTR_Readable, NULL);
		if (sig.IsSignalled()) return false;
		Object_Image *pObjImage = new Object_Image(env, Object_Image::FORMAT_RGBA);
		pObjImage->Read(sig, *pStream, NULL);
		Stream::Delete(pStream);
		if (sig.IsSignalled()) {
			Object::Delete(pObjImage);
			return false;
		}
		value = Value(pObjImage);
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
// Object_Image::Scanner
//-----------------------------------------------------------------------------
Object_Image::Scanner::Scanner(Object_Image *pObjImage,
			size_t x, size_t y, size_t width, size_t height, ScanDir scanDir) :
	_pObjImage(pObjImage), _iPixel(0), _iLine(0)
{
	int bytesPerPixel = static_cast<int>(_pObjImage->GetBytesPerPixel());
	int bytesPerLine = static_cast<int>(_pObjImage->GetBytesPerLine());
	switch (scanDir) {
	case SCAN_LeftTopHorz:
		_pPixel = _pObjImage->GetPointer(x, y);
		_nPixels = width, _nLines = height;
		_pitchPixel = bytesPerPixel;
		_pitchLine = bytesPerLine - bytesPerPixel * static_cast<int>(width);
		break;
	case SCAN_LeftTopVert:
		_pPixel = _pObjImage->GetPointer(x, y);
		_nPixels = height, _nLines = width;
		_pitchPixel = bytesPerLine;
		_pitchLine = bytesPerPixel - bytesPerLine * static_cast<int>(height);
		break;
	case SCAN_RightTopHorz:
		_pPixel = _pObjImage->GetPointer(x + width - 1, y);
		_nPixels = width, _nLines = height;
		_pitchPixel = -bytesPerPixel;
		_pitchLine = bytesPerLine + bytesPerPixel * static_cast<int>(width);
		break;
	case SCAN_RightTopVert:
		_pPixel = _pObjImage->GetPointer(x + width - 1, y);
		_nPixels = height, _nLines = width;
		_pitchPixel = bytesPerLine;
		_pitchLine = -bytesPerPixel - bytesPerLine * static_cast<int>(height);
		break;
	case SCAN_LeftBottomHorz:
		_pPixel = _pObjImage->GetPointer(x, y + height - 1);
		_nPixels = width, _nLines = height;
		_pitchPixel = bytesPerPixel;
		_pitchLine = -bytesPerLine - bytesPerPixel * static_cast<int>(width);
		break;
	case SCAN_LeftBottomVert:
		_pPixel = _pObjImage->GetPointer(x, y + height - 1);
		_nPixels = height, _nLines = width;
		_pitchPixel = -bytesPerLine;
		_pitchLine = bytesPerPixel + bytesPerLine * static_cast<int>(height);
		break;
	case SCAN_RightBottomHorz:
		_pPixel = _pObjImage->GetPointer(x + width - 1, y + height - 1);
		_nPixels = width, _nLines = height;
		_pitchPixel = -bytesPerPixel;
		_pitchLine = -bytesPerLine + bytesPerPixel * static_cast<int>(width);
		break;
	case SCAN_RightBottomVert:
		_pPixel = _pObjImage->GetPointer(x + width - 1, y + height - 1);
		_nPixels = height, _nLines = width;
		_pitchPixel = -bytesPerLine;
		_pitchLine = -bytesPerPixel + bytesPerLine * static_cast<int>(height);
		break;
	default:
		break;
	}
}

Object_Image::Scanner::~Scanner()
{
	Object::Delete(_pObjImage);
}

//-----------------------------------------------------------------------------
// Object_Image::IteratorEach
//-----------------------------------------------------------------------------
Object_Image::IteratorEach::~IteratorEach()
{
}

bool Object_Image::IteratorEach::DoNext(Environment &env, Signal sig, Value &value)
{
	Object_Image *pObjImage = _scanner.GetImageObj();
	//Environment &env = *pObjImage;
	if (_doneFlag) return false;
	unsigned char red = _scanner.GetRed();
	unsigned char green = _scanner.GetGreen();
	unsigned char blue = _scanner.GetBlue();
	unsigned char alpha = (pObjImage->GetFormat() == Object_Image::FORMAT_RGBA)?
											_scanner.GetAlpha() : 0xff;
	value = Value(new Object_Color(env, red, green, blue, alpha));
	_doneFlag = !_scanner.Next();
	return true;
}

String Object_Image::IteratorEach::ToString(Signal sig) const
{
	return String("<iterator:image#each>");
}

//-----------------------------------------------------------------------------
// ImageStreamer
//-----------------------------------------------------------------------------
ImageStreamer::List *ImageStreamer::_pList = NULL;
void ImageStreamer::Register(ImageStreamer *pImageStreamer)
{
	if (_pList == NULL) _pList = new List();
	_pList->push_back(pImageStreamer);
}

ImageStreamer *ImageStreamer::FindResponsible(Signal sig, Stream &stream, const char *imgType)
{
	if (_pList == NULL) return NULL;
	if (imgType != NULL) return FindByImgType(imgType);
	foreach (List, ppImageStreamer, *_pList) {
		ImageStreamer *pImageStreamer = *ppImageStreamer;
		if (pImageStreamer->IsResponsible(sig, stream)) return pImageStreamer;
		if (sig.IsSignalled()) break;
	}
	return NULL;
}

ImageStreamer *ImageStreamer::FindByImgType(const char *imgType)
{
	if (_pList == NULL) return NULL;
	foreach (List, ppImageStreamer, *_pList) {
		ImageStreamer *pImageStreamer = *ppImageStreamer;
		if (::strcasecmp(pImageStreamer->GetImgType(), imgType) == 0) {
			return pImageStreamer;
		}
	}
	return NULL;
}

}
