/*==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 . 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() : plMipmap() { fVisWidth = fVisHeight = 0; fHasAlpha = false; fJustify = kLeftJustify; fInitBuffer = nil; fFontFace = nil; fFontSize = 0; fFontFlags = 0; fFontAntiAliasRGB = false; fFontColor.Set( 0, 0, 0, 1 ); fFontBlockRGB = false; fHasCreateBeenCalled = false; } plDynamicTextMap::~plDynamicTextMap() { Reset(); } plDynamicTextMap::plDynamicTextMap( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth, UInt32 extraHeight ) : plMipmap() { fInitBuffer = nil; fFontFace = nil; Create( width, height, hasAlpha, extraWidth, extraHeight ); } //// 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 ) { SetConfig( hasAlpha ? kARGB32Config : kRGB32Config ); fVisWidth = (UInt16)width; fVisHeight = (UInt16)height; fHasAlpha = hasAlpha; 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; } } //// ClearToColor ///////////////////////////////////////////////////////////// void plDynamicTextMap::ClearToColor( hsColorRGBA &color ) { if( !IIsValid() ) return; UInt32 i, hex = 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) { 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; SetJustify( fJustify ); fCurrFont->SetRenderFlag( plFont::kRenderWrap | plFont::kRenderClip, false ); fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); 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; SetJustify( fJustify ); fCurrFont->SetRenderClipping( x, y, width, height ); fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); 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; SetJustify( fJustify ); fCurrFont->SetRenderClipping( clipX, clipY, width, height ); fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); 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; SetJustify( fJustify ); fCurrFont->SetRenderWrapping( x, y, width, height ); fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); 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 = 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 = 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; 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; 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, 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 ); }