/*  $Id: pixel_buffer_generic.cpp,v 1.5 2004/02/18 14:32:15 sphair Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This library is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "Display/display_precomp.h"
#include "API/Display/pixel_buffer.h"
#include "pixel_buffer_generic.h"

/////////////////////////////////////////////////////////////////////////////
// CL_PixelBuffer_Generic construction:

/////////////////////////////////////////////////////////////////////////////
// CL_PixelBuffer_Generic operations:

void CL_PixelBuffer_Generic::convert(void* in_buffer, const CL_PixelFormat& in_pf, int in_pitch, void* out_buffer, const CL_PixelFormat& out_pf, int out_pitch, const CL_Size& size)
{
	cl_assert(in_pf.get_type() == pixelformat_rgba);
	cl_assert(out_pf.get_type() == pixelformat_rgba);
	
	const int in_bpp = in_pf.get_depth()/8;
	const int out_bpp = out_pf.get_depth()/8;
	char* in_p = static_cast<char*>(in_buffer);
	char* out_p = static_cast<char*>(out_buffer);

	if (in_pf == out_pf)
	{
		const int in_line_bytes = in_bpp*size.width;
		for (int y = 0; y < size.height; y++, in_p += in_pitch, out_p += out_pitch)
			memcpy(out_p, in_p, in_line_bytes);
	}
	else
	{
		const int in_r_shift = CL_PixelFormat::get_mask_shift(in_pf.get_red_mask());
		const int in_g_shift = CL_PixelFormat::get_mask_shift(in_pf.get_green_mask());
		const int in_b_shift = CL_PixelFormat::get_mask_shift(in_pf.get_blue_mask());
		const int in_a_shift = CL_PixelFormat::get_mask_shift(in_pf.get_alpha_mask());

		const int in_r_bits = CL_PixelFormat::get_mask_bits(in_pf.get_red_mask());
		const int in_g_bits = CL_PixelFormat::get_mask_bits(in_pf.get_green_mask());
		const int in_b_bits = CL_PixelFormat::get_mask_bits(in_pf.get_blue_mask());
		const int in_a_bits = CL_PixelFormat::get_mask_bits(in_pf.get_alpha_mask());

		const int in_r_bitmask = CL_PixelFormat::get_bitmask(in_r_bits, 0);
		const int in_g_bitmask = CL_PixelFormat::get_bitmask(in_g_bits, 0);
		const int in_b_bitmask = CL_PixelFormat::get_bitmask(in_b_bits, 0);
		const int in_a_bitmask = CL_PixelFormat::get_bitmask(in_a_bits, 0);

		const int out_r_shift = CL_PixelFormat::get_mask_shift(out_pf.get_red_mask());
		const int out_g_shift = CL_PixelFormat::get_mask_shift(out_pf.get_green_mask());
		const int out_b_shift = CL_PixelFormat::get_mask_shift(out_pf.get_blue_mask());
		const int out_a_shift = CL_PixelFormat::get_mask_shift(out_pf.get_alpha_mask());

		const int out_r_bits = CL_PixelFormat::get_mask_bits(out_pf.get_red_mask());
		const int out_g_bits = CL_PixelFormat::get_mask_bits(out_pf.get_green_mask());
		const int out_b_bits = CL_PixelFormat::get_mask_bits(out_pf.get_blue_mask());
		const int out_a_bits = CL_PixelFormat::get_mask_bits(out_pf.get_alpha_mask());

		const bool in_has_ck = in_pf.has_colorkey();
		unsigned int in_ck;
		if (in_has_ck)
			in_ck = in_pf.get_colorkey();
		const bool out_has_ck = out_pf.has_colorkey();
		unsigned int out_ck;
		if (out_has_ck)
			out_ck = out_pf.get_colorkey();
		
		for (int y = 0; y < size.height; y++, in_p += in_pitch, out_p += out_pitch)
		{
			char* in_p2 = in_p;
			char* out_p2 = out_p;
			for (int x = 0; x < size.width; x++, in_p2 += in_bpp, out_p2 += out_bpp)
			{
				unsigned int in_pixel = 0;
				memcpy(&in_pixel, in_p2, in_bpp);

				// decode
				unsigned int in_r = 0, in_g = 0, in_b = 0, in_a = 0xffffffff;
				if (in_r_bitmask)
				{
					in_r = ((in_pixel >> in_r_shift) & in_r_bitmask);
					if (in_r & 1)
						in_r = (in_r << (32 - in_r_bits)) | CL_PixelFormat::get_bitmask(32 - in_r_bits, 0);
					else
						in_r <<= 32 - in_r_bits;
				}
				if (in_g_bitmask)
				{
					in_g = ((in_pixel >> in_g_shift) & in_g_bitmask);
					if (in_g & 1)
						in_g = (in_g << (32 - in_g_bits)) | CL_PixelFormat::get_bitmask(32 - in_g_bits, 0);
					else
						in_g <<= 32 - in_g_bits;
				}
				if (in_b_bitmask)
				{
					in_b = ((in_pixel >> in_b_shift) & in_b_bitmask);
					if (in_b & 1)
						in_b = (in_b << (32 - in_b_bits)) | CL_PixelFormat::get_bitmask(32 - in_b_bits, 0);
					else
						in_b <<= 32 - in_b_bits;
				}
				if (in_a_bitmask)
				{
					in_a = ((in_pixel >> in_a_shift) & in_a_bitmask);
					if (in_a & 1)
						in_a = (in_a << (32 - in_a_bits)) | CL_PixelFormat::get_bitmask(32 - in_a_bits, 0);
					else
						in_a <<= 32 - in_a_bits;
				}

				unsigned int out_pixel;
				bool encode;
				if (!out_a_bits && (in_a == 0) && out_has_ck)
				{
					out_pixel = out_ck;
					encode = false;
				}
				else if (in_has_ck && (in_pixel == in_ck))
				{
					if (out_has_ck)
					{
						out_pixel = out_ck;
						encode = false;
					}
					else
					{
						in_a = 0;
						encode = true;
					}
				}	
				else
					encode = true;

				// encode
				if (encode)
				{
					out_pixel = 0;
					if (out_r_bits)
						out_pixel |= (in_r >> (32 - out_r_bits)) << out_r_shift;
					if (out_g_bits)
						out_pixel |= (in_g >> (32 - out_g_bits)) << out_g_shift;
					if (out_b_bits)
						out_pixel |= (in_b >> (32 - out_b_bits)) << out_b_shift;
					if (out_a_bits)
						out_pixel |= (in_a >> (32 - out_a_bits)) << out_a_shift;
				}

				memcpy(out_p2, &out_pixel, out_bpp);
			}
		}
	}
}

void CL_PixelBuffer_Generic::convert_pal(void* in_buffer, const CL_PixelFormat& in_pf, int in_pitch, const CL_Palette& in_pal, void* out_buffer, const CL_PixelFormat& out_pf, int out_pitch, const CL_Size& size)
{
	cl_assert(in_pf.get_type() == pixelformat_index);
	cl_assert(out_pf.get_type() == pixelformat_rgba);

	const int in_bpp = in_pf.get_depth()/8;
	const int out_bpp = out_pf.get_depth()/8;
	char* in_p = static_cast<char*>(in_buffer);
	char* out_p = static_cast<char*>(out_buffer);

	const int out_r_shift = CL_PixelFormat::get_mask_shift(out_pf.get_red_mask());
	const int out_g_shift = CL_PixelFormat::get_mask_shift(out_pf.get_green_mask());
	const int out_b_shift = CL_PixelFormat::get_mask_shift(out_pf.get_blue_mask());
	const int out_a_shift = CL_PixelFormat::get_mask_shift(out_pf.get_alpha_mask());

	const int out_r_bits = CL_PixelFormat::get_mask_bits(out_pf.get_red_mask());
	const int out_g_bits = CL_PixelFormat::get_mask_bits(out_pf.get_green_mask());
	const int out_b_bits = CL_PixelFormat::get_mask_bits(out_pf.get_blue_mask());
	const int out_a_bits = CL_PixelFormat::get_mask_bits(out_pf.get_alpha_mask());

	const bool in_has_ck = in_pf.has_colorkey();
	unsigned int in_ck;
	if (in_has_ck)
		in_ck = in_pf.get_colorkey();
	const bool out_has_ck = out_pf.has_colorkey();
	unsigned int out_ck;
	if (out_has_ck)
		out_ck = out_pf.get_colorkey();
	
	for (int y = 0; y < size.height; y++, in_p += in_pitch, out_p += out_pitch)
	{
		char* in_p2 = in_p;
		char* out_p2 = out_p;
		for (int x = 0; x < size.width; x++, in_p2 += in_bpp, out_p2 += out_bpp)
		{
			unsigned int in_pixel = 0;
			memcpy(&in_pixel, in_p2, in_bpp);

			// decode
			unsigned int in_r = 0, in_g = 0, in_b = 0, in_a = 0xffffffff;
			in_r = in_pal.colors[in_pixel].get_red();
			if (in_r & 1)
				in_r = (in_r << (32 - 8)) | CL_PixelFormat::get_bitmask(32 - 8, 0);
			else
				in_r <<= 32 - 8;
			in_g = in_pal.colors[in_pixel].get_green();
			if (in_g & 1)
				in_g = (in_g << (32 - 8)) | CL_PixelFormat::get_bitmask(32 - 8, 0);
			else
				in_g <<= 32 - 8;
			in_b = in_pal.colors[in_pixel].get_blue();
			if (in_b & 1)
				in_b = (in_b << (32 - 8)) | CL_PixelFormat::get_bitmask(32 - 8, 0);
			else
				in_b <<= 32 - 8;
			in_a = in_pal.colors[in_pixel].get_alpha();
			if (in_a & 1)
				in_a = (in_a << (32 - 8)) | CL_PixelFormat::get_bitmask(32 - 8, 0);
			else
				in_a <<= 32 - 8;

			unsigned int out_pixel;
			bool encode;
			if (!out_a_bits && (in_a == 0) && out_has_ck)
			{
				out_pixel = out_ck;
				encode = false;
			}
			else if (in_has_ck && (in_pixel == in_ck))
			{
				if (out_has_ck)
				{
					out_pixel = out_ck;
					encode = false;
				}
				else
				{
					in_a = 0;
					encode = true;
				}
			}	
			else
				encode = true;

			// encode
			if (encode)
			{
				out_pixel = 0;
				if (out_r_bits)
					out_pixel |= (in_r >> (32 - out_r_bits)) << out_r_shift;
				if (out_g_bits)
					out_pixel |= (in_g >> (32 - out_g_bits)) << out_g_shift;
				if (out_b_bits)
					out_pixel |= (in_b >> (32 - out_b_bits)) << out_b_shift;
				if (out_a_bits)
					out_pixel |= (in_a >> (32 - out_a_bits)) << out_a_shift;
			}

			memcpy(out_p2, &out_pixel, out_bpp);
		}
	}
}
