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


#ifndef	__NPICTURE__
#define	__NPICTURE__


		#include "geometry/rect.h"
		#include "picture/pict_color_format.h"
		#include "memory/bit_field.h"
		#include "memory/nshared_ptr.h"


namespace GS {
	struct Color;
	struct Vector4;
	class Gradient;

/*!
	@short	The base image class.

	When used with PictureTools, this class provides high-quality sub-pixel
	bitmap manipulations from shrinking/enlargement to blit/resize.

	Importing/exporting from common image formats can be done through the
	GS::PictureIO class.

	@author Emmanuel Julien (ejulien@nworks.fr)
*/
class	Picture : public SharedObject
{
public:

		NPLACEMENT_NEW(Picture)

enum	BlendMode
{
		BlendReplace		=	0,
		BlendCompose,
		BlendComposeFast,
		BlendAdd,
		BlendMultiply,
		BlendMultiply2x,
		BlendAlphaAdd,
		BlendAlphaMultiply,
		BlendAlphaMultiply2x,
		RgbToAlpha
};

protected:

		uchar		*data;				///< Internal pointer to the picture's data.

		uint		hash,				///< Raster data hash value.
					width,				///< Object's width.
					height;				///< Object's height.

		PixelFormat pxformat;			///< Pixel format.

		/// Zeroify the class but do not free anything.
		void		Zeroify();

		/*!
			@short	Internal free.
			@note	Free raster data only.
		*/
		void		FreeData();

enum
{
		PictureIsStub	=	(1 << 1)
};

		BitField	protected_flag;

public:

enum
{
		HasForeignData	=	(1 << 1)
};

		BitField	pic_flag;
		String		name;

/*!
	@name	Blit functions.
	@{
*/
		/// Blit.
static	bool		Blit(const Picture &src, Picture &dst, const Rect <int> *src_rect = 0, const Rect <int> *dst_rect = 0, BlendMode op = BlendReplace);
		/// Blit using a mask.
static	bool		BlitMask(const Picture &src, Picture &dst, Picture &mask, const Rect <int> *src_rect = 0, const Rect <int> *dst_rect = 0);
		/*!
			@short	Scaled blit.

			This function is designed to be used in high quality compositing
			and perform a bilinear filtered sub-pixel blit.

			@note	When a rectangle is omitted the whole picture is used.
		*/
static	bool		ScaleBlit(Picture &src, Picture &dst, Rect <float> *src_rect = 0, Rect <float> *dst_rect = 0);

/*!
	@}
	@name	Cropping/framing functions.
	@{
*/
		/*!
			@short	Reframe picture.

			All offsets may be negative.
			Fill color can be specified and defaults to black.

			@note	Reframe(-2, -2, 2, 2) to display a 2 pixel-wide frame around
					the picture.
		*/
		bool		Reframe(int offset_sx, int offset_sy, int offset_ex, int offset_ey, const Color *fill = 0);
		/// Crop picture to specified dimension.
		bool		Crop(uint target_width, uint target_height)
		{	return (target_width <= width) && (target_height <= height) ? Reframe(0, 0, target_width - width, target_height - height) : false;	}

/*!
	@}
	@name	Rescaling functions.
	@{
*/
		/*!
			@short	Downscale a picture using area-summing filtering.
			@note	0 can be used on width or height to resize the picture
					proportionally.
		*/
		bool		Downscale(uint width, uint height);
		/*!
			@short	Resize a picture using bilinear filtering.
			@note	0 can be used on width or height to resize the picture
					proportionally.
		*/
		bool		Resize(uint width, uint height);
		/// Flip picture.
		bool		Flip(bool flip_h, bool flip_v);

/*!
	@}
	@name	Color conversion functions.
	@{
*/
protected:

		bool		RealToIntegerConversion(const PixelFormat &);
		bool		IntegerToIntegerConversion(const PixelFormat &);
		bool		Fast8888Conversion(const PixelFormat &);

public:

		bool		Swizzle(uchar r = 0, uchar g = 1, uchar b = 2, uchar a = 3);

		/// Convert YUV buffers to RGB buffer.
static	void		YUV422toRGB32(uchar * const yuv_plane[3], uchar *rgb32, int width, int height, int dst_pitch = 0, int dst_height = 0);

		/// Alpha blend two 32bit RGBA values together.
static	uint		ColorBlend(uint u, uint v, float opacity);

static	void		AlphaCompositePixel(uchar *data, uchar r, uchar g, uchar b, uchar a);
static	uchar		AlphaCompositeAlpha(uchar a, uchar b);
static	uchar		AlphaCompositeColor(uchar u, uchar v, uchar a, uchar b, uchar k);

		bool		ToGrayscale();
		void		Negative(bool r = true, bool g = true, bool b = true, bool a = false);

		void		UnpackYCbCr(uchar *y, uchar *cb, uchar *cr);
		void		PackYCbCr(uchar *y, uchar *cb, uchar *cr);

/*!
	@}
	@name	Sampling functions.
	@{
*/
		void		Sample(float u, float v, Color &out, uint _w = 0, uint _h = 0) const;
		void		Sample(float u, float v, uint &out, uint _w = 0, uint _h = 0) const;
		void		SampleRGBA(float u, float v, Color &out, uint _w = 0, uint _h = 0) const;

		uint		SampleInteger(float u, float v, uint _w = 0, uint _h = 0) const;
		Color		SampleColor(float u, float v, uint _w = 0, uint _h = 0) const;
		Color		SampleRGBAColor(float u, float v, uint _w = 0, uint _h = 0) const;
/*!
	@}
	@name	Drawing functions.
	@note	Most of these functions are for now extremely slow!
	@{
*/
protected:

		/// Low-level draw line.
		void		LowLevelDrawLine(bool hq, float sx, float sy, float ex, float ey, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);

public:

		/// Fill picture with a given color.
		bool		Fill(float r, float g, float b, float a = 1, const Rect <int> *clip_rect = 0, bool lock_alpha = false);

		/// Draw a gradient.
		void		DrawGradient(const Gradient &gradient, const Rect <int> *clip_rect = 0);
		/*!
			@short	Apply a convolution kernel to the picture.
			@note	Do not use in time critical code as this function is slow
					and uses a temporary copy of the picture.
		*/
		bool		ApplyConvolution(uint kernel_width, uint kernel_height, const int *weights, int weight = 256, int pass = 1, const Rect <int> *clip_rect = 0);

		void		DrawPlot(float x, float y, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);
		void		DrawPlotHQ(float x, float y, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);

		void		DrawLine(float sx, float sy, float ex, float ey, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);
		void		DrawLineHQ(float sx, float sy, float ex, float ey, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);

		void		DrawPolygon(uint point_count, Point <float> *point, float r, float g, float b, float a = 1, const Rect <float> *clip_rect = 0);
/// @}

		bool		ComputeHash();
		int			GetHash() const;

		bool		Compare(const Picture &picture) const;

		/// Clone the picture via assignment operator.
		void		operator = (Picture &picture) { Clone(picture); }

		/// Convert the picture to another pixel formats.
		bool		Convert(const PixelFormatDescription &);
		/// Set the picture format without performing any data conversion.
		bool		SetFormat(const PixelFormatDescription &);

		/*!
			@short	Set the picture dimensions but do not perform any allocation.

			This function is used by the 3d engine built on the picture object.
			It should not have any practical usage in standard usage.
		*/
		void		Stub(uint w, uint h);
		/// Is stub.
inline	bool		IsStub() const { return protected_flag.IsSet(PictureIsStub); }

		/*!
			@short	Return image dimensions as a nfRect.
			@note	sx and sy are 0. Only ex and ey are relevant.
		*/
inline	iRect		GetRect() const { return iRect(0, 0, width, height); }
		/*!
			@short	Return image dimensions as a rectangle.
			@note	sx and sy are 0. Only ex and ey are relevant.
		*/
inline	void		GetRect(iRect &rect) const { rect.Set(0, 0, width, height); }

		/// Test is the picture has an alpha component.
		bool		HasAlpha() const;

inline	uchar		*GetData() const { return data; }
		uchar		*GetDataOffset(uint offset_x, uint offset_y) const { return data + (offset_x + offset_y * GetWidth()) * (pxformat.GetBpp() / 8); }

inline	uint		GetWidth() const { return width; }
inline	uint		GetHeight() const { return height; }

inline	uchar		GetBpp() const { return pxformat.GetBpp(); }
inline	uint		GetPitch() const { return (width * (pxformat.GetBpp() / 8)); }

inline	bool		isValid() const { return (width && height && data); }

		/// Return the pixel format descriptor of the picture.
inline	const PixelFormat &GetPixelFormat() const { return pxformat; }

		void		Clone(const Picture &, bool clone_data = true);

/*
	@name	Memory allocator.
	@{
*/
virtual	uchar		*AllocMemory(size_t);
virtual	void		FreeMemory(uchar *);
/// @}

		/*!
			@short	Set the picture object raster data source.

			@note	Ownership of the data buffer can be passed to this object.
		*/
virtual void		SetData(void *data, uint w, uint h, const PixelFormatDescription & = PixelFormat::RGBA8, bool take_ownership = false);
		/*!
			@short	Allocate internal buffers overriding object settings.

			@note	Ownership of the data buffer is passed to this object.
			@note	If the internals already match the new settings	this
					function returns without doing anything unless user data is
					passed.
			@note	If you provide the picture raster data buffer, you must make
					sure that the pointer you pass to this function as user_data
					is at least large enough to hold the complete picture in
					memory.
			@see	SetData().
		*/
virtual bool		AllocAs(uint w, uint h, const PixelFormatDescription & = PixelFormat::RGBA8);
		/*!
			@short	Free all object's data.

			@note	If you used the SetData() function the passed buffer will
					not be freed by this object. You are responsible for
					cleaning memory in such case.
		*/
virtual void		Free()
		{
			FreeData();
			Zeroify();
		}

		Picture(const Picture &p)
		{
			Zeroify();
			Clone(p);
		}
		Picture(void *data, uint w, uint h, const PixelFormatDescription &f = PixelFormat::RGBA8, bool take_ownership = false)
		{
			Zeroify();
			SetData(data, w, h, f, take_ownership);
		}
		Picture(uint w, uint h, const PixelFormatDescription &f = PixelFormat::RGBA8)
		{
			Zeroify();
			AllocAs(w, h, f);
		}
		Picture() { Zeroify(); }
virtual ~Picture();
};

typedef	SharedPtr <Picture> sPicture;

} // GS


#endif	// __NPICTURE__
