
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Platform_ScreenShots.cpp
// Author : Andy Tate
// Date   : 10-Jun-2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_USE_SCREENSHOT_SYSTEM

// This file's header...
#include "Debugging/Asura_ScreenShot_System.h"

// Asura header-files...
#include "Core/Asura_Stream.h"
#include "Core/Asura_String.h"
#include "Input/Asura_PlatformInput.h"
#include "Rendering/Asura_PSP_GU.h"
#include "Rendering/Asura_PSP_Graphics.h"
#include "Rendering/Asura_PSP_MotionBlur.h"
#include "Rendering/Asura_Render.h"
#include "Sound/Asura_Sound.h"

#include <kernel.h>

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

static bool	g_bMotionBlurEnabled	= false;

////////////////////////////////////////////////////////////////////////////////
// Internal functions...
////////////////////////////////////////////////////////////////////////////////
static void RenderInfoQuad( float fX, float fY, float fW, float fH, u_int uColourARGB )
{
	struct
	{
		u_int	m_uDiffuse;
		short	m_asPosition [3];

	} axVertices [2];

	GU_Sync(SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT);

	char acDisplayList[2048] __attribute__( ( aligned( 64 ) ) );

	GU_Start(SCEGU_IMMEDIATE, acDisplayList, sizeof(acDisplayList));

	// Set the render-states...
	Asura_PSP_Graphics::SetUseColourDoubling				(false);

	Asura_Render::CurrentStates.RequestCullMode				(ASURA_CULLMODE_NONE);
	Asura_Render::CurrentStates.RequestTextureIndex			(0, ASURA_TEXTURE_NONE);
	Asura_Render::CurrentStates.RequestTranslucencyMode		(ASURA_TRANSLUCENCY_OFF);
	Asura_Render::CurrentStates.RequestZBufferEnabled		(false);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled	(false);


	// Initialise the quad...
	axVertices [0].m_uDiffuse		= uColourARGB;
	axVertices [0].m_asPosition [0]	= static_cast<short>(fX);
	axVertices [0].m_asPosition [1]	= static_cast<short>(fY);
	axVertices [0].m_asPosition [2]	= 32767;

	axVertices [1].m_uDiffuse		= uColourARGB;
	axVertices [1].m_asPosition [0]	= static_cast<short>(fX + fW);
	axVertices [1].m_asPosition [1]	= static_cast<short>(fY + fH);
	axVertices [1].m_asPosition [2]	= 32767;


	// Render the quad...
	sceKernelDcacheWritebackAll	();
	GU_DrawArray(SCEGU_PRIM_RECTANGLES, SCEGU_COLOR_PF8888 | SCEGU_VERTEX_SHORT | SCEGU_THROUGH, 2, 0, axVertices);

	// Restore the render-states...
	Asura_Render::CurrentStates.RequestZBufferEnabled		(true);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled	(true);

	GU_Finish();
}

DEBUGVAR float dbg_fBackgroundX = 8.0f;
DEBUGVAR float dbg_fBackgroundY = 4.0f;
DEBUGVAR float dbg_fBackgroundW = 480.0f - 16.0f;
DEBUGVAR float dbg_fBackgroundH = 16.0f;
DEBUGVAR u_int dbg_uBackgroundColour = 0xcc000000;
DEBUGVAR u_int dbg_uPromptX = 8;
DEBUGVAR u_int dbg_uPromptY = 8;

static void RenderInfoPrompt( const char* szMsg )
{
	RenderInfoQuad( dbg_fBackgroundX, dbg_fBackgroundY, dbg_fBackgroundW, dbg_fBackgroundH, dbg_uBackgroundColour );

	GU_Sync(SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT);

	char acDisplayList[2048] __attribute__( ( aligned( 64 ) ) );
	GU_Start(SCEGU_IMMEDIATE, acDisplayList, sizeof(acDisplayList));

	GU_DebugPrint(dbg_uPromptX, dbg_uPromptY, 0xffffffff, szMsg); 

	GU_Finish();
}

////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_RenderProgressBar
// Author       : Andy Tate
// Date         : 16-Jun-2005
//
// Description  : 
//
// Parameters   : References to the position and size of the progress-bar and
//				  the colour with which it's to be drawn.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_RenderProgressBar (const Asura_Vector_2& xPosition,
														  const Asura_Vector_2& xSize,
														  const u_int uARGB)
{
	char acBuffer[256];
	u_int uMag = GetMagnification();
	u_int uWidth = 480;
	u_int uHeight = 272;
	Asura_PrintfToBfr( acBuffer, "Taking hi-res shot (%ux%u, %uX AA)", 256, true, 
		uWidth * uMag, uHeight * uMag, GetAntiAliasing() );

	RenderInfoPrompt( acBuffer );

	// progress bar
	RenderInfoQuad( xPosition.x, xPosition.y, xSize.x, xSize.y, uARGB );
}

/// End of function 'Asura_ScreenShot_System::Platform_RenderProgressBar'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_GetScreenDimensions
// Author       : Andy Tate
// Date         : 10-Jun-2005
//
// Description  : Gets the specified screen dimensions.
//
// Parameters   : Pointers to the position, size and bit-depth to get, if these
//				  are NULL then don't bother fetching the data.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_GetScreenDimensions (Asura_IntVector_2* pxPosition,
															Asura_IntVector_2* pxSize,
															u_int* puFormat)
{
	// Do we need to get the position?

	if (NULL != pxPosition)
	{
		// Yes - do so...

		pxPosition->iX	= 0;
		pxPosition->iY	= 0;
	}


	// Do we need to get the size?

	if (NULL != pxSize)
	{
		// Yes - do so...

		pxSize->iX	= SCEGU_SCR_WIDTH;
		pxSize->iY	= SCEGU_SCR_HEIGHT;
	}


	// Do we need to get the bit-depth?

	if (NULL != puFormat)
	{
		// Decide upon the bit-depth...

		*puFormat	= SCEGU_PF8888;
	}
}

/// End of function 'Asura_ScreenShot_System::Platform_GetScreenDimensions'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_WriteScreenAs24Bit
// Author       : Andy Tate
// Date         : 10-Jun-2005
//
// Description  : Writes the specified portion of the screen to the specified
//				  stream as a 24-bit BMP.
//
// Parameters   : A reference to the stream to write to, along with the position
//				  size and format of the screen.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_WriteScreenAs24Bit (Asura_Stream& xStream,
														   const Asura_IntVector_2& xPosition,
														   const Asura_IntVector_2& xSize,
														   const u_int uFormat)
{
	const int				iWidth				= xSize.iX;
	const int				iHeight				= xSize.iY;
	const u_int				uOutputBufferWidth	= iWidth * 3;
	const u_int				uBackBufferWidth	= SCEGU_VRAM_WIDTH * 4;
#ifdef ASURA_PSP_DEFERREDRENDER
	const u_int				uBackBufferOffset	= Asura_PSP_Graphics::GetCurrentDisplayedBuffer();
#else
	const u_int				uBackBufferOffset	= Asura_PSP_Graphics::GetCurrentRenderingBuffer();
#endif

	const unsigned char*	pucBackBuffer		= static_cast<unsigned char *>(sceGeEdramGetAddr()) + uBackBufferOffset + (uBackBufferWidth * (iHeight - 1));
	unsigned char*			pucOutputBuffer		= new unsigned char [uOutputBufferWidth];


	// Ensure that we've finished rendering...

	Asura_Render::FlushDisplay	();


	// Assume that the back buffer format is either "D3DFMT_X8R8G8B8" or "D3DFMT_A8R8G8B8"...
	
	for (int y = (iHeight - 1); y >= 0; y--)
	{
		const unsigned int*	puSource	= reinterpret_cast<const unsigned int*>(pucBackBuffer);
		unsigned char*		pucDest		= pucOutputBuffer;


		// Go through each pixel on this line...

		for (int x = 0; x < iWidth; x++)
		{
			const unsigned int	uColour	= *(puSource++);


			// Write the colour to our output buffer...

			*(pucDest++)	= static_cast<unsigned char>((uColour & 0x00FF0000) >> 16);		// Blue
			*(pucDest++)	= static_cast<unsigned char>((uColour & 0x0000FF00) >> 8);		// Green
			*(pucDest++)	= static_cast<unsigned char>(uColour & 0x000000FF);				// Red
		}


		// Write this line...

		xStream.WriteData	(uOutputBufferWidth, pucOutputBuffer);


		// Go back a line...

		pucBackBuffer	-= uBackBufferWidth;
	}


	// Free-up the memory...

	delete []	(pucOutputBuffer);
}

/// End of function 'Asura_ScreenShot_System::Platform_WriteScreenAs24Bit'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_AddScreenToFPBuffer
// Author       : Andy Tate
// Date         : 10-Jun-2005
//
// Description  : Adds the specified portion of the screen to the floating-point
//				  RGB buffer.
//
// Parameters   : 
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_AddScreenToFPBuffer (const Asura_IntVector_2& xPosition,
															const Asura_IntVector_2& xSize,
															const u_int uFormat,
															float* pfFPBuffer)
{
	const int				iWidth				= xSize.iX;
	const int				iHeight				= xSize.iY;
	const u_int				uBackBufferWidth	= SCEGU_VRAM_WIDTH * 4;
#ifdef ASURA_PSP_DEFERREDRENDER
	const u_int				uBackBufferOffset	= Asura_PSP_Graphics::GetCurrentDisplayedBuffer();
#else
	const u_int				uBackBufferOffset	= Asura_PSP_Graphics::GetCurrentRenderingBuffer();
#endif

	const unsigned char*	pucBackBuffer		= static_cast<unsigned char *>(sceGeEdramGetAddr()) + uBackBufferOffset + (uBackBufferWidth * (iHeight - 1));

	// Ensure that we've finished rendering...
	Asura_Render::FlushDisplay();
	// Wait until drawing is complete...
	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );
	//sceGeDrawSync(0);

	// Assume that the back buffer format is either "D3DFMT_X8R8G8B8" or "D3DFMT_A8R8G8B8"...
	for (int y = (iHeight - 1); y >= 0; y--)
	{
		const unsigned int*	puSource	= reinterpret_cast<const unsigned int*>(pucBackBuffer);

		// Go through each pixel on this line...
		for (int x = 0; x < iWidth; x++)
		{
			const unsigned int	uColour	= *(puSource++);

			// Write the colour to our output buffer...
			*(pfFPBuffer++)	+= static_cast<float>((uColour & 0x00FF0000) >> 16);	// Blue
			*(pfFPBuffer++)	+= static_cast<float>((uColour & 0x0000FF00) >> 8);		// Green
			*(pfFPBuffer++)	+= static_cast<unsigned char>(uColour & 0x000000FF);	// Red
		}

		// Go back a line...
		pucBackBuffer	-= uBackBufferWidth;
	}
}

/// End of function 'Asura_ScreenShot_System::Platform_AddScreenToFPBuffer'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_BeginHiResRender
// Author       : Andy Tate
// Date         : 13-Jun-2005
//
// Description  : Handles the platform specific start of the high-resolution render.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_BeginHiResRender (void)
{
	// stop streaming sounds to prevent fileIO conflicts.
	Asura_Sound_System::EnsureAllStreamingSoundsAreStopped();

	// Wait until drawing is complete...
	//sceGeDrawSync	(0);
	GU_Sync( SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT );

	char acDisplayList[2048] __attribute__( ( aligned( 64 ) ) );
	GU_Start(SCEGU_IMMEDIATE, acDisplayList, sizeof(acDisplayList));

	// Disable motion-blur and mip-mapping......
	g_bMotionBlurEnabled	= Asura_PSP_MotionBlur::IsEnabled ();

	Asura_PSP_MotionBlur::Enable(false);
	
	// (MH - 26.07.2007) this was set to do nearest mipmap and no filtering. 
	// We want filtering (and no mipmaps) for high-res screenshots!
	// I've added dbg_bHiResScreenshotForceNoMipmapping to Asura_PSP_TextureManagement.cpp
	// to help debug this.
	GU_TexFilter(SCEGU_LINEAR, SCEGU_LINEAR);

	GU_Finish();

	// set the keys for accept + cancel
	s_iAcceptKey = ASURA_PSP_KEYID_PAD1_CROSS;
	s_aiCancelKey[0] = ASURA_PSP_KEYID_PAD1_TRIANGLE;
}

/// End of function 'Asura_ScreenShot_System::Platform_BeginHiResRender'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_EndHiResRender
// Author       : Andy Tate
// Date         : 13-Jun-2005
//
// Description  : Handles the platform specific end of the high-resolution render.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_EndHiResRender (void)
{
	GU_Sync(SCEGU_SYNC_FINISH, SCEGU_SYNC_WAIT);

	char acDisplayList[2048] __attribute__( ( aligned( 64 ) ) );
	GU_Start(SCEGU_IMMEDIATE, acDisplayList, sizeof(acDisplayList));

	// Restore the motion-blur and mip-mapping...
	Asura_PSP_MotionBlur::Enable	(g_bMotionBlurEnabled);

	GU_TexFilter(SCEGU_LINEAR_MIPMAP_LINEAR, SCEGU_LINEAR);

	GU_Finish();
}

/// End of function 'Asura_ScreenShot_System::Platform_EndHiResRender'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_TakeScreenShot
// Author       : Andy Tate
// Date         : 10-Jun-2005
//
// Description  : Handles the taking of screen-shots.
//
// Parameters   : The name of the file to be saved.
//
// Return value : The result of the screen shot function.
////////////////////////////////////////////////////////////////////////////////

int Asura_ScreenShot_System::Platform_TakeScreenShot (const char* szFilename)
{
	Asura_IntVector_2	xScreenPosition;
	Asura_IntVector_2	xScreenSize;
	u_int				uScreenFormat;


	// Take the normal screen-shot as a BMP...

	Platform_GetScreenDimensions	(&xScreenPosition, &xScreenSize, &uScreenFormat);
	return							(BMPTakeScreenShot(szFilename,  xScreenPosition, xScreenSize, uScreenFormat));
}

/// End of function 'Asura_ScreenShot_System::Platform_TakeScreenShot'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_NotTakingScreenShot
// Author       : Andy Tate
// Date         : 10-Jun-2005
//
// Description  : Handles the case of being in screen-shot mode but not actually
//				  taking a screen-shot.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_ScreenShot_System::Platform_NotTakingScreenShot (void)
{
	// Nothing to do, on PS2 this is where normal screenshots get triggered.
}

/// End of function 'Asura_ScreenShot_System::Platform_NotTakingScreenShot'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_AllocateFloatBuffer
// Author       : Mike Healey
// Date         : 28-Jun-2005
//
// Description  : returns float buffer big enough for screenshots.
//
// Return value : ptr to float buffer.
////////////////////////////////////////////////////////////////////////////////
float* Asura_ScreenShot_System::Platform_AllocateFloatBuffer(u_int uNumFloats)
{
	return new float[uNumFloats];
}
/// End of function 'Asura_ScreenShot_System::Platform_AllocateFloatBuffer'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_FreeFloatBuffer
// Author       : Mike Healey
// Date         : 28-Jun-2005
//
// Description  : frees float buffer allocated earlier (if necessary)
////////////////////////////////////////////////////////////////////////////////
void Asura_ScreenShot_System::Platform_FreeFloatBuffer(float* pfBuffer)
{
	delete[] pfBuffer;
}
/// End of function 'Asura_ScreenShot_System::Platform_FreeFloatBuffer'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_ScreenShot_System::Platform_RenderPrompt
// Author       : Mike Healey
// Date         : 28-Jun-2005
//
// Description  : render user input prompt - not supported on PC
////////////////////////////////////////////////////////////////////////////////
void Asura_ScreenShot_System::Platform_RenderPrompt(void)
{
	RenderInfoPrompt( "Press X to take this screenshot, triangle to cancel" );
}
/// End of function 'Asura_ScreenShot_System::Platform_RenderPrompt'. ///

#endif // #ifdef ASURA_USE_SCREENSHOT_SYSTEM

/// End of file. ///
