|
|
|
/*==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==*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// plTextGenerator Class Functions //
|
|
|
|
// Cyan, Inc. //
|
|
|
|
// //
|
|
|
|
//// Version History //////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// 12.13.2001 mcn - Created. //
|
|
|
|
// //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "hsWindows.h"
|
|
|
|
#include "hsTypes.h"
|
|
|
|
#include "hsMatrix44.h"
|
|
|
|
#include "pnKeyedObject/hsKeyedObject.h"
|
|
|
|
#include "plTextGenerator.h"
|
|
|
|
#include "plGImage/plMipmap.h"
|
|
|
|
#include "plPipeline/hsGDeviceRef.h"
|
|
|
|
#include "pnMessage/plRefMsg.h"
|
|
|
|
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
|
|
|
|
// Because tempKeys haven't been fixed yet (mf says to blame Eric Ellis), reffing
|
|
|
|
// objects when we have a tempKey (or they have a tempKey) just don't work. In
|
|
|
|
// fact, it will do nasty things like crashing on shutdown. Until then, we simply
|
|
|
|
// won't do the refs. Note that this is BAD, but given the only time we currently
|
|
|
|
// use these objects are very limited, controlled cases that *should* be okay
|
|
|
|
// for now, we should be reasonably safe. For now.
|
|
|
|
//#define MCN_DO_REFS
|
|
|
|
|
|
|
|
//// Constructor & Destructor /////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plTextGenerator::plTextGenerator()
|
|
|
|
{
|
|
|
|
fHost = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plTextGenerator::plTextGenerator( plMipmap *host, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
fHost = nil;
|
|
|
|
Attach( host, width, height );
|
|
|
|
}
|
|
|
|
|
|
|
|
plTextGenerator::~plTextGenerator()
|
|
|
|
{
|
|
|
|
// This also won't work until tempKeys work, since the mipmap will be gone by
|
|
|
|
// this time, in which case, calling Detach() crashes
|
|
|
|
#ifdef MCN_DO_REFS
|
|
|
|
Detach();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Attach ///////////////////////////////////////////////////////////////////
|
|
|
|
// Grab onto a plMipmap, suck the texture out of it and replace it with our
|
|
|
|
// own.
|
|
|
|
|
|
|
|
void plTextGenerator::Attach( plMipmap *host, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
uint16_t textWidth, textHeight;
|
|
|
|
|
|
|
|
|
|
|
|
hsAssert( fHost == nil, "Attempting to attach an already attached plTextGenerator" );
|
|
|
|
|
|
|
|
fHost = host;
|
|
|
|
|
|
|
|
/// Suck the old texture data out
|
|
|
|
fHost->Reset();
|
|
|
|
|
|
|
|
/// Make some new
|
|
|
|
|
|
|
|
// Note that we need POW-2 textures, so we go for the next one up that will
|
|
|
|
// fit what we need
|
|
|
|
for( textWidth = 1; textWidth < width; textWidth <<= 1 );
|
|
|
|
for( textHeight = 1; textHeight < height; textHeight <<= 1 );
|
|
|
|
|
|
|
|
fWidth = width;
|
|
|
|
fHeight = height;
|
|
|
|
fHost->fImage = (void *)IAllocateOSSurface( textWidth, textHeight );
|
|
|
|
fHost->SetConfig( plMipmap::kARGB32Config );
|
|
|
|
fHost->fWidth = textWidth;
|
|
|
|
fHost->fHeight = textHeight;
|
|
|
|
fHost->fPixelSize = 32;
|
|
|
|
fHost->fRowBytes = textWidth * 4;
|
|
|
|
fHost->fNumLevels = 1;
|
|
|
|
fHost->fFlags |= plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage;
|
|
|
|
fHost->fCompressionType = plMipmap::kUncompressed;
|
|
|
|
fHost->fUncompressedInfo.fType = plMipmap::UncompressedInfo::kRGB8888;
|
|
|
|
fHost->IBuildLevelSizes();
|
|
|
|
fHost->fTotalSize = fHost->GetLevelSize( 0 );
|
|
|
|
|
|
|
|
// Destroy the old texture ref, since it's probably completely nutsoid at this point.
|
|
|
|
// This should force the pipeline to recreate one more suitable for our use
|
|
|
|
fHost->SetDeviceRef( nil );
|
|
|
|
|
|
|
|
// Some init color
|
|
|
|
hsColorRGBA color;
|
|
|
|
color.Set( 0.f, 0.f, 0.f, 1.f );
|
|
|
|
ClearToColor( color );
|
|
|
|
FlushToHost();
|
|
|
|
|
|
|
|
#ifdef MCN_DO_REFS
|
|
|
|
/// Of course, brilliantly enough, if we did an attach on the constructor, we don't have a key
|
|
|
|
/// yet, so we better give ourselves one before we can call AddViaNotify()
|
|
|
|
if( GetKey() == nil )
|
|
|
|
{
|
|
|
|
char str[ 256 ];
|
|
|
|
sprintf( str, "plTextGen:%s", fHost->GetKeyName() );
|
|
|
|
hsgResMgr::ResMgr()->NewKey( str, this, plLocation::kGlobalFixedLoc );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send ourselves a passive ref of the mipmap, so we get notified if and when it goes away
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify( fHost->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kActiveRef );
|
|
|
|
#endif
|
|
|
|
/// All done!
|
|
|
|
}
|
|
|
|
|
|
|
|
//// 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_t *plTextGenerator::IAllocateOSSurface( uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
BITMAPINFO bmi;
|
|
|
|
|
|
|
|
|
|
|
|
// Create a new DC and bitmap that we can draw characters to
|
|
|
|
memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) );
|
|
|
|
bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
|
|
|
|
bmi.bmiHeader.biWidth = width;
|
|
|
|
bmi.bmiHeader.biHeight = -(int)height;
|
|
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
|
|
|
|
|
|
fWinRGBDC = CreateCompatibleDC( nil );
|
|
|
|
fWinRGBBitmap = CreateDIBSection( fWinRGBDC, &bmi, DIB_RGB_COLORS, (void **)&fWinRGBBits, nil, 0 );
|
|
|
|
SetMapMode( fWinRGBDC, MM_TEXT );
|
|
|
|
SetBkMode( fWinRGBDC, TRANSPARENT );
|
|
|
|
SetTextAlign( fWinRGBDC, TA_TOP | TA_LEFT );
|
|
|
|
|
|
|
|
SelectObject( fWinRGBDC, fWinRGBBitmap );
|
|
|
|
|
|
|
|
// Now create a second DC/bitmap combo, this one for writing alpha values to
|
|
|
|
memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) );
|
|
|
|
bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
|
|
|
|
bmi.bmiHeader.biWidth = width;
|
|
|
|
bmi.bmiHeader.biHeight = -(int)height;
|
|
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
|
|
bmi.bmiHeader.biBitCount = 8;
|
|
|
|
|
|
|
|
fWinAlphaDC = CreateCompatibleDC( nil );
|
|
|
|
fWinAlphaBitmap = CreateDIBSection( fWinAlphaDC, &bmi, DIB_RGB_COLORS, (void **)&fWinAlphaBits, nil, 0 );
|
|
|
|
SetMapMode( fWinAlphaDC, MM_TEXT );
|
|
|
|
SetBkMode( fWinAlphaDC, TRANSPARENT );
|
|
|
|
SetTextAlign( fWinAlphaDC, TA_TOP | TA_LEFT );
|
|
|
|
|
|
|
|
SelectObject( fWinAlphaDC, fWinAlphaBitmap );
|
|
|
|
|
|
|
|
return (uint32_t *)fWinRGBBits;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Detach ///////////////////////////////////////////////////////////////////
|
|
|
|
// Release the mipmap unto itself.
|
|
|
|
|
|
|
|
void plTextGenerator::Detach( void )
|
|
|
|
{
|
|
|
|
if( fHost == nil )
|
|
|
|
return;
|
|
|
|
// hsAssert( fHost != nil, "Attempting to detach unattached host" );
|
|
|
|
|
|
|
|
SetFont( nil, 0 );
|
|
|
|
IDestroyOSSurface();
|
|
|
|
|
|
|
|
fHost->Reset();
|
|
|
|
fHost->fFlags &= ~( plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage );
|
|
|
|
|
|
|
|
// Destroy the old texture ref, since we're no longer using it
|
|
|
|
fHost->SetDeviceRef( nil );
|
|
|
|
|
|
|
|
plMipmap *oldHost = fHost;
|
|
|
|
|
|
|
|
fHost = nil;
|
|
|
|
|
|
|
|
#ifdef MCN_DO_REFS
|
|
|
|
// Now send ourselves a unref msg, just in case we were called directly (if this was done by
|
|
|
|
// message, we'll get called a few times, but that's ok, we're set up to handle that, and it
|
|
|
|
// won't happen 'cept on destruction so the speed penalty shouldn't be a problem)
|
|
|
|
GetKey()->Release( oldHost->GetKey() );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IDestroyOSSurface ////////////////////////////////////////////////////////
|
|
|
|
// Opposite of allocate. DUH!
|
|
|
|
|
|
|
|
void plTextGenerator::IDestroyOSSurface( void )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
fHost->fImage = nil; // DeleteObject() will get rid of it for us
|
|
|
|
DeleteObject( fWinRGBBitmap );
|
|
|
|
DeleteDC( fWinRGBDC );
|
|
|
|
|
|
|
|
DeleteObject( fWinAlphaBitmap );
|
|
|
|
DeleteDC( fWinAlphaDC );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ClearToColor /////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::ClearToColor( hsColorRGBA &color )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint32_t *data = (uint32_t *)fHost->fImage;
|
|
|
|
uint32_t hexColor = color.ToARGB32();
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
GdiFlush();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for( i = 0; i < fHost->fWidth * fHost->fHeight; i++ )
|
|
|
|
data[ i ] = hexColor;
|
|
|
|
|
|
|
|
// Fill our alpha bitmap as well, since we use that too
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
memset( fWinAlphaBits, (uint8_t)( color.a * 255.f ), fHost->fWidth * fHost->fHeight );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// SetFont //////////////////////////////////////////////////////////////////
|
|
|
|
// OS-specific. Load the given font for drawing the text with.
|
|
|
|
|
|
|
|
void plTextGenerator::SetFont( const char *face, uint16_t size, hsBool antiAliasRGB )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
if( fWinFont != nil )
|
|
|
|
{
|
|
|
|
DeleteObject( fWinFont );
|
|
|
|
fWinFont = nil;
|
|
|
|
}
|
|
|
|
if( fWinAlphaFont != nil )
|
|
|
|
{
|
|
|
|
DeleteObject( fWinAlphaFont );
|
|
|
|
fWinAlphaFont = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( face != nil )
|
|
|
|
{
|
|
|
|
int nHeight = -MulDiv( size, GetDeviceCaps( fWinRGBDC, LOGPIXELSY ), 72 );
|
|
|
|
fWinFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
|
|
|
|
CLIP_DEFAULT_PRECIS, antiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, VARIABLE_PITCH, face );
|
|
|
|
hsAssert( fWinFont != nil, "Cannot create Windows font for plTextGenerator" );
|
|
|
|
|
|
|
|
// The font for the alpha channel is identical except that it's antialiased, whereas the RGB version isn't.
|
|
|
|
fWinAlphaFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
|
|
|
|
CLIP_DEFAULT_PRECIS, (!antiAliasRGB) ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, VARIABLE_PITCH, face );
|
|
|
|
hsAssert( fWinAlphaFont != nil, "Cannot create Windows font for plTextGenerator" );
|
|
|
|
|
|
|
|
SelectObject( fWinRGBDC, fWinFont );
|
|
|
|
SelectObject( fWinAlphaDC, fWinAlphaFont );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// SetTextColor /////////////////////////////////////////////////////////////
|
|
|
|
// blockRGB basically forces the RGB channel to write in blocks instead of
|
|
|
|
// actual characters. This isn't useful unless you're relying on the alpha
|
|
|
|
// channel to do the text (opaque text and transparent background), in which
|
|
|
|
// case you want plenty of block color in your RGB channel because it'll get
|
|
|
|
// alpha-ed out by the alpha channel.
|
|
|
|
|
|
|
|
void plTextGenerator::SetTextColor( hsColorRGBA &color, hsBool blockRGB )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
int r = (int)(color.r * 255.f);
|
|
|
|
int g = (int)(color.g * 255.f);
|
|
|
|
int b = (int)(color.b * 255.f);
|
|
|
|
int a = (int)(color.a * 255.f);
|
|
|
|
|
|
|
|
if( blockRGB )
|
|
|
|
{
|
|
|
|
::SetBkColor( fWinRGBDC, RGB( r, g, b ) );
|
|
|
|
::SetBkMode( fWinRGBDC, OPAQUE );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
::SetBkMode( fWinRGBDC, TRANSPARENT );
|
|
|
|
|
|
|
|
::SetTextColor( fWinRGBDC, RGB( r, g, b ) );
|
|
|
|
::SetTextColor( fWinAlphaDC, RGB( a, a, a ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// DrawString ///////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::DrawString( uint16_t x, uint16_t y, const char *text )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
DrawString(x,y,wText);
|
|
|
|
delete [] wText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plTextGenerator::DrawString( uint16_t x, uint16_t y, const wchar_t *text )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
::TextOutW( fWinRGBDC, x, y, text, wcslen( text ) );
|
|
|
|
::TextOutW( fWinAlphaDC, x, y, text, wcslen( text ) );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// DrawClippedString ////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::DrawClippedString( int16_t x, int16_t y, const char *text, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
DrawClippedString(x,y,wText,width,height);
|
|
|
|
delete [] wText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plTextGenerator::DrawClippedString( int16_t x, int16_t y, const wchar_t *text, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT r;
|
|
|
|
::SetRect( &r, x, y, x + width, y + height );
|
|
|
|
|
|
|
|
::ExtTextOutW( fWinRGBDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil );
|
|
|
|
::ExtTextOutW( fWinAlphaDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// DrawClippedString ////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::DrawClippedString( int16_t x, int16_t y, const char *text, uint16_t clipX, uint16_t clipY, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
DrawClippedString(x,y,wText,clipX,clipY,width,height);
|
|
|
|
delete [] wText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plTextGenerator::DrawClippedString( int16_t x, int16_t y, const wchar_t *text, uint16_t clipX, uint16_t clipY, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT r;
|
|
|
|
::SetRect( &r, clipX, clipY, clipX + width, clipY + height );
|
|
|
|
|
|
|
|
::ExtTextOutW( fWinRGBDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil );
|
|
|
|
::ExtTextOutW( fWinAlphaDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// DrawWrappedString ////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::DrawWrappedString( uint16_t x, uint16_t y, const char *text, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
DrawWrappedString(x,y,wText,width,height);
|
|
|
|
delete [] wText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plTextGenerator::DrawWrappedString( uint16_t x, uint16_t y, const wchar_t *text, uint16_t width, uint16_t height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT r;
|
|
|
|
::SetRect( &r, x, y, x + width, y + height );
|
|
|
|
|
|
|
|
// HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) );
|
|
|
|
// ::FillRect( fWinRGBDC, &r, brush );
|
|
|
|
// ::DeleteObject( brush );
|
|
|
|
::DrawTextW( fWinRGBDC, text, wcslen( text ), &r,
|
|
|
|
DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK );
|
|
|
|
::DrawTextW( fWinAlphaDC, text, wcslen( text ), &r,
|
|
|
|
DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// CalcStringWidth //////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
uint16_t plTextGenerator::CalcStringWidth( const char *text, uint16_t *height )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
uint16_t retVal = CalcStringWidth(wText,height);
|
|
|
|
delete [] wText;
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t plTextGenerator::CalcStringWidth( const wchar_t *text, uint16_t *height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
SIZE size;
|
|
|
|
::GetTextExtentPoint32W( fWinRGBDC, text, wcslen( text ), &size );
|
|
|
|
|
|
|
|
if( height != nil )
|
|
|
|
*height = (uint16_t)size.cy;
|
|
|
|
|
|
|
|
return (uint16_t)size.cx;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// CalcWrappedStringSize ////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::CalcWrappedStringSize( const char *text, uint16_t *width, uint16_t *height )
|
|
|
|
{
|
|
|
|
wchar_t *wText = hsStringToWString(text);
|
|
|
|
CalcWrappedStringSize(wText,width,height);
|
|
|
|
delete [] wText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plTextGenerator::CalcWrappedStringSize( const wchar_t *text, uint16_t *width, uint16_t *height )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT r;
|
|
|
|
::SetRect( &r, 0, 0, *width, 0 );
|
|
|
|
|
|
|
|
::DrawTextW( fWinRGBDC, text, wcslen( text ), &r, DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT );
|
|
|
|
|
|
|
|
*width = (uint16_t)(r.right);
|
|
|
|
if( height != nil )
|
|
|
|
*height = (uint16_t)r.bottom;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// FillRect /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::FillRect( uint16_t x, uint16_t y, uint16_t width, uint16_t height, hsColorRGBA &color )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT rc;
|
|
|
|
::SetRect( &rc, x, y, x + width, y + height );
|
|
|
|
|
|
|
|
int r = (int)(color.r * 255.f);
|
|
|
|
int g = (int)(color.g * 255.f);
|
|
|
|
int b = (int)(color.b * 255.f);
|
|
|
|
int a = (int)(color.a * 255.f);
|
|
|
|
|
|
|
|
HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) );
|
|
|
|
::FillRect( fWinRGBDC, &rc, brush );
|
|
|
|
::DeleteObject( brush );
|
|
|
|
|
|
|
|
brush = ::CreateSolidBrush( RGB( a, a, a ) );
|
|
|
|
::FillRect( fWinAlphaDC, &rc, brush );
|
|
|
|
::DeleteObject( brush );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// FrameRect ////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::FrameRect( uint16_t x, uint16_t y, uint16_t width, uint16_t height, hsColorRGBA &color )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
RECT rc;
|
|
|
|
::SetRect( &rc, x, y, x + width, y + height );
|
|
|
|
|
|
|
|
int r = (int)(color.r * 255.f);
|
|
|
|
int g = (int)(color.g * 255.f);
|
|
|
|
int b = (int)(color.b * 255.f);
|
|
|
|
int a = (int)(color.a * 255.f);
|
|
|
|
|
|
|
|
HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) );
|
|
|
|
::FrameRect( fWinRGBDC, &rc, brush );
|
|
|
|
::DeleteObject( brush );
|
|
|
|
|
|
|
|
brush = ::CreateSolidBrush( RGB( a, a, a ) );
|
|
|
|
::FrameRect( fWinAlphaDC, &rc, brush );
|
|
|
|
::DeleteObject( brush );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//// FlushToHost //////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plTextGenerator::FlushToHost( void )
|
|
|
|
{
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
// Flush the GDI first, to make sure it's done
|
|
|
|
GdiFlush();
|
|
|
|
|
|
|
|
// Now copy our alpha channel over. I hate the GDI
|
|
|
|
uint32_t i = fHost->fWidth * fHost->fHeight;
|
|
|
|
uint32_t *dest = fWinRGBBits;
|
|
|
|
uint8_t *src = fWinAlphaBits;
|
|
|
|
|
|
|
|
/* while( i-- )
|
|
|
|
{
|
|
|
|
*dest &= 0x00ffffff;
|
|
|
|
*dest |= ( *src ) << 24;
|
|
|
|
// *dest |= ( *dest << 16 ) & 0xff000000;
|
|
|
|
dest++;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
i--;
|
|
|
|
dest[ i ] &= 0x00ffffff;
|
|
|
|
dest[ i ] |= src[ i ] << 24;
|
|
|
|
} while( i );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Dirty the mipmap's deviceRef, if there is one
|
|
|
|
if( fHost->GetDeviceRef() != nil )
|
|
|
|
fHost->GetDeviceRef()->SetDirty( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// GetTextWidth/Height //////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
uint16_t plTextGenerator::GetTextWidth( void )
|
|
|
|
{
|
|
|
|
return ( fHost != nil ) ? (uint16_t)(fHost->fWidth) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t plTextGenerator::GetTextHeight( void )
|
|
|
|
{
|
|
|
|
return ( fHost != nil ) ? (uint16_t)(fHost->fHeight) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// 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 plTextGenerator::GetLayerTransform( void )
|
|
|
|
{
|
|
|
|
hsMatrix44 xform;
|
|
|
|
hsVector3 scale;
|
|
|
|
|
|
|
|
scale.Set( (float)GetWidth() / (float)GetTextWidth(),
|
|
|
|
(float)GetHeight() / (float)GetTextHeight(), 1.f );
|
|
|
|
|
|
|
|
xform.MakeScaleMat( &scale );
|
|
|
|
return xform;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// MsgReceive ///////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool plTextGenerator::MsgReceive( plMessage *msg )
|
|
|
|
{
|
|
|
|
#ifdef MCN_DO_REFS
|
|
|
|
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg );
|
|
|
|
if( refMsg != nil )
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest ) )
|
|
|
|
{
|
|
|
|
// Don't do anything--already did an attach
|
|
|
|
}
|
|
|
|
else if( refMsg->GetContext() & ( plRefMsg::kOnDestroy | plRefMsg::kOnRemove ) )
|
|
|
|
{
|
|
|
|
Detach();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return hsKeyedObject::MsgReceive( msg );
|
|
|
|
}
|