/*
 *  psychlops_g_image.cpp
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2006/01/04 by Kenchi HOSOKAWA
 *  (C) 2006 Kenchi HOSOKAWA, Kazushi MARUYA and Takao SATO
 */


#include <stdlib.h>
#include <Math.h>
#include <string.h>
#include <iostream>
#include <string>

#define PSYCHLOPS_WINDOW_API_PLATFORM
#include "../../platform/psychlops_platform_selector.h"
#include "../ApplicationInterfaces/psychlops_app_info.h"
#include "../devices/psychlops_io_file.h"
#include "../math/psychlops_math.h"

#include "psychlops_g_fundamental.h"
#include "psychlops_g_color.h"
#include "psychlops_g_module.h"
#include "psychlops_g_shape.h"
#include "psychlops_g_canvas.h"
#include "psychlops_g_image.h"

#include "../../extension/FileFormat/PNG/psychlops_g_PNG_bridge.h"
#if 0
#include "../../extension/FileFormat/JPEG/psychlops_g_JPEG_bridge.h"
#endif


namespace Psychlops {

	ImageCache_::ImageCache_() : id(0), dirty(false) {}
	ImageCache_::ImageCache_(APIImageCache* id__, bool dirty__) : id(id__), dirty(dirty__) {}

	const int Image::PixCompSize_[3] = { 1, 3, 4 };
	const int Image::PixPrecSize_[2] = { sizeof(unsigned char), sizeof(float) };
	int Image::lineBytesAlingnment_ = AppState::ImageByteAlignment;


	Image& Image::operator =(const Image &source) {
		memcpy(this, &source, sizeof(*this));
		return *this;
	}
	Image::Image(const Image &readimage){}
	Image::Image(const Image &readimage, bool dummt) {
		initPointersNull();
		from(readimage);
	}



	////////	Construst and Destruct	////////

	Image::Image() {
		initPointersNull();
	}

	Image::Image(std::string filename) {
		initPointersNull();
		load(filename);
	}
	Image::Image(const char * filename) {
		initPointersNull();
		load(filename);
	}
	Image::Image(long x, long y, PixelComponentsCnt pixcomp, PixelComponentsPrecision pixprec) {
		initPointersNull();
		set(x, y, pixcomp, pixprec);
	}
	Image::Image(long x, long y, PixelComponentsPrecision pixprec) {
		initPointersNull();
		set(x, y, RGB, pixprec);
	}
	Image::Image(Rectangle rectangle, PixelComponentsCnt pixcomp, PixelComponentsPrecision pixprec) {
		initPointersNull();
		set(rectangle, pixcomp, pixprec);
	}
	Image::Image(Rectangle rectangle, PixelComponentsPrecision pixprec) {
		initPointersNull();
		set(rectangle, RGB, pixprec);
	}

	void Image::setWithoutClear(bool y)
	{
		setWithoutClear_ = y;
	}
	bool Image::setWithoutClear_ = false; // OK?

	void Image::set(long x, long y, PixelComponentsPrecision pixprec) {
		set(x, y, RGB, pixprec);
	}
	void Image::set(Rectangle rectangle, PixelComponentsCnt pixcomp, PixelComponentsPrecision pixprec) {
		set((long)rectangle.getWidth(), (long)rectangle.getHeight(), pixcomp, pixprec);
	}
	void Image::set(Rectangle rectangle, PixelComponentsPrecision pixprec) {
		set(rectangle, RGB, pixprec);
	}
	void Image::initPointersNull() {
		have_instance_ = false;
		//quickened_ = false;
		bitmapub_ = NULL;
		bitmapf_ = NULL;
		width_ = 0;
		height_ = 0;
		pixcomp_ = RGB;
		pixprec_ = BYTE;
	}


	Image::~Image(void) {
		release();
	}
	void Image::release(){
		if(have_instance_) {
			if(!caches.empty()) {
				std::vector<DrawableWithCache*> tmp;
				for(CacheID::iterator i=caches.begin(); i!=caches.end(); i++) tmp.push_back(i->first);
				for(int i=0; i<tmp.size(); i++) tmp[i]->uncacheImage(*this);
			}
			delete api_;
			releasebitmap();
			have_instance_ = false;
		}
	}
	void Image::releasebitmap() {
		if(have_instance_) {
			switch(pixprec_) {
				case BYTE:
					if(bitmapub_ != NULL) delete [] bitmapub_;
					break;
				case FLOAT:
					if(bitmapf_  != NULL) delete [] bitmapf_;
					break;
			}
		}
		initPointersNull();
	}
	bool Image::hasInstance() const {
		return have_instance_;
	}

	Image& Image::cache(DrawableWithCache &target) {
		target.cacheImage(*this);
		return *this;
	}
	Image& Image::uncache(DrawableWithCache &target) {
		target.uncacheImage(*this);
		return *this;
	}

	Image Image::dup() const {
		return Image(*this, true);
	}
	Image Image::duplicate() const {
		return Image(*this, true);
	}
	Image& Image::convert(PixelComponentsCnt pixcompcntval) {
		if(!have_instance_) throw Exception(typeid(*this), "Memory Error", "Image had not allocated.");
		if(pixcomp_==pixcompcntval) {
			return *this;
		} else {
			Image *tmp = new Image;
			tmp->from(*this);
			release();
			set(tmp->width_, tmp->height_, pixcompcntval, tmp->pixprec_);
			targetarea_ = tmp->targetarea_;
			localarea_ = tmp->localarea_;
			unsigned char *p = bitmapub_;
			float *pf = bitmapf_;
			double r, g, b, a;
			if(pixprec_==BYTE) {
				for(int y=height_-1; y>=0; y--) {
					for(int x=0; x<width_; x++) {
						tmp->getPix(x,y).get(r, g, b, a);
						*(p++) = (unsigned char)(r*255.0+.5);
						if(pixcomp_==RGB||pixcomp_==RGBA) *(p++) = (unsigned char)(g*255.0+.5);
						if(pixcomp_==RGB||pixcomp_==RGBA) *(p++) = (unsigned char)(b*255.0+.5);
						if(pixcomp_==RGBA) { *(p++) = (unsigned char)(a*255.0+.5); }
					}
				}
			} else {
				for(int y=height_-1; y>=0; y--) {
					for(int x=0; x<width_; x++) {
						tmp->getPix(x,y).get(r, g, b, a);
						*(pf++) = r;
						if(pixcomp_==RGB||pixcomp_==RGBA) *(pf++) = g;
						if(pixcomp_==RGB||pixcomp_==RGBA) *(pf++) = b;
						if(pixcomp_==RGBA) { *(pf++) = a; }
					}
				}
			}
			//if(tmp->quickened_) quicken();
			delete tmp;
		}
		return *this;
	}
	Image& Image::convertColorCalibration(bool on_off) {
		Color col;
		double r, g, b, a;
		if(!have_instance_) throw Exception(typeid(*this), "Memory Error", "Image had not allocated.");
		Image *tmp = new Image;
		tmp->from(*this);
		release();
		set(tmp->width_, tmp->height_, tmp->pixcomp_, tmp->pixprec_);
		targetarea_ = tmp->targetarea_;
		localarea_ = tmp->localarea_;
		if(on_off) {
			for(int y=0; y<height_; y++) {
				for(int x=0; x<width_; x++) {
					col = getPix(x, y);
					r = col.Red;
					g = col.Green;
					b = col.Blue;
					a = col.Alpha;
					col.set(r, g, b, a);
					tmp->pix_direct(x, y, col);
				}
			}
		} else {
			for(int y=0; y<height_; y++) {
				for(int x=0; x<width_; x++) {
					getPix(x, y).get(r,g,b,a);
					col.Red   = r;
					col.Green = g;
					col.Blue  = b;
					col.Alpha = a;
					tmp->pix_direct(x, y, col);
				}
			}
		}
		//if(tmp->quickened_) quicken();
		delete tmp;

		return *this;
	}


	////////	Drawing	////////
/*
void Image::pix_ub_bits_mono_(int ix, int iy, double lum) {
	int x = ix, y = iy;
	if(0>x || x>width_-1 || y<0 || y>height_-1) throw Exception(typeid(Image), "Out Of Bound Exception", "Specified coordinate is out of bound.");
	int offset = y*lineValue_ + x*PixCompSize_[pixcomp_];
	unsigned char *p = bitmapub_ + offset;
	unsigned short wordlum = (unsigned short)(lum*65535);
	*(p) = (unsigned char)(wordlum>>8);
	*(++p) = (unsigned char)(wordlum&255);
	*(++p) = (unsigned char)0;
}
*/
	Image& Image::alpha(const double a)
	{
		for(int y=0; y<getHeight(); y++)
			for(int x=0; x<getWidth(); x++)
				alpha(x,y,a);
		return *this;
	}
	Image& Image::alpha(const Matrix &a)
	{
		if(getWidth()!=a.getCols() || getHeight()!=a.getRows() || pixcomp_!=Image::RGBA)
			throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent to Image");
		for(int y=0; y<getHeight(); y++)
			for(int x=0; x<getWidth(); x++)
				alpha(x,y,a(y+1,x+1));
		return *this;
	}
	Image& Image::clear(const Color &col) {
		//* classic routine
		//for(int y=0; y<height_; y++) {
		//	for(int x=0; x<width_; x++) {
		//		pix_direct_(*this,x,y,col);

		int x, y;
		int yl = 0;
		unsigned char* pb;
		float* pf;
		pix_raw(0, getHeight()-1, col);

		if(pixprec_ == Image::BYTE) {
			if(pixcomp_ == Image::GRAY) {
				pb = bitmapub_;
				for(x=0; x<width_; x++) {
					(*pb++) = *(bitmapub_);
				}
			} else if(pixcomp_ == Image::RGB) {
				pb = bitmapub_;
				for(x=0; x<width_; x++) {
					(*pb++) = *(bitmapub_);
					(*pb++) = *(bitmapub_+1);
					(*pb++) = *(bitmapub_+2);
				}
			} else if(pixcomp_ == Image::RGBA) {
				pb = bitmapub_;
				for(x=0; x<width_; x++) {
					(*pb++) = *(bitmapub_);
					(*pb++) = *(bitmapub_+1);
					(*pb++) = *(bitmapub_+2);
					(*pb++) = *(bitmapub_+3);
				}
			}
			for(y=1; y<height_; y++) {
				yl = y*lineValue_;
				pb = bitmapub_ + yl;
				memcpy(pb, bitmapub_, lineValue_);
			}
		} else if(pixprec_ == Image::FLOAT) {
			if(pixcomp_ == Image::GRAY) {
				pf = bitmapf_;
				for(x=0; x<width_; x++) {
					(*pf++) = *(bitmapf_);
				}
			} else if(pixcomp_ == Image::RGB) {
				pf = bitmapf_;
				for(x=0; x<width_; x++) {
					(*pf++) = *(bitmapf_);
					(*pf++) = *(bitmapf_+1);
					(*pf++) = *(bitmapf_+2);
				}
			} else if(pixcomp_ == Image::RGBA) {
				pf = bitmapf_;
				for(x=0; x<width_; x++) {
					(*pf++) = *(bitmapf_);
					(*pf++) = *(bitmapf_+1);
					(*pf++) = *(bitmapf_+2);
					(*pf++) = *(bitmapf_+3);
				}
			}
			for(y=1; y<height_; y++) {
				yl = y*lineValue_;
				pf = bitmapf_ + yl;
				memcpy(pf, bitmapf_, lineBytes_);
			}
		}
		return *this;
	}
/*	Image& Image::alpha(const int x, const int iy, const double a) {
		Color col(0,0,0,a);
		pix(x, iy, col);
		return *this;
	}
	Image& Image::pix(const double x, const double y, const Color &col) {
		pix(Math::round(x), Math::round(y), col);
		return *this;
	}
	Image& Image::pix(const Point &po, const Color &col) {
		pix(Math::round(po.x), Math::round(po.y), col);
		return *this;
	}
 */
	void Image::rect(const Rectangle &rectangle, const Color &col) {
		Rectangle filledrect(rectangle);
		filledrect.cripped(area_);
		int t = Math::round(filledrect.top);
		int b = Math::round(filledrect.bottom);
		int l = Math::round(filledrect.left);
		int r = Math::round(filledrect.right);
		for(int y=t; y<=b; y++) {
			for(int x=l; x<=r; x++) {
				pix(x,y,col);
			}
		}
	}
	void Image::line(const double x1, const double y1, const double x2, const double y2, const Color &col) {
		int begin, end, tbegin, ax, ay;
		double obegin;
		if(x1-x2!=0) {
			double slope = (y1-y2)/(x1-x2);
			if(slope<1 && slope>-1) {
				if(x1<x2) {
					tbegin = begin = Math::round(x1);
					end =  Math::round(x2);
					obegin = y1;
				} else {
					tbegin = begin = Math::round(x2);
					end =  Math::round(x1);
					obegin = y2;
				}
				begin = ( begin<0 ? 0 : begin );
				end = ( end>width_-1 ? width_-1 : end );
				for(int x=begin; x<=end; x++) {
					ay = Math::round((x-tbegin)*slope + obegin);
					if(-1<x && x<width_ && -1 <ay && ay<height_ ) pix(x, ay, col);
				}
			} else {
				if(y1<y2) {
					tbegin = begin = Math::round(y1);
					end =  Math::round(y2);
					obegin = x1;
				} else {
					tbegin = begin = Math::round(y2);
					end =  Math::round(y1);
					obegin = x2;
				}
				begin = ( begin<0 ? 0 : begin );
				end = ( end>height_-1 ? height_-1 : end );
				for(int y=begin; y<=end; y++) {
					ax = Math::round((y-tbegin)/slope + obegin);
					if(-1<ax && ax<width_ && -1<y && y<height_ ) pix(ax, y, col);
				}
			}
		} else {
				if(y1<y2) {
					tbegin = begin = Math::round(y1);
					end =  Math::round(y2);
					obegin = x1;
				} else {
					tbegin = begin = Math::round(y2);
					end =  Math::round(y1);
					obegin = x2;
				}
				begin = ( begin<0 ? 0 : begin );
				end = ( end>height_-1 ? height_-1 : end );
				ax = Math::round(obegin);
				for(int y=begin; y<=end; y++) {
					if(-1<ax && ax<width_ && -1<y && y<height_ ) pix(ax, y, col);
				}
		}
	}
	void Image::line(const Point &po1, const Point &po2, const Color &col) {
		line(po1.x, po1.y, po2.x, po2.y, col);
	}
	void Image::ellipse(const Rectangle &rect, const Color &col) {
		double center_x = rect.getCenter().x;
		double center_y = rect.getCenter().y;
		double radius = rect.getHeight() / 2.0;
		double aspect_ratio = rect.getWidth() / rect.getHeight();
		double tmp;
		int l, t, r, b;
		t = ( rect.top < 0 ? 0 : Math::round(rect.top) );
		b = ( rect.bottom > height_-1 ? height_-1 : Math::round(rect.bottom) );
		for(int y=t; y<=b; y++) {
			tmp = ( (y-center_y)/radius );
			l = Math::round( center_x - aspect_ratio * ( radius * sqrt(1-(tmp*tmp)) ) );
			r = Math::round( ( center_x - l ) + center_x );
			l = ( l < 0 ? 0 : l );
			r = ( r > width_-1 ? width_-1 : r );
			for(int x=l; x<=r; x++) {
				pix(x,y,col);
			}
		}
	}
	//  obsolete
		void Image::oval(const Rectangle &rect, const Color &col) {
			ellipse(rect, col);
		}
		void Image::fillRect(const Rectangle &rectangle, const Color &col) {
			rect(rectangle, col);
		}
/*		void Image::msg(Letters &letters, int x, int y, const Color &col) {
			api_->drawLetters(letters, x, y,col.Red,col.Blue,col.Green,col.Alpha,0,0);
		}
*/

	Image& Image::draw(double left, double top, Drawable &target) {
		target.image(*this, left, top);
		return *this;
	}
	Image& Image::draw(Drawable &target)  {
		target.image(*this);
		return *this;
	}
	Image& Image::draw(double alpha, Drawable &target)  {
		target.image(*this, alpha);
		return *this;
	}
	// obsolete
		void Image::display(double x, double y) {
			draw(x, y);
		}
		void Image::display() {
			draw(targetarea_.left, targetarea_.top);
		}


	Image::PartialView::PartialView(Image *src, const Rectangle &d_source)
	{
		img = src;
		source = d_source;
		target.set(source.getWidth(), source.getHeight());
	}
	Image::PartialView& Image::PartialView::set(double width, double height)
	{
		target.set(width, height);
		return *this;
	}
	Image::PartialView::~PartialView()
	{
	}
	const Point Image::PartialView::getDatum() const
	{
		return target.getDatum();
	}
	Image::PartialView& Image::PartialView::setDatum(const Point &p)
	{
		target.setDatum(p);
		return *this;
	}
	Image::PartialView& Image::PartialView::centering(const Point &p)
	{
		target.centering(p);
		return *this;
	}
	Image::PartialView& Image::PartialView::draw(Drawable &target_)
	{
		target_.image(*img, target, source);
		return *this;
	}
	Image::PartialView Image::operator ()(const Rectangle &view)
	{
		PartialView tmp(this, view);
		return tmp;
	}




	// get bitmap properties
	const void * Image::getBitmapPtr() const {
		if(!have_instance_) Exception(typeid(*this), "Memory Error", "The Image does not have bitmap instance.");
		switch(pixprec_) {
			case BYTE:
				return (void *)bitmapub_;
				break;
			case FLOAT:
				return (void *)bitmapf_;
				break;
			default:
				break;
		}
		return NULL;
	}


	// Coordinate
	const Point Image::getDatum() const {
		return targetarea_.getDatum();
	}
	Image& Image::setDatum(const Point& p) {
		targetarea_.setDatum(p);
		return *this;
	}
	Image& Image::centering(const Drawable& target) {
		targetarea_.centering(target);
		return *this;
	}
	Image& Image::centering(const Figure& fig) {
		targetarea_.centering(fig);
		return *this;
	}
	Image& Image::centering(const Point& p) {
		targetarea_.centering(p);
		return *this;
	}
	Image& Image::centering(const double x, const double y, const double z) {
		targetarea_.centering(x, y, z);
		return *this;
	}
	Image& Image::move_to(const double x, const double y, const double z) {
		targetarea_.move_to(x, y, z);
		return *this;
	}
	Image& Image::shift(const double x, const double y, const double z) {
		targetarea_.shift(x, y, z);
		return *this;
	}
	int Image::getWidth() const {	return width_; }
	int Image::getHeight() const {	return height_; }
	double Image::getHcenter() const { return width_/2 - 0.5; }
	double Image::getVcenter() const { return height_/2 - 0.5; }
	const Point Image::getCenter() const {
		Point po(getHcenter(), getVcenter());
		return po;
	}
	double Image::getTop() const { return targetarea_.top; }
	double Image::getLeft() const { return targetarea_.left; }
	double Image::getBottom() const { return targetarea_.bottom; }
	double Image::getRight() const { return targetarea_.right; }


	float* Image::getFloatPrt() { return bitmapf_; }
	unsigned char* Image::getElementPtr() { return bitmapub_; }
	int Image::getPixBytes() { return pixBytes_; }
	int Image::getLineBytes() { return lineBytes_; }
	int Image::getBitmapBytes() { return bitmapBytes_; }
	int Image::getLineNumber() { return lineValue_; }
	int Image::getElementNumber() { return bitmapValue_; }
	const Image::PixelComponentsCnt Image::getComponentKind() const { return pixcomp_; }
	const Image::PixelComponentsPrecision Image::getPrecisionKind() const { return pixprec_; }

	//// Special Utilities

	void Image::to(const Rectangle &source__, Image &other, const Rectangle target__) const {
		Rectangle source = source__, target = target__;
		source.clipped_by(area_);
		target.clipped_by(other.area_);
		if(source.getWidth()!=target.getWidth() || source.getHeight()!=target.getHeight())
			throw new Exception("Image.to: source area does not match target area.");
		int sl = (int)source.getLeft(), st=(int)source.getTop(), tl=(int)target.getLeft(), tt=(int)target.getTop();
		for(int y=0; y<source.getHeight(); y++)
			for(int x=0; x<source.getWidth(); x++)
				other.pix_direct(x+tl,y+tt, getPix(x+sl,y+st));
	}
	void Image::to(const Rectangle &source__, Image &other) const {
		Rectangle source = source__;
		source.clipped_by(area_);
		other.release();
		other.set(source, pixcomp_, pixprec_);
		for(int y=0; y<other.getHeight(); y++)
			for(int x=0; x<other.getWidth(); x++)
				other.pix_direct(x,y, getPix((int)(source.getLeft())+x, (int)(source.getTop())+y));
/*
		int vbase = getHeight() - source.getTop();
		for(int i=0; i<other.getHeight(); i++) {
			memcpy(other.bitmapub_+(other.getHeight()-i-1)*(lineBytes_),
				   bitmapub_+(vbase-i-1)*(lineBytes_)+(size_t)(source.getLeft()),
				   pixBytes_*other.getWidth());
		}
*/	}



	void Image::load(std::string filename) {
		IMAGE_FORMATS::IMAGE_FORMAT *loader;
		if(have_instance_) {
			release();
		}
		std::string::size_type extention_index = filename.find_last_of('.');
		if(extention_index==std::string::npos) throw Exception(typeid(*this), "File Type Error", "Specified image type is not supported.*..");
		std::string file_extension = filename.substr(extention_index+1);
		if(file_extension=="png") {
			loader = new IMAGE_FORMATS::PNG_BRIDGE;
			loader->load(File::decodePath(filename).c_str(), this);
			delete loader;
#if 0
		} else if(file_extension=="jpg") {
			loader = new IMAGE_FORMATS::JPEG_BRIDGE;
			loader->load(File::decodePath(filename).c_str(), this);
			delete loader;
#endif
		} else {
			throw Exception(typeid(*this), "File Type Error", "Specified image type is not supported.");
		}
		switch(Color::getCalibrationMode()) {
			case Color::SOFTWARE_GAMMA_VALUE:
			case Color::SOFTWARE_TABLE:
				convertColorCalibration(true);
				break;
			default:
				break;
		}
	}
	void Image::load(const char * filename) {
		load(std::string(filename));
	}
	void Image::save(std::string filename) {
		IMAGE_FORMATS::IMAGE_FORMAT *loader;
		if(!have_instance_) {
			throw Exception(typeid(*this), "Memory Error", "Image does not have its instance.");
		}

		Color col;
		double r, g, b, a;
		Image *tmp;
		switch(Color::getCalibrationMode()) {
			case Color::SOFTWARE_GAMMA_VALUE:
			case Color::SOFTWARE_TABLE:
				tmp = new Image(width_, height_, pixcomp_, pixprec_);
				for(int y=0; y<height_; y++) {
					for(int x=0; x<width_; x++) {
						col = getPix(x, y);
						col.get(r, g, b, a);
						col.Red = r;
						col.Green = g;
						col.Blue = b;
						col.Alpha = a;
						tmp->pix_direct(x, y, col);
					}
				}
				break;
			default:
				tmp = this;
				break;
		}

		std::string::size_type extention_index = filename.find_last_of('.');
		if(extention_index==std::string::npos) throw Exception(typeid(*this), "File Type Error", "Specified image type is not supported.");
		std::string file_extension = filename.substr(extention_index+1);
		if(file_extension=="png") {
			loader = new IMAGE_FORMATS::PNG_BRIDGE;
			loader->save(File::decodePath(filename).c_str(), tmp);
			delete loader;
		} else {
			throw Exception(typeid(*this), "File Type Error", "Specified image type is not supported.");
		}
		if(tmp != this) delete tmp;
	}
	void Image::save(const char * filename) {
		save(std::string(filename));
	}

	void Image::from(const Matrix &mtx) {
		Color colour;
		if(getWidth()!=mtx.getCols() || getHeight()!=mtx.getRows() || pixcomp_!=Image::GRAY) {
			//if (have_instance_==false) set(mtx.getCols(),mtx.getRows(), GRAY);
			//else throw Exception(typeid(*this), "Memory Error", "Image has been allocated.");
			release();
			set(mtx.getCols(),mtx.getRows(), GRAY);
		}
		int rowmax = mtx.getRows();
		int colmax = mtx.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				//colour.set(mtx(row,col));
				pix_direct(col-1,row-1,mtx(row,col));
			}
		}
	}
	void Image::from(const Matrix &r, const Matrix &g, const Matrix &b) {
		Color colour;
		if(r.getRows()!=g.getRows() || r.getRows()!=b.getRows()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(r.getCols()!=g.getCols() || r.getCols()!=b.getCols()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(getWidth()!=r.getCols() || getHeight()!=r.getRows() || pixcomp_!=Image::RGB) {
			//if (have_instance_==false) set(r.getCols(),r.getRows(), RGB);
			//else throw Exception(typeid(*this), "Memory Error", "Image has been allocated.");
			release();
			set(r.getCols(),r.getRows(), RGB);
		}
		int rowmax = r.getRows();
		int colmax = r.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				//colour.set(r(row,col), g(row,col), b(row,col));
				pix_direct(col-1,row-1,r(row,col), g(row,col), b(row,col),1.0);
			}
		}
	}
	void Image::from(const Matrix &r, const Matrix &g, const Matrix &b, const Matrix &a) {
		Color colour;
		if(r.getRows()!=g.getRows() || r.getRows()!=b.getRows() || r.getRows()!=a.getRows()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(r.getCols()!=g.getCols() || r.getCols()!=b.getCols() || r.getCols()!=a.getCols()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(getWidth()!=r.getCols() || getHeight()!=r.getRows() || pixcomp_!=Image::RGBA) {
			//if (have_instance_==false) set(r.getCols(),r.getRows(), RGBA);
			//else throw Exception(typeid(*this), "Memory Error", "Image has been allocated.");
			release();
			set(r.getCols(),r.getRows(), RGBA);
		}
		int rowmax = r.getRows();
		int colmax = r.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				//colour.set(r(row,col), g(row,col), b(row,col), a(row,col));
				pix_direct(col-1,row-1,r(row,col), g(row,col), b(row,col), a(row,col));
			}
		}
	}

	void Image::to(Matrix &mtx) const {
		to(Point(0,0), getWidth(), getHeight(), mtx);
	}
	void Image::to(Point p, const Rectangle &rect, Matrix &gray) const {
		to(Point(0,0), (int)rect.getWidth(), (int)rect.getHeight(), gray);
	}
	void Image::to(const Interval &horiz, const Interval &vert, Matrix &gray) const {
		to(Point(horiz.int_floor(0), vert.int_floor(0)),
		   horiz.int_ceil(getWidth())-horiz.int_floor(0), vert.int_ceil(getHeight())-vert.int_floor(0),
		   gray);
	}
	void Image::to(Point p, int width_, int height_, Matrix &mtx) const {
		Color colour;
		double r, g, b, a;
		double width  = width_+p.x<getWidth()   ? width_  : getWidth()-p.x;
		double height = height_+p.y<getHeight() ? height_ : getHeight()-p.y;
		if(pixcomp_!=GRAY) throw Exception(typeid(*this), "Argument Error", "Color types of Image(GRAY) and Matrix were not matched.");
		if(!have_instance_) throw Exception(typeid(*this), "Memory Error", "Image had not allocated.");
		if(width!=mtx.getCols() || height!=mtx.getRows()) { mtx.release(); mtx.set((int)height, (int)width); }
		int rowmax = mtx.getRows(), colmax = mtx.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				colour = getPix((int)(p.x)+col-1, (int)(p.y)+row-1);
				colour.get(r,g,b,a);
				mtx(row,col) = r;
			}
		}
	}

	void Image::to(Matrix &r, Matrix &g, Matrix &b) const {
		to(Point(0,0), getWidth(), getHeight(), r, g, b);
	}
	void Image::to(Point p, const Rectangle &rect, Matrix &r, Matrix &g, Matrix &b) const {
		to(Point(0,0), (int)rect.getWidth(), (int)rect.getHeight(), r, g, b);
	}
	void Image::to(const Interval &horiz, const Interval &vert, Matrix &r, Matrix &g, Matrix &b) const {
		to(Point(horiz.int_floor(0), vert.int_floor(0)),
		   horiz.int_ceil(getWidth())-horiz.int_floor(0), vert.int_ceil(getHeight())-vert.int_floor(0),
		   r, g, b);
	}
	void Image::to(Point p, int width_, int height_, Matrix &r, Matrix &g, Matrix &b) const {
		Color colour;
		double rr, gg, bb, aa;
		double width  = width_+p.x<getWidth()   ? width_  : getWidth()-p.x;
		double height = height_+p.y<getHeight() ? height_ : getHeight()-p.y;
		if(pixcomp_!=RGB) throw Exception(typeid(*this), "Argument Error", "Color types of Image(RGB) and Matrix were not matched.");
		if(!have_instance_) throw Exception(typeid(*this), "Memory Error", "Image had not allocated.");
		if(r.getRows()!=g.getRows() || r.getRows()!=b.getRows()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(r.getCols()!=g.getCols() || r.getCols()!=b.getCols()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(width!=r.getCols() || height!=r.getRows()) { r.release(); r.set((int)height, (int)width); }
		if(width!=g.getCols() || height!=g.getRows()) { g.release(); g.set((int)height, (int)width); }
		if(width!=b.getCols() || height!=b.getRows()) { b.release(); b.set((int)height, (int)width); }
		int rowmax = r.getRows(), colmax = r.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				colour = getPix((int)(p.x)+col-1, (int)(p.y)+row-1);
				colour.get(rr,gg,bb,aa);
				r(row,col) = rr;
				g(row,col) = gg;
				b(row,col) = bb;
			}
		}
	}

	void Image::to(Matrix &r, Matrix &g, Matrix &b, Matrix &a) const {
		to(Point(0,0), getWidth(), getHeight(), r, g, b, a);
	}
	void Image::to(Point p, const Rectangle &rect, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const {
		to(Point(0,0), (int)rect.getWidth(), (int)rect.getHeight(), r, g, b, a);
	}
	void Image::to(const Interval &horiz, const Interval &vert, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const {
		to(Point(horiz.int_floor(0), vert.int_floor(0)),
		   horiz.int_ceil(getWidth())-horiz.int_floor(0), vert.int_ceil(getHeight())-vert.int_floor(0),
		   r, g, b, a);
	}
	void Image::to(Point p, int width_, int height_, Matrix &r, Matrix &g, Matrix &b, Matrix &a) const {
		Color colour;
		double rr, gg, bb, aa;
		double width  = width_+p.x<getWidth()   ? width_  : getWidth()-p.x;
		double height = height_+p.y<getHeight() ? height_ : getHeight()-p.y;
		if(pixcomp_!=RGBA) throw Exception(typeid(*this), "Argument Error", "Color types of Image(RGBA) and Matrix were not matched.");
		if(!have_instance_) throw Exception(typeid(*this), "Memory Error", "Image had not allocated.");
		if(r.getRows()!=g.getRows() || r.getRows()!=b.getRows() || r.getRows()!=a.getRows()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(r.getCols()!=g.getCols() || r.getCols()!=b.getCols() || r.getCols()!=a.getCols()) throw Exception(typeid(*this), "Format Error", "Matrix sizes are defferent between each color.");
		if(width!=r.getCols() || height!=r.getRows()) { r.release(); r.set((int)height, (int)width); }
		if(width!=g.getCols() || height!=g.getRows()) { g.release(); g.set((int)height, (int)width); }
		if(width!=b.getCols() || height!=b.getRows()) { b.release(); b.set((int)height, (int)width); }
		if(width!=a.getCols() || height!=a.getRows()) { a.release(); a.set((int)height, (int)width); }
		int rowmax = r.getRows(), colmax = r.getCols();
		for(int row=1; row<=rowmax; row++) {
			for(int col=1; col<=colmax; col++) {
				colour = getPix((int)(p.x)+col-1, (int)(p.y)+row-1);
				colour.get(rr,gg,bb,aa);
				r(row,col) =  rr;
				g(row,col) =  gg;
				b(row,col) =  bb;
				a(row,col) =  aa;
			}
		}
	}

		//test
		Image & Image::operator -=(Image &rhs) {
			if(pixcomp_!=rhs.pixcomp_) throw Exception(typeid(*this), "Format Error", "Difference must be resolved between same format Images.");
			if(pixprec_!=rhs.pixprec_) throw Exception(typeid(*this), "Format Error", "Difference must be resolved between same format Images.");
			if(width_!=rhs.width_) throw Exception(typeid(*this), "Format Error", "Difference must be resolved between same format Images.");
			if(height_!=rhs.height_) throw Exception(typeid(*this), "Format Error", "Difference must be resolved between same format Images.");

			unsigned char *pb=bitmapub_, *qb=rhs.bitmapub_;
			float *pf=bitmapf_, *qf=bitmapf_;

			switch(pixprec_) {
				case BYTE:
					for(int i=0; i<bitmapValue_; i++) {
						pb[i] -= qb[i];
						if(pb[i]<0) pb[i] = -pb[i];
						if(pb[i]>15) pb[i]=256; else pb[i]*=16;
					}
					break;
				case FLOAT:
					for(int i=0; i<bitmapValue_; i++) {
						pf[i] -= qf[i];
						if(pf[i]<0) pf[i] = -pf[i];
						if(pf[i]>15) pf[i]=256; else pf[i]*=16;
					}
					break;
			}
			return *this;
		}



namespace IMAGE_FORMATS {
	IMAGE_FORMAT::~IMAGE_FORMAT() {
	}
	void IMAGE_FORMAT::readTargetMemoryAlignment(Image *target) {
		target_bitmap_ub_ = target->bitmapub_;
		target_bytes_per_line_ = target->lineBytes_;
		pix_components_ = target->pixcomp_;
		pix_precision_ = target->pixprec_;
	}
}

}	/*	<- namespace Psycholops 	*/

