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


		#include "picture/pict.h"
		#include "color/color.h"
		#include "log/log.h"

		using namespace GS;


//------------------------------------------------------------------------------
bool			Picture::ApplyConvolution(uint k_width, uint k_height, const int *p_w, int weight, int pass, const Rect <int> *clip_rect)
{
	if	(GetBpp() != 32)
		__ERR__(__LOG_E__ << "Convolution filter only supported on 32bpp picture.\n", false);
	if	(!k_width || !k_height)
		__ERR__(__LOG_E__ << "Invalid kernel size (" << k_width << "x" << k_height << ").\n", false);

	if	(pass <= 0)
		return true;

	// Clipping rect.
	Rect <int>	rect = GetRect();
	if	(!clip_rect)
		clip_rect = &rect;

	// Setup flip chain.
	Picture tmp(*this), *src, *dst;

	if	(pass & 1)
			{ src = &tmp; dst = this; }
	else	{ src = this; dst = &tmp; }

	// Perform convolution.
	for	(int p = 0; p < pass; ++p)
	{
		uchar *pdata = dst->GetDataOffset(clip_rect->sx, clip_rect->sy);

		for (int y = 0; y < clip_rect->GetHeight(); ++y)
		{
			uchar *pscan = pdata;

			for	(int x = 0; x < rect.GetWidth(); ++x)
			{
				int sx = x - k_width / 2,
					sy = y - k_height / 2;

				int	k_sx = Types::Max <int> (sx, clip_rect->sx),
					k_sy = Types::Max <int> (sy, clip_rect->sy);
				int	k_ex = Types::Min <int> (k_width + x - k_width / 2, clip_rect->ex),
					k_ey = Types::Min <int> (k_height + y - k_height / 2, clip_rect->ey);

				const int *w = p_w + Types::Max(0, clip_rect->sx - sx)
								   + Types::Max(0, clip_rect->sy - sy) * k_width;

				int k = 0;
				int	accu[4] = { 0, 0, 0, 0 };
				for (int ky = k_sy; ky < k_ey; ++ky)
				{
					const int *sw = w;

					for (int kx = k_sx; kx < k_ex; ++kx)
					{
						uchar *psrc = src->GetDataOffset(kx, ky);

						accu[0] += psrc[0] * *sw;
						accu[1] += psrc[1] * *sw;
						accu[2] += psrc[2] * *sw;
						accu[3] += psrc[3] * *sw;

						k += *sw++;
					}
					w += k_width;
				}

				k = k ? (weight << 6) / k : (weight << 6);
				pscan[0] = (uchar)Types::Clamp((accu[0] * k) >> 14, 0, 255);
				pscan[1] = (uchar)Types::Clamp((accu[1] * k) >> 14, 0, 255);
				pscan[2] = (uchar)Types::Clamp((accu[2] * k) >> 14, 0, 255);
				pscan[3] = (uchar)Types::Clamp((accu[3] * k) >> 14, 0, 255);

				pscan += 4;
			}
			pdata += dst->GetPitch();
		}

		Picture *swp = dst; dst = src; src = swp;
	}
	return true;
}
//------------------------------------------------------------------------------
