/****************************************************************************************

	Sand.cpp

	Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

	Author:	Jonathan Trachtenberg

	Date:	06/04/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods


****************************************************************************************/

#include "StdLib.h"
#include "Sand.h"
#include "I3DShPostRenderer.h"

#define MAX_COLOR 255


const MCGUID CLSID_Sand(R_CLSID_Sand);


//	***********************************************************************************
//	This function generates a random value between 0 and the maximum channel value - 1.
//	If this random value is greater than or equal to the input value, it turns
//	the channel off.  Otherwise, it sets the channel to the maximum value.
//	***********************************************************************************

uint8 RandomColor(uint8 value)
{
	const real32 randVal= rand();
	const real32 mulRes = randVal * (MAX_COLOR-1);
	const real32 divRes= mulRes/RAND_MAX;
	
	if ( divRes >= value) return 0;
	else return (MAX_COLOR);
}

void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}

TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the object Sand
	TBasicUnknown* res = NULL;

	if (classId == CLSID_Sand) res = new Sand;

	return res;
}

Sand::Sand()
{
}

void* Sand::GetExtensionDataBuffer()
{
	return &fData;
}

void Sand::GetBufferNeeds(RenderFilterNeeds &needs, void *renderer)
{
	//No G-Buffers are needed for this example
}

// ***************************************************************
//				Sand::PrepareDraw
//
// called for each frame before beginning the postrenderering
// by tile
//
// input   : is a rasterLayer with 3 channels (R,G,B) in 16 bits
// bounds  : is the rectangle of the full image
// buffers : is the array of pointer to the GBuffers
// scene   : is a pointer to the current scene being renderered
// uvBox   : is the coordinates of the screen in screen coordinates
//
// ***************************************************************

MCCOMErr Sand::PrepareDraw(IShRasterLayer* input,
  						   const TMCRect& bounds,
						   IShChannel* buffers[],
						   I3DShScene* scene,
						   I3DShCamera* renderingCamera,
						   const TBBox2D& uvBox,
						   const TBBox2D& productionFrame)
{
	//fColorOffScreen is assigned to the input buffer
	fColorOffscreen=input;

	return MC_S_OK;
}

// ***************************************************************
//				Sand::DrawRect
//
// called for each tile of the image
//
// outputRect   : is the rectangle of the tile in the image
// red			: is a pointer to the output rasterLayer ( 3 channels of 16 bits for R,G,B )
//
// ***************************************************************

MCCOMErr Sand::DrawRect(const TMCRect& outputRect, const TChannelDataBucket* output[], 
						const TChannelDataBucket* compositedOutputTile[], const TBBox2D &uvBox)
{	
	// first we need to check whether there is an alpha channel or not
	int32 channelCount = fColorOffscreen->GetChannelCount();

	MCAssert(channelCount==3 || channelCount==4); // RGB or RGBA

	boolean hasAlphaChannel = (channelCount==4);

	const TChannelDataBucket *color[4];
	uint16 index[4]={0,1,2,3};
	fColorOffscreen->GetTile(outputRect,color,index,channelCount,eTileRead);

	int32 x,y;
	int32 width=outputRect.GetWidth();
	int32 height=outputRect.GetHeight();

	for (y=0;y<height;y++)
	{
		uint8 *newRed	= output[0]->RowPtr(y);
		uint8 *newGreen	= output[1]->RowPtr(y);
		uint8 *newBlue	= output[2]->RowPtr(y);

		uint8 *oldRed   = color[0]->RowPtr(y);
		uint8 *oldGreen = color[1]->RowPtr(y);
		uint8 *oldBlue  = color[2]->RowPtr(y);

		// get row pointers for alpha channel if any
		uint8 *newAlpha = NULL;
		uint8 *oldAlpha = NULL;

		if (hasAlphaChannel)
		{
			newAlpha = output[3]->RowPtr(y);
			oldAlpha = color[3]->RowPtr(y);
		}
		for (x=0;x<width;x++)
		{
			newRed[x]	=RandomColor(oldRed[x]);
			newGreen[x]	=RandomColor(oldGreen[x]);
			newBlue[x]	=RandomColor(oldBlue[x]);

			if (hasAlphaChannel)
			{
				newAlpha[x] = oldAlpha[x];
			}
		}
	}
	fColorOffscreen->UnGetTile(color,index,channelCount,false);

	return MC_S_OK;
}
// ***************************************************************
//				Sand::FinishDraw
//
// called for each frame after the postrenderering
//
//
// ***************************************************************
MCCOMErr Sand::FinishDraw()
{
	fColorOffscreen=NULL;
	return MC_S_OK;
}
