/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.
*/
#ifndef PRCORE_PIXELFORMAT_HPP
#define PRCORE_PIXELFORMAT_HPP



namespace prcore
{

	// predefined formats
	#define PALETTE8(pal)   8,(Color32*)pal,0xff,0x00
	#define INTENSITY8      8,0xff,0x00
	#define RGB332			8,0xe0,0x1c,0x03,0x00
	#define RGB565          16,0x0000f800,0x000007e0,0x0000001f,0x00000000
	#define RGB888          24,0x00ff0000,0x0000ff00,0x000000ff,0x00000000
	#define ARGB1555        16,0x00007c00,0x000003e0,0x0000001f,0x00008000
	#define ARGB4444        16,0x00000f00,0x000000f0,0x0000000f,0x0000f000
	#define ARGB8888        32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000


	class PixelFormat
	{
		public:

		enum Type
		{
			INDEXED,
			INTENSITY,
			DIRECT
		};

inline	PixelFormat();
inline	PixelFormat(uint32 bits, uint32 intensity, uint32 alpha);
inline	PixelFormat(uint32 bits, const Color32 palette[], uint32 index, uint32 alpha);
inline	PixelFormat(uint32 bits, uint32 red, uint32 green, uint32 blue, uint32 alpha);
inline	PixelFormat(const PixelFormat& pxf);
inline	~PixelFormat();

inline	void		operator  = (const PixelFormat& pxf);
inline	bool		operator == (const PixelFormat& pxf) const;
inline	bool		operator != (const PixelFormat& pxf) const;
inline	bool		IsIntensity() const;
inline	bool		IsIndexed() const;
inline	bool		IsDirect() const;
inline	Type		GetType() const;
inline	uint32		GetBits() const;
inline	uint32		GetBytes() const;
inline	Color32*	GetPalette() const;
inline	void		SetPalette(const Color32[]);
inline	uint32		GetPixel(const Color32& color) const;
inline	uint32		GetRedMask() const;
inline	uint32		GetGreenMask() const;
inline	uint32		GetBlueMask() const;
inline	uint32		GetAlphaMask() const;
inline	uint32		GetIntensityMask() const;
inline	uint8		GetRedBits() const;
inline	uint8		GetGreenBits() const;
inline	uint8		GetBlueBits() const;
inline	uint8		GetAlphaBits() const;
inline	uint8		GetIntensityBits() const;
inline	uint8		GetRedShift() const;
inline	uint8		GetGreenShift() const;
inline	uint8		GetBlueShift() const;
inline	uint8		GetAlphaShift() const;
inline	uint8		GetIntensityShift() const;

		private:

		enum { BIT = 0x80000000, MASK = 0x7fffffff };

		Color32*	mPalette;		// palette
		uint32		mBits;			// bits
		uint32		mRedMask;		// red mask
		uint32		mGreenMask;		// green mask
		uint32		mBlueMask;		// blue mask
		uint32		mAlphaMask;		// alpha mask
		Color32		mCount;			// component bitcounts
		Color32		mShift;			// component shifts

inline	void		UseMask(uint32 mask, uint8& count, uint8& shift);
	};


inline void PixelFormat::UseMask(uint32 mask, uint8& count, uint8& shift)
{
	shift = HighestBit( mask );
	count = mask ? (shift - LowestBit(mask) + 1) : 0;
}

inline PixelFormat::PixelFormat()
{
	mPalette = NULL;
	mBits = 0;
	mRedMask = 0;
	mGreenMask = 0;
	mBlueMask = 0;
	mAlphaMask = 0;
	mCount = 0;
	mShift = 0;
}

inline PixelFormat::PixelFormat(const PixelFormat& format)
{
	mPalette = NULL;
	*this = format;
}

inline PixelFormat::PixelFormat(uint32 bits, uint32 intensity, uint32 alpha)
{
	assert( (bits%8)==0 );

	mPalette = NULL;
	mBits = bits | uint32(BIT);
	mRedMask = 0;
	mGreenMask = 0;
	mBlueMask = intensity;
	mAlphaMask = alpha;
	mCount = 0;
	mShift = 0;
	UseMask( intensity, mCount.b, mShift.b );
	UseMask( alpha, mCount.a, mShift.a );
}

inline PixelFormat::PixelFormat(uint32 bits, const Color32 palette[], uint32 index, uint32 alpha)
{
	assert( (bits%8)==0 );

	mPalette = new Color32[256];
	mBits = bits;
	mRedMask = 0;
	mGreenMask = 0;
	mBlueMask = index;
	mAlphaMask = alpha;
	mCount = 0;
	mShift = 0;
	UseMask( index, mCount.b, mShift.b );
	UseMask( alpha, mCount.a, mShift.a );

	if ( palette )
		memcpy( mPalette, palette, sizeof(Color32)*256 );
}

inline PixelFormat::PixelFormat(uint32 bits, uint32 red, uint32 green, uint32 blue, uint32 alpha)
{
	assert( (bits%8)==0 );

	mPalette = NULL;
	mBits = bits;
	mRedMask = red;
	mGreenMask = green;
	mBlueMask = blue;
	mAlphaMask = alpha;
	UseMask( red, mCount.r, mShift.r );
	UseMask( green, mCount.g, mShift.g );
	UseMask( blue, mCount.b, mShift.b );
	UseMask( alpha, mCount.a, mShift.a );
}

inline PixelFormat::~PixelFormat()
{
	delete[] mPalette;
}

inline void PixelFormat::operator = (const PixelFormat& format)
{
	mBits = format.mBits;
	mRedMask = format.mRedMask;
	mGreenMask = format.mGreenMask;
	mBlueMask = format.mBlueMask;
	mAlphaMask = format.mAlphaMask;
	mCount = format.mCount;
	mShift = format.mShift;

	if ( format.mPalette )
	{
		if ( !mPalette ) 
			mPalette = new Color32[256];

		memcpy( mPalette, format.mPalette, sizeof(Color32)*256 );
	}
	else
	{
		delete[] mPalette;
		mPalette = NULL;
	}
}

inline bool PixelFormat::operator == (const PixelFormat& format) const
{
	if ( (mRedMask != format.mRedMask) ||
		(mGreenMask != format.mGreenMask) ||
		(mBlueMask != format.mBlueMask) ||
		(mAlphaMask != format.mAlphaMask) )
		return false;

	if ( mBits != format.mBits ) 
		return false;

	if ( (mPalette==NULL) != (format.mPalette==NULL) ) 
		return false;

	return true;
}

inline bool PixelFormat::operator != (const PixelFormat& format) const
{
	return !(*this==format);
}

inline bool PixelFormat::IsIntensity() const
{
	return ((mBits & uint32(BIT)) == uint32(BIT));
}

inline bool PixelFormat::IsIndexed() const
{
	return (mPalette!=NULL);
}

inline bool PixelFormat::IsDirect() const
{
	return (!(mBits & uint32(BIT)) && !mPalette);
}

inline PixelFormat::Type PixelFormat::GetType() const
{
	if ( IsIndexed() ) return INDEXED;
	if ( IsIntensity() ) return INTENSITY;
	return DIRECT;
}

inline uint32 PixelFormat::GetBits() const
{
	return (mBits & MASK);
}

inline uint32 PixelFormat::GetBytes() const
{
	return GetBits() >> 3;
}

inline Color32* PixelFormat::GetPalette() const
{
	return mPalette;
}

inline void PixelFormat::SetPalette(const Color32 palette[])
{
	if ( !IsIndexed() || !palette )
		return;

	for ( int i=0; i<256; i++ )
		mPalette[i] = palette[i];
}

inline uint32 PixelFormat::GetPixel(const Color32& color) const
{
	// implementation note: 
	// defined only for "DIRECT" PixelFormat::Type
	return
		((uint32)color.r  >> (31 - mShift.r)) & mRedMask |
		((uint32)color.g  >> (31 - mShift.g)) & mGreenMask |
		((uint32)color.b  >> (31 - mShift.b)) & mBlueMask |
		((uint32)color.a  >> (31 - mShift.a)) & mAlphaMask;
}

inline uint32 PixelFormat::GetRedMask() const
{
	return mRedMask;
}

inline uint32 PixelFormat::GetGreenMask() const
{
	return mGreenMask;
}

inline uint32 PixelFormat::GetBlueMask() const
{
	return mBlueMask;
}

inline uint32 PixelFormat::GetAlphaMask() const
{
	return mAlphaMask;
}

inline uint32 PixelFormat::GetIntensityMask() const
{
	return mBlueMask;
}

inline uint8 PixelFormat::GetRedBits() const
{
	return mCount.r;
}

inline uint8 PixelFormat::GetGreenBits() const
{
	return mCount.g;
}

inline uint8 PixelFormat::GetBlueBits() const
{
	return mCount.b;
}

inline uint8 PixelFormat::GetAlphaBits() const
{
	return mCount.a;
}

inline uint8 PixelFormat::GetIntensityBits() const
{
	return mCount.b;
}

inline uint8 PixelFormat::GetRedShift() const
{
	return mShift.r;
}

inline uint8 PixelFormat::GetGreenShift() const
{
	return mShift.g;
}

inline uint8 PixelFormat::GetBlueShift() const
{
	return mShift.b;
}

inline uint8 PixelFormat::GetAlphaShift() const
{
	return mShift.a;
}

inline uint8 PixelFormat::GetIntensityShift() const
{
	return mShift.b;
}

} // namespace prcore



#endif