/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "picture/pict_color_format.h"
		#include "picture/pict.h"
		#include "math/nmath.h"
		#include "sort/sort.h"
		#include "memory/endian.h"
		#include "log/log.h"

		using namespace GS;


PixelFormatDescription	PixelFormat::NONE
					= {PixelColorSpace_NULL, 0, 0, 0, 0, 0, false};
PixelFormatDescription	PixelFormat::BGRA8
					= {PixelColorSpace_RGB, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 32, false};
PixelFormatDescription	PixelFormat::RGBA8
					= {PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 32, false};
PixelFormatDescription	PixelFormat::ARGB8
					= {PixelColorSpace_RGB, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, 32, false};
PixelFormatDescription	PixelFormat::BGR8
					= {PixelColorSpace_RGB, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 24, false};
PixelFormatDescription	PixelFormat::RGB8
					= {PixelColorSpace_RGB, 0, 0x000000ff, 0x0000ff00,0x00ff0000, 24, false};
PixelFormatDescription	PixelFormat::RGB555
					= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000003e0, 0x00007c00, 16, false};
PixelFormatDescription	PixelFormat::RGB565
					= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000007e0, 0x0000f800, 16, false};
PixelFormatDescription	PixelFormat::RGBA4444
					= {PixelColorSpace_RGB, 0x0000f000, 0x0000000f, 0x000000f0, 0x00000f00, 16, false};
PixelFormatDescription	PixelFormat::RGBF
= { PixelColorSpace_RGB, 0, 0, 1, 2, sizeof(float) * 3 * 8, true };
PixelFormatDescription	PixelFormat::RGBAF
= { PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 4 * 16, true };

//------------------------------------------------------------------------------
String				PixelFormat::GetName() const
{
	// Sort components.
	uint count[4] = { Memory::GetBitCount(desc.rmask), Memory::GetBitCount(desc.gmask), Memory::GetBitCount(desc.bmask), Memory::GetBitCount(desc.amask) },
		 shift[4] = { Memory::GetShiftCount(desc.rmask), Memory::GetShiftCount(desc.gmask), Memory::GetShiftCount(desc.bmask), Memory::GetShiftCount(desc.amask) };

	Sort<uint, uint>::Entry comp_sort[4];
	for	(uint n = 0; n < 4; ++n)
	{
		comp_sort[n].v = shift[n];
		comp_sort[n].o = n;
	}
	Sort<uint, uint>::QuickSort(4, comp_sort);

	// Build name.
	static const char *comp_name[4] = {"R", "G", "B", "A"};

	String name;
	switch (desc.space)
	{
		case PixelColorSpace_RGB:
		{
			for	(uint n = 0; n < 4; ++n)
			{
				int i = comp_sort[n].o;
				if	(count[i] != 0)
				{
					name += String::Format("%s%d", comp_name[i], count[i]);
					if	(desc.real)
						name += "F";
				}
			}
		}
		break;

		default:
			name = "NONE";
			break;
	}
	return name;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uint				PixelFormat::Format(float r, float g, float b, float a) const
{
	switch (GetBpp())
	{
		case 32:
			return Endian::ToHost((int(r * 255) << rshift) + (int(g * 255) << gshift) + (int(b * 255) << bshift) + (int(a * 255) << ashift), Endian::Intel);

		default:
			__LOG_W__ << "Unimplemented formatting.\n";
	}
	return 0;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Picture::Negative(bool r, bool g, bool b, bool a)
{
	if	((GetBpp() != 32) || (!r && !g && !b && !a) || !GetData())
		return;

	int	iia = pxformat.ashift >> 3,
		iir = pxformat.rshift >> 3,
		iig = pxformat.gshift >> 3,
		iib = pxformat.bshift >> 3;

	unsigned char *p = GetData();
	for	(uint v = 0; v < height; ++v)
		for	(uint u = 0; u < width; ++u)
		{
			if	(r)	p[iir] = 255 - p[iir];
			if	(g)	p[iig] = 255 - p[iig];
			if	(b)	p[iib] = 255 - p[iib];
			if	(a)	p[iia] = 255 - p[iia];
			p += 4;
		}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Picture::Fast8888Conversion(const PixelFormat &dsformat)
{
	int	iia = pxformat.ashift >> 3,
		iir = pxformat.rshift >> 3,
		iig = pxformat.gshift >> 3,
		iib = pxformat.bshift >> 3,
		dia = dsformat.ashift >> 3,
		dir = dsformat.rshift >> 3,
		dig = dsformat.gshift >> 3,
		dib = dsformat.bshift >> 3;

	unsigned char *p = GetData();
	if	(!p)
		return false;

	// Optimized for the most common reorganizing done on 8888 ARGB data.
	unsigned char r, g, b, a;
	if	(iia == dia)
	{
		if	(iig == dig)	// Fixed green & alpha.
			for	(uint v = 0; v < GetHeight(); ++v)
				for	(uint u = 0; u < GetWidth(); ++u)
				{
					r = p[iir]; b = p[iib];
					p[dir] = r; p[dib] = b;
					p += 4;
				}
		else				// Fixed alpha.
			for	(uint v = 0; v < GetHeight(); ++v)
				for	(uint u = 0; u < GetWidth(); ++u)
				{
					r = p[iir]; g = p[iig]; b = p[iib];
					p[dir] = r; p[dig] = g; p[dib] = b;
					p += 4;
				}
	}
	else					// Fully generic.
		for	(uint v = 0; v < GetHeight(); ++v)
			for	(uint u = 0; u < GetWidth(); ++u)
			{
				a = p[iia]; r = p[iir]; g = p[iig]; b = p[iib];
				p[dia] = a; p[dir] = r; p[dig] = g; p[dib] = b;
				p += 4;
			}

	pxformat = dsformat;
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Picture::RealToIntegerConversion(const PixelFormat &dstfmt)
{
	uchar *new_data = new uchar[width * height * (dstfmt.GetBpp() / 8)];
	if	(!new_data)
		return false;

	uchar *out = new_data;
	float *pdata = (float *)GetData();

	// Convert.
	for	(uint y = 0; y < GetHeight(); ++y)
		for	(uint x = 0; x < GetWidth(); ++x)
		{
			uchar r = (uchar)Types::Min(pdata[2] * 255.f, 255.f), g = (uchar)Types::Min(pdata[1] * 255.f, 255.f), b = (uchar)Types::Min(pdata[0] * 255.f, 255.f), a = 255;
			pdata += 3;

			// Convert components.
			a >>= (8 - dstfmt.acount);
			r >>= (8 - dstfmt.rcount);
			g >>= (8 - dstfmt.gcount);
			b >>= (8 - dstfmt.bcount);

			// Repack components and output.
			uint packed;
			uchar *ppack = (uchar *)&packed;

			packed = (a << dstfmt.ashift) + (r << dstfmt.rshift) + (g << dstfmt.gshift) + (b << dstfmt.bshift);

			switch (dstfmt.GetBpp())
			{
				case 8:		out[0] = ppack[0]; out++;	break;
				case 16:	out[0] = ppack[0]; out[1] = ppack[1]; out += 2;		break;
				case 24:	out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3;		break;
				case 32:	out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4;		break;
			}
		}

	// Replace data.
	SetData(new_data, width, height, dstfmt.GetDesc());
	return true;
}
bool				Picture::IntegerToIntegerConversion(const PixelFormat &dsformat)
{
	switch (pxformat.GetBpp())
	{
		case 8: case 16: case 24: case 32: break;
		default: return false;				// Unsupported source mode.
	}

	// Allocate destination buffer.
	uchar *new_data = new uchar[width * height * (dsformat.GetBpp() / 8)];
	if	(!new_data)
		return false;

	uchar *out = new_data;
	uchar *pdata = GetData();

	// Convert.
	for	(uint y = 0; y < GetHeight(); ++y)
	{
		uint packed;
		uchar *ppack = (uchar *)&packed;

		for	(uint x = 0; x < GetWidth(); ++x)
		{
			// Extract packed color.
			switch	(pxformat.GetBpp())
			{
				case 8:		ppack[0] = pdata[0]; pdata++;	break;
				case 16:	ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2;	break;
				case 24:	ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3;	break;
				case 32:	ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4;	break;
			}

			// Extract components.
			uint	a = (uchar)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount)),
					r = (uchar)(((packed & pxformat.desc.rmask) >> pxformat.rshift) << (8 - pxformat.rcount)),
					g = (uchar)(((packed & pxformat.desc.gmask) >> pxformat.gshift) << (8 - pxformat.gcount)),
					b = (uchar)(((packed & pxformat.desc.bmask) >> pxformat.bshift) << (8 - pxformat.bcount));

			// Convert components.
			a >>= (8 - dsformat.acount);
			r >>= (8 - dsformat.rcount);
			g >>= (8 - dsformat.gcount);
			b >>= (8 - dsformat.bcount);

			// Repack components and output.
			packed = Endian::ToHost((a << dsformat.ashift) + (r << dsformat.rshift) + (g << dsformat.gshift) + (b << dsformat.bshift), Endian::Intel);

			switch (dsformat.GetBpp())
			{
				case 8:		out[0] = ppack[0]; out++;	break;
				case 16:	out[0] = ppack[0]; out[1] = ppack[1]; out += 2;		break;
				case 24:	out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3;		break;
				case 32:	out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4;		break;
			}
		}
	}

	// Replace data.
	SetData(new_data, width, height, dsformat.GetDesc(), true);
	return true;
}
bool				Picture::Convert(const PixelFormatDescription &dsc)
{
	if	(pxformat == dsc)
		return true;

	if	(!dsc.bpp)
	{
		Free();
		return true;
	}

	PixelFormat dsformat(dsc);

	// Real to integer.
	if	(pxformat.IsReal())
		return RealToIntegerConversion(dsformat);

	// Fast 8888 conversion.
	if	(
			(pxformat.acount == 8) && (pxformat.rcount == 8) && (pxformat.gcount == 8) && (pxformat.bcount == 8) &&
			(dsformat.acount == 8) && (dsformat.rcount == 8) && (dsformat.gcount == 8) && (dsformat.bcount == 8)
		)
		return Fast8888Conversion(dsformat);

	// Slower generic integer->integer conversion.
	return IntegerToIntegerConversion(dsformat);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Picture::Swizzle(uchar r, uchar g, uchar b, uchar a)
{
	// Swizzle output format...
	PixelFormatDescription out_format = GetPixelFormat().GetDesc();

	const uint in_mask[4] = { out_format.rmask, out_format.gmask, out_format.bmask, out_format.amask };
	out_format.rmask = in_mask[r];
	out_format.gmask = in_mask[g];
	out_format.bmask = in_mask[b];
	out_format.amask = in_mask[a];

	// ...and convert picture.
	return Convert(out_format);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Picture::SetFormat(const PixelFormatDescription &dsc)
{
	if	(dsc.bpp != pxformat.GetDesc().bpp)
		__ERR__(__LOG_E__ << "Target format requires a data conversion, see Convert().\n", false)

	pxformat.Set(dsc);
	return true;
}
//------------------------------------------------------------------------------
