

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XError.h>
#include <XDraw.h>

#if OPT_WINOS

XGBitmap::XGBitmap()
{
	bitmap_buff = 0;
}

XGBitmap::~XGBitmap()
{
	FreeInternalBitmap();
}
		
void XGBitmap::FreeInternalBitmap()
{
	if(bitmap_buff != 0)
		delete[] bitmap_buff;
	bitmap_buff = 0;
}
		
void XGBitmap::SetBitmapRGBA( const void *rgba, int width, int height, int rowbytes)
{
	FreeInternalBitmap();

	if(!rgba || width < 1 || height < 1)
		return;
	
	if(rowbytes == 0)
		rowbytes = width*4;
	
	unsigned char *rgb = new unsigned char[width * height * 3];
	unsigned char *out = rgb;
	for (int y = 0; y < height; ++y)
	{
		const unsigned char *in = (const unsigned char *)rgba + y * rowbytes;
		for (int x = 0; x < width; ++x)
		{
			int r = in[0];
			int g = in[1];
			int b = in[2];
			int a = in[3];
			in += 4;
			if (a < 255)
			{
				int back = (((y ^ x) & 8) ? 0xcc : 0xff) * (256 - a);
				r = (r * a + back) >> 8;
				g = (g * a + back) >> 8;
				b = (b * a + back) >> 8;
			}
			out[0] = r;
			out[1] = g;
			out[2] = b;
			out += 3;
		}
	}
	
	SetBitmapRGB( rgb, width, height, 0 );
	
	delete [] rgb;
}

void XGBitmap::SetBitmapRGB( const void *rgb, int width, int height, int rowbytes)
{
	FreeInternalBitmap();
	
	if(!rgb || width < 1 || height < 1)
		return;
		
	if(rowbytes == 0)
		rowbytes = width*3;
		
	// padd.
	bitmap_buff = WordAlignScanLines((unsigned char*)rgb, width, height, rowbytes);
	FillBitmapInfo(&bitmap_info, width, height, rowbytes);
	
	// make it bgr.
	ReverseBytes(bitmap_buff, width, height, rowbytes);
}

void XGBitmap::SetBitmapAlpha( const void *alpha, int width, int height, int rowbytes)
{
	FreeInternalBitmap();
	
	if(!alpha || width < 1 || height < 1)
		return;
	
	if(rowbytes == 0 || rowbytes == width)
		rowbytes = width*3;
	
	// expand to rgb.
	unsigned char* dst;
	unsigned char* new_alpha = new unsigned char[height*rowbytes];
	unsigned char* rel_alpha = (unsigned char*)alpha;
	int len = width*height;
	
	dst = new_alpha;
	while(len-- > 0)
	{
		dst[0] = *rel_alpha;
		dst[1] = *rel_alpha;
		dst[2] = *rel_alpha;
		
		rel_alpha++;
		dst+=3;
	}

	// padd.
	bitmap_buff = WordAlignScanLines(new_alpha, width, height, rowbytes);
	FillBitmapInfo(&bitmap_info, width, height, rowbytes);
	
	delete[] new_alpha;
}

void XGBitmap::ReverseBytes(unsigned char* in_buff, int width, int height, int rowbytes)
{
	int numbytes = rowbytes/width;
	int len = height*rowbytes;
	int i, k;
	unsigned char store[8];
	
	if(numbytes >= 8)
		return;
	
	for(i = 0; i < len; i += numbytes)
	{
		for(k = 0; k < numbytes; k++)
			store[k] = in_buff[i+k];
		// reverse;
		for(k = numbytes-1; k >= 0; k--)
			in_buff[i+k] = store[numbytes-k-1];
	}
}

/*
returned buffer must always be freed by caller, it will never be in_buff.
*/
unsigned char* XGBitmap::WordAlignScanLines(const unsigned char* in_buff, int width, int height, int rowbytes)
{
#ifdef __MWERKS__
	#pragma unused(width)
#endif
	if(rowbytes&1)
	{
		// pad out by one byte.
		unsigned char* dst_buff;
		unsigned char* new_buff = new unsigned char[height*(rowbytes+1)];
		
		dst_buff = new_buff;
		// copy each scanline over and padd it.
		for(int y = 0; y < height; y++)
		{
			//memcpy(dst_buff, in_buff, rowbytes);			
			*(dst_buff+rowbytes) = 0;
			dst_buff += rowbytes+1;
			in_buff  += rowbytes;
		}
		
		return new_buff;
	}

	// already word aligned, just copy.
	unsigned char* new_buff = new unsigned char[height*rowbytes];
	memcpy(new_buff, in_buff, height*rowbytes);
	return new_buff;
}

void XGBitmap::FillBitmapInfo(BITMAPINFO* info, int width, int height, int rowbytes)
{
	memset(info, 0, sizeof(BITMAPINFO));
	info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	info->bmiHeader.biWidth = width;
	info->bmiHeader.biHeight = height;
	info->bmiHeader.biPlanes = 1;
	info->bmiHeader.biBitCount = (rowbytes/width)*8;
	info->bmiHeader.biCompression = BI_RGB;
	info->bmiHeader.biSizeImage = 0;
	info->bmiHeader.biXPelsPerMeter = 0;
	info->bmiHeader.biYPelsPerMeter = 0;
	info->bmiHeader.biClrUsed = 0;
	info->bmiHeader.biClrImportant = 0;	
}
	
/*
** not quite yet

void XGBitmap::LoadFromPicture( int resID );
void XGBitmap::LoadFromIcon( int resID );

*/
		
int	XGBitmap::GetWidth()
{
	if(bitmap_buff == 0)
		return -1;
	
	return bitmap_info.bmiHeader.biWidth;
}

int	XGBitmap::GetHeight()
{
	if(bitmap_buff == 0)
		return -1;
	return bitmap_info.bmiHeader.biHeight;
}

#endif

#if OPT_MACOS	// no xgbitmap for other platforms YET

XGBitmap::XGBitmap()
{
	fGWorld = 0;
}


XGBitmap::~XGBitmap()
{
	FreeInternalBitmap();
}


void XGBitmap::FreeInternalBitmap()
{
	if (fGWorld)
	{
		DisposeGWorld( fGWorld );
		fGWorld = 0;
	}
}


void XGBitmap::SetBitmapRGBA( const void *rgba, int width, int height, int rowbytes )
{
	OSErr err;
	Rect bounds;
	
	FreeInternalBitmap();
	if (!rgba || width < 1 || height < 1) return;
	if (rowbytes < width * 4 && rowbytes > -width * 4)
	{
		if (rowbytes >= 0)
		{
			rowbytes = width * 4;
		}
		else
		{
			rowbytes = -width * 4;
		}
	}
	
	bounds.left = 0;
	bounds.top = 0;
	bounds.right = width;
	bounds.bottom = height;
	err = NewGWorld( &fGWorld, 32, &bounds, 0, 0, 0 );
	if (err)
	{
		if (fGWorld)
		{
			DisposeGWorld( fGWorld );
			fGWorld = 0;
		}
	}
	if (!fGWorld) return;
	
	PixMapHandle pmap = GetGWorldPixMap( fGWorld );
	LockPixels( pmap );
	int pmap_rb = pmap[0]->rowBytes & 0x3fff;
	Rect *b = &pmap[0]->bounds;
	Ptr topleft = GetPixBaseAddr(pmap) - b->left * 4 - b->top * pmap_rb;
	
	for (int y = 0; y < height; ++y)
	{
		uint32 *out = (uint32 *)(topleft + y * pmap_rb);
		const uint32 *in = (const uint32 *)(((const char *)rgba) + y * rowbytes);
		for (int x = 0; x < width; ++x)
		{
			uint32 p = *in++;
			*out++ = (p << 24) | (p >> 8);
		}
	}
	
	UnlockPixels( pmap );
}


void XGBitmap::SetBitmapRGB( const void *rgb, int width, int height, int rowbytes )
{
	OSErr err;
	Rect bounds;
	
	FreeInternalBitmap();
	if (!rgb || width < 1 || height < 1) return;
	if (rowbytes < width * 3 && rowbytes > -width * 3)
	{
		if (rowbytes >= 0)
		{
			rowbytes = width * 3;
		}
		else
		{
			rowbytes = -width * 3;
		}
	}
	
	bounds.left = 0;
	bounds.top = 0;
	bounds.right = width;
	bounds.bottom = height;
	err = NewGWorld( &fGWorld, 32, &bounds, 0, 0, 0 );
	if (err)
	{
		if (fGWorld)
		{
			DisposeGWorld( fGWorld );
			fGWorld = 0;
		}
	}
	if (!fGWorld) return;
	
	PixMapHandle pmap = GetGWorldPixMap( fGWorld );
	LockPixels( pmap );
	int pmap_rb = pmap[0]->rowBytes & 0x3fff;
	Rect *b = &pmap[0]->bounds;
	Ptr topleft = GetPixBaseAddr(pmap) - b->left * 4 - b->top * pmap_rb;
	
	for (int y = 0; y < height; ++y)
	{
		uint32 *out = (uint32 *)(topleft + y * pmap_rb);
		const unsigned char *in = ((const unsigned char *)rgb) + y * rowbytes;
		for (int x = 0; x < width; ++x)
		{
			uint32 p = (in[0] << 16) + (in[1] << 8) + in[2];
			*out++ = p;
			in += 3;
		}
	}
	
	UnlockPixels( pmap );
}


void XGBitmap::SetBitmapAlpha( const void *alpha, int width, int height, int rowbytes )
{
	OSErr err;
	Rect bounds;
	
	FreeInternalBitmap();
	if (!alpha || width < 1 || height < 1) return;
	if (rowbytes < width && rowbytes > -width)
	{
		if (rowbytes >= 0)
		{
			rowbytes = width;
		}
		else
		{
			rowbytes = -width;
		}
	}
	
	bounds.left = 0;
	bounds.top = 0;
	bounds.right = width;
	bounds.bottom = height;
	err = NewGWorld( &fGWorld, 32, &bounds, 0, 0, 0 );
	if (err)
	{
		if (fGWorld)
		{
			DisposeGWorld( fGWorld );
			fGWorld = 0;
		}
	}
	if (!fGWorld) return;
	
	PixMapHandle pmap = GetGWorldPixMap( fGWorld );
	LockPixels( pmap );
	int pmap_rb = pmap[0]->rowBytes & 0x3fff;
	Rect *b = &pmap[0]->bounds;
	Ptr topleft = GetPixBaseAddr(pmap) - b->left * 4 - b->top * pmap_rb;
	
	for (int y = 0; y < height; ++y)
	{
		uint32 *out = (uint32 *)(topleft + y * pmap_rb);
		const unsigned char *in = ((const unsigned char *)alpha) + y * rowbytes;
		for (int x = 0; x < width; ++x)
		{
			uint32 p = *in++ * 0x01010101;
			*out++ = p;
		}
	}
	
	UnlockPixels( pmap );
}


// void XGBitmap::LoadFromPicture( int resID ); // to be implemented
// void XGBitmap::LoadFromIcon( int resID ); // to be implemented

int XGBitmap::GetWidth()
{
	Rect r;
	if (!fGWorld) return 0;
	r = fGWorld->portRect;
	//GetPortBounds( fGWorld, &r );
	return r.right - r.left;
}


int XGBitmap::GetHeight()
{
	Rect r;
	if (!fGWorld) return 0;
	r = fGWorld->portRect;
	//GetPortBounds( fGWorld, &r );
	return r.bottom - r.top;
}

#endif

