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

	ColorBalance.cpp

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

	Author:	Jonathan Trachtenberg

	Date:	06/08/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
	06/16/99	Jonathan	Added 'Ignore Background' feature to use G-Buffers in example

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

#include "ColorBalance.h"
#include "IShRasterLayer.h"

const MCGUID CLSID_ColorBalance(R_CLSID_ColorBalance);


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 ColorBalance
	TBasicUnknown* res = NULL;

	if (classId == CLSID_ColorBalance) res = new ColorBalance;

	return res;
}

ColorBalance::ColorBalance()
{
		fData.fRIntensity = 1.0;	// Intensity in %
		fData.fGIntensity = 1.0;
		fData.fBIntensity = 1.0;
		fData.fIgnoreBack = false;	// Ignore Background
}

ColorBalance::~ColorBalance()
{
}

MCCOMErr ColorBalance::ExtensionDataChanged()
{
	return MC_S_OK;
}

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

void ColorBalance::GetBufferNeeds(RenderFilterNeeds& needs, void *renderer)
{
	//Uses the index Channel
	needs.Set(k32Index);
}

// ***************************************************************
//				ColorBalance::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 ColorBalance::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;

	//fZBuffer is set to the Distance Channel
	fIndexChannel=buffers[k32Index];

	// get tile size

	TMCRect tileRect;
	input->GetTileSize(tileRect);

	uint32 width  = tileRect.GetWidth();
	uint32 height = tileRect.GetHeight();

	// allocation of the temporary buffers

	fIndexBuffer.SetElemCount(width*height);

	return MC_S_OK;
}

// ***************************************************************
//				ColorBalance::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 or 4 channels of 8 bits for R,G,B,A )
//
// ***************************************************************

MCCOMErr ColorBalance::DrawRect(const TMCRect& outputRect, const TChannelDataBucket* output[], 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();

	// normally we should use GetTile for speed but this is an example of how to use getData
	void *baseAddress=(void*)fIndexBuffer.BaseAddress();
	fIndexChannel->GetData(outputRect, &fIndexBucket, baseAddress, eTileRead);

	for (y=0;y<height;y++)
	{
		// get pointers to each row of the output image
		uint8 *newRed	= output[0]->RowPtr(y);
		uint8 *newGreen	= output[1]->RowPtr(y);
		uint8 *newBlue	= output[2]->RowPtr(y);

		// get pointers to each row of the input image
		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);
		}

		int32* indexRowPtr = (int32*)fIndexBucket.RowPtr32(y);

		for (x=0;x<width;x++)
		{
			// the index of the background is 0
			if (indexRowPtr[x] != 0 || fData.fIgnoreBack == false)
			{
				int32 red = int32(oldRed[x]) * fData.fRIntensity;
				if (red>255) red=255;
				newRed[x] = red;

				int32 green = int32(oldGreen[x]) * fData.fGIntensity;
				if (green>255) green=255;
				newGreen[x] = green;

				int32 blue = int32(oldBlue[x]) * fData.fBIntensity;
				if (blue>255) blue=255;
				newBlue[x] = blue;
			}
			else //Pixel is part of the Background and Ignore Background is True
			{
				newRed[x]	= oldRed[x];
				newGreen[x] = oldGreen[x];
				newBlue[x]	= oldBlue[x];
			}
			if (newAlpha)
			{
				newAlpha[x] = oldAlpha[x]; // leave alpha channel unchanged
			}
		}//For x
	}//For y

	// don't forget to release the tile otherwise they will stay in main memory !
	fIndexChannel->UnGetData(&fIndexBucket,false);
	fColorOffscreen->UnGetTile(color,index,channelCount,false);

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

	fIndexBuffer.ArrayFree();	// release allocated memory

	return MC_S_OK;
}
