#include <map>
#include <functional>
#include <iostream>
#include <fstream>
#include <math.h>

#include "ogTypes.h"
#include "ogEdgeTable.h"
#include "ogPixCon.h"
#include "ogPixelFmt.h"
#include "objgfx.h"

const static ogRGBA8 DEFAULT_PALETTE[256] =
	{{0,   0,   0,   255},       // 0
	 {0,   0,   170, 255},
	 {0,   170, 0,   255},
	 {0,   170, 170, 255},   // 3
	 {170, 0,   0,   255},
	 {170, 0,   170, 255},
	 {170, 85,  0,   255},
	 {170, 170, 170, 255}, // 7
	 {85,  85,  85,  255},
	 {85,  85,  255, 255},
	 {85,  255, 85,  255},
	 {85,  255, 255, 255},  // 11
	 {255, 85,  85,  255},
	 {255, 85,  255, 255},
	 {255, 255, 85,  255},
	 {255, 255, 255, 255}, //15
	 {16,  16,  16,  255},    // 16
	 {32,  32,  32,  255},
	 {48,  48,  48,  255},
	 {64,  64,  64,  255},
	 {80,  80,  80,  255},
	 {96,  96,  96,  255},
	 {112, 112, 112, 255},
	 {128, 128, 128, 255},
	 {144, 144, 144, 255},
	 {160, 160, 160, 255},
	 {176, 176, 176, 255},
	 {192, 192, 192, 255},
	 {208, 208, 208, 255},
	 {224, 224, 224, 255},
	 {240, 240, 240, 255},
	 {255, 255, 255, 255}, //31
	 {59,  0,   0,   255},      // 32
	 {79,  0,   0,   255},
	 {103, 0,   0,   255},
	 {123, 0,   0,   255},
	 {143, 7,   7,   255},
	 {167, 7,   7,   255},
	 {187, 11,  11,  255},
	 {211, 15,  15,  255},
	 {231, 19,  19,  255},
	 {255, 27,  27,  255},
	 {255, 59,  59,  255},
	 {255, 91,  91,  255},
	 {255, 119, 119, 255},
	 {255, 151, 151, 255},
	 {255, 183, 183, 255},
	 {255, 215, 215, 255},
	 {55,  55,  0,   255},   // 48
	 {71,  71,  0,   255},
	 {87,  87,  0,   255},
	 {103, 103, 7,   255},
	 {119, 119, 7,   255},
	 {135, 135, 11,  255},
	 {155, 155, 19,  255},
	 {171, 171, 23,  255},
	 {187, 187, 31,  255},
	 {203, 203, 35,  255},
	 {219, 219, 43,  255},
	 {239, 239, 59,  255},
	 {255, 255, 63,  255},
	 {255, 255, 127, 255},
	 {255, 255, 187, 255},
	 {255, 255, 255, 255},
	 {0,   43,  0,   255},   // 64
	 {0,   63,  0,   255},
	 {0,   83,  0,   255},
	 {0,   103, 0,   255},
	 {7,   127, 7,   255},
	 {7,   147, 7,   255},
	 {11,  167, 11,  255},
	 {15,  187, 15,  255},
	 {19,  211, 19,  255},
	 {27,  231, 27,  255},
	 {59,  235, 59,  255},
	 {91,  239, 91,  255},
	 {127, 239, 127, 255},
	 {159, 243, 159, 255},
	 {195, 247, 195, 255},
	 {231, 251, 231, 255},
	 {0,   55,  55,  255},   // 80
	 {0,   71,  71,  255},
	 {0,   87,  87,  255},
	 {7,   103, 103, 255},
	 {7,   119, 119, 255},
	 {11,  135, 135, 255},
	 {19,  155, 155, 255},
	 {23,  171, 171, 255},
	 {31,  187, 187, 255},
	 {35,  203, 203, 255},
	 {43,  219, 219, 255},
	 {51,  235, 235, 255},
	 {63,  255, 255, 255},
	 {127, 255, 255, 255},
	 {187, 255, 255, 255},
	 {255, 255, 255, 255},
	 {15,  15,  55,  255},   // 96
	 {19,  19,  79,  255},
	 {27,  27,  103, 255},
	 {31,  31,  127, 255},
	 {35,  35,  155, 255},
	 {39,  39,  179, 255},
	 {43,  43,  203, 255},
	 {47,  47,  227, 255},
	 {51,  51,  255, 255},
	 {71,  71,  255, 255},
	 {91,  91,  255, 255},
	 {111, 111, 255, 255},
	 {131, 131, 255, 255},
	 {151, 151, 255, 255},
	 {175, 175, 255, 255},
	 {195, 195, 255, 255},
	 {59,  51,  59,  255},   // 112
	 {79,  63,  79,  255},
	 {103, 71,  103, 255},
	 {123, 75,  123, 255},
	 {143, 75,  143, 255},
	 {167, 71,  167, 255},
	 {187, 67,  187, 255},
	 {211, 55,  211, 255},
	 {231, 43,  231, 255},
	 {255, 27,  255, 255},
	 {255, 59,  255, 255},
	 {255, 91,  255, 255},
	 {255, 119, 255, 255},
	 {255, 151, 255, 255},
	 {255, 183, 255, 255},
	 {255, 215, 255, 255},
	 {59,  51,  59,  255},   // 128
	 {71,  59,  71,  255},
	 {83,  71,  83,  255},
	 {95,  83,  95,  255},
	 {111, 95,  111, 255},
	 {123, 103, 123, 255},
	 {135, 115, 135, 255},
	 {147, 127, 147, 255},
	 {163, 139, 163, 255},
	 {175, 151, 175, 255},
	 {187, 159, 187, 255},
	 {203, 171, 203, 255},
	 {215, 183, 215, 255},
	 {227, 191, 227, 255},
	 {239, 203, 239, 255},
	 {255, 215, 255, 255},
	 {55,  27,  27,  255},   // 144
	 {71,  35,  35,  255},
	 {91,  43,  43,  255},
	 {107, 55,  55,  255},
	 {127, 67,  67,  255},
	 {143, 75,  75,  255},
	 {163, 87,  87,  255},
	 {179, 99,  99,  255},
	 {199, 111, 111, 255},
	 {203, 127, 127, 255},
	 {211, 139, 139, 255},
	 {219, 159, 159, 255},
	 {223, 175, 175, 255},
	 {231, 191, 191, 255},
	 {239, 211, 211, 255},
	 {247, 231, 231, 255},
	 {91,  63,  27,  255},   // 160
	 {111, 75,  31,  255},
	 {127, 87,  39,  255},
	 {147, 103, 43,  255},
	 {167, 115, 51,  255},
	 {187, 127, 55,  255},
	 {207, 139, 63,  255},
	 {227, 155, 67,  255},
	 {247, 167, 75,  255},
	 {247, 175, 95,  255},
	 {247, 183, 119, 255},
	 {247, 195, 139, 255},
	 {247, 203, 159, 255},
	 {247, 215, 183, 255},
	 {247, 227, 203, 255},
	 {251, 239, 227, 255},
	 {63,  63,  31,  255},   // 176
	 {75,  75,  35,  255},
	 {87,  87,  43,  255},
	 {99,  99,  51,  255},
	 {115, 115, 55,  255},
	 {127, 127, 63,  255},
	 {139, 139, 67,  255},
	 {151, 151, 75,  255},
	 {167, 167, 83,  255},
	 {175, 175, 95,  255},
	 {183, 183, 107, 255},
	 {191, 191, 123, 255},
	 {203, 203, 139, 255},
	 {211, 211, 159, 255},
	 {219, 219, 175, 255},
	 {231, 231, 195, 255},
	 {27,  59,  47,  255},   // 192
	 {31,  75,  59,  255},
	 {39,  87,  67,  255},
	 {47,  103, 79,  255},
	 {55,  119, 91,  255},
	 {59,  135, 99,  255},
	 {67,  151, 111, 255},
	 {71,  167, 119, 255},
	 {79,  183, 127, 255},
	 {87,  199, 139, 255},
	 {91,  215, 147, 255},
	 {99,  231, 155, 255},
	 {127, 235, 183, 255},
	 {163, 239, 211, 255},
	 {195, 243, 231, 255},
	 {231, 251, 247, 255},
	 {23,  55,  55,  255},   // 208
	 {31,  71,  71,  255},
	 {39,  87,  87,  255},
	 {47,  103, 103, 255},
	 {55,  119, 119, 255},
	 {67,  139, 139, 255},
	 {75,  155, 155, 255},
	 {87,  171, 171, 255},
	 {99,  187, 187, 255},
	 {111, 203, 203, 255},
	 {123, 223, 223, 255},
	 {143, 227, 227, 255},
	 {163, 231, 231, 255},
	 {183, 235, 235, 255},
	 {203, 239, 239, 255},
	 {227, 247, 247, 255},
	 {39,  39,  79,  255},   // 224
	 {47,  47,  91,  255},
	 {55,  55,  107, 255},
	 {63,  63,  123, 255},
	 {71,  71,  139, 255},
	 {79,  79,  151, 255},
	 {87,  87,  167, 255},
	 {99,  99,  183, 255},
	 {107, 107, 199, 255},
	 {123, 123, 203, 255},
	 {139, 139, 211, 255},
	 {155, 155, 219, 255},
	 {171, 171, 223, 255},
	 {187, 187, 231, 255},
	 {207, 207, 239, 255},
	 {227, 227, 247, 255},
	 {63,  27,  63,  255},   // 240
	 {75,  31,  75,  255},
	 {91,  39,  91,  255},
	 {103, 47,  103, 255},
	 {119, 51,  119, 255},
	 {131, 59,  131, 255},
	 {147, 67,  147, 255},
	 {163, 75,  163, 255},
	 {175, 83,  175, 255},
	 {191, 91,  191, 255},
	 {199, 107, 199, 255},
	 {207, 127, 207, 255},
	 {215, 147, 215, 255},
	 {223, 171, 223, 255},
	 {231, 195, 231, 255},
	 {243, 219, 243, 255}};

const double ogSurface::INTENSITIES[32] = {
	1.0,             // 0
	0.984250984251,  // 1
	0.968245836552,  // 2
	0.951971638233,  // 3
	0.935414346693,  // 4
	0.938558653544,  // 5
	0.901387818866,  // 6
	0.883883476483,  // 7
	0.866025403784,  // 8
	0.847791247891,  // 9
	0.829156197589,  // 10
	0.810092587301,  // 11
	0.790569415042,  // 12
	0.770551750371,  // 13
	0.75,            // 14
	0.728868986856,  // 15
	0.707106781187,  // 16
	0.684653196881,  // 17
	0.661437827766,  // 18
	0.637377439199,  // 19
	0.612372435696,  // 20
	0.586301969978,  // 21
	0.559016994375,  // 22
	0.53033008589,   // 23
	0.5,             // 24
	0.467707173347,  // 25
	0.433012701892,  // 26
	0.395284707521,  // 27
	0.353553390593,  // 28
	0.306186217848,  // 29
	0.25,            // 30
	0.176776695297   // 31
}; // INTENSITIES[]

// ogSurface constructor
ogSurface::ogSurface(void) 
{
	version		= ogVERSION;

	dataState	= ogNone;
	buffer		= NULL;
	lineOfs		= NULL;
	pal			= NULL;
	attributes	= NULL;
	xRes		= 0;
	yRes		= 0;
	maxX		= 0;
	maxY		= 0;
	bSize		= 0;
	lSize		= 0;
	BPP			= 0;
	bytesPerPix	= 0;
	pixFmtID	= 0;
	redShifter	= 0;
	greenShifter= 0;
	blueShifter	= 0;
	alphaShifter= 0;
	redFieldPosition   = 0;
	greenFieldPosition = 0;
	blueFieldPosition  = 0;
	alphaFieldPosition = 0;
	alphaMasker = 0;
	lastError	= ogOK;

	// Set these function pointers to do nothing (but not crash)
	// in case somebody actually calls them.
	setPixel = ([] (void * ptr, uint32_t p) -> void { });
	getPixel = ([] (void * ptr) -> uint32_t { return 0; });
} // ogSurface::ogSurface()


void ogSurface::AARawLine(uint32_t x1, uInt32 y1, uInt32 x2, uInt32 y2, uInt32 colour) 
{
	/*
	* aaRawLine
	*
	* private method
	*
	* draws an unclipped anti-aliased line from (x1,y1) to (x2,y2) using colour
	*
	*/
	uint32_t erradj, erracc;
	uint32_t erracctmp, intshift, wgt, wgtCompMask;
	int32  dx, dy, tmp, xDir, i;
	uInt8 r, g, b, a;
	uint32_t alphas[32];
	bool oldBlending;

	if (y1 > y2) 
	{
		tmp= y1;
		y1 = y2;
		y2 = tmp;

		tmp= x1;
		x1 = x2;
		x2 = tmp;
	} // if

	dx = (x2-x1);
	if (dx >= 0) xDir=1; else { dx = -dx; xDir=-1; }
	//  dx = abs(dx);
	dy = (y2 - y1);

	if (dy == 0) 
	{
		ogHLine(x1, x2, y1, colour);
		return;
	}

	if (dx == 0) 
	{
		ogVLine(x1, y1, y2, colour);
		return;
	}

	ogUnpack(colour, r, g, b, a);
	if (!ogIsBlending()) a = 255;

	for (i = 0; i < 32; i++) 
	{
		alphas[i] = static_cast<uint32_t>(INTENSITIES[i]*a + 0.5f);
	} // for

	oldBlending = ogSetBlending(true);

	RawSetPixel(x1, y1, r, g, b, a);

	// this is incomplete.. diagonal lines don't travel through the 
	// center of pixels exactly

	do {

		if (dx == dy) 
		{
			for (; dy != 0; dy--) 
			{
				x1 += xDir;
				++y1;
				RawSetPixel(x1, y1, r, g, b, a);
			} // for
			break;  // pop out to the bottom and restore the old blending state
		} // if dx==dy

		erracc = 0;
		intshift = 32-5;
		wgt = 12;
		wgtCompMask = 31;

		if (dy > dx) {
			/* y-major.  Calculate 32-bit fixed point fractional part of a pixel that
			* X advances every time Y advances 1 pixel, truncating the result so that
			* we won't overrun the endpoint along the X axis 
			*/
#ifndef __UBIXOS__
			erradj = ((uInt64) dx << 32) / (uInt64)dy;
#else
			asm volatile (  // fixed
				" xor %%eax, %%eax        \n"
				" div %%ecx              \n"
				: "=a" (erradj)
				: "d" (dx), "c" (dy)
				);
#endif

			while (--dy) {
				erracctmp = erracc;
				erracc += erradj;
				if (erracc <= erracctmp) x1 += xDir;
				y1++;     // y-major so always advance Y
				/* the nbits most significant bits of erracc give us the intensity
				*  weighting for this pixel, and the complement of the weighting for
				*  the paired pixel. 
				*/
				wgt = erracc >> intshift;
				ogSetPixel(x1, y1, r, g, b, alphas[wgt]);
				ogSetPixel(x1+xDir, y1, r, g, b, alphas[wgt ^ wgtCompMask]);
			} // while

		} else {

			/* x-major line.  Calculate 32-bit fixed-point fractional part of a pixel
			* that Y advances each time X advances 1 pixel, truncating the result so
			* that we won't overrun the endpoint along the X axis. 
			*/
#ifndef __UBIXOS__
			erradj = ((uInt64)dy << 32) / (uInt64)dx;
#else
			asm volatile(  // fixed
				" xor %%eax, %%eax        \n"
				" div %%ecx              \n"
				: "=a" (erradj)
				: "d" (dy), "c" (dx)
				);
#endif
			// draw all pixels other than the first and last 
			while (--dx) {
				erracctmp = erracc;
				erracc += erradj;
				if (erracc <= erracctmp) y1++; 
				x1 += xDir;                 // x-major so always advance X
				/* the nbits most significant bits of erracc give us the intensity
				* weighting for this pixel, and the complement of the weighting for
				* the paired pixel. 
				*/
				wgt = erracc >> intshift;
				ogSetPixel(x1, y1, r, g, b, alphas[wgt]);
				ogSetPixel(x1, y1+1, r, g, b, alphas[wgt ^ wgtCompMask]);
			} // while
		} // else
		RawSetPixel(x2, y2, r, g, b, alphas[wgt]);

	} while (false);

	ogSetBlending(oldBlending);
} // void ogSurface::AARawLine()

bool ogSurface::ClipLine(int32& x1, int32& y1, int32& x2, int32& y2) 
{
	/*
	*  clipLine()
	*
	*  private method 
	*
	*  clips a line to (0,0),(maxX,maxY); returns true if
	*  the line segment is in bounds, false if none of the line segment is
	*  on the screen.  Uses HJI's line clipping algorithm.
	*/

	int32 tx1, ty1, tx2, ty2;
	int32 outCode;
	uint32_t andResult, orResult;
	andResult = 15;
	orResult = 0;
	outCode = 0;
	if (x1 < 0) outCode+=8;
	if (x1 > (int32)maxX) outCode+=4;
	if (y1 < 0) outCode+=2;
	if (y1 > (int32)maxY) outCode++;

	andResult &= outCode;
	orResult |= outCode;
	outCode = 0;

	if (x2 < 0) outCode+=8;
	if (x2 > (int32)maxX) outCode+=4;
	if (y2 < 0) outCode+=2;
	if (y2 > (int32)maxY) outCode++;

	andResult &= outCode;
	orResult |= outCode;

	if (andResult > 0) return false;
	if (orResult == 0) return true;

	// some clipping is required here.  

	tx1 = x1;
	ty1 = y1;
	tx2 = x2;
	ty2 = y2;

	if (x1 < 0) 
	{
		ty1 = (x2*y1-x1*y2) / (x2-x1);
		tx1 = 0;
	} // if
	else
	{
		if (x2 < 0) 
		{
			ty2 = (x2*y1-x1*y2) / (x2-x1);
			tx2 = 0;
		} // elseif
	}

	if (x1 > (int32)maxX) 
	{
		ty1 = (y1*(x2-maxX)+y2*(maxX-x1)) / (x2-x1);
		tx1 = maxX;
	} // if
	else
	{
		if (x2 > (int32)maxX) 
		{
			ty2 = (y1*(x2-maxX)+y2*(maxX-x1)) / (x2-x1);
			tx2 = maxX;
		} // elseif
	}

	if (((ty1 < 0) && (ty2 < 0)) || 
		((ty1>(int32)maxY) && (ty2>(int32)maxY))) return false;

	if (ty1 < 0) 
	{
		tx1 = (x1*y2-x2*y1) / (y2-y1);
		ty1 = 0;
	} // if
	else
	{
		if (ty2 < 0) 
		{
			tx2 = (x1*y2-x2*y1) / (y2-y1);
			ty2 = 0;
		} // elseif 
	}

	if (ty1 > (int32)maxY) 
	{
		tx1 = (x1*(y2-maxY)+x2*(maxY-y1)) / (y2-y1);
		ty1 = maxY;
	} // if
	else
	{
		if (ty2 > (int32)maxY) 
		{
			tx2 = (x1*(y2-maxY)+x2*(maxY-y1)) / (y2-y1);
			ty2 = maxY;
		} // elseif 
	}

	if (((uint32_t)tx1 > maxX) || ((uInt32)tx2 > maxX)) return false;

	x1 = tx1;
	y1 = ty1;
	x2 = tx2;
	y2 = ty2;

	return true;
} // bool ogSurface::ClipLine()

uint32_t ogSurface::RawGetPixel(uInt32 x, uInt32 y) 
{
	//uInt8* ptr = (uInt8*)buffer + lineOfs[y];	// Pointer to pixel at (0,y).
	void * ptr = reinterpret_cast<void *>(buffer + lineOfs[y] + x*bytesPerPix);
	return getPixel(ptr);
#if 0
	uint32_t result = 0;
	uint32_t * buf32;
	uInt8 * buf24;
	uInt16 * buf16;
	uInt8 * buf8;

	switch (bytesPerPix) {
	case 4:
		buf32 = reinterpret_cast<uint32_t *>(buffer + lineOfs[y] + x*4);
		result = *buf32;
		//uint32_t * 
		//	uint32_t * buf = static_cast<uInt32 *>(buffer + lineOfs[y])
		_asm {
			mov		eax, this						;
			mov     edi, x							;
			mov     ebx, y							;

			shl		edi, 2							; // adjust for pixel size 
			mov     esi, [eax + ogSurface::lineOfs] ;
			add     edi, [esi + ebx*4]				;
			add     edi, [eax + ogSurface::buffer]	;

			mov     eax, [edi]						;
			mov		result, eax						;
		};
		break;
	case 3:
		buf24 = (buffer + lineOfs[y] + x);
		result = *buf24;
		break;
	case 2:
		buf16 = reinterpret_cast<uInt16 *>(buffer + lineOfs[y] + x*2);
		result = *buf16;
		break;
	case 1:
		buf8 = (buffer + lineOfs[y] + x);
		result = *buf8;
		break;
	} // switch
	return result;
#endif
} // uint32_t ogSurface::RawGetPixel()

// wu's double step line algorithm blatently borrowed from:
// http://www.edepot.com/linewu.html

void ogSurface::RawLine(uint32_t x1, uInt32 y1, uInt32 x2, uInt32 y2, uInt32 colour) 
{
	int32 dy = y2 - y1;
	int32 dx = x2 - x1;
	int32 stepx, stepy;

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

	RawSetPixel(x1, y1, colour);
	RawSetPixel(x2, y2, colour);

	if (dx > dy) 
	{
		int32 length = (dx - 1) >> 2;
		int32 extras = (dx - 1) & 3;
		int32 incr2 = (dy << 2) - (dx << 1);

		if (incr2 < 0) 
		{
			int32 c = dy << 1;
			int32 incr1 = c << 1;
			int32 d =  incr1 - dx;

			for (int32 i = 0; i < length; i++) 
			{
				x1 += stepx;
				x2 -= stepx;

				if (d < 0) 
				{                                      // Pattern:
					RawSetPixel(x1, y1, colour);                    //
					RawSetPixel(x1 += stepx, y1, colour);           //  x o o
					RawSetPixel(x2, y2, colour);                    //
					RawSetPixel(x2 -= stepx, y2, colour);

					d += incr1;
				} 
				else 
				{

					if (d < c) 
					{                                     // Pattern:
						RawSetPixel(x1, y1, colour);                   //      o
						RawSetPixel(x1 += stepx, y1 += stepy, colour); //  x o
						RawSetPixel(x2, y2, colour);                   //
						RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					} 
					else 
					{
						RawSetPixel(x1, y1 += stepy, colour);             // Pattern:
						RawSetPixel(x1 += stepx, y1, colour);             //    o o 
						RawSetPixel(x2, y2 -= stepy, colour);             //  x
						RawSetPixel(x2 -= stepx, y2, colour);             //
					} // else

					d += incr2;
				} // else
			} // for i

			if (extras > 0) 
			{

				if (d < 0) 
				{
					RawSetPixel(x1 += stepx, y1, colour);
					if (extras > 1) RawSetPixel(x1 += stepx, y1, colour);
					if (extras > 2) RawSetPixel(x2 -= stepx, y2, colour);
				} 
				else
					if (d < c) {
						RawSetPixel(x1 += stepx, y1, colour);
						if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 2) RawSetPixel(x2 -= stepx, y2, colour);
					} 
					else 
					{
						RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 1) RawSetPixel(x1 += stepx, y1, colour);
						if (extras > 2) RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					}
			} // if extras > 0
		} 
		else 
		{
			int32 c = (dy - dx) << 1;
			int32 incr1 = c << 1;
			int32 d =  incr1 + dx;

			for (int32 i = 0; i < length; i++) 
			{
				x1 += stepx;
				x2 -= stepx;

				if (d > 0) 
				{
					RawSetPixel(x1, y1 += stepy, colour);                   // Pattern:
					RawSetPixel(x1 += stepx, y1 += stepy, colour);          //      o
					RawSetPixel(x2, y2 -= stepy, colour);                   //    o
					RawSetPixel(x2 -= stepx, y2 -= stepy, colour);	  //  x
					d += incr1;
				} 
				else 
				{
					if (d < c) 
					{
						RawSetPixel(x1, y1, colour);                          // Pattern:
						RawSetPixel(x1 += stepx, y1 += stepy, colour);        //      o
						RawSetPixel(x2, y2, colour);                          //  x o
						RawSetPixel(x2 -= stepx, y2 -= stepy, colour);        //
					} 
					else 
					{
						RawSetPixel(x1, y1 += stepy, colour);                 // Pattern:
						RawSetPixel(x1 += stepx, y1, colour);                 //    o o
						RawSetPixel(x2, y2 -= stepy, colour);                 //  x
						RawSetPixel(x2 -= stepx, y2, colour);                 //
					}

					d += incr2;
				} // else
			} // for i

			if (extras > 0) 
			{
				if (d > 0) 
				{
					RawSetPixel(x1 += stepx, y1 += stepy, colour);
					if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
					if (extras > 2) RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
				} 
				else
					if (d < c) 
					{
						RawSetPixel(x1 += stepx, y1, colour);
						if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 2) RawSetPixel(x2 -= stepx, y2, colour);
					} 
					else 
					{

						RawSetPixel(x1 += stepx, y1 += stepy, colour);

						if (extras > 1) RawSetPixel(x1 += stepx, y1, colour);
						if (extras > 2) 
						{
							if (d > c)
								RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
							else
								RawSetPixel(x2 -= stepx, y2, colour);
						} // if extras > 2

					} // else
			} // if extras > 0
		} // else

	} 
	else 
	{

		int32 length = (dy - 1) >> 2;
		int32 extras = (dy - 1) & 3;
		int32 incr2 = (dx << 2) - (dy << 1);

		if (incr2 < 0) 
		{
			int32 c = dx << 1;
			int32 incr1 = c << 1;
			int32 d =  incr1 - dy;

			for (int32 i = 0; i < length; i++) 
			{
				y1 += stepy;
				y2 -= stepy;

				if (d < 0) {
					RawSetPixel(x1, y1, colour);
					RawSetPixel(x1, y1 += stepy, colour);
					RawSetPixel(x2, y2, colour);
					RawSetPixel(x2, y2 -= stepy, colour);

					d += incr1;
				} 
				else 
				{

					if (d < c) {
						RawSetPixel(x1, y1, colour);
						RawSetPixel(x1 += stepx, y1 += stepy, colour);
						RawSetPixel(x2, y2, colour);
						RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					} else {
						RawSetPixel(x1 += stepx, y1, colour);
						RawSetPixel(x1, y1 += stepy, colour);
						RawSetPixel(x2 -= stepx, y2, colour);
						RawSetPixel(x2, y2 -= stepy, colour);
					} // else

					d += incr2;
				} // else
			} // for i

			if (extras > 0) 
			{
				if (d < 0) 
				{
					RawSetPixel(x1, y1 += stepy, colour);
					if (extras > 1) RawSetPixel(x1, y1 += stepy, colour);
					if (extras > 2) RawSetPixel(x2, y2 -= stepy, colour);
				} 
				else
					if (d < c) {
						RawSetPixel(x1, y1 += stepy, colour);
						if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 2) RawSetPixel(x2, y2 -= stepy, colour);
					} else {
						RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 1) RawSetPixel(x1, y1 += stepy, colour);
						if (extras > 2) RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					} // else
			} // if extras > 0
		} 
		else 
		{
			int32 c = (dx - dy) << 1;
			int32 incr1 = c << 1;
			int32 d =  incr1 + dy;

			for (int32 i = 0; i < length; i++) 
			{
				y1 += stepy;
				y2 -= stepy;

				if (d > 0) 
				{
					RawSetPixel(x1 += stepx, y1, colour);
					RawSetPixel(x1 += stepx, y1 += stepy, colour);
					RawSetPixel(x2 -= stepx, y2, colour);
					RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					d += incr1;
				} 
				else 
				{
					if (d < c) {
						RawSetPixel(x1, y1, colour);
						RawSetPixel(x1 += stepx, y1 += stepy, colour);
						RawSetPixel(x2, y2, colour);
						RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
					} else {
						RawSetPixel(x1 += stepx, y1, colour);
						RawSetPixel(x1, y1 += stepy, colour);
						RawSetPixel(x2 -= stepx, y2, colour);
						RawSetPixel(x2, y2 -= stepy, colour);
					} // else
					d += incr2;
				} // else
			} // for

			if (extras > 0) 
			{
				if (d > 0) 
				{
					RawSetPixel(x1 += stepx, y1 += stepy, colour);
					if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
					if (extras > 2) RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
				} 
				else
					if (d < c) 
					{
						RawSetPixel(x1, y1 += stepy, colour);
						if (extras > 1) RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 2) RawSetPixel(x2, y2 -= stepy, colour);
					} 
					else 
					{
						RawSetPixel(x1 += stepx, y1 += stepy, colour);
						if (extras > 1) RawSetPixel(x1, y1 += stepy, colour);
						if (extras > 2) 
						{
							if (d > c)
								RawSetPixel(x2 -= stepx, y2 -= stepy, colour);
							else
								RawSetPixel(x2, y2 -= stepy, colour);
						} // if extras > 2
					} // else
			} // if extras > 0
		} // else
	} // else
} // void ogSurface::RawLine()

void ogSurface::RawSetPixel(uint32_t x, uInt32 y, uInt32 colour) 
{
	if (ogIsBlending()) 
	{
		uInt8 sR, sG, sB, sA;
		uInt8 dR, dG, dB;
		ogUnpack(colour, sR, sG, sB, sA);
		if (sA == 0) return;
		if (sA != 255) 
		{
			uint32_t inverseA = 255 - sA;
			ogUnpack(RawGetPixel(x, y), dR, dG, dB);
			uint32_t newR = (dR * inverseA + sR * sA) >> 8;
			uint32_t newG = (dG * inverseA + sG * sA) >> 8;
			uint32_t newB = (dB * inverseA + sB * sA) >> 8;
			//mji for gtk      colour = ogPack(newR, newG, newB, inverseA);
			colour = ogPack(newR, newG, newB, 255);
		}
	} // if

	void * ptr = reinterpret_cast<void *>(buffer + lineOfs[y] + x*bytesPerPix);
	setPixel(ptr, colour);
#if 0
	//ptr = static_cast<uInt8*>(buffer) + lineOfs[y];

	switch (bytesPerPix) {
	case 4:
		_asm {
			mov     eax, this						;
			mov     edi, [eax + ogSurface::buffer]	;
			mov     esi, [eax + ogSurface::lineOfs]	;
			mov     ebx, [y]						;
			mov     ecx, [x]						;

			// Calculate offset, prepare the pixel to be drawn 
			mov     edx, [esi + ebx * 4]
			nop										;
			add     edi, edx						;
			nop										;

			shl		ecx, 2	  ; //  adjust for pixel size
			add     edi, ecx  ;

			// Draw the pixel 
			mov     eax, [colour]					;
			mov     [edi], eax						;
		}; // asm
		break;
	case 3:
		_asm {
			mov     eax, this						;
			mov     edi, [eax + ogSurface::buffer]	;
			mov     esi, [eax + ogSurface::lineOfs]	;
			mov     ebx, [y]						;
			mov     ecx, [x]						;

			// Calculate offset, prepare the pixel to be drawn 
			mov     edx, [esi + ebx * 4]			;
			lea     ecx, [ecx *2 + ecx]				;
			add     edi, edx						;
			add     edi, ecx						; //  adjust for pixel size 

			// Draw the pixel 
			mov     ax, word ptr [colour]			;
			mov     bl, byte ptr [colour+2]			;
			mov     [edi], ax						;
			mov     [edi+2], bl						;
		}; //
		break;
	case 2:
		_asm {
			mov     eax, this						;
			mov     edi, [eax + ogSurface::buffer]	;
			mov     esi, [eax + ogSurface::lineOfs]	;
			mov     ebx, [y]						;
			mov     ecx, [x]						;

			// Calculate offset, prepare the pixel to be drawn 
			mov     edx, [esi + ebx * 4]			;
			add     edi, edx						;
			add     ecx, ecx						; // adjust for pixel size 
			mov     ax, word ptr [colour]			;
			add     edi, ecx						;

			// Draw the pixel 
			mov     [edi], ax						;
		}; // asm
	case 1:
		_asm {
			mov     eax, this						;
			mov     edi, [eax + ogSurface::buffer]	;
			mov     esi, [eax + ogSurface::lineOfs]	;
			mov     ebx, [y]						;
			mov     ecx, [x]						;

			// Calculate offset, prepare the pixel to be drawn 
			mov     edx, [esi + ebx * 4]			;
			nop										;
			add     edi, edx						;
			nop										;
			mov     al, byte ptr [colour]			;
			add     edi, ecx						;

			// Draw the pixel 
			mov     [edi], al						;
		};
		break;
	} // switch
#endif
} // void ogSurface::RawSetPixel()

void ogSurface::RawSetPixel(uint32_t x, uInt32 y, uInt8 r, uInt8 g, uInt8 b, uInt8 a) 
{
	uint32_t newR, newG, newB, inverseA;
	uInt8 dR, dG, dB;
	uint32_t colour;

	do {
		if (ogIsBlending()) {
			if (a == 0) return;
			if (a == 255) {
				colour = ogPack(r, g, b, a);
				break;
			} // if a == 255

			inverseA = 255 - a;
			ogUnpack(RawGetPixel(x, y), dR, dG, dB);
			newR = (dR * inverseA + r * a) >> 8;
			newG = (dG * inverseA + g * a) >> 8;
			newB = (dB * inverseA + b * a) >> 8;
			//mji for gtk      colour = ogPack(newR, newG, newB, inverseA);
			colour = ogPack(newR, newG, newB, 255);
		} else colour = ogPack(r, g, b, a);
	} while (false);

	void * ptr = reinterpret_cast<void *>(buffer + lineOfs[y] + x*bytesPerPix);
	setPixel(ptr, colour);

} // void ogSurface::RawSetPixel()


bool ogSurface::ogAlias(ogSurface& src, uint32_t x1, uInt32 y1, uInt32 x2, uInt32 y2) 
{
	uint32_t tmp;

	if (dataState == ogOwner) 
	{ 
		ogSetLastError(ogAlreadyOwner);
		return false;
	} // if

	if (x2 < x1) 
	{
		tmp= x2;
		x2 = x1;
		x1 = tmp;
	} // if

	if (y2 < y1) 
	{
		tmp= y2;
		y2 = y1;
		y1 = tmp;
	} // if 

	maxX = (x2-x1);
	maxY = (y2-y1);

	dataState = ogAliasing;

	bSize = 0;
	lSize = 0;

	owner = &src;
	buffer =(reinterpret_cast<uInt8*>(src.buffer)+x1*(src.bytesPerPix));
	lineOfs=&src.lineOfs[y1];
	attributes = src.attributes;

	pal  = src.pal;
	xRes = src.xRes;
	yRes = src.yRes;
	BPP  = src.BPP;
	bytesPerPix = src.bytesPerPix;
	pixFmtID = src.pixFmtID;

	// For 8bpp modes the next part doesn't matter
	redFieldPosition   = src.redFieldPosition;
	greenFieldPosition = src.greenFieldPosition;
	blueFieldPosition  = src.blueFieldPosition;
	alphaFieldPosition = src.alphaFieldPosition;
	// The next part is only used by 15/16bpp
	redShifter   = src.redShifter;
	greenShifter = src.greenShifter;
	blueShifter  = src.blueShifter;
	alphaShifter = src.alphaShifter;

	alphaMasker = src.alphaMasker;

	// Use the current pixel functions
	setPixel = src.setPixel;
	getPixel = src.getPixel;

	return true;
} // bool ogSurface::ogAlias()

void ogSurface::ogArc(int32 xCenter, int32 yCenter, uint32_t radius, 
					  uint32_t sAngle, uInt32 eAngle, uInt32 colour) 
{
	int32 p;
	uint32_t x, y, tmp;
	double alpha;

	if (radius == 0) 
	{
		ogSetPixel(xCenter, yCenter, colour);
		return;
	} // if

	sAngle %= 361;
	eAngle %= 361;

	if (sAngle > eAngle) {
		tmp = sAngle;
		sAngle = eAngle;
		eAngle = tmp;
	} // if

	x = 0;
	y = radius;
	p = 3-2*radius;

	while (x <= y) 
	{
		alpha = (180.0/3.14159265358979)*atan((double)x/(double)y);

		if ((alpha >= sAngle) && (alpha <= eAngle))
			ogSetPixel(xCenter-x, yCenter-y, colour);

		if ((90-alpha >= sAngle) && (90-alpha <= eAngle))
			ogSetPixel(xCenter-y, yCenter-x, colour);

		if ((90+alpha >= sAngle) && (90+alpha <= eAngle))
			ogSetPixel(xCenter-y, yCenter+x, colour);

		if ((180-alpha >= sAngle) && (180-alpha <= eAngle))
			ogSetPixel(xCenter-x, yCenter+y, colour);

		if ((180+alpha >= sAngle) && (180+alpha <= eAngle))
			ogSetPixel(xCenter+x, yCenter+y, colour);

		if ((270-alpha >= sAngle) && (270-alpha <= eAngle))
			ogSetPixel(xCenter+y, yCenter+x, colour);

		if ((270+alpha >= sAngle) && (270+alpha <= eAngle))
			ogSetPixel(xCenter+y, yCenter-x, colour);

		if ((360-alpha >= sAngle) && (360-alpha <= eAngle))
			ogSetPixel(xCenter+x, yCenter-y, colour);

		if (p < 0)
			p += 4*x+6;
		else {
			p += 4*(x-y)+10;
			--y;
		} // else
		++x;
	} // while
} // void ogSurface::ogArc()

bool ogSurface::ogAvail()
{
	return ((buffer != NULL) && (lineOfs != NULL));
} // bool ogSurface::ogAvail()

void ogSurface::ogBSpline(uint32_t numPoints, ogPoint2d* points, uInt32 segments, uInt32 colour) 
{
	if (points == NULL) return;

	auto calculate = ([] (double mu, int32 p0, int32 p1, int32 p2, int32 p3) 
	{
		double mu2, mu3;
		mu2 = mu*mu;
		mu3 = mu2*mu;
		return (int32)(0.5f+(1.0/6.0)*(mu3*(-p0+3.0*p1-3.0*p2+p3)+
			mu2*(3.0*p0-6.0*p1+3.0*p2)+
			mu*(-3.0*p0+3.0*p2)+(p0+4.0*p1+p2)));
	}); // calculate

	if ((numPoints < 4) || (numPoints > 255) || (segments == 0)) return;

	double mu, mudelta;
	int32 x1, y1, x2, y2;
	uint32_t n, h;
	mudelta = 1.0/segments;
	for (n=3; n<numPoints; n++) {
		mu = 0.0;
		x1=calculate(mu,points[n-3].x,points[n-2].x, points[n-1].x,points[n].x);
		y1=calculate(mu,points[n-3].y,points[n-2].y, points[n-1].y,points[n].y);
		mu += mudelta;

		for (h=0; h<segments; h++) {
			x2=calculate(mu,points[n-3].x,points[n-2].x, points[n-1].x,points[n].x);
			y2=calculate(mu,points[n-3].y,points[n-2].y, points[n-1].y,points[n].y);
			ogLine(x1, y1, x2, y2, colour);
			mu += mudelta;
			x1 = x2;
			y1 = y2;
		} // for h

	} // for n
} // void ogSurface::ogBSpline()


void ogSurface::ogCircle(int32 xCenter, int32 yCenter, uint32_t radius, uInt32 colour) 
{
	int32 x, y, d;

	x = 0;
	y = radius;
	d = 2*(1-radius);
	while (y >= 0) 
	{
		ogSetPixel(xCenter+x, yCenter+y, colour);
		ogSetPixel(xCenter+x, yCenter-y, colour);
		ogSetPixel(xCenter-x, yCenter+y, colour);
		ogSetPixel(xCenter-x, yCenter-y, colour);

		if (d + y > 0) {
			--y;
			d -= 2*y+1;
		} // if

		if (x > d) {
			++x;
			d += 2*x+1;
		} // if
	} // while
} // void ogSurface::ogCircle()

void ogSurface::ogClear(uint32_t colour) 
{
	if (!ogAvail()) return;

	if (ogIsBlending()) 
	{
		uInt8 r, g, b, a;
		ogUnpack(colour, r, g, b, a);
		if (a == 0) return;
		if (a != 255) 
		{
			for (uint32_t yy = 0; yy <= maxY; yy++) 
				for (uint32_t xx = 0; xx <= maxX; xx++) 
					RawSetPixel(xx, yy, r, g, b, a);
			return;
		} // if
	} // if blending

	for (uint32_t yy = 0; yy <= maxY; yy++) 
		for (uint32_t xx = 0; xx <= maxX; xx++) 
			RawSetPixel(xx, yy, colour);
} // void ogSurface::ogClear()

void ogSurface::ogClear()
{
	ogClear(ogGetTransparentColor());
} // void ogSurface::ogClear()

bool ogSurface::ogClone(ogSurface& src) 
{
	ogPixelFmt pixFmt;

	if (src.dataState == ogNone) 
	{
		ogSetLastError(ogNoSurface);
		return false;
	} // if

	src.ogGetPixFmt(pixFmt);

	if (!ogCreate(src.maxX+1, src.maxY+1, pixFmt)) return false;

	*attributes = *src.attributes;

	ogCopyPalette(src);
	ogCopy(src);
	return true;
} // bool ogSurface::ogClone()

void ogSurface::ogCopy(ogSurface& src) 
{
	uint32_t pixMap[256];
	uint32_t count, xCount, yCount;
	uint32_t xx, yy;
	uInt8  r, g, b, a;
	void * srcPtr;

std::cout << "ogCopy" << std::endl;

	if (!ogAvail()) {
std::cout << "ogAvail" << std::endl;
          return;
        }
	if (!src.ogAvail()) {
std::cout << "src.ogAvail" << std::endl;
         return;
        }

	xCount = src.maxX+1;
	if (xCount > maxX+1) xCount = maxX+1;
	yCount = src.maxY+1;
	if (yCount > maxY+1) yCount = maxY+1;

	if (ogIsBlending()) 
	{

		for (yy = 0; yy < yCount; yy++) 
			for (xx = 0; xx < xCount; xx++) 
			{
				src.ogUnpack(src.RawGetPixel(xx, yy), r, g, b, a);
				RawSetPixel(xx, yy, r, g, b, a);
			} // for xx

			return;
	} // if blending

	if (pixFmtID != src.pixFmtID) 
	{
		if (src.bytesPerPix == 1) 
		{
			for (xx = 0; xx < 256; xx++)
				pixMap[xx] = ogPack(src.pal[xx].red,
				src.pal[xx].green,
				src.pal[xx].blue,
				src.pal[xx].alpha);

			for (yy = 0; yy < yCount; yy++) 
				for (xx = 0; xx < xCount; xx++) 
					RawSetPixel(xx, yy, pixMap[src.RawGetPixel(xx, yy)]);

		} 
		else 
		{  // if src.bytesPerPix == 1
			ogPixelFmt srcPixFmt, dstPixFmt;
			src.ogGetPixFmt(srcPixFmt);
			ogGetPixFmt(dstPixFmt);
			ogPixCon pc(srcPixFmt, dstPixFmt);

			for (yy = 0; yy < yCount; yy++) 
				for (xx = 0; xx < xCount; xx++) 
					RawSetPixel(xx, yy, pc.ConvPix(src.RawGetPixel(xx, yy)));

		} // else
	} 
	else 
	{

		xCount *= bytesPerPix;

		for (count = 0; count < yCount; count++)
			if ((srcPtr = src.ogGetPtr(0, count)) == NULL) 
			{
				/*
				* if we are here then we couldn't get a direct memory pointer
				* from the source object.  This means that it is not a normal
				* "memory" buffer and we have to use the implementation inspecific
				* interface.  We let the source buffer fill a "temporary" buffer
				* and then we copy it to where it needs to go.
				*/
#ifdef __UBIXOS_KERNEL__
				srcPtr = kmalloc(xCount);  // allocate space
#else
				srcPtr = malloc(xCount);  // allocate space
#endif
				if (srcPtr != NULL) {
					src.ogCopyLineFrom(0, count, srcPtr, xCount);
					ogCopyLineTo(0, count, srcPtr, xCount);
#ifdef __UBIXOS_KERNEL__
					kfree(srcPtr);
#else
					free(srcPtr);
#endif
				} // if srcPtr!=NULL
			} 
			else 
			{
				ogCopyLineTo(0, count, srcPtr, xCount);
			}
	} // else
} // void ogSurface::ogCopy()


void ogSurface::ogCopyBuf(int32 dX1, int32 dY1, 
						  ogSurface& src, 
						  int32 sX1, int32 sY1, int32 sX2, int32 sY2) 
{
	uint32_t pixMap[256];
	int32 xx, yy, count, xCount, yCount;
	uInt8 r, g, b, a;
	void *srcPtr;

	ogPixelFmt srcPixFmt, dstPixFmt;

	if (!ogAvail()) return;
	if (!src.ogAvail()) return;

	if ((dX1 > (int32)maxX) || (dY1 > (int32)maxY)) return;

	// if any of the source buffer is out of bounds then do nothing 
	if (( (uint32_t)sX1 > src.maxX) || ((uInt32)sX2 > src.maxX) ||
		( (uint32_t)sY1 > src.maxY) || ((uInt32)sY2 > src.maxY)) return;

	if (sX1 > sX2) 
	{
		int32 xSwap= sX1;
		sX1= sX2;
		sX2= xSwap;
	} // if

	if (sY1 > sY2) 
	{
		int32 ySwap = sY1;
		sY1= sY2;
		sY2= ySwap;
	} // if

	xCount = abs(sX2-sX1)+1;
	yCount = abs(sY2-sY1)+1;

	if (dX1+xCount > (int32)maxX+1) xCount = maxX-dX1+1;
	if (dY1+yCount > (int32)maxY+1) yCount = maxY-dY1+1;

	if (dX1 < 0) 
	{
		xCount += dX1;
		sX1 -= dX1;
		dX1 = 0;
	} // if

	if (dY1 < 0) 
	{
		yCount += dY1;
		sY1 -= dY1;
		dY1 = 0;
	} // if

	if ((dX1+xCount < 0) || (dY1+yCount < 0)) return;

	if (ogIsBlending()) 
	{
		for (yy = 0; yy < yCount; yy++) 
			for (xx = 0; xx < xCount; xx++) 
			{
				src.ogUnpack(src.RawGetPixel(sX1+xx, sY1+yy), r, g, b, a);
				RawSetPixel(dX1+xx, dY1+yy, r, g, b, a);
			} // for xx
			return;
	} // if IsBlending

	if (pixFmtID != src.pixFmtID) 
	{

		if (src.bytesPerPix == 1) 
		{
			for (xx = 0; xx < 256; xx++) 
				pixMap[xx] = ogPack(src.pal[xx].red,
				src.pal[xx].green,
				src.pal[xx].blue,
				src.pal[xx].alpha
				);

			for (yy = 0; yy < yCount; yy++) 
				for (xx = 0; xx < xCount; xx++)
					RawSetPixel(dX1+xx,dY1+yy,	pixMap[src.ogGetPixel(sX1+xx,sY1+yy)]); 
		} 
		else 
		{

			src.ogGetPixFmt(srcPixFmt);
			ogGetPixFmt(dstPixFmt);
			ogPixCon pc(srcPixFmt, dstPixFmt);  // allocate the pixel converter

			for (yy = 0; yy < yCount; yy++)
				for (xx = 0; xx < xCount; xx++) 
					RawSetPixel(dX1+xx, dY1+yy, pc.ConvPix(src.RawGetPixel(sX1+xx, sY1+yy)));

		} // else
	} 
	else 
	{
		xCount *= bytesPerPix;

		for (count = 0; count < yCount; count++)
			if ((srcPtr = src.ogGetPtr(sX1, sY1+count)) == NULL) 
			{
				// if we are here then we couldn't get a direct memory pointer
				// from the source object.  This means that it is not a normal
				// "memory" buffer and we have to use the implementation inspecific
				// interface.  We let the source buffer fill a "temporary" buffer
				// and then we copy it to where it needs to go.

#ifdef __UBIXOS_KERNEL__
				srcPtr = kmalloc(xCount);  // allocate space
#else
				srcPtr = malloc(xCount);  // allocate space
#endif
				if (srcPtr != NULL) {
					src.ogCopyLineFrom(sX1, sY1+count, srcPtr, xCount);
					ogCopyLineTo(dX1, dY1+count, srcPtr, xCount);
#ifdef __UBIXOS_KERNEL__
					kfree(srcPtr);
#else
					free(srcPtr);
#endif
				} // if srcPtr!=NULL
			} 
			else 
			{
				ogCopyLineTo(dX1,dY1+count,srcPtr,xCount);
			}
	} // else
} // void ogSurface::ogCopyBuf()

void ogSurface::ogCopyLineTo(uint32_t dx, uInt32 dy, const void * src, uInt32 size) 
{
	/*
	* CopyLineTo()
	*
	* Inputs:
	*
	* dx   - Destination X of the target buffer
	* dy   - Destination Y of the target buffer
	* src  - buffer to copy
	* size - size in bytes *NOT* pixels
	*
	* Copies a run of pixels (of the same format) to (x,y) of a buffer
	*
	* This method is required because of the different implementations of
	* copying a run of pixels to a buffer
	*
	* WARNING!!! This does *NO* error checking. It is assumed that you've
	* done all of that.  CopyLineTo and CopyLineFrom are the only
	* methods that don't check to make sure you're hosing things.  Don't
	* use this method unless YOU KNOW WHAT YOU'RE DOING!!!!!!!!!
	*/

	memcpy( buffer+lineOfs[dy]+dx*bytesPerPix,         // dest
		src,                                               // src
		size);                                             // size

} // ogSurface::ogCopyLineTo

void ogSurface::ogCopyLineFrom(uint32_t sx, uInt32 sy, void * dst, uInt32 size) 
{
	/*
	* CopyLineFrom()
	*
	* Inputs:
	*
	* sx   - Source X of the target buffer
	* sy   - Source Y of the target buffer
	* dest - where to put it
	* size - size in bytes *NOT* pixels
	*
	* Copies a run of pixels (of the same format) to (x,y) of a buffer
	*
	* This method is required because of the different implementations of
	* copying a run of pixels to a buffer
	*
	* WARNING!!! This does *NO* error checking. It is assumed that you've
	* done all of that.  CopyLineTo and CopyLineFrom are the only
	* methods that don't check to make sure you're hosing things.  Don't
	* use this method unless YOU KNOW WHAT YOU'RE DOING!!!!!!!!!
	*/

	memcpy( dst,                                               // dest
		(uInt8*)buffer+lineOfs[sy]+sx*bytesPerPix,         // src
		size);                                             // size

	return;
} // ogSurface::ogCopyLineFrom

void ogSurface::ogCopyPalette(ogSurface& src) 
{
	if (src.pal == NULL) return;
	if (pal == NULL) pal = new ogRGBA8[256];
	if (pal == NULL) return;
	src.ogGetPalette(pal);
	// memcpy(pal, src.pal, sizeof(ogRGBA8)*256);
	return;
} // void ogSurface::ogCopyPalette()


bool ogSurface::ogCreate(uint32_t _xRes, uInt32 _yRes, ogPixelFmt _pixFormat)
{
	/*
	*  ogSurface::ogCreate()
	*  Allocates memory for a buffer of size _xRes by _yRes with
	*  the pixel format defined in _pixformat.  Allocates memory
	*  for pal and lineOfs.
	*/
	uInt8		* newBuffer = NULL;
	ptrdiff_t	* newLineOfs = NULL;
	ogRGBA8     * newPal = NULL;
	ogAttribute * newAttributes = NULL;

	uint32_t        newBSize;
	uint32_t        newLSize;

	bool          status = false;

	switch (_pixFormat.BPP) {
	case 8:
		getPixel = ([] (void * ptr) mutable -> uint32_t {	return *(reinterpret_cast<uInt8*>(ptr)); });
		setPixel = ([] (void * ptr, uint32_t colour) -> void { *(reinterpret_cast<uInt8*>(ptr)) = colour; });
		break;
	case 15:
	case 16:
		getPixel = ([] (void * ptr) mutable -> uint32_t { return *(reinterpret_cast<uInt16*>(ptr)); });
		setPixel = ([] (void * ptr, uint32_t colour) -> void { *(reinterpret_cast<uInt16*>(ptr)) = colour; });
		break;
	case 24:
		getPixel = ([] (void * ptr) -> uint32_t { 
			uint32_t colour = 0;
			uInt8* src = reinterpret_cast<uInt8*>(ptr);
			uInt8* dest = reinterpret_cast<uInt8*>(&colour);
			// This may break depending on endian-ness. TODO: Requires testing.
			*dest++ = *src++;
			*dest++ = *src++;
			*dest++ = *src++;
			return colour;
		}); //  getPixel() 24bpp lambda
		setPixel = ([] (void * ptr, uint32_t colour) -> void { 
			uInt8* src = reinterpret_cast<uInt8*>(&colour);
			uInt8* dest = reinterpret_cast<uInt8*>(ptr);
			// This may break depending on endian-ness. TODO: Requires testing.
			*dest++ = *src++;
			*dest++ = *src++;
			*dest++ = *src++;
		}); // setPixel() 24bpp lambda
		break;
	case 32:
		getPixel = ([] (void * ptr) -> uint32_t { return *(reinterpret_cast<uInt32*>(ptr)); });
		setPixel = ([] (void * ptr, uint32_t colour)  -> void { *(reinterpret_cast<uInt32*>(ptr)) = colour; });
		break;
	default:
		ogSetLastError(ogBadBPP);
		return false;
	} // switch

	newBSize = _xRes * _yRes * ((_pixFormat.BPP + 7) >> 3);
	newLSize = _yRes * sizeof(uint32_t);  // number of scan lines * sizeof(uInt32)

#ifdef __UBIXOS_KERNEL__
	newBuffer = kmalloc(newBSize);
#else
	newBuffer = reinterpret_cast<uInt8 *>(malloc(newBSize));
#endif
	newLineOfs = new ptrdiff_t[_yRes];
	newPal = new ogRGBA8[256];
	newAttributes = new ogAttribute();

	do {

		if ((newBuffer == NULL) || 
			(newLineOfs == NULL) ||
			(newPal == NULL) || 
			(newAttributes == NULL))
		{
			ogSetLastError(ogMemAllocFail);
			break;                           // break out of do {...} while(false) 
		} // if 

		// check to see if we have already allocated memory .. if so, free it

		if (dataState == ogOwner) {
#ifdef __UBIXOS_KERNEL__
			kfree(buffer);
#else
			free((void *)buffer);
#endif
			delete [] lineOfs;
			delete [] pal;
			delete attributes;
		}  // if dataState

		buffer = newBuffer;
		lineOfs = newLineOfs;
		pal = newPal;
		attributes = newAttributes;
		bSize = newBSize;
		lSize = newLSize; 

		newBuffer     = NULL;
		newLineOfs    = NULL;
		newPal        = NULL;
		newAttributes = NULL;

		BPP = _pixFormat.BPP;
		bytesPerPix = (BPP + 7) >> 3;

		ogSetPalette(DEFAULT_PALETTE); 
		// memcpy(pal, DEFAULT_PALETTE, sizeof(ogRGBA8)*256);

		maxX = _xRes -1;
		xRes = _xRes * bytesPerPix;
		maxY = _yRes -1;
		yRes = _yRes;

		// in the pascal version we go from 1 to maxY .. here we use yy < yRes
		// (which is the same)

		lineOfs[0] = 0;
		for (size_t yy = 1; yy < yRes; yy++)
			lineOfs[yy] = lineOfs[yy-1]+xRes;

		dataState = ogOwner;

		// For 8bpp modes the next part doesn't matter 

		redFieldPosition   = _pixFormat.redFieldPosition;
		greenFieldPosition = _pixFormat.greenFieldPosition;
		blueFieldPosition  = _pixFormat.blueFieldPosition;
		alphaFieldPosition = _pixFormat.alphaFieldPosition;
		// The next part is only used by 15/16hpp 
		redShifter   = 8-_pixFormat.redMaskSize;
		greenShifter = 8-_pixFormat.greenMaskSize;
		blueShifter  = 8-_pixFormat.blueMaskSize;
		alphaShifter = 8-_pixFormat.alphaMaskSize;

		if (_pixFormat.alphaMaskSize != 0) 
			alphaMasker = ~(ogPixelFmt::OG_MASKS[_pixFormat.alphaMaskSize] << alphaFieldPosition);
		else
			alphaMasker = ~0;

		if (bytesPerPix == 1)
		{
			pixFmtID = 0x08080808;
			// turn anti aliasing off by default for 8bpp modes
			ogSetAntiAliasing(false);
		} 
		else 
		{
			pixFmtID = (redFieldPosition) | 
				(greenFieldPosition << 8) | 
				(blueFieldPosition << 16) |
				(alphaFieldPosition << 24);
			ogSetAntiAliasing(true);
		} // else

		ogClear(ogPack(0, 0, 0));

		owner = this;
		status = true;
	} while(false);

#ifdef __UBIXOS_KERNEL__
	if (newBuffer) kfree(newBuffer);
#else
	if (newBuffer) free(newBuffer);
#endif
	if (newLineOfs) delete [] newLineOfs;
	if (newPal) delete [] newPal;
	if (newAttributes) delete newAttributes;

	return status;
} // bool ogSurface::ogCreate()

void ogSurface::ogCubicBezierCurve(int32 x1, int32 y1, int32 x2, int32 y2,
								   int32 x3, int32 y3, int32 x4, int32 y4,
								   uint32_t segments, uInt32 colour) 
{
	double tX1, tY1, tX2, tY2, tX3, tY3, mu, mu2, mu3, mudelta;
	int32 xStart, yStart, xEnd, yEnd;
	uint32_t n;
	if (segments < 1) return;
	if (segments > 128) segments=128;

	mudelta = 1.0/segments;
	mu = mudelta;
	tX1 =-x1+3*x2-3*x3+x4;
	tY1 =-y1+3*y2-3*y3+y4;
	tX2 =3*x1-6*x2+3*x3;
	tY2 =3*y1-6*y2+3*y3;
	tX3 =-3*x1+3*x2;
	tY3 =-3*y1+3*y2;

	xStart = x1;
	yStart = y1;

	for (n = 1; n < segments; n++)
	{
		mu2 = mu*mu;
		mu3 = mu2*mu;
		xEnd = static_cast<int32>(mu3*tX1+mu2*tX2+mu*tX3+x1 +0.5f);
		yEnd = static_cast<int32>(mu3*tY1+mu2*tY2+mu*tY3+y1 +0.5f);
		ogLine(xStart, yStart, xEnd, yEnd, colour);
		mu += mudelta;
		xStart = xEnd;
		yStart = yEnd;
	} // for
} // void ogSurface::ogCubicBezierCurve()

void ogSurface::ogCurve(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3,
						uint32_t segments, uInt32 colour) 
{
	// This is currently broken. 
	// ToDo: fix ogCurve

	int64 ex, ey, fx, fy;
	int64 t1, t2;

	if (segments<2) segments=2; else if (segments>128) segments=128;

	int64 s = segments;

	x2 = (x2*2)-((x1+x3)/2);
	y2 = (y2*2)-((y1+y3)/2);

	ex = (static_cast<int64>(x2-x1) << 17) / s;
	ey = (static_cast<int64>(y2-y1) << 17) / s;
	fx = (static_cast<int64>(x3-(2*x2)+x1) << 16) / (s*s);
	fy = (static_cast<int64>(y3-(2*y2)+y1) << 16) / (s*s);

	while (--s > 0) 
	{
		t1 = x3;
		t2 = y3;
		x3 = (static_cast<int64>((fx*segments+ex)*segments) / 65536L)+x1;
		y3 = (static_cast<int64>((fy*segments+ey)*segments) / 65536L)+y1;
		ogLine(static_cast<int32>(t1), static_cast<int32>(t2), x3, y3, colour);
	} // while

	ogLine(x3, y3, x1, y1, colour);

} // void ogSurface::ogCurve()


void ogSurface::ogFillCircle(int32 xCenter, int32 yCenter, uint32_t radius, uInt32 colour) 
{
	int32 x, y, d;
	x = 0;
	y = radius;
	d = 4*(1-radius);

	while (y >= 0) 
	{
		if (d + y > 0) 
		{
			ogHLine(xCenter-x, xCenter+x, yCenter-y, colour);
			if (y != 0) ogHLine(xCenter-x, xCenter+x, yCenter+y, colour);

			--y;
			d -= 4*y+1;
		} // if


		if (x > d) 
		{
			++x;
			d += 4*x+1;
		} // if
	} // while
} // void ogSurface::ogFillCircle()

void ogSurface::ogFillGouraudPolygon(uint32_t numPoints, ogPoint2d* polyPoints, ogRGBA8 * colours) 
{

	ogEdgeTable * edges;
	int32 currentY = ~0;

	if (numPoints < 3) return;

	edges = new ogEdgeTable();

	if (edges == NULL) return;  // sanity check

	edges->BuildGET_G(numPoints, polyPoints, colours);

	if (edges->globalEdges != NULL)
		currentY = edges->globalEdges->startY;

	while ((edges->globalEdges != NULL) || (edges->activeEdges != NULL)) 
	{
		edges->MoveXSortedToAET(currentY);
		edges->ScanOutAET_G(*this, currentY);
		edges->AdvanceAET();
		edges->XSortAET();
		++currentY;
		if (currentY > (int32)maxY) break; // if we've gone past the bottom, stop
	} // while

	delete edges;
} // void ogSurface::ogFillGouraudPolygon()

void ogSurface::ogFillPolygon(uint32_t numPoints, ogPoint2d* polyPoints, uInt32 colour) 
{
	ogEdgeTable * edges;
	int32 currentY = ~0;

	if (numPoints < 3) return;

	if (!ogIsBlending()) ogPolygon(numPoints, polyPoints, colour);

	edges = new ogEdgeTable();

	if (edges == NULL) return;  // sanity check

	edges->BuildGET(numPoints, polyPoints);

	if (edges->globalEdges != NULL)
		currentY = edges->globalEdges->startY;

	while ((edges->globalEdges != NULL) || (edges->activeEdges != NULL)) 
	{
		edges->MoveXSortedToAET(currentY);
		edges->ScanOutAET(*this, currentY, colour);
		edges->AdvanceAET();
		edges->XSortAET();
		++currentY;
		if (currentY > (int32)maxY) break; // if we've gone past the bottom, stop
	} // while
	delete edges;
} // void ogSurface::ogFillPolygon()


void ogSurface::ogFillRect(int32 x1, int32 y1, int32 x2, int32 y2, uint32_t colour) 
{
	int32 yy, tmp;

	if (x2 < x1) 
	{
		tmp= x2;
		x2 = x1;
		x1 = tmp;
	} // if

	if (y2 < y1) 
	{
		tmp= y2;
		y2 = y1;
		y1 = tmp;
	} // if 

	if ((y2 < 0) || (y1 > (int32)maxY)) return;
	if (y1 < 0) y1 = 0;
	if (y2 > (int32)maxY) y2 = maxY;
	for (yy = y1; yy <= y2; yy++)
		ogHLine(x1, x2, yy, colour);
} // ogSurface::ogFillRect

void ogSurface::ogFillTriangle(int32 x1, int32 y1, int32 x2, int32 y2,
							   int32 x3, int32 y3, uint32_t colour) 
{
	ogPoint2d points[3];
	points[0].x = x1;
	points[0].y = y1;
	points[1].x = x2;
	points[1].y = y2;
	points[2].x = x3;
	points[2].y = y3;

	ogFillPolygon(3, points, colour);

} // void ogSurface::ogFillTriangle()
uint32_t ogSurface::ogGetAlpha(void) 
{
	return (attributes != NULL ? attributes->defaultAlpha : 255L);
} // uint32_t ogSurface::ogGetAlpha()

uint32_t ogSurface::ogGetColorCount()
{
	if (!ogAvail() || ogGetBytesPerPix() != 1) return 0;

	uint32_t colourCount = 0;
	uint32_t colourCounter[256] = {};
	for (uint32_t y = 0; y <= ogGetMaxY(); y++)
	{
		for (uint32_t x = 0; x <= ogGetMaxX(); x++)
		{
			colourCounter[ogGetPixel(x, y)]++;
		} // for x
	} // for y

	for (size_t index = 0; index < std::extent<decltype(colourCounter)>::value; index++)
	{
		if (colourCounter[index] != 0) colourCount++;
	} // for index

	return colourCount;
} // void ogSurface::ogCountColors()

uint32_t ogSurface::ogGetPixel(int32 x, int32 y) 
{
	if (!ogAvail()) return ogGetTransparentColor();

	if (((uint32_t)x > maxX) || ((uInt32)y > maxY)) return ogGetTransparentColor();

	return RawGetPixel(x, y);
} // uint32_t ogSurface::ogGetPixel()

void ogSurface::ogGetPalette(ogRGBA8 _pal[256])
{
	if (pal == NULL) return;

	for (size_t index = 0; index <256; index++)
	{
		_pal[index].red = pal[index].red;
		_pal[index].green = pal[index].green;
		_pal[index].blue = pal[index].blue;
		_pal[index].alpha = pal[index].alpha;
	} // for index
} // void ogSurface::ogGetPalette()

void ogSurface::ogGetPixFmt(ogPixelFmt& pixfmt) 
{
	pixfmt.BPP = BPP;
	pixfmt.redFieldPosition   = redFieldPosition;
	pixfmt.greenFieldPosition = greenFieldPosition;
	pixfmt.blueFieldPosition  = blueFieldPosition;
	pixfmt.alphaFieldPosition = alphaFieldPosition;
	pixfmt.redMaskSize   = 8-redShifter;
	pixfmt.greenMaskSize = 8-greenShifter;
	pixfmt.blueMaskSize  = 8-blueShifter;
	pixfmt.alphaMaskSize = 8-alphaShifter;
} // void ogSurface::ogGetPixFmt()

void* ogSurface::ogGetPtr(uint32_t x, uInt32 y) 
{
	//  return (Avail() ? ( (uInt8*)buffer+(lineOfs[y]+x*((BPP+7) >> 3)) ) : NULL );
	return reinterpret_cast<uInt8*>(buffer+(lineOfs[y]+x*bytesPerPix));
} // void* ogSurface::ogGetPtr

uint32_t ogSurface::ogGetTransparentColor(void)
{
	return (attributes != NULL ? attributes->transparentColor : 0);
} // ogSurface::ogGetTransparentColor

void ogSurface::ogHFlip(void) 
{
	void * tmpBuf1;
	void * tmpBuf2;
	uint32_t xWidth, count;

	if (!ogAvail()) return;

	xWidth = (maxX+1)*bytesPerPix;

#ifdef __UBIXOS_KERNEL__
	tmpBuf1 = kmalloc(xWidth);
	tmpBuf2 = kmalloc(xWidth);
#else
	tmpBuf1 = malloc(xWidth);
	tmpBuf2 = malloc(xWidth);
#endif

	if ((tmpBuf1 != NULL) && (tmpBuf2 != NULL))
	{
		for (count = 0; count <= (maxY/2); count++) 
		{
			ogCopyLineFrom(0, count, tmpBuf1, xWidth);
			ogCopyLineFrom(0, maxY-count,tmpBuf2, xWidth);
			ogCopyLineTo(0, maxY-count,tmpBuf1, xWidth);
			ogCopyLineTo(0, count, tmpBuf2, xWidth);
		} // for count
	}
#ifdef __UBIXOS_KERNEL__
	kfree(tmpBuf2);
	kfree(tmpBuf1);
#else
	free(tmpBuf2);
	free(tmpBuf1);
#endif

} // void ogSurface::ogHFlip()

void ogSurface::ogHLine(int32 x1, int32 x2, int32 y, uint32_t colour) 
{
	int32 tmp;
	uInt8 r, g, b, a;

	if (!ogAvail()) return;
	if ((uint32_t)y > maxY) return;

	if (x1 > x2) 
	{
		tmp= x1;
		x1 = x2;
		x2 = tmp;
	} // if

	if (x1 < 0) x1 = 0;
	if (x2 > (int32)maxX) x2 = maxX;
	if (x2 < x1) return;

	if (ogIsBlending()) {
		ogUnpack(colour, r, g, b, a);
		if (a == 0) return;
		if (a == 255) {
			for (tmp = x1; tmp <= x2; tmp++) 
				RawSetPixel(tmp, y, r, g, b, a);
			return;
		} // if a == 255
	} // if blending
	for (int32 xx = x1; xx <= x2; xx++)
		RawSetPixel(xx, y, colour);

} // void ogSurface::ogHLine()

bool ogSurface::ogIsAntiAliasing(void) 
{
	return (attributes != NULL ? attributes->antiAlias : false);
} // bool ogSurface::ogIsAntiAliasing()

bool ogSurface::ogIsBlending(void)
{
	return (attributes != NULL ? attributes->blending : false);
} // bool ogSurface::ogIsBlending()

void ogSurface::ogLine(int32 x1, int32 y1, int32 x2, int32 y2, uint32_t colour) 
{
	if (ClipLine(x1,y1,x2,y2)) 
	{
		if (ogIsAntiAliasing()) 
			AARawLine(x1, y1, x2, y2, colour); 
		else  
			RawLine(x1, y1, x2, y2, colour);
	} // if clipLine
	return;
} // void ogSurface::ogLine()

bool ogSurface::ogLoadPalette(const char *palfile) 
{
	ogRGBA8 oldPalette[256];
#ifdef __UBIXOS_KERNEL__
	fileDescriptor *f;
	This is possibly incompatible with the kernel. Will need a rewrite.
#endif
	bool result;

	if (pal == NULL) 
	{
		pal = new ogRGBA8[256];
		if (pal == NULL) 
		{
			ogSetLastError(ogMemAllocFail);
			return false;
		} // if 
		ogSetPalette(DEFAULT_PALETTE);
		//   memcpy(pal, DEFAULT_PALETTE, sizeof(ogRGBA8)*256);
	} // if

	ogGetPalette(oldPalette);
	// memcpy(&oldPalette, pal, sizeof(ogRGBA8)*256);

	std::ifstream file;
	file.open(palfile, std::ios::in | std::ios::binary);

	if (!file) 
	{
		ogSetLastError(ogFileNotFound);
		return false;
	} // if !file

	size_t index = 0;
	while (index < sizeof(pal) / sizeof(pal[0])) 
	{
		if (!(file >> pal[index].red)) break;
		if (!(file >> pal[index].green)) break;
		if (!(file >> pal[index].blue)) break;
		if (!(file >> pal[index].alpha)) break;
		index++;
	}
	//lresult = fread(pal, sizeof(ogRGBA8), 256, f);
	result = (index == 256);

	if (!result) 
	{
		ogSetLastError(ogFileReadError);
		ogSetPalette(oldPalette);
		// memcpy(pal, &oldPalette, sizeof(ogRGBA8)*256);
	} // if

	file.close();
	return result;
} // bool ogSurface::ogLoadPalette()

void ogSurface::ogOptimize()
{
	if (!ogAvail() || ogGetBytesPerPix() != 1) return;

	int colourCounter[256] = {};	// count of how much that colour is used
	size_t swapIndices[256];		// swap indices
	size_t reverseIndices[256];		// reverse indices
	ogRGBA8 pal[256];				// We also swap the palette entries

	// First acquire a count of all the colours used
	for (uint32_t y = 0; y <= ogGetMaxY(); y++)
	{
		for (uint32_t x = 0; x <= ogGetMaxX(); x++)
		{
			colourCounter[ogGetPixel(x, y)]++;
		} // for x
	} // for y

	colourCounter[ogGetTransparentColor()] = INT_MAX;	// set 0 so high it's always first
	//SortDescending(colourCounter, swapIndices, 256);

	// Sort them in descending order
	for (size_t i = 0; i < 256; i++)
		swapIndices[i] = i;

	for (size_t k = 1; k < 256; k++)
	{
		for (size_t i = 0; i <256 - k; i++)
		{
			if (colourCounter[swapIndices[i]] < colourCounter[swapIndices[i+1]])
			{
				size_t temp = swapIndices[i];
				swapIndices[i] = swapIndices[i+1];
				swapIndices[i+1] = temp;
			}  // if
		} // for i
	} // for k

	ogGetPalette(pal);
	for (size_t i = 0; i < 256; i++)
	{
		reverseIndices[swapIndices[i]] = i;
		//cout << "colourCounter[" << i << "] = " << swapIndices[i] << endl;
		// Also swap the palette entries
		ogSetPalette(i, pal[swapIndices[i]].red, pal[swapIndices[i]].green, pal[swapIndices[i]].blue);
	} // for i

	for (uint32_t y = 0; y <= ogGetMaxY(); y++)
	{
		for (uint32_t x = 0; x <= ogGetMaxX(); x++)
		{
			ogSetPixel(x, y, reverseIndices[ogGetPixel(x, y)]);
		} // for x
	} // for y
} // void ogSurface::ogOptimize()

uint32_t ogSurface::ogPack(uInt8 red, uInt8 green, uInt8 blue) 
{
	uint32_t colour = 0;

	switch (bytesPerPix) {
	case 4:
		colour = ( (red << redFieldPosition) |
			(green << greenFieldPosition) |
			(blue << blueFieldPosition) |
			(ogGetAlpha() << alphaFieldPosition) );
		break;
	case 3:
		colour = ( (red << redFieldPosition) |
			(green << greenFieldPosition) |
			(blue << blueFieldPosition) );
		break;
	case 2:
		colour = ((red >> redShifter) << redFieldPosition) |
			((green >> greenShifter) << greenFieldPosition) |
			((blue >> blueShifter) << blueFieldPosition) |
			((ogGetAlpha() >> alphaShifter) << alphaFieldPosition);
		break;
	case 1:
		uint32_t dist = 255+255+255;
		for (uint32_t idx = 0; idx <= 255; idx++) 
		{
			uint32_t rd = abs(red-pal[idx].red);
			uint32_t gd = abs(green-pal[idx].green);
			uint32_t bd = abs(blue-pal[idx].blue);
			uint32_t newdist = rd + gd + bd;

			if (newdist < dist) 
			{
				dist = newdist;
				colour = idx;
			} // if
		} // for
		break;
	} // switch

	return colour;

} // uint32_t ogSurface::ogPack()

uint32_t ogSurface::ogPack(uInt8 red, uInt8 green, uInt8 blue, uInt8 alpha) 
{
	uint32_t colour = 0;

	switch (bytesPerPix) {
	case 4:
		colour = ( (red << redFieldPosition) |
			(green << greenFieldPosition) |
			(blue << blueFieldPosition) |
			(alpha << alphaFieldPosition) );
		break;
	case 3:
		colour = ( (red << redFieldPosition) |
			(green << greenFieldPosition) |
			(blue << blueFieldPosition) );
		break;
	case 2:
		colour = ((red >> redShifter) << redFieldPosition) |
			((green >> greenShifter) << greenFieldPosition) |
			((blue >> blueShifter) << blueFieldPosition) |
			((alpha >> alphaShifter) << alphaFieldPosition);
		break;
	case 1:
		uint32_t dist = 255+255+255;
		for (uint32_t idx = 0; idx <= 255; idx++)
		{
			uint32_t rd = abs(red-pal[idx].red);
			uint32_t gd = abs(green-pal[idx].green);
			uint32_t bd = abs(blue-pal[idx].blue);
			uint32_t newdist = rd + gd + bd;

			if (newdist < dist) 
			{
				dist = newdist;
				colour = idx;
			} // if
		} // for
		break;
	} // switch

	return colour;
} // uint32_t ogSurface::ogPack()

void ogSurface::ogPolygon(uint32_t numPoints, ogPoint2d* polyPoints, uInt32 colour) 
{
	if (polyPoints == NULL) return;

	if (numPoints == 1) 
	{
		ogSetPixel(polyPoints[0].x, polyPoints[0].y, colour);
	}
	else
	{
		for (size_t count = 0; count < numPoints; count++)
			ogLine(polyPoints[count].x, polyPoints[count].y,
			polyPoints[(count+1) % numPoints].x,
			polyPoints[(count+1) % numPoints].y,
			colour);
	}
} // void ogSurface::ogPolygon()

void ogSurface::ogRect(int32 x1, int32 y1, int32 x2, int32 y2, uint32_t colour) 
{
	if ((x1 == x2) || (y1 == y2)) 
	{

		if ((x1 == x2) && (y1 == y2))
			ogSetPixel(x1, y1, colour);
		else
			ogLine(x1, y1, x2, y2, colour);

	} 
	else 
	{

		if (y1 > y2) 
		{
			int32 tmp= y1;
			y1 = y2;
			y2 = tmp;
		} // if

		ogHLine(x1, x2, y1, colour);  // Horizline has built in clipping
		ogVLine(x1, y1+1, y2-1, colour);  // vertline has built in clipping too
		ogVLine(x2, y1+1, y2-1, colour);
		ogHLine(x1, x2, y2, colour);

	} // else

} // void ogSurface::ogRect()

void ogSurface::ogScale(ogSurface& src) {
		ogScaleBuf(0, 0, maxX, maxY, src, 0, 0, src.maxX, src.maxY);
		return;
} // ogSurface::ogScale

void ogSurface::ogScaleBuf(int32 dX1, int32 dY1, int32 dX2, int32 dY2,
						   ogSurface& src, 
						   int32 sX1, int32 sY1, int32 sX2, int32 sY2) 
{

	uint32_t sWidth, dWidth;
	uint32_t sHeight, dHeight;
	int32 sx, sy, xx, yy;
	uint32_t xInc, yInc;
	uint32_t origdX1, origdY1;
	ogPixelFmt pixFmt;
	ogSurface * tmpBuf;
	ogSurface * sBuf;
	ogSurface * dBuf;
	bool doCopyBuf;

	origdX1 = origdY1 = 0; // to keep the compiler from generating a warning

	if (!ogAvail()) return;
	if (!src.ogAvail()) return;

	if (sX1 > sX2) 
	{
		xx = sX1;
		sX1= sX2;
		sX2= xx;
	}

	if (sY1 > sY2) 
	{
		yy = sY1;
		sY1= sY2;
		sY2= yy;
	}

	// if any part of the source falls outside the buffer then don't do anything

	if (((uint32_t)sX1 > src.maxX) || ((uInt32)sX2 > src.maxX) ||
		((uint32_t)sY1 > src.maxY) || ((uInt32)sY2 > src.maxY)) return;

	if (dX1 > dX2) 
	{
		xx = dX1;
		dX1= dX1;
		dX2= xx;
	}

	if (dY1 > dY2) 
	{
		yy = dY1;
		dY1= dY2;
		dY2= yy;
	}

	dWidth = (dX2-dX1)+1;
	if (dWidth <= 0) return;

	dHeight = (dY2-dY1)+1;
	if (dHeight <= 0) return;

	sWidth = (sX2-sX1)+1;
	sHeight = (sY2-sY1)+1;

	// convert into 16:16 fixed point ratio
	xInc = (sWidth << 16) / dWidth;
	yInc = (sHeight << 16) / dHeight;

	if (dX2 > (int32)maxX) 
	{
		xx = (xInc*(dX1-maxX)) >> 16;
		sX1 -= xx;
		sWidth -= xx;
		dWidth -= (dX1-maxX);
		dX1 = maxX;
	}

	if (dY2 > (int32)maxY) 
	{
		yy = (yInc*(dY2-maxY)) >> 16;
		sY2 -= yy;
		sHeight -= yy;
		dHeight -= (dY2-maxY);
		dY2 = maxY;
	}

	if (dX1 < 0) 
	{
		xx = (xInc*(-dX1)) >> 16;
		sX1 += xx;
		sWidth -= xx;
		dWidth += dX1;
		dX1 = 0;
	}

	if (dY1 < 0) 
	{
		yy = (yInc*(-dY1)) >> 16;
		sY1 += yy;
		sHeight -= yy;
		dHeight += dY1;
		dY1 = 0;
	}

	if ((dWidth <= 0) || (dHeight <= 0)) return;
	if ((sWidth <= 0) || (sHeight <= 0)) return;

	// Do a quick check to see if the scale is 1:1 .. in that case just copy
	// the image

	if ((dWidth == sWidth) && (dHeight == sHeight)) 
	{
		ogCopyBuf(dX1, dY1, src, sX1, sY1, sX2, sY2);
		return;
	}

	tmpBuf = NULL;

	/*
	* Alright.. this is how we're going to optimize the case of different
	* pixel formats.  We are going to use copyBuf() to automagically do
	* the conversion for us using tmpBuf.  Here's how it works:
	* If the source buffer is smaller than the dest buffer (ie, we're making
	* something bigger) we will convert the source buffer first into the dest
	* buffer's pixel format.  Then we do the scaling.
	* If the source buffer is larger than the dest buffer (ie, we're making
	* something smaller) we will scale first and then use copyBuf to do
	* the conversion.
	* This method puts the onus of conversion on the copyBuf() function which,
	* while not excessively fast, does the job.
	* The case in which the source and dest are the same size is handled above.
	*
	*/
	if (pixFmtID != src.pixFmtID) 
	{

		tmpBuf = new ogSurface();
		if (tmpBuf == NULL) return;
		if (sWidth*sHeight*src.bytesPerPix <= dWidth*dHeight*bytesPerPix) 
		{
			// if the number of pixels in the source buffer is less than the
			// number of pixels in the dest buffer then...
			ogGetPixFmt(pixFmt);
			if (!tmpBuf->ogCreate(sWidth, sHeight, pixFmt)) return;
			tmpBuf->ogCopyPalette(src);
			tmpBuf->ogCopyBuf(0, 0, src, sX1, sY1, sX2, sY2);
			sX2 -= sX1;
			sY2 -= sY1;
			sX1 = 0;
			sY1 = 0;
			sBuf = tmpBuf;
			dBuf = this;
			doCopyBuf = false; // do we do a copyBuf later?
		} 
		else 
		{
			src.ogGetPixFmt(pixFmt);
			if (!tmpBuf->ogCreate(dWidth,dHeight,pixFmt)) return;
			tmpBuf->ogCopyPalette(*this);
			origdX1 = dX1;
			origdY1 = dY1;
			dX1 = 0;
			dY1 = 0;
			dX2 = tmpBuf->maxX;
			dY2 = tmpBuf->maxY;
			sBuf = &src;
			dBuf = tmpBuf;
			doCopyBuf = true;
		} // else
	} 
	else 
	{
		// pixel formats are identical
		sBuf = &src;
		dBuf = this;
		doCopyBuf = false;
	} // else

	sy = sY1 << 16;

	for (yy = dY1; yy <= dY2; yy++) 
	{
		sx = 0;
		for (xx = dX1; xx <= dX2; xx++) 
		{
			dBuf->RawSetPixel(xx, yy,
				sBuf->RawGetPixel(sX1+(sx >> 16),(sy>>16)));
			sx += xInc;
		} // for xx
		sy += yInc;
	} // for yy

	if ((doCopyBuf) && (tmpBuf != NULL))
		ogCopyBuf(origdX1, origdY1, *tmpBuf, 0, 0, tmpBuf->maxX, tmpBuf->maxY);

	delete tmpBuf;
} // void ogSurface::ogScaleBuf()

uint32_t ogSurface::ogSetAlpha(uInt32 newAlpha) 
{
	if (attributes != NULL) 
	{
		uint32_t tmp = attributes->defaultAlpha;
		attributes->defaultAlpha = newAlpha;
		return tmp;
	} 
	else 
	{
		return newAlpha;
	}
} // ogSurface::ogSetAlpha

bool ogSurface::ogSetAntiAliasing(bool antiAliasing) 
{
	if (attributes != NULL)
	{
		bool previousAA = attributes->antiAlias;
		attributes->antiAlias = antiAliasing;
		return previousAA;
	} 
	else 
	{
		return antiAliasing;	// fail quietly
	}
} // bool ogSurface::ogSetAntiAliasing()

bool ogSurface::ogSetBlending(bool _blending) 
{
	bool tmp;

	if (attributes != NULL) {
		tmp = attributes->blending;
		attributes->blending = _blending;
		return tmp;
	} else return _blending;

} // bool ogSurface::ogSetBlending()

ogErrorCode ogSurface::ogSetLastError(ogErrorCode latestError) 
{
	ogErrorCode tmp = lastError;
	lastError = latestError;
	return tmp;
} // ogErrorCode ogSurface::ogSetLastError()

void ogSurface::ogSetPalette(const ogRGBA8 newPal[256]) 
{
	if (pal == NULL) return;
	for (size_t index = 0; index < 256; index++)
	{
		pal[index].red = newPal[index].red;
		pal[index].green = newPal[index].green;
		pal[index].blue = newPal[index].blue;
		pal[index].alpha = newPal[index].alpha;
	} // for index
} // void ogSurface::ogSetPalette()

void ogSurface::ogSetPalette(uInt8 colour, uInt8 red, uInt8 green, uInt8 blue, uInt8 alpha) 
{
	if (pal == NULL) return;
	pal[colour].red   = red;
	pal[colour].green = green;
	pal[colour].blue  = blue;
	pal[colour].alpha = alpha;
} // void ogSurface::ogSetPalette()

void ogSurface::ogSetPalette(uInt8 colour, uInt8 red, uInt8 green, uInt8 blue) 
{
	if (pal == NULL) return;
	pal[colour].red   = red;
	pal[colour].green = green;
	pal[colour].blue  = blue;
	pal[colour].alpha = ogGetAlpha();
} // void ogSurface::ogSetPalette()

void ogSurface::ogSetPixel(int32 x, int32 y, uint32_t colour) 
{
	if (!ogAvail()) return;

	if (((uint32_t)x > maxX) || ((uInt32)y > maxY)) return;

	RawSetPixel(x, y, colour);
} // void ogSurface::ogSetPixel()

void ogSurface::ogSetPixel(int32 x, int32 y, uInt8 r, uInt8 g, uInt8 b, uInt8 a) {
	if (!ogAvail()) return;

	if (((uint32_t)x > maxX) || ((uInt32)y > maxY)) return;
	
	RawSetPixel(x, y, r, g, b, a);
} // void ogSurface::ogSetPixel()

uint32_t ogSurface::ogSetTransparentColor(uInt32 colour) 
{
	uint32_t preColour = 0;  

	if (attributes != NULL) 
	{
		preColour = attributes->transparentColor & ogGetAlphaMasker();
		attributes->transparentColor = colour & ogGetAlphaMasker();
	} // if

	return preColour;
} // uint32_t ogSurface::ogSetTransparentColor()

//static double f(double g) { return g*g*g-g; }

void ogSurface::ogSpline(uint32_t numPoints, ogPoint2d* points, uInt32 segments,
						 uint32_t colour) 
{
	int32 i, oldY, oldX, x, y, j;
	double part, t, xx, yy, tmp;
	double * zc;
	double * dx;
	double * dy;
	double * u;
	double * wndX1;
	double * wndY1;
	double * px;
	double * py;

	auto f = ([] (double g) -> double { return g*g*g*-g; });
	bool runOnce;

	if ((numPoints < 2) || (points == NULL)) return;

	zc = new double[numPoints];
	dx = new double[numPoints];
	dy = new double[numPoints];
	u  = new double[numPoints];
	wndX1 = new double[numPoints];
	wndY1 = new double[numPoints];
	px = new double[numPoints];
	py = new double[numPoints];

	do {
		if (zc == NULL) break;
		if (dx == NULL) break;
		if (dy == NULL) break;
		if (wndX1 == NULL) break;
		if (wndY1 == NULL) break;
		if (px == NULL) break;
		if (py == NULL) break;

		for (i = 0; (uint32_t)i < numPoints; i++) 
		{
			zc[i] = dx[i] = dy[i] = u[i] = wndX1[i] = wndY1[i] = px[i] = py[i] = 0.0f;
		}

		runOnce = false;
		oldX = oldY = 0;

		x = points[0].x;
		y = points[0].y;

		for (i = 1; (uint32_t)i < numPoints; i++) 
		{
			xx = points[i-1].x - points[i].x;
			yy = points[i-1].y - points[i].y;
			t = sqrt(xx*xx + yy*yy);
			zc[i] = zc[i-1]+t;
		} // for

		u[0] = zc[1] - zc[0] +1;
		for (i = 1; (uint32_t)i < numPoints-1; i++)
		{
			u[i] = zc[i+1]-zc[i]+1;
			tmp = 2*(zc[i+1]-zc[i-1]);
			dx[i] = tmp;
			dy[i] = tmp;
			wndY1[i] = 6.0f*((points[i+1].y-points[i].y)/u[i]-
				(points[i].y-points[i-1].y)/u[i-1]);
			wndX1[i] = 6.0f*((points[i+1].x-points[i].x)/u[i]-
				(points[i].x-points[i-1].x)/u[i-1]);
		} // for

		for (i = 1; (uint32_t)i < numPoints-2; i++) 
		{
			wndY1[i+1] = wndY1[i+1]-wndY1[i]*u[i]/dy[i];
			dy[i+1] = dy[i+1]-u[i]*u[i]/dy[i];
			wndX1[i+1] = wndX1[i+1]-wndX1[i]*u[i]/dx[i];
			dx[i+1] = dx[i+1]-u[i]*u[i]/dx[i];
		} // for

		for (i = numPoints-2; i > 0; i--) 
		{
			py[i] = (wndY1[i]-u[i]*py[i+1])/dy[i];
			px[i] = (wndX1[i]-u[i]*px[i+1])/dx[i];
		} // for

		for (i = 0; (uint32_t)i < numPoints-1; i++) 
		{
			for (j = 0; (uint32_t)j <= segments; j++) 
			{
				part = zc[i]-(((zc[i]-zc[i+1])/segments)*j);
				t = (part-zc[i])/u[i];
				part = t * points[i+1].y +
					(1.0-t)*points[i].y +
					u[i] * u[i] * ( f(t) * py[i+1] + f(1.0-t) * py[i]) /6.0;
				//        y = Round(part);
				y = static_cast<int32>(part+0.5f);
				part = zc[i]-(((zc[i]-zc[i+1])/segments)*j);
				t = (part-zc[i])/u[i];
				part = t*points[i+1].x+(1.0-t)*points[i].x+u[i]*u[i]*(f(t)*px[i+1]+
					f(1.0-t)*px[i])/6.0;

				//      x = Round(part);
				x = static_cast<int32>(part+0.5f);
				if (runOnce) ogLine(oldX, oldY, x, y, colour); else runOnce = true;
				oldX = x;
				oldY = y;
			} // for j
		} // for i
	} while (false);

	delete [] py;
	delete [] px;
	delete [] wndY1;
	delete [] wndX1;
	delete [] u;
	delete [] dy;
	delete [] dx;
	delete [] zc;

} // void ogSurface::ogSpline()

void ogSurface::ogTriangle(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, uint32_t colour) 
{
	ogLine(x1, y1, x2, y2,colour);
	ogLine(x2, y2, x3, y3,colour);
	ogLine(x3, y3, x1, y1,colour);
} // void ogSurface::ogTriangle()

void ogSurface::ogUnpack(uint32_t colour, uInt8& red, uInt8& green, uInt8& blue) 
{
	switch (bytesPerPix) 
	{
	case 4:
	case 3:
		red   = colour >> redFieldPosition;
		green = colour >> greenFieldPosition;
		blue  = colour >> blueFieldPosition;
		break;
	case 2:
		red   = ((colour >> redFieldPosition) << redShifter);
		green = ((colour >> greenFieldPosition) << greenShifter);
		blue  = ((colour >> blueFieldPosition) << blueShifter);
		if (red != 0)   red += ogPixelFmt::OG_MASKS[redShifter];
		if (green != 0) green += ogPixelFmt::OG_MASKS[greenShifter];
		if (blue != 0)  blue += ogPixelFmt::OG_MASKS[blueShifter];
		break;
	case 1:

		if (pal == NULL) 
		{
			red = green = blue = 0;
			return;
		} // if pal == null

		if (colour > 255) colour &= 255;
		red   = pal[colour].red;
		green = pal[colour].green;
		blue  = pal[colour].blue; 
		break;
	default:
		red   = 0;
		green = 0;
		blue  = 0;
	} // switch

} // void ogSurface::ogUnpack()

void ogSurface::ogUnpack(uint32_t colour, uInt8& red, uInt8& green, uInt8& blue, uInt8& alpha) 
{

	switch (bytesPerPix) {
	case 4:
		red   = colour >> redFieldPosition;
		green = colour >> greenFieldPosition;
		blue  = colour >> blueFieldPosition;
		alpha = colour >> alphaFieldPosition;
		break;
	case 3:
		red   = colour >> redFieldPosition;
		green = colour >> greenFieldPosition;
		blue  = colour >> blueFieldPosition;
		alpha = ogGetAlpha();
		break;
	case 2:
		red   = ((colour >> redFieldPosition)   << redShifter);
		green = ((colour >> greenFieldPosition) << greenShifter);
		blue  = ((colour >> blueFieldPosition)  << blueShifter);
		if (red != 0)   red += ogPixelFmt::OG_MASKS[redShifter];
		if (green != 0) green += ogPixelFmt::OG_MASKS[greenShifter];
		if (blue != 0)  blue += ogPixelFmt::OG_MASKS[blueShifter];

		if (alphaShifter != 8) {
			alpha = (colour >> alphaFieldPosition) << alphaShifter;
			if (alpha != 0) alpha += ogPixelFmt::OG_MASKS[alphaShifter];
		} else alpha = ogGetAlpha();

		break;
	case 1:

		if (pal == NULL) 
		{
			red = green = blue = alpha = 0;
			return;
		} // if pal == null

		if (colour > 255) colour &= 255;
		red   = pal[colour].red;
		green = pal[colour].green;
		blue  = pal[colour].blue; 
		alpha = pal[colour].alpha;
		break;
	default:
		red = green = blue = alpha = 0;
	} // switch

	return;
} // void ogSurface::ogUnpack()

void ogSurface::ogVFlip()
{
	if (!ogAvail()) return;

	for (uint32_t yy = 0; yy <= maxY; yy++)
	{
		for (uint32_t xx = 0; xx < maxX/2; xx++)
		{
			uint32_t swapColour = RawGetPixel(xx, yy);
			RawSetPixel(xx, yy, RawGetPixel(maxX-xx, yy));
			RawSetPixel(maxX-xx, yy, swapColour);
		} // for xx
	} // for yy
} // void ogSurface::ogVFlip()

void ogSurface::ogVLine(int32 x, int32 y1, int32 y2, uint32_t colour) 
{
	int32 tmp;
	uInt8 r, g, b, a;

	if (!ogAvail()) return;
	if ((uint32_t)x > maxX) return;

	if (y1 > y2) 
	{
		tmp= y1;
		y1 = y2;
		y2 = tmp;
	} // if

	if (y1 < 0) y1 = 0;
	if (y2 > (int32)maxY) y2 = maxY;
	if (y2 < y1) return;

	if (ogIsBlending()) {

		ogUnpack(colour, r, g, b, a);

		if (a == 0) return;

		if (a != 255) {
			for (tmp = y1; tmp <= y2; tmp++) 
				RawSetPixel(x, tmp, r, g, b, a);
			return;
		} // if 

	} // if blending
	for (int32 yy = y1; yy <= y2; yy++)
	{
		RawSetPixel(x, yy, colour);
	}
} // void ogSurface::ogVLine()

ogSurface::~ogSurface(void) {

	if (dataState == ogOwner) 
	{
		delete [] pal;
		delete [] lineOfs;
		delete attributes;
#ifdef __UBIXOS_KERNEL__
		kfree(buffer);
#else
		free(buffer);
#endif
	}  // if datastate == ogOwner

	pal    = NULL;
	lineOfs= NULL;
	buffer = NULL;
	attributes = NULL;
	bSize  = 0;
	lSize  = 0;
	dataState = ogNone;
	return;
} // ogSurface::~ogSurface
