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


		#include <cstring>
		#include <iostream>
		#include "picture/pict.h"
		#include "log/log.h"

		using namespace GS;


//------------------------------------------------------------------------------
bool				Picture::HasAlpha() const
{
	uchar *pdata = GetData();
	if	(!pdata || !width || !height)
		return false;

	for	(uint y = 0; y < height; y++)
		for	(uint x = 0; x < width; x++)
		{
			union
			{
				uint packed;
				uchar ppack[4];
			};

			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;
			}

			int a = (int)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount));
			if	(a < ((1 << pxformat.acount) - 1))
				return true;
		}
	return false;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uint				Picture::ColorBlend(uint u, uint v, float opacity)
{
	int		fk = (int)(opacity * 65536),
			ik = 65536 - fk;

	uint	ta = ((u >> 24) & 0xff) * ik + ((v >> 24) & 0xff) * fk,
			tr = ((u >> 16) & 0xff) * ik + ((v >> 16) & 0xff) * fk,
			tg = ((u >> 8) & 0xff) * ik + ((v >> 8) & 0xff) * fk,
			tb = (u & 0xff) * ik + (v & 0xff) * fk;

	return ((ta << 8) & 0xff000000) + (tr & 0xff0000) + ((tg >> 8) & 0xff00) + (tb >> 16);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Picture::Compare(const Picture &picture) const
{
	if	(pxformat != picture.GetPixelFormat().GetDesc())
		return false;
	// Binary comparison.
	return !Memory::Compare(data, picture.GetData(), width * height * (pxformat.GetBpp() >> 3));
}
bool				Picture::ComputeHash()
{
	uchar *pdata = GetData();
	if	(!pdata || !width || !height)
		return false;

	hash = 0;
	for	(uint y = 0; y < height; y++)
		for	(uint x = 0; x < width; x++)
		{
			union
			{
				uint packed;
				uchar ppack[4];
			};

			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;
			}

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

			hash += (a << 24) + (r << 16) + (g << 8) + b;
		}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Picture::SetData(void *ud, uint w, uint h, const PixelFormatDescription &fmt, bool take_ownership)
{
	if	(!ud)
		return;

	FreeData();
	hash = 0;

	data = (uchar *)ud;
	if	(!take_ownership)
		pic_flag.Set(HasForeignData);

	width = w;
	height = h;
	pxformat.Set(fmt);
}
bool				Picture::AllocAs(uint w, uint h, const PixelFormatDescription &fmt)
{
	if	(!pic_flag.IsSet(HasForeignData) && data && (width == w) && (height == h) && (pxformat.GetBpp() == fmt.bpp))
	{
		pxformat.Set(fmt);
		return true;
	}

	FreeData();

	if	(w && h && fmt.bpp)
	{
		size_t count = w * h * (fmt.bpp / 8);

		data = AllocMemory(count);
		if	(data)
			memset(data, 0, sizeof(uchar) * count);
		else
			__ERR__(__LOG_E__ << "Failed to allocate picture buffer (" << w << "x" << h << "@" << fmt.bpp << "bpp).\n", false);

		width = w;
		height = h;
		pxformat.Set(fmt);
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Picture::Clone(const Picture &src, bool clone_data)
{
	Free();

	if	(src.IsStub())
		Stub(src.GetWidth(), src.GetHeight());
	else
	{
		if	(clone_data)
		{
			if	(AllocAs(src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc()))
				Memory::Copy((char *)data, (char *)src.GetData(), width * height * (pxformat.GetBpp() / 8));
		}
		else
			SetData(src.GetData(), src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc(), false);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Picture::Stub(uint w, uint h)
{
	Free();
	protected_flag.Set(PictureIsStub);
	width = w;
	height = h;
}
void				Picture::Zeroify()
{
	data = NULL;
	hash = 0;
	width = height = 0;
	pxformat.Set(PixelFormat::NONE);
	pic_flag = 0;
	protected_flag = 0;
}
void				Picture::FreeData()
{
	if	(!pic_flag.IsSet(HasForeignData))
		FreeMemory(data);
	data = NULL;
	pic_flag.Remove(HasForeignData);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uchar				*Picture::AllocMemory(size_t size)	
{	return (uchar *)Alloc::DefaultAllocator::Alloc(size, Alloc::Picture);	}
void				Picture::FreeMemory(uchar *data)
{	Alloc::DefaultAllocator::Delete(data, Alloc::Picture);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Picture::~Picture() { Free(); }
//------------------------------------------------------------------------------
