mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-02-13 03:33:48 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
449
Engine/source/platformMac/macCarbFont.cpp
Normal file
449
Engine/source/platformMac/macCarbFont.cpp
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platformMac/macCarbFont.h"
|
||||
#include "platformMac/platformMacCarb.h"
|
||||
#include "math/mRect.h"
|
||||
#include "console/console.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// New Unicode capable font class.
|
||||
PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
|
||||
{
|
||||
PlatformFont *retFont = new MacCarbFont;
|
||||
|
||||
if(retFont->create(name, size, charset))
|
||||
return retFont;
|
||||
|
||||
delete retFont;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
MacCarbFont::MacCarbFont()
|
||||
{
|
||||
mStyle = NULL;
|
||||
mLayout = NULL;
|
||||
mColorSpace = NULL;
|
||||
}
|
||||
|
||||
MacCarbFont::~MacCarbFont()
|
||||
{
|
||||
// apple docs say we should dispose the layout first.
|
||||
ATSUDisposeTextLayout(mLayout);
|
||||
ATSUDisposeStyle(mStyle);
|
||||
CGColorSpaceRelease(mColorSpace);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MacCarbFont::create( const char* name, U32 size, U32 charset)
|
||||
{
|
||||
String nameStr = name;
|
||||
nameStr = nameStr.trim();
|
||||
|
||||
// create and cache the style and layout.
|
||||
// based on apple sample code at http://developer.apple.com/qa/qa2001/qa1027.html
|
||||
|
||||
// note: charset is ignored on mac. -- we don't need it to get the right chars.
|
||||
// But do we need it to translate encodings? hmm...
|
||||
|
||||
CFStringRef cfsName;
|
||||
ATSUFontID atsuFontID;
|
||||
ATSFontRef atsFontRef;
|
||||
Fixed atsuSize;
|
||||
ATSURGBAlphaColor black;
|
||||
ATSFontMetrics fontMetrics;
|
||||
U32 scaledSize;
|
||||
|
||||
bool isBold = false;
|
||||
bool isItalic = false;
|
||||
|
||||
bool haveModifier;
|
||||
do
|
||||
{
|
||||
haveModifier = false;
|
||||
if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 )
|
||||
{
|
||||
isBold = true;
|
||||
nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim();
|
||||
haveModifier = true;
|
||||
}
|
||||
if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 )
|
||||
{
|
||||
isItalic = true;
|
||||
nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim();
|
||||
haveModifier = true;
|
||||
}
|
||||
}
|
||||
while( haveModifier );
|
||||
|
||||
// Look up the font. We need it in 2 differnt formats, for differnt Apple APIs.
|
||||
cfsName = CFStringCreateWithCString( kCFAllocatorDefault, nameStr.c_str(), kCFStringEncodingUTF8);
|
||||
if(!cfsName)
|
||||
Con::errorf("Error: could not make a cfstring out of \"%s\" ",nameStr.c_str());
|
||||
|
||||
atsFontRef = ATSFontFindFromName( cfsName, kATSOptionFlagsDefault);
|
||||
atsuFontID = FMGetFontFromATSFontRef( atsFontRef);
|
||||
|
||||
// make sure we found it. ATSFontFindFromName() appears to return 0 if it cant find anything. Apple docs contain no info on error returns.
|
||||
if( !atsFontRef || !atsuFontID )
|
||||
{
|
||||
Con::errorf("MacCarbFont::create - could not load font -%s-",name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// adjust the size. win dpi = 96, mac dpi = 72. 72/96 = .75
|
||||
// Interestingly enough, 0.75 is not what makes things the right size.
|
||||
scaledSize = size - 2 - (int)((float)size * 0.1);
|
||||
mSize = scaledSize;
|
||||
|
||||
// Set up the size and color. We send these to ATSUSetAttributes().
|
||||
atsuSize = IntToFixed(scaledSize);
|
||||
black.red = black.green = black.blue = black.alpha = 1.0;
|
||||
|
||||
// Three parrallel arrays for setting up font, size, and color attributes.
|
||||
ATSUAttributeTag theTags[] = { kATSUFontTag, kATSUSizeTag, kATSURGBAlphaColorTag};
|
||||
ByteCount theSizes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(ATSURGBAlphaColor) };
|
||||
ATSUAttributeValuePtr theValues[] = { &atsuFontID, &atsuSize, &black };
|
||||
|
||||
// create and configure the style object.
|
||||
ATSUCreateStyle(&mStyle);
|
||||
ATSUSetAttributes( mStyle, 3, theTags, theSizes, theValues );
|
||||
|
||||
if( isBold )
|
||||
{
|
||||
ATSUAttributeTag tag = kATSUQDBoldfaceTag;
|
||||
ByteCount size = sizeof( Boolean );
|
||||
Boolean value = true;
|
||||
ATSUAttributeValuePtr valuePtr = &value;
|
||||
ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr );
|
||||
}
|
||||
|
||||
if( isItalic )
|
||||
{
|
||||
ATSUAttributeTag tag = kATSUQDItalicTag;
|
||||
ByteCount size = sizeof( Boolean );
|
||||
Boolean value = true;
|
||||
ATSUAttributeValuePtr valuePtr = &value;
|
||||
ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr );
|
||||
}
|
||||
|
||||
// create the layout object,
|
||||
ATSUCreateTextLayout(&mLayout);
|
||||
// we'll bind the layout to a bitmap context when we actually draw.
|
||||
// ATSUSetTextPointerLocation() - will set the text buffer
|
||||
// ATSUSetLayoutControls() - will set the cg context.
|
||||
|
||||
// get font metrics, save our baseline and height
|
||||
ATSFontGetHorizontalMetrics(atsFontRef, kATSOptionFlagsDefault, &fontMetrics);
|
||||
mBaseline = scaledSize * fontMetrics.ascent;
|
||||
mHeight = scaledSize * ( fontMetrics.ascent - fontMetrics.descent + fontMetrics.leading ) + 1;
|
||||
|
||||
// cache our grey color space, so we dont have to re create it every time.
|
||||
mColorSpace = CGColorSpaceCreateDeviceGray();
|
||||
|
||||
// and finally cache the font's name. We use this to cheat some antialiasing options below.
|
||||
mName = StringTable->insert(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MacCarbFont::isValidChar(const UTF8 *str) const
|
||||
{
|
||||
// since only low order characters are invalid, and since those characters
|
||||
// are single codeunits in UTF8, we can safely cast here.
|
||||
return isValidChar((UTF16)*str);
|
||||
}
|
||||
|
||||
bool MacCarbFont::isValidChar( const UTF16 ch) const
|
||||
{
|
||||
// We cut out the ASCII control chars here. Only printable characters are valid.
|
||||
// 0x20 == 32 == space
|
||||
if( ch < 0x20 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF8 *str) const
|
||||
{
|
||||
return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL)));
|
||||
}
|
||||
|
||||
PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF16 ch) const
|
||||
{
|
||||
// We use some static data here to avoid re allocating the same variable in a loop.
|
||||
// this func is primarily called by GFont::loadCharInfo(),
|
||||
Rect imageRect;
|
||||
CGContextRef imageCtx;
|
||||
U32 bitmapDataSize;
|
||||
ATSUTextMeasurement tbefore, tafter, tascent, tdescent;
|
||||
OSStatus err;
|
||||
|
||||
// 16 bit character buffer for the ATUSI calls.
|
||||
// -- hey... could we cache this at the class level, set style and loc *once*,
|
||||
// then just write to this buffer and clear the layout cache, to speed up drawing?
|
||||
static UniChar chUniChar[1];
|
||||
chUniChar[0] = ch;
|
||||
|
||||
// Declare and clear out the CharInfo that will be returned.
|
||||
static PlatformFont::CharInfo c;
|
||||
dMemset(&c, 0, sizeof(c));
|
||||
|
||||
// prep values for GFont::addBitmap()
|
||||
c.bitmapIndex = 0;
|
||||
c.xOffset = 0;
|
||||
c.yOffset = 0;
|
||||
|
||||
// put the text in the layout.
|
||||
// we've hardcoded a string length of 1 here, but this could work for longer strings... (hint hint)
|
||||
// note: ATSUSetTextPointerLocation() also clears the previous cached layout information.
|
||||
ATSUSetTextPointerLocation( mLayout, chUniChar, 0, 1, 1);
|
||||
ATSUSetRunStyle( mLayout, mStyle, 0,1);
|
||||
|
||||
// get the typographic bounds. this tells us how characters are placed relative to other characters.
|
||||
ATSUGetUnjustifiedBounds( mLayout, 0, 1, &tbefore, &tafter, &tascent, &tdescent);
|
||||
c.xIncrement = FixedToInt(tafter);
|
||||
|
||||
// find out how big of a bitmap we'll need.
|
||||
// as a bonus, we also get the origin where we should draw, encoded in the Rect.
|
||||
ATSUMeasureTextImage( mLayout, 0, 1, 0, 0, &imageRect);
|
||||
U32 xFudge = 2;
|
||||
U32 yFudge = 1;
|
||||
c.width = imageRect.right - imageRect.left + xFudge; // add 2 because small fonts don't always have enough room
|
||||
c.height = imageRect.bottom - imageRect.top + yFudge;
|
||||
c.xOrigin = imageRect.left; // dist x0 -> center line
|
||||
c.yOrigin = -imageRect.top; // dist y0 -> base line
|
||||
|
||||
// kick out early if the character is undrawable
|
||||
if( c.width == xFudge || c.height == yFudge)
|
||||
return c;
|
||||
|
||||
// allocate a greyscale bitmap and clear it.
|
||||
bitmapDataSize = c.width * c.height;
|
||||
c.bitmapData = new U8[bitmapDataSize];
|
||||
dMemset(c.bitmapData,0x00,bitmapDataSize);
|
||||
|
||||
// get a graphics context on the bitmap
|
||||
imageCtx = CGBitmapContextCreate( c.bitmapData, c.width, c.height, 8, c.width, mColorSpace, kCGImageAlphaNone);
|
||||
if(!imageCtx) {
|
||||
Con::errorf("Error: failed to create a graphics context on the CharInfo bitmap! Drawing a blank block.");
|
||||
c.xIncrement = c.width;
|
||||
dMemset(c.bitmapData,0x0F,bitmapDataSize);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Turn off antialiasing for monospaced console fonts. yes, this is cheating.
|
||||
if(mSize < 12 && ( dStrstr(mName,"Monaco")!=NULL || dStrstr(mName,"Courier")!=NULL ))
|
||||
CGContextSetShouldAntialias(imageCtx, false);
|
||||
|
||||
// Set up drawing options for the context.
|
||||
// Since we're not going straight to the screen, we need to adjust accordingly
|
||||
CGContextSetShouldSmoothFonts(imageCtx, false);
|
||||
CGContextSetRenderingIntent(imageCtx, kCGRenderingIntentAbsoluteColorimetric);
|
||||
CGContextSetInterpolationQuality( imageCtx, kCGInterpolationNone);
|
||||
CGContextSetGrayFillColor( imageCtx, 1.0, 1.0);
|
||||
CGContextSetTextDrawingMode( imageCtx, kCGTextFill);
|
||||
|
||||
// tell ATSUI to substitute fonts as needed for missing glyphs
|
||||
ATSUSetTransientFontMatching(mLayout, true);
|
||||
|
||||
// set up three parrallel arrays for setting up attributes.
|
||||
// this is how most options in ATSUI are set, by passing arrays of options.
|
||||
ATSUAttributeTag theTags[] = { kATSUCGContextTag };
|
||||
ByteCount theSizes[] = { sizeof(CGContextRef) };
|
||||
ATSUAttributeValuePtr theValues[] = { &imageCtx };
|
||||
|
||||
// bind the layout to the context.
|
||||
ATSUSetLayoutControls( mLayout, 1, theTags, theSizes, theValues );
|
||||
|
||||
// Draw the character!
|
||||
int yoff = c.height < 3 ? 1 : 0; // kludge for 1 pixel high characters, such as '-' and '_'
|
||||
int xoff = 1;
|
||||
err = ATSUDrawText( mLayout, 0, 1, IntToFixed(-imageRect.left + xoff), IntToFixed(imageRect.bottom + yoff ) );
|
||||
CGContextRelease(imageCtx);
|
||||
|
||||
if(err != noErr) {
|
||||
Con::errorf("Error: could not draw the character! Drawing a blank box.");
|
||||
dMemset(c.bitmapData,0x0F,bitmapDataSize);
|
||||
}
|
||||
|
||||
|
||||
#if TORQUE_DEBUG
|
||||
// Con::printf("Font Metrics: Rect = %2i %2i %2i %2i Char= %C, 0x%x Size= %i, Baseline= %i, Height= %i",imageRect.top, imageRect.bottom, imageRect.left, imageRect.right,ch,ch, mSize,mBaseline, mHeight);
|
||||
// Con::printf("Font Bounds: left= %2i right= %2i Char= %C, 0x%x Size= %i",FixedToInt(tbefore), FixedToInt(tafter), ch,ch, mSize);
|
||||
#endif
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void PlatformFont::enumeratePlatformFonts( Vector< StringTableEntry >& fonts, UTF16* fontFamily )
|
||||
{
|
||||
if( fontFamily )
|
||||
{
|
||||
// Determine the font ID from the family name.
|
||||
|
||||
ATSUFontID fontID;
|
||||
if( ATSUFindFontFromName(
|
||||
fontFamily,
|
||||
dStrlen( fontFamily ) * 2,
|
||||
kFontFamilyName,
|
||||
kFontMicrosoftPlatform,
|
||||
kFontNoScriptCode,
|
||||
kFontNoLanguageCode, &fontID ) != kATSUInvalidFontErr )
|
||||
{
|
||||
// Get the number of fonts in the family.
|
||||
|
||||
ItemCount numFonts;
|
||||
ATSUCountFontNames( fontID, &numFonts );
|
||||
|
||||
// Read out font names.
|
||||
|
||||
U32 bufferSize = 512;
|
||||
char* buffer = ( char* ) dMalloc( bufferSize );
|
||||
|
||||
for( U32 i = 0; i < numFonts; ++ i )
|
||||
{
|
||||
for( U32 n = 0; n < 2; ++ n )
|
||||
{
|
||||
ByteCount actualNameLength;
|
||||
FontNameCode fontNameCode;
|
||||
FontPlatformCode fontPlatformCode;
|
||||
FontScriptCode fontScriptCode;
|
||||
FontLanguageCode fontLanguageCode;
|
||||
|
||||
if( ATSUGetIndFontName(
|
||||
fontID,
|
||||
i,
|
||||
bufferSize - 2,
|
||||
buffer,
|
||||
&actualNameLength,
|
||||
&fontNameCode,
|
||||
&fontPlatformCode,
|
||||
&fontScriptCode,
|
||||
&fontLanguageCode ) == noErr )
|
||||
{
|
||||
*( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0';
|
||||
char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer );
|
||||
fonts.push_back( StringTable->insert( utf8 ) );
|
||||
delete [] utf8;
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate larger buffer.
|
||||
|
||||
bufferSize = actualNameLength + 2;
|
||||
buffer = ( char* ) dRealloc( buffer, bufferSize );
|
||||
}
|
||||
}
|
||||
|
||||
dFree( buffer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the number of installed fonts.
|
||||
|
||||
ItemCount numFonts;
|
||||
ATSUFontCount( &numFonts );
|
||||
|
||||
// Get all the font IDs.
|
||||
|
||||
ATSUFontID* fontIDs = new ATSUFontID[ numFonts ];
|
||||
if( ATSUGetFontIDs( fontIDs, numFonts, &numFonts ) == noErr )
|
||||
{
|
||||
U32 bufferSize = 512;
|
||||
char* buffer = ( char* ) dMalloc( bufferSize );
|
||||
|
||||
// Read all family names.
|
||||
|
||||
for( U32 i = 0; i < numFonts; ++ i )
|
||||
{
|
||||
for( U32 n = 0; n < 2; ++ n )
|
||||
{
|
||||
ByteCount actualNameLength;
|
||||
ItemCount fontIndex;
|
||||
|
||||
OSStatus result = ATSUFindFontName(
|
||||
fontIDs[ i ],
|
||||
kFontFamilyName,
|
||||
kFontMicrosoftPlatform,
|
||||
kFontNoScriptCode,
|
||||
kFontNoLanguageCode,
|
||||
bufferSize - 2,
|
||||
buffer,
|
||||
&actualNameLength,
|
||||
&fontIndex );
|
||||
|
||||
if( result == kATSUNoFontNameErr )
|
||||
break;
|
||||
else if( result == noErr )
|
||||
{
|
||||
*( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0';
|
||||
char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer );
|
||||
StringTableEntry name = StringTable->insert( utf8 );
|
||||
delete [] utf8;
|
||||
|
||||
// Avoid duplicates.
|
||||
|
||||
bool duplicate = false;
|
||||
for( U32 i = 0, num = fonts.size(); i < num; ++ i )
|
||||
if( fonts[ i ] == name )
|
||||
{
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if( !duplicate )
|
||||
fonts.push_back( name );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate larger buffer.
|
||||
|
||||
bufferSize = actualNameLength + 2;
|
||||
buffer = ( char* ) dRealloc( buffer, bufferSize );
|
||||
}
|
||||
}
|
||||
|
||||
dFree( buffer );
|
||||
}
|
||||
|
||||
delete [] fontIDs;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The following code snippet demonstrates how to get the elusive GlyphIDs,
|
||||
// which are needed when you want to do various complex and arcane things
|
||||
// with ATSUI and CoreGraphics.
|
||||
//
|
||||
// ATSUGlyphInfoArray glyphinfoArr;
|
||||
// ATSUGetGlyphInfo( mLayout, kATSUFromTextBeginning, kATSUToTextEnd,sizeof(ATSUGlyphInfoArray), &glyphinfoArr);
|
||||
// ATSUGlyphInfo glyphinfo = glyphinfoArr.glyphs[0];
|
||||
// Con::printf(" Glyphinfo: screenX= %i, idealX=%f, deltaY=%f", glyphinfo.screenX, glyphinfo.idealX, glyphinfo.deltaY);
|
||||
Loading…
Add table
Add a link
Reference in a new issue