//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS2_GS_Util.h
//
// Copyright (c) 2004 Rebellion. All rights reserved.
//
// Description:
//
//     class to help with writing special effects that use special GS
//     processing e.g. colour channel copying and other full-screen operations
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_PS2_GS_UTIL_H___
#define ___ASURA_PS2_GS_UTIL_H___ 1

// I N C L U D E S ///////////////////////////////////////////////////////////

// Stdlib platform-specific.
#include <libgraph.h>

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

union Asura_PS2_QW;
class Asura_Vector_2;

// T Y P E S /////////////////////////////////////////////////////////////////

typedef u_int(*Asura_PS2_DeformGetUVFunction)(u_int uU, u_int uV);

// C O N S T A N T S /////////////////////////////////////////////////////////

static const u_int uASURA_PS2_DEFORMMESH_SIZE_LOG2 = 8; // 256x256

// C L A S S E S /////////////////////////////////////////////////////////////

class Asura_PS2_GS_Util
{
	public:
	
		// NB many of the operations here assume that all VRAM from the start
		// of the Z-buffer onwards can be used.

		// DisplayTextureBufferAsScreen() - display main work area in draw 
		// buffer - used for testing.
		static void DisplayTextureBufferAsScreen();

		// CopyRGToBA() - copies red/green channels of source to blue/alpha channels of dest.
		// based on "32bit Colour Channel Shifting using 8bit and 16bit texture formats" in 
		// GS_colour_channel_copying.zip by Mark Breugelmans
		static void CopyRGToBA(u_int uSourceFBP, u_int uDestFBP, u_int uWidth, u_int uHeight);

		// MoveBToA() - moves blue channel to alpha channel in-place. Uses a custom VU1 renderer.
		static void MoveBToA( u_int uFBP, u_int uWidth, u_int uHeight );

		// ScaleTextureBuffer() - takes the existing values of each pixel in the area pointed to by
		// uFBP, uWidth, uHeight and scales them in-place by uScaleColourARGB
		static void ScaleTextureBuffer(u_int uFBP, u_int uWidth, u_int uHeight, u_int uScaleColourARGB);

		// RenderAreaUnTextured() - renders specified source area to specified dest area, alpha blend setting
		static void RenderAreaUntextured( const u_int uDstXOffset, const u_int uDstYOffset, const u_int uDstAreaWidth, const u_int uDstAreaHeight,
										  const u_int uDstFBP, const u_int uDstBfrWidth,
										  const u_int uFBMask, const u_int uRGBA, const u_long ulALPHA );

		// RenderArea() - renders specified source area to specified dest area, using specified
		// texture function (TFX), alpha blend setting, and source PSM and CBP if applicable,
		static void RenderArea( const u_int uDstXOffset, const u_int uDstYOffset, const u_int uDstAreaWidth, const u_int uDstAreaHeight,
                                const u_int uSrcUOffset, const u_int uSrcVOffset, const u_int uSrcAreaWidth, const u_int uSrcAreaHeight, 
                                const u_int uDstFBP, const u_int uDstBfrWidth,  
                                const u_int uSrcTBP, const u_int uSrcBfrWidth, const u_int uSrcBfrHeight, 
                                const u_int uFBMask, const u_int uRGBA, const u_long ulALPHA,
                                const u_int uSrcPSM, const u_int uCBP, const int iTFXOverride = -1,
                                const u_long128* puPreambleGIFCmds = NULL, const u_int uNumPreambleGIFQWs = 0,
                                const u_long128* puPostambleGIFCmds = NULL, const u_int uNumPostambleGIFQWs = 0,
                                const u_int uDstPSM = SCE_GS_PSMCT32 );

		// UploadGreyscalePalette() - greyscale palette upload to last buffer of 4_SMALL layout.
		static void UploadGreyscalePalette();
		static u_int GetGreyscalePaletteCBP();

		// PixelShaderDP3() - writes alpha channel of DST with dp3 of SRC with uScaleRed, uScaleGreen, uScaleBlue
		// The alpha channel can be used directly as an 8-bit clut index.
		// e.g. used to make luminence texture
		static void PixelShaderDP3( const u_int uSRC, const u_int uDST, const u_int uWidth, const u_int uHeight, 
									const u_int uScaleRed, const u_int uScaleGreen, const u_int uScaleBlue );

		// CreateLuminanceTexture() - writes alpha channel of DST with luminance of SRC.
		// The alpha channel can be used directly as an 8-bit clut index.
		static void CreateLuminanceTexture( u_int uSRC, u_int uDST, u_int uWidth, u_int uHeight );

		// RenderGlow() - renders glow to frame buffer based on its luminance.
		// level is scaled for each camera by entry in array. (0x80 is 1.0)
		static void RenderGlow( u_int uLevel, u_int uNumDownsamples, u_int* auLevelForCamera, u_int uNumCameras );

		// RenderMaskedBlurEffect() - renders a blurred version of the frame buffer 
		// using a mask texture (alpha channel) to control the effect
		static void RenderMaskedBlurEffect( u_int uCamIdx, float fAlpha, int iMaskTexID, bool bInverted = false );

		// RenderDepthOfField() - renders a blurred version of the frame buffer in front of near Z
		// and behind far Z. Could use gradual change at far plane using Z->alpha trick but that 
		// would probably cause range problems with the fog system's range.
		static void RenderDepthOfField( float fNearPlaneZ, float fFarPlaneZ, const float fSize = 4.0f, const float fIntensity = 1.0f );

		// RenderGaussianBlur() - renders a blurred version of the frame buffer at all z-depths
		static void RenderGaussianBlur( const float fSizeIn, const float fIntensityIn );

		// RenderRadialBlur() - render a radial blur effect onto the frame buffer (rotated and scaled with arbitary centre)
		// Strength = 0-1
		// Angle = is in radians
		// CentreX = 0->1
		// CentreY = 0->1
		static void RenderRadialBlur( const float fSizeIn, const float fStrengthIn, const float fAngleIn, const float fCentreXIn, const float fCentreYIn );

		// Does full screen pass to de-saturate and tint the screen!
		// See source code for the pixel operations
		// Tint Colour is 0-255 (0->2), Scale = 0-255 ( 0->2)
		// Because tint colour is applied twice it ends up as tint_colour^2 so you get tint_colour (0->4)
		static void DesaturateAndTint( const u_int uTintRedIn, const u_int uTintGreenIn, const u_int uTintBlueIn, const u_int uDesatScaleIn );

		// Does full screen pass to alter brightness & contrast on the screen!
		// See source code for the pixel operations: a very simple linear operation on the frame buffer
		// Brightness = -255->255 (-1->1), Contrast = -128-128 (-1->1)
		static void BrightnessAndContrast( const int iBrightnessIn, const int iContrastIn );

		// InvertZBuffer() - draws untextured sprite over entire Z-buffer with alpha blend
		// set to (Cs - Cd) and Cs is set to 0xffffffff.
		static void InvertZBuffer( void );

		// ShrinkAndDeformFB() - creates 256x256 texture in buffer 1 of ASURA_PS2_TEXBFRLAYOUT_2_LARGE.
		// The frame buffer is deformed using a grid of 32x32 on the source and the UV for each 
		// vertex in the deform mesh is provided by a caller-specified function.
		// This is used to create the heathaze effect, and also for an underwater effect.
		static void ShrinkAndDeformFB( Asura_PS2_DeformGetUVFunction pfnGetU, Asura_PS2_DeformGetUVFunction pfnGetV );

		// following group of functions are used to build up sequences of operations e.g. a series
		// of downsamples, in a single DMA packet. Each one has a matching *_GetNumQWs() function.

		// AddBlurredToFB - helper function for blur. Adds a blur texture to the frame buffer.
		// assumes that the following are already set: XYOFFSET(0,0), TEX1_1(LINEAR,LINEAR),
		// PRIM(sprite using STs with alpha blending).
		static u_int AddBlurredToFB_GetNumQWs(u_int uScreenWidth);
		static Asura_PS2_QW* AddBlurredToFB(Asura_PS2_QW* pxOut, u_int uScreenWidth, u_int uScreenHeight, 
			u_int uTextureWidth, u_int uTextureHeight, u_int uSourceFBP, u_int uDestFBP, u_long ulAlpha);

		static u_int CopyBuffer_GetNumQWs( u_int uSrcWidth, bool bDownsize = true );
		static Asura_PS2_QW* CopyBuffer( Asura_PS2_QW* pxOut,
                                         u_int uSrcAreaWidth, u_int uSrcAreaHeight,
			                             u_int uSourceFBP, u_int uDestFBP,
                                         u_int uXYOffset, u_int uLevel, u_int uPSM, u_int uCBP,
                                         bool bDownsize = true,
                                         u_int uDestBfrWidth = 0,
                                         u_int uSrcBfrWidth = 0, u_int uSrcBfrHeight = 0 );

		// WriteData_ConvolveImage1d_121 - does in-place 121 convolution filter horizontally or 
		// vertically depending on second parameter. Drifts up and left so after use specify a 
		// 1 pixel offset to WriteBlurData() to compensate.
		// assumes XYOFFSET(0,0) and TEX1_1(LINEAR,LINEAR). PRIM(sprite using STs with alpha blending).
		static Asura_PS2_QW* WriteData_ConvolveImage1d_121( Asura_PS2_QW* pxOut,
                                                            bool bHorizontal,
                                                            u_int uSrcFBP, u_int uRenderWidth, u_int uRenderHeight,
                                                            u_int uBufferWidth = 0, u_int uBufferHeight = 0, u_int uFBMask = 0 );
		static u_int ConvolveImage1d_121_GetNumQWs( u_int uRenderWidth );

		// WriteDrawBlackLinesData - helper function to draw black lines on left and top sides to 
		// help in accounting for CV121 drifting down and right.
		// Assumes that XYOFFSET(0,0) and PRIM is set to LINESTRIP with no texturing or alphablending
		static Asura_PS2_QW* WriteDrawBlackLinesData( Asura_PS2_QW* pxOut, u_int uFBP, u_int uWidth, u_int uHeight );
		static u_int WriteDrawBlackLines_GetNumQWs();

		// WriteDrawBlackLinesData2 - helper function to draw black lines on right and bottom sides to 
		// help in accounting for CV121 drifting down and right.
		// Assumes that XYOFFSET(0,0) and PRIM is set to LINESTRIP with no texturing or alphablending
		static Asura_PS2_QW* WriteDrawBlackLinesData2( Asura_PS2_QW* pxOut, u_int uFBP, u_int uWidth, u_int uHeight );
		static u_int WriteDrawBlackLines_GetNumQWs2();

        // Relocates and/or resamples the z buffer. Changes are propogated
        // to Asura_PS2_GS. However, the caller will need to worry about
        // invalidating/regenerating any external cached stuff based upon
        // the z buffer parameters.
        static void ChangeZBuffer( u_int uNewZBP, u_int uNewBitDepth );

		// Handy function for setting GS registers.
		// puGIFCmds should be in A+D packing format
		static void SendGIFCmdsDirect( const u_long128* puGIFCmds, const u_int uNumGIFQWs);

	private:
		
		static void PaletteUploadCallback_Greyscale( u_int uTBP, u_int* puDmaChainCallAddr, u_int* puDataSize );

        static void PrepareBlurredBuffer( Asura_Vector_2* pxBottomRightST, u_long* puTEX0,
                                          u_int uSampleAreaX, u_int uSampleAreaY, u_int uSampleAreaWidth, u_int uSampleAreaHeight,
                                          int iMaskTexID, const u_int uNumConvolves = 1 );

};


// Asura_PS2_PaletteUploader - helper class for managing dynamically generated palettes.
// To use it, create a derived class which implements the InitialisePalette() virtual function.
// Create a global instance of the derived class. Create a global function to pass in to the 
// Asura_PS2_TextureManagement::UploadTexture function and have that function call PaletteUploadCallback
// on your class instance. e.g.
//
//	static Asura_PS2_GreyscaleClut g_xGreyscaleClut;
//	static void PaletteUploadCallback_Greyscale( u_int uTBP, u_int* puDmaChainCallAddr, u_int* puDataSize )
//	{
//		g_xGreyscaleClut.PaletteUploadCallback(uTBP, puDmaChainCallAddr, puDataSize);
//	}
class Asura_PS2_PaletteUploader
{
    enum 
	{
		uSetupNLOOP = 4,
		uQWsForSetupDMA = 1 + uSetupNLOOP + 1,
		uTotalQWs       = 1 + uQWsForSetupDMA + 1 + 1,
		uPaletteEntries = 256,
		uPaletteBytes   = uPaletteEntries << 2, // Each entry is a 32-bit value.
		uPaletteQWs     = uPaletteBytes >> 4
	};
	
public:

	Asura_PS2_PaletteUploader();
	virtual ~Asura_PS2_PaletteUploader();
	
	void PaletteUploadCallback( u_int uTBP, u_int* puDmaChainCallAddr, u_int* puDataSize );

	u_int GetCBP() const { return m_uCBP; }

protected:

	void InitialiseClutUploadBuffers();

	// derived class must implement InitialisePalette function
	virtual void InitialisePalette() = 0;

private:
	
	// unimplemented operator= and copy constructor
	Asura_PS2_PaletteUploader& operator= (const Asura_PS2_PaletteUploader&);
	Asura_PS2_PaletteUploader(const Asura_PS2_PaletteUploader&);


protected:
	
	// member data
	bool m_bInitialisedPalette;
	u_int* m_auPalette;
	u_int m_uCBP;
	u_int m_uCurBfr;
	Asura_PS2_QW* m_apxBfrs[2];
};


// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ___ASURA_PS2_GS_UTIL_H___
