/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
///////////////////////////////////////////////////////////////////////////////
//																			 //
//	plDynamicTextMap Class Functions										 //
//	Derived bitmap class representing a single mipmap.						 //
//	Cyan, Inc.																 //
//																			 //
//// Version History //////////////////////////////////////////////////////////
//																			 //
//	6.7.2001 mcn - Created.													 //
//																			 //
///////////////////////////////////////////////////////////////////////////////

#include "hsTypes.h"
#include "plDynamicTextMap.h"

#include "hsStream.h"
#include "hsExceptions.h"
#include "hsUtils.h"
#include "hsMatrix44.h"
#include "../plPipeline/hsGDeviceRef.h"
#include "../plMessage/plDynamicTextMsg.h"
#include "../pnKeyedObject/plKey.h"
#include "plProfile.h"
#include "../plStatusLog/plStatusLog.h"
#include "plFont.h"
#include "plFontCache.h"
#include "../plResMgr/plLocalization.h"


plProfile_CreateMemCounter("DynaTextMem", "PipeC", DynaTextMem);
plProfile_CreateCounterNoReset("DynaTexts", "PipeC", DynaTexts);
plProfile_Extern(MemMipmaps);


//// Constructor & Destructor /////////////////////////////////////////////////

plDynamicTextMap::plDynamicTextMap()
	: fVisWidth(0), fVisHeight(0), fHasAlpha(false), fPremultipliedAlpha(false), fJustify(kLeftJustify),
	  fInitBuffer(nil), fFontFace(nil), fFontSize(0), fFontFlags(0),
	  fFontAntiAliasRGB(false), fFontBlockRGB(false), fHasCreateBeenCalled(false)
{
	fFontColor.Set(0, 0, 0, 1);
}

plDynamicTextMap::~plDynamicTextMap()
{
	Reset();
}

plDynamicTextMap::plDynamicTextMap( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth, UInt32 extraHeight, hsBool premultipliedAlpha )
	: fInitBuffer(nil), fFontFace(nil)
{
	Create( width, height, hasAlpha, extraWidth, extraHeight, premultipliedAlpha );
}

//// SetNoCreate //////////////////////////////////////////////////////////////
//	For export time, we want to set up the config to write to disk, but we
//	don't want to actually be creating OS surfaces. So we call this function
//	instead, which does just that. It basically does all the setup work that
//	Create() does, or enough for us to write out later.

void	plDynamicTextMap::SetNoCreate( UInt32 width, UInt32 height, hsBool hasAlpha )
{
	// OK, so it really isn't that much work...
	fVisWidth = (UInt16)width;
	fVisHeight = (UInt16)height;
	fHasAlpha = hasAlpha;
	fImage = nil;		// So we know we haven't actually done anything yet
	delete [] fInitBuffer;
	fInitBuffer = nil;
}

//// Create ///////////////////////////////////////////////////////////////////

void	plDynamicTextMap::Create( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth, UInt32 extraHeight, hsBool premultipliedAlpha )
{
	SetConfig( hasAlpha ? kARGB32Config : kRGB32Config );


	fVisWidth = (UInt16)width;
	fVisHeight = (UInt16)height;
	fHasAlpha = hasAlpha;
	fPremultipliedAlpha = premultipliedAlpha;

	for( fWidth = 1; fWidth < width + extraWidth; fWidth <<= 1 );
	for( fHeight = 1; fHeight < height + extraHeight; fHeight <<= 1 );

	// instead of allocating the fImage here, we'll wait for the first draw operation to be called (in IIsValid)
	fHasCreateBeenCalled = true;

	fRowBytes = fWidth << 2;
	fNumLevels = 1;
	fFlags |= plMipmap::kDontThrowAwayImage;
	fCompressionType = plMipmap::kUncompressed;
	fUncompressedInfo.fType = plMipmap::UncompressedInfo::kRGB8888;

	// Destroy the old texture ref, if we have one. This should force the 
	// pipeline to recreate one more suitable for our use
	SetDeviceRef( nil );

	// Some init color
	SetFont( "Arial", 12 );
	hsColorRGBA	color;
	color.Set( 0,0,1,1);
	SetTextColor( color );

	SetCurrLevel( 0 );
	plProfile_Inc(DynaTexts);

}

//// Reset ////////////////////////////////////////////////////////////////////

void	plDynamicTextMap::Reset( void )
{
	IDestroyOSSurface();

	plMipmap::Reset();

	// they need to call create again to undo the affects of call Reset()
	fHasCreateBeenCalled = false;

	delete [] fInitBuffer;
	fInitBuffer = nil;

	delete [] fFontFace;
	fFontFace = nil;

	// Destroy the old texture ref, since we're no longer using it
	SetDeviceRef( nil );
}

///////////////////////////////////////////////////////////////////////////////
//// OS-Specific Functions ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

hsBool		plDynamicTextMap::IIsValid( void )
{
	if( GetImage() == nil && fHasCreateBeenCalled )
	{
		// we are going to allocate the fImage at this point... when someone is looking for it
		fImage = (void *)IAllocateOSSurface( (UInt16)fWidth, (UInt16)fHeight );
		hsColorRGBA	color;
		if( fInitBuffer != nil )
		{
			IClearFromBuffer( fInitBuffer );
		}
		else
		{
			color.Set( 0.f, 0.f, 0.f, 1.f );
			ClearToColor( color );
			FlushToHost();
		}
		IBuildLevelSizes();
		fTotalSize = GetLevelSize( 0 );
		SetCurrLevel( 0 );
		// Destroy the old texture ref, if we have one. This should force the 
		// pipeline to recreate one more suitable for our use
		SetDeviceRef( nil );
		plProfile_NewMem(MemMipmaps, fTotalSize);
		plProfile_NewMem(DynaTextMem, fTotalSize);
#ifdef MEMORY_LEAK_TRACER
		IAddToMemRecord( this, plRecord::kViaCreate );
#endif
	}

	if( GetImage() == nil )
		return false;

	return true;//fWriter->IsValid();
}

// allow the user of the DynaTextMap that they are done with the image... for now
// ... the fImage will be re-created on the next operation that requires the image
void plDynamicTextMap::PurgeImage()
{
	IDestroyOSSurface();
	fTotalSize = 0;
	SetCurrLevel( 0 );
	// Destroy the old texture ref, if we have one. This should force the 
	// pipeline to recreate one more suitable for our use
	SetDeviceRef( nil );
}

//// IAllocateOSSurface ///////////////////////////////////////////////////////
//	OS-specific. Allocates a rectangular bitmap of the given dimensions that
//	the OS can draw text into. Returns a pointer to the pixels.

UInt32* plDynamicTextMap::IAllocateOSSurface( UInt16 width, UInt16 height )
{
	UInt32* pixels = TRACKED_NEW UInt32[ width * height ];
	return pixels;
}

//// IDestroyOSSurface ////////////////////////////////////////////////////////
//	Opposite of allocate. DUH!

void	plDynamicTextMap::IDestroyOSSurface( void )
{
#ifdef MEMORY_LEAK_TRACER
	if( fImage != nil )
		IRemoveFromMemRecord( (UInt8 *)fImage );
#endif

	delete [] fImage;
	fImage = nil;

	plProfile_Dec(DynaTexts);
	plProfile_DelMem(DynaTextMem, fTotalSize);
	plProfile_DelMem(MemMipmaps, fTotalSize);
}

///////////////////////////////////////////////////////////////////////////////
//// Virtual Functions ////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//// Read /////////////////////////////////////////////////////////////////////

UInt32  plDynamicTextMap::Read( hsStream *s )
{
	UInt32 totalRead = plBitmap::Read( s );

	// The funny thing is that we don't read anything like a mipmap; we just
	// keep the width and height and call Create() after we read those in

	fVisWidth = (UInt16)(s->ReadSwap32());
	fVisHeight = (UInt16)(s->ReadSwap32());
	fHasAlpha = s->ReadBool();
	totalRead += 2 * 4;

	UInt32 initSize = s->ReadSwap32();
	totalRead += 4;
	if( initSize > 0 )
	{
		fInitBuffer = TRACKED_NEW UInt32[ initSize ];

		s->ReadSwap32( initSize, fInitBuffer );
		totalRead += initSize * 4;
	}
	else
		fInitBuffer = nil;

	Create( fVisWidth, fVisHeight, fHasAlpha );
	
	delete [] fInitBuffer;
	fInitBuffer = nil;

	return totalRead;
}

//// Write ////////////////////////////////////////////////////////////////////

UInt32 	plDynamicTextMap::Write( hsStream *s )
{
	UInt32 totalWritten = plBitmap::Write( s );

	s->WriteSwap32( fVisWidth );
	s->WriteSwap32( fVisHeight );
	s->WriteBool( fHasAlpha );

	s->WriteSwap32( fInitBuffer != nil ? fVisWidth * fVisHeight * sizeof( UInt32 ) : 0 );
	if( fInitBuffer != nil )
	{
		s->WriteSwap32( fVisWidth * fVisHeight, fInitBuffer );
	}

	return totalWritten;
}

///////////////////////////////////////////////////////////////////////////////
//// Some More Functions //////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//// SetInitBuffer ////////////////////////////////////////////////////////////
//	Sets an initial buffer, which is written to disk and read in to use for
//	initializing the color buffer upon creation. If not specified, we init to
//	black. ASSUMES the buffer is of dimensions fVisWidth x fVisHeight.

void	plDynamicTextMap::SetInitBuffer( UInt32 *buffer )
{
	delete [] fInitBuffer;
	if( buffer == nil )
	{
		fInitBuffer = nil;
		return;
	}

	fInitBuffer = TRACKED_NEW UInt32[ fVisWidth * fVisHeight ];
	memcpy( fInitBuffer, buffer, fVisWidth * fVisHeight * sizeof( UInt32 ) );
}

//// CopyFrom /////////////////////////////////////////////////////////////////

void	plDynamicTextMap::CopyFrom( plMipmap *source )
{
	hsAssert( false, "Copying plDynamicTextMaps is not supported." );
}

//// Clone ////////////////////////////////////////////////////////////////////

plMipmap	*plDynamicTextMap::Clone( void )
{
	static bool alreadyWarned = false;

	if( !alreadyWarned )
	{
		hsAssert( false, "Cloning plDynamicTextMaps is not supported." );
		alreadyWarned = true;
	}

	return nil;
}

///////////////////////////////////////////////////////////////////////////////
//// Rendering Functions //////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//// IClearFromBuffer /////////////////////////////////////////////////////////

void	plDynamicTextMap::IClearFromBuffer( UInt32 *clearBuffer )
{
	int			y;
	UInt32		*data = (UInt32 *)fImage, *srcData = clearBuffer;
	UInt8		*destAlpha = nil;


	if( !IIsValid() )
		return;

	// Clear *all* to zero
	memset( data, 0, fWidth * fHeight * sizeof( UInt32 ) );

	// Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right
	for( y = 0; y < fVisHeight; y++ )
	{
		memcpy( data, srcData, fVisWidth * sizeof( UInt32 ) );
		data += fWidth;
		srcData += fVisWidth;
	}
}

//// IPropagateFlags //////////////////////////////////////////////////////////

void	plDynamicTextMap::IPropagateFlags()
{
	SetJustify(fJustify);
	fCurrFont->SetRenderFlag(plFont::kRenderShadow, fFontFlags & kFontShadowed);
	fCurrFont->SetRenderFlag(plFont::kRenderIntoAlpha, fFontBlockRGB);
	fCurrFont->SetRenderFlag(plFont::kRenderAlphaPremultiplied, fPremultipliedAlpha);
	fCurrFont->SetRenderColor(fFontColor.ToARGB32());
}

//// ClearToColor /////////////////////////////////////////////////////////////

void	plDynamicTextMap::ClearToColor( hsColorRGBA &color )
{
	if( !IIsValid() )
		return;

	UInt32		i, hex = fPremultipliedAlpha ? color.ToARGB32Premultiplied() : color.ToARGB32();
	UInt32		*data = (UInt32 *)fImage;

	// Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right
	for( i = 0; i < fHeight * fWidth; i++ )
		data[ i ] = hex;
}

//// SetJustify ///////////////////////////////////////////////////////////////

void	plDynamicTextMap::SetJustify( Justify j )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	fJustify = j; 
	switch( fJustify )
	{
		case kLeftJustify:	fCurrFont->SetRenderXJustify( plFont::kRenderJustXForceLeft ); break;
		case kCenter:		fCurrFont->SetRenderXJustify( plFont::kRenderJustXCenter ); break;
		case kRightJustify:	fCurrFont->SetRenderXJustify( plFont::kRenderJustXRight ); break;
	}
}

//// SetFont //////////////////////////////////////////////////////////////////

void	plDynamicTextMap::SetFont( const char *face, UInt16 size, UInt8 fontFlags, hsBool antiAliasRGB )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	delete [] fFontFace;
	if (plLocalization::UsingUnicode())
	{
		// unicode has a bunch of chars that most fonts don't have, so we override the font choice with one
		// that will work with the desired language
		hsStatusMessageF("We are using a unicode language, overriding font choice of %s", face ? face : "nil");
		fFontFace = hsStrcpy( "Unicode" );
	}
	else
		fFontFace = ( face != nil ) ? hsStrcpy( face ) : nil;
	fFontSize = size;
	fFontFlags = fontFlags;
	fFontAntiAliasRGB = antiAliasRGB;

	fCurrFont = plFontCache::GetInstance().GetFont( fFontFace, (UInt8)fFontSize,
						( ( fFontFlags & kFontBold ) ? plFont::kFlagBold : 0 ) | 
						( ( fFontFlags & kFontItalic ) ? plFont::kFlagItalic : 0 ) );
	if ( fCurrFont == nil )
	{
		if (!fCurrFont)
			hsStatusMessageF("Font missing - %s. Using Arial", fFontFace ? fFontFace : "nil");

		if ( fFontFace )
			delete [] fFontFace;
		fFontFace = hsStrcpy( "Arial" );
		// lets try again with Arial
		fCurrFont = plFontCache::GetInstance().GetFont( fFontFace, (UInt8)fFontSize,
							( ( fFontFlags & kFontBold ) ? plFont::kFlagBold : 0 ) | 
							( ( fFontFlags & kFontItalic ) ? plFont::kFlagItalic : 0 ) );
	}

	// This will be nil if we're just running the page optimizer.
	if (fCurrFont)
	{
		if (fFontFlags & kFontShadowed)
			fCurrFont->SetRenderFlag(plFont::kRenderShadow, true);
		fCurrFont->SetRenderYJustify( plFont::kRenderJustYTop );
		SetJustify( fJustify );
	}
}

void	plDynamicTextMap::SetFont( const wchar_t *face, UInt16 size, UInt8 fontFlags , hsBool antiAliasRGB )
{
	char *sFace = hsWStringToString(face);
	SetFont(sFace,size,fontFlags,antiAliasRGB);
	delete [] sFace;
}

//// SetLineSpacing ///////////////////////////////////////////////////////////

void	plDynamicTextMap::SetLineSpacing( Int16 spacing )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	fLineSpacing = spacing;
	fCurrFont->SetRenderLineSpacing(spacing);
}

//// SetTextColor /////////////////////////////////////////////////////////////

void	plDynamicTextMap::SetTextColor( hsColorRGBA &color, hsBool blockRGB )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	fFontColor = color;
	fFontBlockRGB = blockRGB;

	if (fCurrFont)
		fCurrFont->SetRenderColor( fFontColor.ToARGB32() );
}

//// DrawString ///////////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawString( UInt16 x, UInt16 y, const char *text )
{
	wchar_t *wText = hsStringToWString(text);
	DrawString(x,y,wText);
	delete [] wText;
}

void	plDynamicTextMap::DrawString( UInt16 x, UInt16 y, const wchar_t *text )
{
	if( !IIsValid() )
		return;

	IPropagateFlags();
	fCurrFont->SetRenderFlag( plFont::kRenderWrap | plFont::kRenderClip, false );
	fCurrFont->SetRenderClipRect( 0, 0, fVisWidth, fVisHeight );
	fCurrFont->RenderString( this, x, y, text );
}

//// DrawClippedString ////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height )
{
	wchar_t *wText = hsStringToWString(text);
	DrawClippedString(x,y,wText,width,height);
	delete [] wText;
}

void	plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 width, UInt16 height )
{
	if( !IIsValid() )
		return;

	IPropagateFlags();
	fCurrFont->SetRenderClipping( x, y, width, height );
	fCurrFont->RenderString( this, x, y, text );
}

//// DrawClippedString ////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height )
{
	wchar_t *wText = hsStringToWString(text);
	DrawClippedString(x,y,wText,clipX,clipY,width,height);
	delete [] wText;
}

void	plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height )
{
	if( !IIsValid() )
		return;

	IPropagateFlags();
	fCurrFont->SetRenderClipping( clipX, clipY, width, height );
	fCurrFont->RenderString( this, x, y, text );
}

//// DrawWrappedString ////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height, UInt16 *lastX, UInt16 *lastY )
{
	wchar_t *wText = hsStringToWString(text);
	DrawWrappedString(x,y,wText,width,height,lastX,lastY);
	delete [] wText;
}

void	plDynamicTextMap::DrawWrappedString( UInt16 x, UInt16 y, const wchar_t *text, UInt16 width, UInt16 height, UInt16 *lastX, UInt16 *lastY )
{
	if( !IIsValid() )
		return;

	IPropagateFlags();
	fCurrFont->SetRenderWrapping( x, y, width, height );
	fCurrFont->RenderString( this, x, y, text, lastX, lastY );
}

//// CalcStringWidth //////////////////////////////////////////////////////////

UInt16		plDynamicTextMap::CalcStringWidth( const char *text, UInt16 *height )
{
	wchar_t *wText = hsStringToWString(text);
	UInt16 w = CalcStringWidth(wText,height);
	delete [] wText;
	return w;
}

UInt16		plDynamicTextMap::CalcStringWidth( const wchar_t *text, UInt16 *height )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return 0;

	SetJustify( fJustify );
	UInt16 w, h, a, lastX, lastY;
	UInt32 firstClipped;
	fCurrFont->SetRenderFlag( plFont::kRenderClip | plFont::kRenderWrap, false );
	fCurrFont->CalcStringExtents( text, w, h, a, firstClipped, lastX, lastY );
	if( height != nil )
		*height = h;
	return w;
}

//// SetFirstLineIndent ///////////////////////////////////////////////////////

void	plDynamicTextMap::SetFirstLineIndent( Int16 indent )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	fCurrFont->SetRenderFirstLineIndent( indent );
}

//// CalcWrappedStringSize ////////////////////////////////////////////////////

void	plDynamicTextMap::CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar, UInt16 *maxAscent, UInt16 *lastX, UInt16 *lastY )
{
	wchar_t *wText = hsStringToWString(text);
	CalcWrappedStringSize(wText,width,height,firstClippedChar,maxAscent,lastX,lastY);
	delete [] wText;
}

void	plDynamicTextMap::CalcWrappedStringSize( const wchar_t *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar, UInt16 *maxAscent, UInt16 *lastX, UInt16 *lastY )
{
// ===> Don't need to validate creation
//	if( !IIsValid() )
//		return;

	SetJustify( fJustify );
	UInt16 w, h, a, lX, lY;
	UInt32 firstClipped;
	fCurrFont->SetRenderWrapping( 0, 0, *width, *height );
	fCurrFont->CalcStringExtents( text, w, h, a, firstClipped, lX, lY );
	*width = w;
	*height = h;
	if( firstClippedChar != nil )
		*firstClippedChar = firstClipped;
	if( maxAscent != nil )
		*maxAscent = a;
	if( lastX != nil )
		*lastX = lX;
	if( lastY != nil )
		*lastY = lY;
}

//// FillRect /////////////////////////////////////////////////////////////////

void	plDynamicTextMap::FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color )
{
	if( !IIsValid() )
		return;

	if( x + width > fWidth )
		width = (UInt16)(fWidth - x);

	// Gee, how hard can it REALLY be?
	UInt32 i, hex = fPremultipliedAlpha ? color.ToARGB32Premultiplied() : color.ToARGB32();
	height += y;
	if( height > fHeight )
		height = (UInt16)fHeight;
	for( ; y < height; y++ )
	{
		UInt32 *destPtr = GetAddr32( x, y );
		for( i = 0; i < width; i++ )
			destPtr[ i ] = hex;
	}
}

//// FrameRect ////////////////////////////////////////////////////////////////

void	plDynamicTextMap::FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color )
{
	if( !IIsValid() )
		return;

	if( x + width > fWidth )
		width = (UInt16)(fWidth - x);
	if( y + height > fHeight )
		height = (UInt16)(fHeight - y);

	// Shouldn't be much harder
	UInt32 i, hex = fPremultipliedAlpha ? color.ToARGB32Premultiplied() : color.ToARGB32();
	UInt32 *dest1, *dest2;

	dest1 = GetAddr32( x, y );
	dest2 = GetAddr32( x, y + height - 1 );
	for( i = 0; i < width; i++ )
		dest1[ i ] = dest2[ i ] = hex;

	for( i = 0; i < height; i++ )
	{
		dest1[ 0 ] = dest1[ width - 1 ] = hex;
		dest1 += fWidth;
	}
}

//// DrawImage ////////////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawImage( UInt16 x, UInt16 y, plMipmap *image, DrawMethods method )
{
	if( !IIsValid() )
		return;

	plMipmap::CompositeOptions	opts;
	if( method == kImgNoAlpha )
	{
		if( fHasAlpha )
			opts.fFlags = plMipmap::kForceOpaque;
		else
			opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest
	}
	else if( method == kImgBlend )
		opts.fFlags = 0;		// Default opts
	else if( method == kImgSprite )
		opts.fFlags = plMipmap::kCopySrcAlpha;

	if( fPremultipliedAlpha )
		opts.fFlags |= plMipmap::kDestPremultiplied;

	Composite( image, x, y, &opts );

	/// HACK for now, since the alpha in the mipmap gets copied straight into the
	/// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated
/*	if( fHasAlpha && !respectAlpha )
	{
		HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) );
		RECT rc;
		::SetRect( &rc, x, y, x + image->GetWidth(), y + image->GetHeight() );
		::FillRect( fWinAlphaDC, &rc, brush );
		::DeleteObject( brush );
	}
*/
}

//// DrawClippedImage /////////////////////////////////////////////////////////

void	plDynamicTextMap::DrawClippedImage( UInt16 x, UInt16 y, plMipmap *image, 
											UInt16 srcClipX, UInt16 srcClipY, 
											UInt16 srcClipWidth, UInt16 srcClipHeight, 
											DrawMethods method )
{
	if( !IIsValid() )
		return;

	plMipmap::CompositeOptions	opts;
	if( method == kImgNoAlpha )
	{
		if( fHasAlpha )
			opts.fFlags = plMipmap::kForceOpaque;
		else
			opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest
	}
	else if( method == kImgBlend )
		opts.fFlags = 0;		// Default opts
	else if( method == kImgSprite )
		opts.fFlags = plMipmap::kCopySrcAlpha;

	if( fPremultipliedAlpha )
		opts.fFlags |= plMipmap::kDestPremultiplied;

	opts.fSrcClipX = srcClipX;
	opts.fSrcClipY = srcClipY;
	opts.fSrcClipWidth = srcClipWidth;
	opts.fSrcClipHeight = srcClipHeight;
	Composite( image, x, y, &opts );

	/// HACK for now, since the alpha in the mipmap gets copied straight into the
	/// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated
/*	if( fHasAlpha && !respectAlpha )
	{
		HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) );
		RECT rc;
		::SetRect( &rc, x, y, x + ( srcClipWidth > 0 ? srcClipWidth : image->GetWidth() ),
							y + ( srcClipHeight > 0 ? srcClipHeight : image->GetHeight() ) );
		::FillRect( fWinAlphaDC, &rc, brush );
		::DeleteObject( brush );
	}
*/
}

//// FlushToHost //////////////////////////////////////////////////////////////

void	plDynamicTextMap::FlushToHost( void )
{
	if( !IIsValid() )
		return;

	// Dirty the mipmap's deviceRef, if there is one
	if( GetDeviceRef() != nil )
		GetDeviceRef()->SetDirty( true );
}

//// GetLayerTransform ////////////////////////////////////////////////////////
//	Since the textGen can actually create a texture bigger than you were expecting,
//	you want to be able to apply a layer texture transform that will compensate. This
//	function will give you that transform. Just feed it into plLayer->SetTransform().

hsMatrix44	plDynamicTextMap::GetLayerTransform( void )
{
	hsMatrix44	xform;
	hsVector3	scale;

	scale.Set( (float)GetVisibleWidth() / (float)GetWidth(), 
			   (float)GetVisibleHeight() / (float)GetHeight(), 1.f );

	xform.MakeScaleMat( &scale );
	return xform;
}

//// MsgReceive ///////////////////////////////////////////////////////////////

hsBool	plDynamicTextMap::MsgReceive( plMessage *msg )
{
	plDynamicTextMsg	*textMsg = plDynamicTextMsg::ConvertNoRef( msg );
	if( textMsg != nil )
	{
		if( textMsg->fCmd & plDynamicTextMsg::kClear )
			ClearToColor( textMsg->fClearColor );

		if( textMsg->fCmd & plDynamicTextMsg::kSetTextColor )
			SetTextColor( textMsg->fColor, textMsg->fBlockRGB );

		if( (textMsg->fCmd & plDynamicTextMsg::kSetFont ) && textMsg->fString)
			SetFont( textMsg->fString, textMsg->fX, (UInt8)(textMsg->fFlags) );
		
		if( textMsg->fCmd & plDynamicTextMsg::kSetLineSpacing )
			SetLineSpacing( textMsg->fLineSpacing );

		if( textMsg->fCmd & plDynamicTextMsg::kSetJustify )
			SetJustify( (Justify)textMsg->fFlags );

		if( textMsg->fCmd & plDynamicTextMsg::kFillRect )
			FillRect( textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1,
						textMsg->fBottom - textMsg->fTop + 1, textMsg->fColor );

		if( textMsg->fCmd & plDynamicTextMsg::kFrameRect )
			FrameRect( textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1,
						textMsg->fBottom - textMsg->fTop + 1, textMsg->fColor );

		if( (textMsg->fCmd & plDynamicTextMsg::kDrawString ) && textMsg->fString)
			DrawString( textMsg->fX, textMsg->fY, textMsg->fString );

		if( (textMsg->fCmd & plDynamicTextMsg::kDrawClippedString ) && textMsg->fString)
			DrawClippedString( textMsg->fX, textMsg->fY, textMsg->fString,
								textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1,
								textMsg->fBottom - textMsg->fTop + 1 );

		if( (textMsg->fCmd & plDynamicTextMsg::kDrawWrappedString ) && textMsg->fString)
			DrawWrappedString( textMsg->fX, textMsg->fY, textMsg->fString, textMsg->fRight, textMsg->fBottom );

		if( textMsg->fCmd & plDynamicTextMsg::kDrawImage )
		{
			plMipmap *mip = plMipmap::ConvertNoRef( textMsg->fImageKey ? textMsg->fImageKey->ObjectIsLoaded() : nil);
			if( mip != nil )
				DrawImage( textMsg->fX, textMsg->fY, mip, textMsg->fFlags ? kImgBlend : kImgNoAlpha );
		}

		if( textMsg->fCmd & plDynamicTextMsg::kDrawClippedImage )
		{
			plMipmap *mip = plMipmap::ConvertNoRef( textMsg->fImageKey ? textMsg->fImageKey->ObjectIsLoaded() : nil);
			if( mip != nil )
				DrawClippedImage( textMsg->fX, textMsg->fY, mip, textMsg->fLeft, textMsg->fTop, 
								textMsg->fRight, textMsg->fBottom, textMsg->fFlags ? kImgBlend : kImgNoAlpha );
		}

		if( textMsg->fCmd & plDynamicTextMsg::kFlush )
			FlushToHost();

		if( textMsg->fCmd & plDynamicTextMsg::kPurgeImage )
			PurgeImage();

		return true;
	}

	return plMipmap::MsgReceive( msg );
}

//// Swap /////////////////////////////////////////////////////////////////////
//	Swapping is an evil little trick. It's also something that should probably
//	be exposed at the mipmap level eventually, but there's no need for it yet.
//	Basically, it lets you take the contents of two DTMaps and swap them, as
//	if you had swapped pointers, but you didn't. This is so you can, well, swap
//	DTMaps without swapping pointers! (Like, say, you don't have access to them)

#define SWAP_ME( Type, a, b ) { Type temp; temp = a; a = b; b = temp; }

void	plDynamicTextMap::Swap( plDynamicTextMap *other )
{
	// We only do this if the two are the same size, color depth, etc
	if( other->GetWidth() != GetWidth() || other->GetHeight() != GetHeight() ||
		other->GetPixelSize() != GetPixelSize() )
		return;

	// Swap image pointers
	void *ptr = other->fImage;
	other->fImage = fImage;
	fImage = ptr;

	// Invalidate both device refs (don't risk swapping THOSE)
	if( GetDeviceRef() != nil )
		GetDeviceRef()->SetDirty( true );
	if( other->GetDeviceRef() != nil )
		other->GetDeviceRef()->SetDirty( true );

	// Swap DTMap info
	SWAP_ME( hsBool, fHasAlpha, other->fHasAlpha );
	SWAP_ME( hsBool, fPremultipliedAlpha, other->fPremultipliedAlpha );
	SWAP_ME( hsBool, fShadowed, other->fShadowed );

	SWAP_ME( Justify, fJustify, other->fJustify );
	SWAP_ME( char *, fFontFace, other->fFontFace );
	SWAP_ME( UInt16, fFontSize, other->fFontSize );
	SWAP_ME( UInt8, fFontFlags, other->fFontFlags );
	SWAP_ME( hsBool, fFontAntiAliasRGB, other->fFontAntiAliasRGB );
	SWAP_ME( hsColorRGBA, fFontColor, other->fFontColor );
	SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB );

	SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB );
	SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB );
	SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB );

	SWAP_ME( plFont *, fCurrFont, other->fCurrFont );
	SWAP_ME( UInt32 *, fInitBuffer, other->fInitBuffer );
}