Skip to content

Instantly share code, notes, and snippets.

@programmarchy
Created September 8, 2014 23:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save programmarchy/948eedc619417296fe9c to your computer and use it in GitHub Desktop.
Save programmarchy/948eedc619417296fe9c to your computer and use it in GitHub Desktop.
Patched CCLabelTTF for custom fonts in Cocos2D with Apportable
/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2008-2010 Ricardo Quesada
* Copyright (c) 2011 Zynga Inc.
* Copyright (c) 2013-2014 Cocos2D Authors
*
* 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.
*
*/
#import "CCTexture.h"
#import "CCSprite.h"
#import "Platforms/CCNS.h"
/**
CCLabelTTF displays a rendered TTF font texture. The label is created from a platform font.
Attributed strings are supported on Mac and iOS6+ which allows for greater text formatting control.
### Notes
Each time you modify the label text you are effectivley creating a new CCLabelTTF so there may be a performance hit if you need to frequently update the label. In this scenario you should also look at CCLabelBMFont which does not have this overhead.
### Resources
- http://iosfonts.com/ (Please use Safari for accurate font rendering)
*/
@interface CCLabelTTF : CCSprite <CCLabelProtocol> {
// True if the label needs to be updated.
BOOL _isTextureDirty;
}
/// -----------------------------------------------------------------------
/// @name Accessing the Text Attributes
/// -----------------------------------------------------------------------
/** The label text. */
@property (nonatomic,copy) NSString* string;
/** The label attributed text. */
@property (nonatomic,copy) NSAttributedString* attributedString;
/** The platform font to use for the text. */
@property (nonatomic,strong) NSString* fontName;
/** The font size of the text. */
@property (nonatomic,assign) CGFloat fontSize;
/** The color of the text (If not using shadow or outline). */
@property (nonatomic,strong) CCColor* fontColor;
/** The horizontal alignment technique of the text. */
@property (nonatomic,assign) CCTextAlignment horizontalAlignment;
/** The vertical alignment technique of the text. */
@property (nonatomic,assign) CCVerticalTextAlignment verticalAlignment;
/// -----------------------------------------------------------------------
/// @name Sizing the Label
/// -----------------------------------------------------------------------
/** Dimensions of the label in Points. */
@property (nonatomic,assign) CGSize dimensions;
/** Dimension type of the label. */
@property (nonatomic,assign) CCSizeType dimensionsType;
/** If true, the label will be scaled down to fit into the size provided by the dimensions property. Only has an effect if dimensions are set. */
@property (nonatomic,assign) BOOL adjustsFontSizeToFit;
/** Used together with adjustsFontSizeToFit. Fonts will not be scaled down below this size (the label will instead be clipped). */
@property (nonatomic,assign) CGFloat minimumFontSize;
/** Adjusts the fonts baseline, the value is set in points. */
@property (nonatomic,assign) CGFloat baselineAdjustment;
/// -----------------------------------------------------------------------
/// @name Drawing a Shadow
/// -----------------------------------------------------------------------
/** The color of the text shadow. If the color is transparent, no shadow will be used. */
@property (nonatomic,strong) CCColor* shadowColor;
/** The offset of the shadow. */
@property (nonatomic,assign) CGPoint shadowOffset;
/** The offset of the shadow in points */
@property(nonatomic,readonly) CGPoint shadowOffsetInPoints;
/** The position type to be used for the shadow offset. */
@property (nonatomic,assign) CCPositionType shadowOffsetType;
/** The blur radius of the shadow. */
@property (nonatomic,assign) CGFloat shadowBlurRadius;
/// -----------------------------------------------------------------------
/// @name Drawing an Outline
/// -----------------------------------------------------------------------
/** The color of the text's outline. */
@property (nonatomic,strong) CCColor* outlineColor;
/** The width of the text's outline. */
@property (nonatomic,assign) CGFloat outlineWidth;
/// -----------------------------------------------------------------------
/// @name Creating a CCLabelTTF Object
/// -----------------------------------------------------------------------
/**
* Creates and returns a label object using the specified text, font name and font size.
*
* @param string Label text.
* @param name Label font name.
* @param size Label font size (in points).
*
* @return The CCLabelTTF Object.
*/
+(id) labelWithString:(NSString *)string fontName:(NSString *)name fontSize:(CGFloat)size;
/**
* Creates and returns a label object using the specified text, font name, font size and dimensions.
*
* @param string Label text.
* @param name Label font name.
* @param size Label font size (in points).
* @param dimensions Label dimensions.
*
* @return The CCLabelTTF Object.
*/
+(id) labelWithString:(NSString *)string fontName:(NSString *)name fontSize:(CGFloat)size dimensions:(CGSize)dimensions;
/**
* Creates and returns a label object using the specified attributed text.
*
* @param attrString Label Attributed text.
*
* @since Available in iOS 6.0+ and OS X.
*
* @return The CCLabelTTF Object.
*/
+(id) labelWithAttributedString:(NSAttributedString *)attrString;
/**
* Creates and returns a label object using the specified attributed text and dimensions.
*
* @param attrString Label Attributed text.
* @param dimensions Label dimensions.
*
* @since Available in iOS 6.0+ and OS X.
*
* @return The CCLabelTTF Object.
*/
+(id) labelWithAttributedString:(NSAttributedString *)attrString dimensions:(CGSize)dimensions;
/// -----------------------------------------------------------------------
/// @name Initializing a CCLabelTTF Object
/// -----------------------------------------------------------------------
/**
* Initializes and returns a label object using the specified text, font name and font size.
*
* @param string Label text.
* @param name Label font name.
* @param size Label font size (in points).
*
* @return An initialized CCLabelTTF Object.
*/
-(id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
/**
* Initializes and returns a label object using the specified text, font name, font size and dimensions.
*
* @param string Label text.
* @param name Label font name.
* @param size Label font size (in points).
* @param dimensions Label dimensions.
*
* @return An initialized CCLabelTTF Object.
*/
-(id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size dimensions:(CGSize)dimensions;
/**
* Initializes and returns a label object using the specified attributed text.
*
* @param attrString Label Attributed text.
*
* @since Available in iOS 6.0+ and OS X.
*
* @return An initialized CCLabelTTF Object.
*/
-(id) initWithAttributedString:(NSAttributedString *)attrString;
/**
* Initializes and returns a label object using the specified attributed text and dimensions.
*
* @param attrString Label Attributed text.
* @param dimensions Label dimensions.
*
* @since Available in iOS 6.0+ and OS X.
*
* @return An initialized CCLabelTTF Object.
*/
-(id) initWithAttributedString:(NSAttributedString *)attrString dimensions:(CGSize)dimensions;
/// -----------------------------------------------------------------------
/// @name OS X
/// -----------------------------------------------------------------------
#ifdef __CC_PLATFORM_MAC
/**
* (OS X) HTML Label
*
* @param html HTML Description.
*/
- (void) setHTML:(NSString*) html;
#endif
/// -----------------------------------------------------------------------
/// @name TTF Management
/// -----------------------------------------------------------------------
/**
* Register a TTF font resource.
*
* @param fontFile Font file path.
*/
+(NSString*) registerCustomTTF:(NSString*)fontFile;
@end
/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2008-2010 Ricardo Quesada
* Copyright (c) 2011 Zynga Inc.
* Copyright (c) 2013-2014 Cocos2D Authors
*
* 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.
*
*/
#import "CCLabelTTF.h"
#import "Support/CGPointExtension.h"
#import "ccMacros.h"
#import "CCShader.h"
#import "Support/CCFileUtils.h"
#import "ccMacros.h"
#import "ccUtils.h"
#import "NSAttributedString+CCAdditions.h"
#import "CCConfiguration.h"
#import "CCNode_Private.h"
#import "CCDirector.h"
#ifdef __CC_PLATFORM_IOS
#import "Platforms/iOS/CCDirectorIOS.h"
#import <CoreText/CoreText.h>
#endif
static __strong NSMutableDictionary* ccLabelTTF_registeredFonts;
@implementation CCTexture (CCLabelTTF)
- (void) setPremultipliedAlpha:(BOOL)flag
{
_premultipliedAlpha = flag;
}
@end
#pragma mark CCLabelTTF
@implementation CCLabelTTF
+ (id) labelWithString:(NSString *)string fontName:(NSString *)name fontSize:(CGFloat)size
{
return [[self alloc] initWithString:string fontName:name fontSize:size];
}
+ (id) labelWithString:(NSString *)string fontName:(NSString *)name fontSize:(CGFloat)size dimensions:(CGSize)dimensions
{
return [[self alloc] initWithString:string fontName:name fontSize:size dimensions:dimensions];
}
+ (id) labelWithAttributedString:(NSAttributedString *)attrString
{
return [[self alloc] initWithAttributedString:attrString];
}
+ (id) labelWithAttributedString:(NSAttributedString *)attrString dimensions:(CGSize)dimensions
{
return [[self alloc] initWithAttributedString:attrString dimensions:dimensions];
}
- (id) init
{
return [self initWithString:@"" fontName:@"Helvetica" fontSize:12];
}
- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size
{
return [self initWithAttributedString:[[NSAttributedString alloc] initWithString:str] fontName:name fontSize:size dimensions:CGSizeZero];
}
- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size dimensions:(CGSize)dimensions
{
return [self initWithAttributedString:[[NSAttributedString alloc] initWithString:str] fontName:name fontSize:size dimensions:dimensions];
}
- (id) initWithAttributedString:(NSAttributedString *)attrString;
{
NSAssert([CCConfiguration sharedConfiguration].OSVersion >= CCSystemVersion_iOS_6_0, @"Attributed strings are only supported on iOS 6 or later");
return [self initWithAttributedString:attrString fontName:@"Helvetica" fontSize:12 dimensions:CGSizeZero];
}
- (id) initWithAttributedString:(NSAttributedString *)attrString dimensions:(CGSize)dimensions
{
NSAssert([CCConfiguration sharedConfiguration].OSVersion >= CCSystemVersion_iOS_6_0, @"Attributed strings are only supported on iOS 6 or later");
return [self initWithAttributedString:attrString fontName:@"Helvetica" fontSize:12 dimensions:dimensions];
}
// This is a private initializer
- (id) initWithAttributedString:(NSAttributedString *)attrString fontName:(NSString*)fontName fontSize:(CGFloat)fontSize dimensions:(CGSize)dimensions
{
if ( (self = [super init]) )
{
if (!fontName) fontName = @"Helvetica";
if (!fontSize) fontSize = 12;
self.blendMode = [CCBlendMode premultipliedAlphaMode];
// other properties
self.fontName = fontName;
self.fontSize = fontSize;
self.dimensions = dimensions;
self.fontColor = [CCColor whiteColor];
self.shadowColor = [CCColor clearColor];
self.outlineColor = [CCColor clearColor];
self.outlineWidth = 1;
[self _setAttributedString:attrString];
}
return self;
}
#pragma mark Properties
- (void) _setAttributedString:(NSAttributedString *)attributedString
{
NSAssert(attributedString, @"Invalid attributedString");
if ( _attributedString.hash != attributedString.hash)
{
_attributedString = [attributedString copy];
[self setTextureDirty];
}
}
- (void) setAttributedString:(NSAttributedString *)attributedString
{
NSAssert([CCConfiguration sharedConfiguration].OSVersion >= CCSystemVersion_iOS_6_0, @"Attributed strings are only supported on iOS 6 or later");
[self _setAttributedString:attributedString];
}
- (void) setString:(NSString*)str
{
NSAssert( str, @"Invalid string" );
[self _setAttributedString:[[NSAttributedString alloc] initWithString:str]];
}
-(NSString*) string
{
return [_attributedString string];
}
- (void)setFontName:(NSString*)fontName
{
// Handle passing of complete file paths
if ([[[fontName pathExtension] lowercaseString] isEqualToString:@"ttf"] || [[[fontName pathExtension] lowercaseString] isEqualToString:@"otf"])
{
fontName = [CCLabelTTF registerCustomTTF:fontName];;
}
if( fontName.hash != _fontName.hash ) {
_fontName = [fontName copy];
[self setTextureDirty];
}
}
- (void) setFontSize:(CGFloat)fontSize
{
if( fontSize != _fontSize ) {
_fontSize = fontSize;
[self setTextureDirty];
}
}
- (void) setAdjustsFontSizeToFit:(BOOL)adjustsFontSizeToFit
{
if (adjustsFontSizeToFit != _adjustsFontSizeToFit)
{
_adjustsFontSizeToFit = adjustsFontSizeToFit;
[self setTextureDirty];
}
}
- (void) setFontColor:(CCColor*)fontColor
{
if (![fontColor isEqualToColor:_fontColor])
{
_fontColor = fontColor;
[self setTextureDirty];
}
}
- (void) setMinimumFontSize:(CGFloat)minimumFontSize
{
if (minimumFontSize != _minimumFontSize)
{
_minimumFontSize = minimumFontSize;
[self setTextureDirty];
}
}
-(void) setDimensions:(CGSize) dim
{
if( dim.width != _dimensions.width || dim.height != _dimensions.height)
{
_dimensions = dim;
[self setTextureDirty];
}
}
- (CGSize) contentSize
{
[self updateTexture];
return _contentSize;
}
-(void) setHorizontalAlignment:(CCTextAlignment)alignment
{
if (alignment != _horizontalAlignment)
{
_horizontalAlignment = alignment;
[self setTextureDirty];
}
}
-(void) setVerticalAlignment:(CCVerticalTextAlignment)verticalAlignment
{
if (_verticalAlignment != verticalAlignment)
{
_verticalAlignment = verticalAlignment;
[self setTextureDirty];
}
}
- (void) setShadowColor:(CCColor*)shadowColor
{
if (![shadowColor isEqualToColor:_shadowColor])
{
_shadowColor = shadowColor;
[self setTextureDirty];
}
}
- (void) setShadowOffset:(CGPoint)shadowOffset
{
if (!CGPointEqualToPoint(_shadowOffset, shadowOffset))
{
_shadowOffset = shadowOffset;
[self setTextureDirty];
}
}
-(CGPoint)shadowOffsetInPoints
{
return [self convertPositionToPoints:self.shadowOffset type:_shadowOffsetType];
}
- (void) setShadowBlurRadius:(CGFloat)shadowBlurRadius
{
if (_shadowBlurRadius != shadowBlurRadius)
{
_shadowBlurRadius = shadowBlurRadius;
[self setTextureDirty];
}
}
- (void) setOutlineColor:(CCColor*)outlineColor
{
if (![outlineColor isEqualToColor:_outlineColor])
{
_outlineColor = outlineColor;
[self setTextureDirty];
}
}
- (void) setOutlineWidth:(CGFloat)outlineWidth
{
if (outlineWidth != _outlineWidth)
{
_outlineWidth = outlineWidth;
[self setTextureDirty];
}
}
- (NSString*) description
{
// XXX: _string, _fontName can't be displayed here, since they might be already released
return [NSString stringWithFormat:@"<%@ = %p | FontSize = %.1f>", [self class], self, _fontSize];
}
-(void)onEnter
{
[self setTextureDirty];
[super onEnter];
}
- (void) visit:(CCRenderer *)renderer parentTransform:(const GLKMatrix4 *)parentTransform
{
if (_isTextureDirty)
{
[self updateTexture];
}
[super visit:renderer parentTransform:parentTransform];
}
- (void) setTextureDirty
{
_isTextureDirty = YES;
}
#pragma mark -
#pragma mark Render Font Mac & iOS 6
- (BOOL) updateTexture
{
if (!_attributedString) return NO;
if (!_isTextureDirty) return NO;
_isTextureDirty = NO;
#ifdef __CC_PLATFORM_IOS
// Handle fonts on iOS 5
if ([CCConfiguration sharedConfiguration].OSVersion < CCSystemVersion_iOS_6_0)
{
return [self updateTextureOld];
}
#endif
// Set default values for font attributes if they are not set in the attributed string
NSMutableAttributedString* formattedAttributedString = [_attributedString mutableCopy];
NSRange fullRange = NSMakeRange(0, formattedAttributedString.length);
BOOL useFullColor = NO;
if (_shadowColor.alpha > 0) useFullColor = YES;
if (_outlineColor.alpha > 0 && _outlineWidth > 0) useFullColor = YES;
#ifdef __CC_PLATFORM_IOS
// Font color
if (![formattedAttributedString hasAttribute:NSForegroundColorAttributeName])
{
if (![_fontColor isEqualToColor:[CCColor whiteColor]])
{
useFullColor = YES;
}
UIColor* color = _fontColor.UIColor;
[formattedAttributedString addAttribute:NSForegroundColorAttributeName value:color range:fullRange];
}
else
{
useFullColor = YES;
}
// Font
if (![formattedAttributedString hasAttribute:NSFontAttributeName])
{
UIFont* font = [UIFont fontWithName:_fontName size:_fontSize];
if (!font) font = [UIFont fontWithName:@"Helvetica" size:_fontSize];
[formattedAttributedString addAttribute:NSFontAttributeName value:font range:fullRange];
}
// Shadow
if ([formattedAttributedString hasAttribute:NSShadowAttributeName])
{
useFullColor = YES;
}
// Text alignment
if (![formattedAttributedString hasAttribute:NSParagraphStyleAttributeName])
{
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
if (_horizontalAlignment == CCTextAlignmentLeft) style.alignment = NSTextAlignmentLeft;
else if (_horizontalAlignment == CCTextAlignmentCenter) style.alignment = NSTextAlignmentCenter;
else if (_horizontalAlignment == CCTextAlignmentRight) style.alignment = NSTextAlignmentRight;
[formattedAttributedString addAttribute:NSParagraphStyleAttributeName value:style range:fullRange];
}
#elif defined(__CC_PLATFORM_MAC)
// Font color
if (![formattedAttributedString hasAttribute:NSForegroundColorAttributeName])
{
if (![_fontColor isEqualToColor:[CCColor whiteColor]])
{
useFullColor = YES;
}
NSColor* color = [NSColor colorWithCalibratedRed:_fontColor.red green:_fontColor.green blue:_fontColor.blue alpha:_fontColor.alpha];
[formattedAttributedString addAttribute:NSForegroundColorAttributeName value:color range:fullRange];
}
else
{
useFullColor = YES;
}
// Font
if (![formattedAttributedString hasAttribute:NSFontAttributeName])
{
NSFont* font = [NSFont fontWithName:_fontName size:_fontSize];
if (!font) font = [NSFont fontWithName:@"Helvetica" size:_fontSize];
[formattedAttributedString addAttribute:NSFontAttributeName value:font range:fullRange];
}
// Shadow
if ([formattedAttributedString hasAttribute:NSShadowAttributeName])
{
useFullColor = YES;
}
// Text alignment
if (![formattedAttributedString hasAttribute:NSParagraphStyleAttributeName])
{
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
if (_horizontalAlignment == CCTextAlignmentLeft) style.alignment = NSLeftTextAlignment;
else if (_horizontalAlignment == CCTextAlignmentCenter) style.alignment = NSCenterTextAlignment;
else if (_horizontalAlignment == CCTextAlignmentRight) style.alignment = NSRightTextAlignment;
[formattedAttributedString addAttribute:NSParagraphStyleAttributeName value:style range:fullRange];
}
#endif
// Generate a new texture from the attributed string
CCTexture *tex;
tex = [self createTextureWithAttributedString:[formattedAttributedString copyAdjustedForContentScaleFactor] useFullColor:useFullColor];
if(!tex) return NO;
self.shader = (useFullColor ? [CCShader positionTextureColorShader] : [CCShader positionTextureA8ColorShader]);
// Update texture and content size
[self setTexture:tex];
CGRect rect = CGRectZero;
rect.size = [self.texture contentSize];
[self setTextureRect: rect];
return YES;
}
- (CCTexture*) createTextureWithAttributedString:(NSAttributedString*)attributedString useFullColor:(BOOL) fullColor
{
NSAssert(attributedString, @"Invalid attributedString");
CGSize originalDimensions = _dimensions;
CGFloat scale = [CCDirector sharedDirector].contentScaleFactor;
originalDimensions.width *= scale;
originalDimensions.height *= scale;
CGSize dimensions = [self convertContentSizeToPoints:originalDimensions type:_dimensionsType];
CGFloat shadowBlurRadius = _shadowBlurRadius * scale;
CGPoint shadowOffset = ccpMult(self.shadowOffsetInPoints, scale);
CGFloat outlineWidth = _outlineWidth * scale;
BOOL hasShadow = (_shadowColor.alpha > 0);
BOOL hasOutline = (_outlineColor.alpha > 0 && _outlineWidth > 0);
CGFloat xOffset = 0;
CGFloat yOffset = 0;
CGFloat scaleFactor = 1;
CGFloat xPadding = 0;
CGFloat yPadding = 0;
CGFloat wDrawArea = 0;
CGFloat hDrawArea = 0;
// Calculate padding
if (hasShadow)
{
xPadding = (shadowBlurRadius + fabs(shadowOffset.x));
yPadding = (shadowBlurRadius + fabs(shadowOffset.y));
}
if (hasOutline)
{
xPadding += outlineWidth;
yPadding += outlineWidth;
}
// Get actual rendered dimensions
if (dimensions.height == 0)
{
// Get dimensions for string without dimensions of string with variable height
#ifdef __CC_PLATFORM_IOS
dimensions = [attributedString boundingRectWithSize:dimensions options:NSStringDrawingUsesLineFragmentOrigin context:NULL].size;
#elif defined(__CC_PLATFORM_MAC)
dimensions = [attributedString boundingRectWithSize:NSSizeFromCGSize(dimensions) options:NSStringDrawingUsesLineFragmentOrigin].size;
#endif
dimensions.width = ceil(dimensions.width);
dimensions.height = ceil(dimensions.height);
wDrawArea = dimensions.width;
hDrawArea = dimensions.height;
dimensions.width += xPadding * 2;
dimensions.height += yPadding * 2;
}
else if (dimensions.width > 0 && dimensions.height > 0)
{
wDrawArea = dimensions.width - xPadding * 2;
hDrawArea = dimensions.height - yPadding * 2;
// Handle strings with fixed dimensions
if (_adjustsFontSizeToFit)
{
CGFloat fontSize = [attributedString singleFontSize];
if (fontSize)
{
// This is a string that can be resized (it only uses one font and size)
#ifdef __CC_PLATFORM_IOS
CGSize wantedSize = [attributedString boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin context:NULL].size;
#elif defined(__CC_PLATFORM_MAC)
CGSize wantedSize = [attributedString boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin].size;
#endif
CGFloat wScaleFactor = 1;
CGFloat hScaleFactor = 1;
if (wantedSize.width > wDrawArea)
{
wScaleFactor = wDrawArea/wantedSize.width;
}
if (wantedSize.height > hDrawArea)
{
hScaleFactor = hDrawArea/wantedSize.height;
}
if (wScaleFactor < hScaleFactor) scaleFactor = wScaleFactor;
else scaleFactor = hScaleFactor;
if (scaleFactor != 1)
{
CGFloat newFontSize = fontSize * scaleFactor;
CGFloat minFontSize = _minimumFontSize * scale;
if (minFontSize && newFontSize < minFontSize) newFontSize = minFontSize;
attributedString = [attributedString copyWithNewFontSize:newFontSize];
}
}
}
// Handle vertical alignment
#ifdef __CC_PLATFORM_IOS
CGSize actualSize = [attributedString boundingRectWithSize:CGSizeMake(wDrawArea, 0) options:NSStringDrawingUsesLineFragmentOrigin context:NULL].size;
#elif defined(__CC_PLATFORM_MAC)
CGSize actualSize = NSSizeToCGSize([attributedString boundingRectWithSize:NSMakeSize(wDrawArea, 0) options:NSStringDrawingUsesLineFragmentOrigin].size);
#endif
if (_verticalAlignment == CCVerticalTextAlignmentBottom)
{
yOffset = hDrawArea - actualSize.height;
}
else if (_verticalAlignment == CCVerticalTextAlignmentCenter)
{
yOffset = (hDrawArea - actualSize.height)/2;
}
}
// Handle baseline adjustments
yOffset += _baselineAdjustment * scaleFactor * scale + yPadding;
xOffset += xPadding;
// Round dimensions to nearest number that is dividable by 2
dimensions.width = ceilf(dimensions.width/2)*2;
dimensions.height = ceilf(dimensions.height/2)*2;
// get nearest power of two
CGSize POTSize = CGSizeMake(CCNextPOT(dimensions.width), CCNextPOT(dimensions.height));
// Mac crashes if the width or height is 0
if( POTSize.width == 0 )
POTSize.width = 2;
if( POTSize.height == 0)
POTSize.height = 2;
// Render the label - different code for Mac / iOS
#ifdef __CC_PLATFORM_IOS
CGRect drawArea = CGRectMake(xOffset, yOffset, wDrawArea, hDrawArea);
unsigned char* data = calloc(POTSize.width, POTSize.height * 4);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(data, POTSize.width, POTSize.height, 8, POTSize.width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
if (!context)
{
free(data);
return NULL;
}
UIGraphicsPushContext(context);
// Handle shadow
if (hasShadow)
{
UIColor* color = _shadowColor.UIColor;
CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset.x, -shadowOffset.y), shadowBlurRadius, [color CGColor]);
}
// Handle outline
if (hasOutline)
{
UIColor* color = _outlineColor.UIColor;
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetLineWidth(context, outlineWidth * 2);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSMutableAttributedString* outlineString = [attributedString mutableCopy];
[outlineString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, outlineString.length)];
[outlineString drawInRect:drawArea];
// Don't draw shadow for main font
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
if (hasShadow)
{
// Draw outline again because shadow overlap
[outlineString drawInRect:drawArea];
}
CGContextSetTextDrawingMode(context, kCGTextFill);
}
[attributedString drawInRect:drawArea];
UIGraphicsPopContext();
CGContextRelease(context);
#elif defined(__CC_PLATFORM_MAC)
yOffset = (POTSize.height - hDrawArea) - yOffset;
CGRect drawArea = CGRectMake(xOffset, yOffset, wDrawArea, hDrawArea);
NSImage *image = [[NSImage alloc] initWithSize:POTSize];
[image lockFocus];
[[NSAffineTransform transform] set];
// XXX: The shadows are for some reason scaled on OS X if a retina display is connected
CGFloat retinaFix = 1;
for (NSScreen* screen in [NSScreen screens])
{
if (screen.backingScaleFactor > retinaFix) retinaFix = screen.backingScaleFactor;
}
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context);
CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, POTSize.height));
// Handle shadow
if (hasShadow || hasOutline)
{
NSMutableAttributedString* effectsString = [attributedString mutableCopy];
if (hasShadow)
{
NSColor* color = [NSColor colorWithCalibratedRed:_shadowColor.red green:_shadowColor.green blue:_shadowColor.blue alpha:_shadowColor.alpha];
CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset.x/retinaFix, -shadowOffset.y/retinaFix), shadowBlurRadius/retinaFix, [color CGColor]);
}
if (hasOutline)
{
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetLineWidth(context, outlineWidth * 2);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSColor* color = [NSColor colorWithCalibratedRed:_outlineColor.red green:_outlineColor.green blue:_outlineColor.blue alpha:_outlineColor.alpha];
[effectsString addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, effectsString.length)];
[effectsString drawWithRect:NSRectFromCGRect(drawArea) options:NSStringDrawingUsesLineFragmentOrigin];
if (hasShadow)
{
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
[effectsString drawInRect:drawArea];
}
CGContextSetTextDrawingMode(context, kCGTextFill);
}
}
[attributedString drawWithRect:NSRectFromCGRect(drawArea) options:NSStringDrawingUsesLineFragmentOrigin];
CGContextRestoreGState(context);
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTSize.width, POTSize.height)];
[image unlockFocus];
unsigned char *data = (unsigned char*) [bitmap bitmapData]; //Use the same buffer to improve the performance.
#endif
CCTexture* texture = NULL;
// Initialize the texture
if (fullColor)
{
// RGBA8888 format
texture = [[CCTexture alloc] initWithData:data pixelFormat:CCTexturePixelFormat_RGBA8888 pixelsWide:POTSize.width pixelsHigh:POTSize.height contentSizeInPixels:dimensions contentScale:[CCDirector sharedDirector].contentScaleFactor];
[texture setPremultipliedAlpha:YES];
}
else
{
NSUInteger textureSize = POTSize.width * POTSize.height;
// A8 format (alpha channel only)
unsigned char* dst = data;
for(int i = 0; i<textureSize; i++)
dst[i] = data[i*4+3];
texture = [[CCTexture alloc] initWithData:data pixelFormat:CCTexturePixelFormat_A8 pixelsWide:POTSize.width pixelsHigh:POTSize.height contentSizeInPixels:dimensions contentScale:[CCDirector sharedDirector].contentScaleFactor];
self.shader = [CCShader positionTextureA8ColorShader];
}
#ifdef __CC_PLATFORM_IOS
free(data); // On Mac data is freed by NSBitmapImageRep
#endif
return texture;
}
#pragma mark -
#pragma mark Render Font iOS 5
#ifdef __CC_PLATFORM_IOS
- (BOOL) updateTextureOld
{
NSString* string = [self string];
if (!string) return NO;
BOOL useFullColor = NO;
if (_shadowColor.alpha > 0) useFullColor = YES;
if (![_fontColor isEqualToColor:[CCColor whiteColor]]) useFullColor = YES;
if (_outlineColor.alpha > 0 && _outlineWidth > 0) useFullColor = YES;
CCTexture* tex = [self createTextureWithString:string useFullColor:useFullColor];
if (!tex) return NO;
self.shader = (useFullColor ? [CCShader positionTextureColorShader] : [CCShader positionTextureA8ColorShader]);
// Update texture and content size
[self setTexture:tex];
CGRect rect = CGRectZero;
rect.size = [self.texture contentSize];
[self setTextureRect: rect];
return YES;
}
- (CCTexture*) createTextureWithString:(NSString*) string useFullColor:(BOOL)useFullColor
{
// Scale everything up by content scale
CGFloat scale = [CCDirector sharedDirector].contentScaleFactor;
UIFont* font = [UIFont fontWithName:_fontName size:_fontSize * scale];
CGFloat shadowBlurRadius = _shadowBlurRadius * scale;
CGPoint shadowOffset = ccpMult(self.shadowOffsetInPoints, scale);
CGFloat outlineWidth = _outlineWidth * scale;
BOOL hasShadow = (_shadowColor.alpha > 0);
BOOL hasOutline = (_outlineColor.alpha > 0 && _outlineWidth > 0);
CGFloat xOffset = 0;
CGFloat yOffset = 0;
CGFloat scaleFactor = 1;
CGFloat xPadding = 0;
CGFloat yPadding = 0;
CGFloat wDrawArea = 0;
CGFloat hDrawArea = 0;
CGSize originalDimensions = _dimensions;
originalDimensions.width *= scale;
originalDimensions.height *= scale;
// Calculate padding
if (hasShadow)
{
xPadding = (shadowBlurRadius + fabs(shadowOffset.x));
yPadding = (shadowBlurRadius + fabs(shadowOffset.y));
}
if (hasOutline)
{
xPadding += outlineWidth;
yPadding += outlineWidth;
}
CGSize dimensions = originalDimensions;
// Get actual rendered dimensions
if (dimensions.height == 0)
{
// Get dimensions for string without dimensions of string with variable height
if (dimensions.width > 0)
{
dimensions = [string sizeWithFont:font forWidth:dimensions.width lineBreakMode:0];
}
else
{
CGSize firstLineSize = [string sizeWithFont:font];
dimensions = [string sizeWithFont:font constrainedToSize:CGSizeMake(firstLineSize.width,1024) lineBreakMode:0];
}
wDrawArea = dimensions.width;
hDrawArea = dimensions.height;
dimensions.width += xPadding * 2;
dimensions.height += yPadding * 2;
}
else if (dimensions.width > 0 && dimensions.height > 0)
{
wDrawArea = dimensions.width - xPadding * 2;
hDrawArea = dimensions.height - yPadding * 2;
// Handle strings with fixed dimensions
if (_adjustsFontSizeToFit)
{
CGFloat fontSize = font.pointSize;
CGSize wantedSizeFirstLine = [string sizeWithFont:font];
CGSize wantedSize = [string sizeWithFont:font constrainedToSize:CGSizeMake(wantedSizeFirstLine.width, 1024) lineBreakMode:0];
CGFloat wScaleFactor = 1;
CGFloat hScaleFactor = 1;
if (wantedSize.width > wDrawArea)
{
wScaleFactor = wDrawArea/wantedSize.width;
}
if (wantedSize.height > hDrawArea)
{
hScaleFactor = hDrawArea/wantedSize.height;
}
if (wScaleFactor < hScaleFactor) scaleFactor = wScaleFactor;
else scaleFactor = hScaleFactor;
if (scaleFactor != 1)
{
CGFloat newFontSize = fontSize * scaleFactor;
CGFloat minFontSize = _minimumFontSize * scale;
if (minFontSize && newFontSize < minFontSize) newFontSize = minFontSize;
font = [UIFont fontWithName:font.fontName size:newFontSize];
}
}
// Handle vertical alignment
CGSize actualSize = [string sizeWithFont:font constrainedToSize:CGSizeMake(wDrawArea, 1024) lineBreakMode:0];
if (_verticalAlignment == CCVerticalTextAlignmentBottom)
{
yOffset = hDrawArea - actualSize.height;
}
else if (_verticalAlignment == CCVerticalTextAlignmentCenter)
{
yOffset = (hDrawArea - actualSize.height)/2;
}
}
// Handle baseline adjustments
yOffset += _baselineAdjustment * scaleFactor * scale + yPadding;
xOffset += xPadding;
// Round dimensions to nearest number that is dividable by 2
dimensions.width = ceilf(dimensions.width/2)*2;
dimensions.height = ceilf(dimensions.height/2)*2;
// get nearest power of two
CGSize POTSize = CGSizeMake(CCNextPOT(dimensions.width), CCNextPOT(dimensions.height));
// Mac crashes if the width or height is 0
if( POTSize.width == 0 )
POTSize.width = 2;
if( POTSize.height == 0)
POTSize.height = 2;
CGRect drawArea = CGRectMake(xOffset, yOffset, wDrawArea, hDrawArea);
unsigned char* data = calloc(POTSize.width, POTSize.height * 4);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(data, POTSize.width, POTSize.height, 8, POTSize.width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
if (!context)
{
free(data);
return NULL;
}
UIGraphicsPushContext(context);
// Handle shadow
if (hasShadow)
{
UIColor* color = _shadowColor.UIColor;
CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset.x, -shadowOffset.y), shadowBlurRadius, [color CGColor]);
}
// Handle outline
if (hasOutline)
{
ccColor4F outlineColor = _outlineColor.ccColor4f;
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetRGBStrokeColor(context, outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
CGContextSetRGBFillColor(context, outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
CGContextSetLineWidth(context, outlineWidth * 2);
CGContextSetLineJoin(context, kCGLineJoinRound);
[string drawInRect:drawArea withFont:font lineBreakMode:0 alignment:(int)_horizontalAlignment];
// Don't draw shadow for main font
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
if (hasShadow)
{
// Draw again, because shadows overlap
[string drawInRect:drawArea withFont:font lineBreakMode:0 alignment:(int)_horizontalAlignment];
}
CGContextSetTextDrawingMode(context, kCGTextFill);
}
// Handle font color
UIColor* color = [UIColor colorWithRed:_fontColor.red green:_fontColor.green blue:_fontColor.blue alpha:_fontColor.alpha];
[color set];
[string drawInRect:drawArea withFont:font lineBreakMode:0 alignment:(int)_horizontalAlignment];
UIGraphicsPopContext();
CGContextRelease(context);
CCTexture* texture = NULL;
// Initialize the texture
if (useFullColor)
{
// RGBA8888 format
texture = [[CCTexture alloc] initWithData:data pixelFormat:CCTexturePixelFormat_RGBA8888 pixelsWide:POTSize.width pixelsHigh:POTSize.height contentSizeInPixels:dimensions contentScale:[CCDirector sharedDirector].contentScaleFactor];
[texture setPremultipliedAlpha:YES];
}
else
{
NSUInteger textureSize = POTSize.width * POTSize.height;
// A8 format (alpha channel only)
unsigned char* dst = data;
for(int i = 0; i<textureSize; i++)
dst[i] = data[i*4+3];
texture = [[CCTexture alloc] initWithData:data pixelFormat:CCTexturePixelFormat_A8 pixelsWide:POTSize.width pixelsHigh:POTSize.height contentSizeInPixels:dimensions contentScale:[CCDirector sharedDirector].contentScaleFactor];
self.shader = [CCShader positionTextureA8ColorShader];
}
free(data);
return texture;
}
#endif
#pragma mark -
#pragma mark Handle HTML
#ifdef __CC_PLATFORM_MAC
- (void) setHTML:(NSString *)html
{
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
self.attributedString = [[NSAttributedString alloc] initWithHTML:data documentAttributes:NULL];
}
#endif
#pragma mark Class functions
+ (NSString*) registerCustomTTF:(NSString *)fontFile
{
// Do not register a font if it has already been registered
if (!ccLabelTTF_registeredFonts)
{
ccLabelTTF_registeredFonts = [[NSMutableDictionary alloc] init];
}
if ([ccLabelTTF_registeredFonts objectForKey:fontFile])
{
return [ccLabelTTF_registeredFonts objectForKey:fontFile];
}
[ccLabelTTF_registeredFonts setObject:[NSNumber numberWithBool:YES] forKey:fontFile];
// Register with font manager
if ([[fontFile lowercaseString] hasSuffix:@".ttf"] || [[fontFile lowercaseString] hasSuffix:@".otf"])
{
// This is a file, register font with font manager
NSString* fontPath = [[CCFileUtils sharedFileUtils] fullPathForFilename:fontFile];
NSURL* fontURL = [NSURL fileURLWithPath:fontPath];
CTFontManagerRegisterFontsForURL((__bridge CFURLRef)fontURL, kCTFontManagerScopeProcess, NULL);
NSString *fontName = nil;
#ifdef APPORTABLE
BOOL needsCGFontFailback = NO;
#elif __CC_PLATFORM_MAC
BOOL needsCGFontFailback = NO;
#elif __CC_PLATFORM_IOS
BOOL needsCGFontFailback = [[[UIDevice currentDevice] systemVersion] compare:@"7.0" options:NSNumericSearch] != NSOrderedAscending;
#endif
if (!needsCGFontFailback) {
CFArrayRef descriptors = CTFontManagerCreateFontDescriptorsFromURL((__bridge CFURLRef)fontURL);
if (!descriptors || CFArrayGetCount(descriptors)<1) {
return nil;
}
CTFontDescriptorRef descriptor = CFArrayGetValueAtIndex(descriptors, 0);
fontName = (__bridge NSString *)CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);
CFRelease(descriptors);
} else {
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)fontURL);
CGFontRef loadedFont = CGFontCreateWithDataProvider(fontDataProvider);
fontName = (__bridge NSString *)CGFontCopyPostScriptName(loadedFont);
CGFontRelease(loadedFont);
CGDataProviderRelease(fontDataProvider);
}
[ccLabelTTF_registeredFonts setObject:fontName forKey:fontFile];
return fontName;
}
return nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment