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.
1134 lines
34 KiB
1134 lines
34 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==*/ |
|
/////////////////////////////////////////////////////////////////////////////// |
|
// // |
|
// plDynSurfaceWriter Class Header // |
|
// Abstract class wrapping around Windows GDI functionality for writing to // |
|
// a generic RGBA surface. Allows us to create one writer per DTMap or a // |
|
// single shared writer to conserve OS resources on 98/ME. // |
|
// // |
|
// Cyan, Inc. // |
|
// // |
|
//// Version History ////////////////////////////////////////////////////////// |
|
// // |
|
// 10.28.2002 mcn - Created. // |
|
// // |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "HeadSpin.h" |
|
|
|
#include "plDynSurfaceWriter.h" |
|
|
|
#include "plDynamicTextMap.h" |
|
#include "hsExceptions.h" |
|
|
|
#include "hsMatrix44.h" |
|
#include "plMessage/plDynamicTextMsg.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "plProfile.h" |
|
#include "plStatusLog/plStatusLog.h" |
|
#include "plWinFontCache.h" |
|
|
|
|
|
//// plWinSurface Helper Functions //////////////////////////////////////////// |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
static uint32_t sNumDCsAllocated; |
|
static uint32_t sNumBitmapsAllocated; |
|
|
|
plDynSurfaceWriter::plWinSurface::plWinSurface() |
|
{ |
|
fDC = nil; |
|
fBitmap = nil; |
|
fFont = nil; |
|
fBits = nil; |
|
fTextColor = RGB( 255, 255, 255 ); |
|
fWidth = fHeight = 0; |
|
|
|
fSaveNum = 0; |
|
fFontSize = 0; |
|
fFontFlags = 0; |
|
fFontAntiAliasRGB = false; |
|
fFontBlockedRGB = false; |
|
} |
|
|
|
plDynSurfaceWriter::plWinSurface::~plWinSurface() |
|
{ |
|
Release(); |
|
} |
|
|
|
void plDynSurfaceWriter::plWinSurface::Allocate( uint16_t w, uint16_t h ) |
|
{ |
|
int i; |
|
BITMAPINFO *bmi; |
|
|
|
|
|
Release(); |
|
|
|
fWidth = w; |
|
fHeight = h; |
|
|
|
/// Initialize a bitmap info struct to describe our surface |
|
if( IBitsPerPixel() == 8 ) |
|
bmi = (BITMAPINFO *)( new uint8_t[ sizeof( BITMAPINFOHEADER ) + sizeof( RGBQUAD ) * 256 ] ); |
|
else |
|
bmi = new BITMAPINFO; |
|
|
|
memset( &bmi->bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); |
|
bmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); |
|
bmi->bmiHeader.biWidth = (int)fWidth; |
|
bmi->bmiHeader.biHeight = -(int)fHeight; |
|
bmi->bmiHeader.biPlanes = 1; |
|
bmi->bmiHeader.biCompression = BI_RGB; |
|
bmi->bmiHeader.biBitCount = IBitsPerPixel(); |
|
if( IBitsPerPixel() == 8 ) |
|
{ |
|
// Set up map for grayscale bitmap |
|
for( i = 0; i < 256; i++ ) |
|
{ |
|
bmi->bmiColors[ i ].rgbRed = i; |
|
bmi->bmiColors[ i ].rgbGreen = i; |
|
bmi->bmiColors[ i ].rgbBlue = i; |
|
bmi->bmiColors[ i ].rgbReserved = i; |
|
} |
|
} |
|
|
|
/// Create a screen-compatible DC |
|
fDC = CreateCompatibleDC( nil ); |
|
if( fDC == nil ) |
|
{ |
|
char msg[ 256 ]; |
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); |
|
char *ret = strrchr( msg, '\n' ); |
|
if( ret != nil ) |
|
*ret = 0; |
|
|
|
plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to allocate DC for dynamic text map (%s, %d DCs allocated already)", msg, sNumDCsAllocated ); |
|
if (IBitsPerPixel() == 8 ) |
|
delete [] bmi; |
|
else |
|
delete bmi; |
|
return; |
|
} |
|
sNumDCsAllocated++; |
|
|
|
/// Create a bitmap using the DC and the bitmapInfo struct we filled out |
|
fBitmap = CreateDIBSection( fDC, bmi, DIB_RGB_COLORS, (void **)&fBits, nil, 0 ); |
|
if( fBitmap == nil ) |
|
{ |
|
char msg[ 256 ]; |
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); |
|
char *ret = strrchr( msg, '\n' ); |
|
if( ret != nil ) |
|
*ret = 0; |
|
|
|
plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to allocate RGB DIB section for dynamic text map (%s, %d bitmaps allocated already)", msg, sNumBitmapsAllocated ); |
|
if (IBitsPerPixel() == 8 ) |
|
delete [] bmi; |
|
else |
|
delete bmi; |
|
return; |
|
} |
|
sNumBitmapsAllocated++; |
|
|
|
/// Set up some basic props |
|
SetMapMode( fDC, MM_TEXT ); |
|
SetBkMode( fDC, TRANSPARENT ); |
|
SetTextAlign( fDC, TA_TOP | TA_LEFT ); |
|
|
|
fSaveNum = SaveDC( fDC ); |
|
|
|
SelectObject( fDC, fBitmap ); |
|
|
|
if (IBitsPerPixel() == 8 ) |
|
delete [] bmi; |
|
else |
|
delete bmi; |
|
} |
|
|
|
void plDynSurfaceWriter::plWinSurface::Release( void ) |
|
{ |
|
if( fBitmap != nil ) |
|
sNumBitmapsAllocated--; |
|
if( fDC != nil ) |
|
sNumDCsAllocated--; |
|
|
|
if( fSaveNum != 0 ) |
|
RestoreDC( fDC, fSaveNum ); |
|
fSaveNum = 0; |
|
|
|
DeleteObject( fBitmap ); |
|
DeleteDC( fDC ); |
|
|
|
fDC = nil; |
|
fBitmap = nil; |
|
fFont = nil; |
|
fBits = nil; |
|
fWidth = fHeight = 0; |
|
|
|
fFontFace = plString::Null; |
|
fFontSize = 0; |
|
fFontFlags = 0; |
|
fFontAntiAliasRGB = false; |
|
fFontBlockedRGB = false; |
|
} |
|
|
|
bool plDynSurfaceWriter::plWinSurface::WillFit( uint16_t w, uint16_t h ) |
|
{ |
|
if( fWidth >= w && fHeight >= h ) |
|
return true; |
|
return false; |
|
} |
|
|
|
static int SafeStrCmp( const char *str1, const char *str2 ) |
|
{ |
|
if( str1 == nil && str2 == nil ) |
|
return -1; |
|
if( str1 != nil && str2 != nil ) |
|
return strcmp( str1, str2 ); |
|
return -1; |
|
} |
|
|
|
bool plDynSurfaceWriter::plWinSurface::FontMatches( const plString &face, uint16_t size, uint8_t flags, bool aaRGB ) |
|
{ |
|
if( face == fFontFace && fFontSize == size && |
|
fFontFlags == flags && fFontAntiAliasRGB == aaRGB ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
void plDynSurfaceWriter::plWinSurface::SetFont( const plString &face, uint16_t size, uint8_t flags, bool aaRGB ) |
|
{ |
|
fFontFace = face; |
|
fFontSize = size; |
|
fFontFlags = flags; |
|
fFontAntiAliasRGB = aaRGB; |
|
|
|
bool hadAFont = false; |
|
if( fFont != nil ) |
|
{ |
|
hadAFont = true; |
|
plWinFontCache::GetInstance().FreeFont( fFont ); |
|
fFont = nil; |
|
} |
|
|
|
if (face.IsEmpty()) |
|
return; |
|
|
|
bool bold = ( fFontFlags & plDynSurfaceWriter::kFontBold ) ? true : false; |
|
bool italic = ( fFontFlags & plDynSurfaceWriter::kFontItalic ) ? true : false; |
|
|
|
int nHeight = -MulDiv( size, GetDeviceCaps( fDC, LOGPIXELSY ), 72 ); |
|
fFont = plWinFontCache::GetInstance().GetMeAFont( face, nHeight, bold ? FW_BOLD : FW_NORMAL, italic, |
|
fFontAntiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY ); |
|
if( fFont == nil && fFontAntiAliasRGB ) |
|
{ |
|
static bool warnedCantAntiAlias = false; |
|
|
|
// Creation of font failed; could be that we can't do anti-aliasing? Try not doing it... |
|
if( !warnedCantAntiAlias ) |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "WARNING: Cannot allocate anti-aliased font. Falling back to non-anti-aliased. This will be the only warning" ); |
|
warnedCantAntiAlias = true; |
|
} |
|
|
|
fFont = plWinFontCache::GetInstance().GetMeAFont( face, nHeight, bold ? FW_BOLD : FW_NORMAL, italic, |
|
fFontAntiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY ); |
|
} |
|
|
|
if( fFont == nil ) |
|
{ |
|
hsAssert( false, "Cannot create Windows font for plDynSurfaceWriter" ); |
|
plStatusLog::AddLineS( "pipeline.log", "ERROR: Cannot allocate font for RGB surface! (face: %s, size: %d %s %s)", |
|
face.c_str(), nHeight, bold ? "bold" : "", italic ? "italic" : "" ); |
|
|
|
fFontFace = plString::Null; |
|
fFontSize = 0; |
|
return; |
|
} |
|
|
|
if( SelectObject( fDC, fFont ) == nil && hadAFont ) |
|
{ |
|
char msg[ 256 ]; |
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); |
|
char *ret = strrchr( msg, '\n' ); |
|
if( ret != nil ) |
|
*ret = 0; |
|
|
|
plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "SelectObject() FAILED (%s)", msg ); |
|
} |
|
|
|
} |
|
|
|
#endif // BUILD_FOR_WIN32 |
|
|
|
//// StupidStatic ///////////////////////////////////////////////////////////// |
|
|
|
bool plDynSurfaceWriter::fForceSharedSurfaces = false; |
|
bool plDynSurfaceWriter::fOSDetected = false; |
|
bool plDynSurfaceWriter::fOSCanShareSurfaces = false; |
|
|
|
bool plDynSurfaceWriter::CanHandleLotsOfThem( void ) |
|
{ |
|
if( fOSDetected ) |
|
return fOSCanShareSurfaces; |
|
|
|
fOSDetected = true; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
OSVERSIONINFO versionInfo; |
|
memset( &versionInfo, 0, sizeof( versionInfo ) ); |
|
versionInfo.dwOSVersionInfoSize = sizeof( versionInfo ); |
|
|
|
if( GetVersionEx( &versionInfo ) ) |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "OS version detection results:" ); |
|
plStatusLog::AddLineS( "pipeline.log", " Version: %d.%d", versionInfo.dwMajorVersion, versionInfo.dwMinorVersion ); |
|
plStatusLog::AddLineS( "pipeline.log", " Build #: %d", versionInfo.dwBuildNumber ); |
|
plStatusLog::AddLineS( "pipeline.log", " Platform ID: %d", versionInfo.dwPlatformId ); |
|
|
|
if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) |
|
{ |
|
if( fForceSharedSurfaces ) |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "Detected NT-based platform, but sharing surfaces due to override" ); |
|
fOSCanShareSurfaces = false; |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "Detected NT-based platform, allowing separate surfaces" ); |
|
fOSCanShareSurfaces = true; |
|
} |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "Detected non-NT-based platform: sharing surfaces" ); |
|
fOSCanShareSurfaces = false; |
|
} |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS( "pipeline.log", "OS version detection failed" ); |
|
fOSCanShareSurfaces = false; |
|
} |
|
|
|
#endif |
|
return fOSCanShareSurfaces; |
|
} |
|
|
|
//// Constructor & Destructor ///////////////////////////////////////////////// |
|
|
|
plDynSurfaceWriter::plDynSurfaceWriter() |
|
{ |
|
IInit(); |
|
} |
|
|
|
plDynSurfaceWriter::~plDynSurfaceWriter() |
|
{ |
|
Reset(); |
|
} |
|
|
|
plDynSurfaceWriter::plDynSurfaceWriter( plDynamicTextMap *target, uint32_t flags ) |
|
{ |
|
IInit(); |
|
fFlags = flags; |
|
SwitchTarget( target ); |
|
} |
|
|
|
void plDynSurfaceWriter::IInit( void ) |
|
{ |
|
fCurrTarget = 0; |
|
fJustify = kLeftJustify; |
|
fFlags = 0; |
|
fFlushed = true; |
|
fFontFace = plString::Null; |
|
fFontSize = 0; |
|
fFontBlockedRGB = false; |
|
} |
|
|
|
//// Reset //////////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::Reset( void ) |
|
{ |
|
#if HS_BUILD_FOR_WIN32 |
|
fRGBSurface.Release(); |
|
fAlphaSurface.Release(); |
|
#endif |
|
fCurrTarget = nil; |
|
fFlushed = true; |
|
|
|
fFontFace = plString::Null; |
|
fFontSize = 0; |
|
} |
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
//// Target Switching ///////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// FlushToTarget //////////////////////////////////////////////////////////// |
|
// Flushes all ops to the target. |
|
|
|
void plDynSurfaceWriter::FlushToTarget( void ) |
|
{ |
|
int x, y; |
|
|
|
|
|
if( fCurrTarget != nil && !fFlushed ) |
|
{ |
|
#if HS_BUILD_FOR_WIN32 |
|
// Flush the GDI so we can grab the bits |
|
GdiFlush(); |
|
|
|
uint32_t *destBits = (uint32_t *)fCurrTarget->GetImage(); |
|
|
|
// Are we merging in the alpha bits? |
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
// Yup, munge 'em |
|
uint32_t *srcRGBBits = fRGBSurface.GetBits(); |
|
uint8_t *srcAlphaBits = fAlphaSurface.GetBits(); |
|
uint32_t destWidth = fCurrTarget->GetWidth(); |
|
|
|
for( y = 0; y < fCurrTarget->GetHeight(); y++ ) |
|
{ |
|
for( x = 0; x < destWidth; x++ ) |
|
destBits[ x ] = ( srcRGBBits[ x ] & 0x00ffffff ) | ( (uint32_t)srcAlphaBits[ x ] << 24 ); |
|
|
|
destBits += destWidth; |
|
srcRGBBits += fRGBSurface.fWidth; |
|
srcAlphaBits += fAlphaSurface.fWidth; |
|
} |
|
} |
|
else |
|
{ |
|
// Nope, just a 24-bit copy and set alphas to ff |
|
uint32_t *srcBits = fRGBSurface.GetBits(); |
|
uint32_t destWidth = fCurrTarget->GetWidth(); |
|
|
|
for( y = 0; y < fCurrTarget->GetHeight(); y++ ) |
|
{ |
|
memcpy( destBits, srcBits, destWidth * sizeof( uint32_t ) ); |
|
|
|
// Fill in 0xff |
|
for( x = 0; x < destWidth; x++ ) |
|
destBits[ x ] |= 0xff000000; |
|
|
|
destBits += destWidth; |
|
srcBits += fRGBSurface.fWidth; |
|
} |
|
} |
|
#endif |
|
} |
|
fFlushed = true; |
|
} |
|
|
|
//// SwitchTarget ///////////////////////////////////////////////////////////// |
|
// Switches targets. Will flush to old target before switching. Also, if |
|
// kDiscard isn't specified, will copy contents of new target to working |
|
// surface. |
|
|
|
void plDynSurfaceWriter::SwitchTarget( plDynamicTextMap *target ) |
|
{ |
|
if( target == fCurrTarget ) |
|
return; |
|
|
|
if( !fFlushed ) |
|
FlushToTarget(); |
|
|
|
fCurrTarget = target; |
|
fFlushed = true; // Will force a copy next IEnsureSurfaceUpdated() |
|
|
|
// Make sure our surfaces fit |
|
bool hadToAllocate = false; |
|
#if HS_BUILD_FOR_WIN32 |
|
if( target != nil ) |
|
{ |
|
if( !fRGBSurface.WillFit( (uint16_t)(target->GetWidth()), (uint16_t)(target->GetHeight()) ) ) |
|
{ |
|
fRGBSurface.Allocate( (uint16_t)(target->GetWidth()), (uint16_t)(target->GetHeight()) ); |
|
hadToAllocate = true; |
|
} |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
if( !fAlphaSurface.WillFit( (uint16_t)(target->GetWidth()), (uint16_t)(target->GetHeight()) ) ) |
|
{ |
|
fAlphaSurface.Allocate( (uint16_t)(target->GetWidth()), (uint16_t)(target->GetHeight()) ); |
|
hadToAllocate = true; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
fRGBSurface.Release(); |
|
fAlphaSurface.Release(); |
|
hadToAllocate = true; |
|
} |
|
#endif |
|
|
|
if( hadToAllocate ) |
|
{ |
|
fFontFace = plString::Null; |
|
fFontSize = 0; |
|
fFontFlags = 0; |
|
} |
|
} |
|
|
|
//// IEnsureSurfaceUpdated //////////////////////////////////////////////////// |
|
// Makes sure our surfaces are ready to write to. |
|
|
|
void plDynSurfaceWriter::IEnsureSurfaceUpdated( void ) |
|
{ |
|
uint32_t x, y; |
|
|
|
|
|
// If we're flushed, then we haven't drawn since the last flush, |
|
// which means we want to copy from our target before we start drawing. |
|
// If we've already drawn, then we won't be flushed and we don't want |
|
// to be copying over what we've already drawn |
|
if( fCurrTarget != nil && fFlushed ) |
|
{ |
|
uint32_t *srcBits = (uint32_t *)fCurrTarget->GetImage(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
// Are we merging in the alpha bits? |
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
// Yup, de-munge 'em |
|
uint32_t *destRGBBits = fRGBSurface.GetBits(); |
|
uint8_t *destAlphaBits = fAlphaSurface.GetBits(); |
|
uint32_t srcWidth = fCurrTarget->GetWidth(); |
|
|
|
for( y = 0; y < fCurrTarget->GetHeight(); y++ ) |
|
{ |
|
for( x = 0; x < srcWidth; x++ ) |
|
{ |
|
destRGBBits[ x ] = srcBits[ x ]; // Windows GDI probably doesn't care about the alpha bits here. Hopefully... |
|
destAlphaBits[ x ] = (uint8_t)(srcBits[ x ] >> 24); |
|
} |
|
|
|
srcBits += srcWidth; |
|
destRGBBits += fRGBSurface.fWidth; |
|
destAlphaBits += fAlphaSurface.fWidth; |
|
} |
|
} |
|
else |
|
{ |
|
// Nope, just do a straight memcopy |
|
uint32_t *destBits = fRGBSurface.GetBits(); |
|
uint32_t srcWidth = fCurrTarget->GetWidth(); |
|
|
|
for( y = 0; y < fCurrTarget->GetHeight(); y++ ) |
|
{ |
|
memcpy( destBits, srcBits, srcWidth * sizeof( uint32_t ) ); |
|
|
|
srcBits += srcWidth; |
|
destBits += fRGBSurface.fWidth; |
|
} |
|
} |
|
#endif |
|
|
|
// ALSO, we need to re-update our settings, since different targets |
|
// can have different fonts or justifications |
|
ISetFont( fCurrTarget->GetFontFace(), fCurrTarget->GetFontSize(), 0/*fCurrTarget->GetWriterFontFlags()*/, fCurrTarget->GetFontAARGB() ); |
|
SetJustify( (Justify)fCurrTarget->GetFontJustify() ); |
|
hsColorRGBA col = fCurrTarget->GetFontColor(); |
|
ISetTextColor( col, fCurrTarget->GetFontBlockRGB() ); |
|
|
|
fFlushed = false; |
|
} |
|
} |
|
|
|
bool plDynSurfaceWriter::IsValid( void ) const |
|
{ |
|
if( fCurrTarget == nil ) |
|
return false; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
if( fRGBSurface.fDC == nil || fRGBSurface.fBitmap == nil ) |
|
return false; |
|
|
|
if( ( fFlags & kSupportAlpha ) && ( fAlphaSurface.fDC == nil || fAlphaSurface.fBitmap == nil ) ) |
|
return false; |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
//// Rendering Functions ////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// SetBitsFromBuffer //////////////////////////////////////////////////////// |
|
|
|
/* |
|
void plDynSurfaceWriter::SetBitsFromBuffer( uint32_t *clearBuffer, uint16_t width, uint16_t height ) |
|
{ |
|
int x, y; |
|
uint32_t *data = (uint32_t *)fImage, *srcData = clearBuffer; |
|
uint8_t *destAlpha = nil; |
|
|
|
|
|
if( !IsValid() ) |
|
return; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
GdiFlush(); |
|
#endif |
|
|
|
// Clear *all* to zero |
|
memset( data, 0, fWidth * fHeight * sizeof( uint32_t ) ); |
|
if( fHasAlpha ) |
|
{ |
|
#if HS_BUILD_FOR_WIN32 |
|
memset( fWinAlphaBits, 0, fWidth * fHeight ); |
|
destAlpha = fWinAlphaBits; |
|
#endif |
|
} |
|
|
|
// Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right |
|
for( y = 0; y < fVisHeight; y++ ) |
|
{ |
|
for( x = 0; x < fVisWidth; x++ ) |
|
{ |
|
data[ x ] = srcData[ x ]; |
|
} |
|
|
|
if( destAlpha != nil ) |
|
{ |
|
for( x = 0; x < fVisWidth; x++ ) |
|
destAlpha[ x ] = srcData[ x ] >> 24; |
|
|
|
destAlpha += fWidth; |
|
} |
|
|
|
data += fWidth; |
|
srcData += fVisWidth; |
|
} |
|
} |
|
*/ |
|
|
|
//// ClearToColor ///////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::ClearToColor( hsColorRGBA &color ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
uint32_t i, hexColor = color.ToARGB32(); |
|
|
|
|
|
// Flush the GDI first, so it doesn't decide to overwrite us later |
|
GdiFlush(); |
|
|
|
uint32_t *rgbBits = fRGBSurface.GetBits(); |
|
for( i = 0; i < fRGBSurface.fWidth * fRGBSurface.fHeight; i++ ) |
|
rgbBits[ i ] = hexColor; |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
uint8_t *alphaBits = fAlphaSurface.GetBits(), alpha = (uint8_t)(hexColor >> 24); |
|
|
|
for( i = 0; i < fAlphaSurface.fWidth * fAlphaSurface.fHeight; i++ ) |
|
alphaBits[ i ] = alpha; |
|
} |
|
#endif |
|
} |
|
|
|
//// SetFont ////////////////////////////////////////////////////////////////// |
|
// OS-specific. Load the given font for drawing the text with. |
|
|
|
void plDynSurfaceWriter::SetFont( const char *face, uint16_t size, uint8_t fontFlags, bool antiAliasRGB ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
ISetFont( face, size, fontFlags, antiAliasRGB ); |
|
} |
|
|
|
//// ISetFont ///////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::ISetFont( const plString &face, uint16_t size, uint8_t fontFlags, bool antiAliasRGB ) |
|
{ |
|
fFlags = ( fFlags & ~kFontShadowed ) | ( fontFlags & kFontShadowed ); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
if( !fRGBSurface.FontMatches( face, size, fontFlags, antiAliasRGB ) ) |
|
fRGBSurface.SetFont( face, size, fontFlags, antiAliasRGB ); |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
if( !fAlphaSurface.FontMatches( face, size, fontFlags, !antiAliasRGB ) ) |
|
fAlphaSurface.SetFont( face, size, fontFlags, !antiAliasRGB ); |
|
} |
|
#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 plDynSurfaceWriter::SetTextColor( hsColorRGBA &color, bool blockRGB ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
ISetTextColor( color, blockRGB ); |
|
} |
|
|
|
//// IRefreshTextColor //////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::ISetTextColor( hsColorRGBA &color, bool 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); |
|
|
|
fRGBSurface.fTextColor = RGB( r, g, b ); |
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
int a = (int)(color.a * 255.f); |
|
fAlphaSurface.fTextColor = RGB( a, a, a ); |
|
} |
|
|
|
fFontBlockedRGB = blockRGB; |
|
|
|
if( fFontBlockedRGB && !( fFlags & kFontShadowed ) ) |
|
{ |
|
::SetBkColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
::SetBkMode( fRGBSurface.fDC, OPAQUE ); |
|
} |
|
else |
|
::SetBkMode( fRGBSurface.fDC, TRANSPARENT ); |
|
|
|
::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
|
|
if( fFlags & kSupportAlpha ) |
|
::SetTextColor( fAlphaSurface.fDC, fAlphaSurface.fTextColor ); |
|
#endif |
|
} |
|
|
|
//// SetJustify /////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::SetJustify( Justify j ) |
|
{ |
|
fJustify = j; |
|
} |
|
|
|
//// IRefreshOSJustify //////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::IRefreshOSJustify( void ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
uint32_t justMode; |
|
switch( fJustify ) |
|
{ |
|
case kLeftJustify: justMode = TA_LEFT; break; |
|
case kCenter: justMode = TA_CENTER; break; |
|
case kRightJustify: justMode = TA_RIGHT; break; |
|
} |
|
::SetTextAlign( fRGBSurface.fDC, justMode ); |
|
if( fFlags & kSupportAlpha ) |
|
::SetTextAlign( fAlphaSurface.fDC, justMode ); |
|
#endif |
|
} |
|
|
|
//// DrawString /////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::DrawString( uint16_t x, uint16_t y, const char *text ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
IRefreshOSJustify(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
if( fFlags & kFontShadowed ) |
|
{ |
|
::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); |
|
::TextOut( fRGBSurface.fDC, x + 1, y + 1, text, strlen( text ) ); |
|
|
|
::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
::TextOut( fRGBSurface.fDC, x, y, text, strlen( text ) ); |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
::TextOut( fAlphaSurface.fDC, x + 1, y + 1, text, strlen( text ) ); |
|
::TextOut( fAlphaSurface.fDC, x, y, text, strlen( text ) ); |
|
} |
|
} |
|
else |
|
{ |
|
::TextOut( fRGBSurface.fDC, x, y, text, strlen( text ) ); |
|
if( fFlags & kSupportAlpha ) |
|
::TextOut( fAlphaSurface.fDC, x, y, text, strlen( text ) ); |
|
} |
|
|
|
#endif |
|
} |
|
|
|
//// DrawClippedString //////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::DrawClippedString( int16_t x, int16_t y, const char *text, uint16_t width, uint16_t height ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
IRefreshOSJustify(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
RECT r; |
|
::SetRect( &r, x, y, x + width, y + height ); |
|
|
|
if( fJustify == kRightJustify ) |
|
x += width - 1; |
|
else if( fJustify == kCenter ) |
|
x += width >> 1; |
|
|
|
if( fFlags & kFontShadowed ) |
|
{ |
|
::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); |
|
|
|
::OffsetRect( &r, 1, 1 ); |
|
::ExtTextOut( fRGBSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
if( fFlags & kSupportAlpha ) |
|
::ExtTextOut( fAlphaSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
::OffsetRect( &r, -1, -1 ); |
|
|
|
::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
} |
|
|
|
::ExtTextOut( fRGBSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
if( fFlags & kSupportAlpha ) |
|
::ExtTextOut( fAlphaSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
|
|
#endif |
|
} |
|
|
|
//// DrawClippedString //////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::DrawClippedString( int16_t x, int16_t y, const char *text, uint16_t clipX, uint16_t clipY, uint16_t width, uint16_t height ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
IRefreshOSJustify(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
RECT r; |
|
::SetRect( &r, clipX, clipY, clipX + width, clipY + height ); |
|
|
|
if( fFlags & kFontShadowed ) |
|
{ |
|
::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); |
|
|
|
::OffsetRect( &r, 1, 1 ); |
|
::ExtTextOut( fRGBSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
if( fFlags & kSupportAlpha ) |
|
::ExtTextOut( fAlphaSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
::OffsetRect( &r, -1, -1 ); |
|
|
|
::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
} |
|
|
|
::ExtTextOut( fRGBSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
if( fFlags & kSupportAlpha ) |
|
::ExtTextOut( fAlphaSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); |
|
|
|
#endif |
|
} |
|
|
|
//// DrawWrappedString //////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::DrawWrappedString( uint16_t x, uint16_t y, const char *text, uint16_t width, uint16_t height ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
RECT r; |
|
::SetRect( &r, x, y, x + width, y + height ); |
|
|
|
UINT format = DT_TOP | DT_NOPREFIX | DT_WORDBREAK; |
|
switch( fJustify ) |
|
{ |
|
case kLeftJustify: format |= DT_LEFT; break; |
|
case kCenter: format |= DT_CENTER; break; |
|
case kRightJustify: format |= DT_RIGHT; break; |
|
} |
|
|
|
if( fFlags & kFontShadowed ) |
|
{ |
|
::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); |
|
|
|
::OffsetRect( &r, 1, 1 ); |
|
::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); |
|
if( fFlags & kSupportAlpha ) |
|
::DrawText( fAlphaSurface.fDC, text, strlen( text ), &r, format ); |
|
::OffsetRect( &r, -1, -1 ); |
|
|
|
::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); |
|
} |
|
|
|
::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); |
|
if( fFlags & kSupportAlpha ) |
|
::DrawText( fAlphaSurface.fDC, text, strlen( text ), &r, format ); |
|
|
|
#endif |
|
} |
|
|
|
//// CalcStringWidth ////////////////////////////////////////////////////////// |
|
|
|
uint16_t plDynSurfaceWriter::CalcStringWidth( const char *text, uint16_t *height ) |
|
{ |
|
if( !IsValid() ) |
|
return 0; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
SIZE size; |
|
::GetTextExtentPoint32( fRGBSurface.fDC, text, strlen( text ), &size ); |
|
|
|
if( height != nil ) |
|
*height = (uint16_t)size.cy; |
|
|
|
return (uint16_t)size.cx; |
|
#else |
|
return 0; |
|
#endif |
|
} |
|
|
|
//// CalcWrappedStringSize //////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::CalcWrappedStringSize( const char *text, uint16_t *width, uint16_t *height ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
|
|
RECT r; |
|
::SetRect( &r, 0, 0, *width, 0 ); |
|
|
|
UINT format = DT_TOP | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT; |
|
switch( fJustify ) |
|
{ |
|
case kLeftJustify: format |= DT_LEFT; break; |
|
case kCenter: format |= DT_CENTER; break; |
|
case kRightJustify: format |= DT_RIGHT; break; |
|
} |
|
|
|
::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); |
|
|
|
*width = (uint16_t)(r.right); |
|
if( height != nil ) |
|
*height = (uint16_t)r.bottom; |
|
#endif |
|
} |
|
|
|
//// FillRect ///////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::FillRect( uint16_t x, uint16_t y, uint16_t width, uint16_t height, hsColorRGBA &color ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#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( fRGBSurface.fDC, &rc, brush ); |
|
::DeleteObject( brush ); |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
brush = ::CreateSolidBrush( RGB( a, a, a ) ); |
|
::FillRect( fAlphaSurface.fDC, &rc, brush ); |
|
::DeleteObject( brush ); |
|
} |
|
|
|
#endif |
|
} |
|
|
|
//// FrameRect //////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::FrameRect( uint16_t x, uint16_t y, uint16_t width, uint16_t height, hsColorRGBA &color ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
IEnsureSurfaceUpdated(); |
|
|
|
#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( fRGBSurface.fDC, &rc, brush ); |
|
::DeleteObject( brush ); |
|
|
|
if( fFlags & kSupportAlpha ) |
|
{ |
|
brush = ::CreateSolidBrush( RGB( a, a, a ) ); |
|
::FrameRect( fAlphaSurface.fDC, &rc, brush ); |
|
::DeleteObject( brush ); |
|
} |
|
|
|
#endif |
|
} |
|
|
|
/* |
|
//// DrawImage //////////////////////////////////////////////////////////////// |
|
|
|
void plDynSurfaceWriter::DrawImage( uint16_t x, uint16_t y, plMipmap *image, bool respectAlpha ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
// Flush the GDI first, to make sure it's done |
|
GdiFlush(); |
|
#endif |
|
|
|
plMipmap::CompositeOptions opts( respectAlpha ? 0 : plMipmap::kForceOpaque ); |
|
if( !fHasAlpha ) |
|
opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest |
|
|
|
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 plDynSurfaceWriter::DrawClippedImage( uint16_t x, uint16_t y, plMipmap *image, |
|
uint16_t srcClipX, uint16_t srcClipY, |
|
uint16_t srcClipWidth, uint16_t srcClipHeight, |
|
bool respectAlpha ) |
|
{ |
|
if( !IsValid() ) |
|
return; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
// Flush the GDI first, to make sure it's done |
|
GdiFlush(); |
|
#endif |
|
|
|
plMipmap::CompositeOptions opts( respectAlpha ? 0 : plMipmap::kForceOpaque ); |
|
if( !fHasAlpha ) |
|
opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest |
|
|
|
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 ); |
|
} |
|
} |
|
*/
|
|
|