You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

933 lines
29 KiB

/*==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 );
}