summary refs log tree commit diff stats
path: root/libs/cocos2d/CCLabelBMFont.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCLabelBMFont.m')
-rwxr-xr-xlibs/cocos2d/CCLabelBMFont.m675
1 files changed, 675 insertions, 0 deletions
diff --git a/libs/cocos2d/CCLabelBMFont.m b/libs/cocos2d/CCLabelBMFont.m new file mode 100755 index 0000000..614cd6e --- /dev/null +++ b/libs/cocos2d/CCLabelBMFont.m
@@ -0,0 +1,675 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (c) 2008-2010 Ricardo Quesada
5 * Copyright (c) 2011 Zynga Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * Portions of this code are based and inspired on:
26 * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
27 * by Michael Daley
28 *
29 *
30 * Use any of these editors to generate BMFonts:
31 * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
32 * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
33 * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
34 * http://www.angelcode.com/products/bmfont/ (Free, Windows only)
35 */
36
37#import "ccConfig.h"
38#import "CCLabelBMFont.h"
39#import "CCSprite.h"
40#import "CCDrawingPrimitives.h"
41#import "CCConfiguration.h"
42#import "Support/CCFileUtils.h"
43#import "Support/CGPointExtension.h"
44#import "Support/uthash.h"
45
46#pragma mark -
47#pragma mark FNTConfig Cache - free functions
48
49NSMutableDictionary *configurations = nil;
50CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile)
51{
52 CCBMFontConfiguration *ret = nil;
53
54 if( configurations == nil )
55 configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain];
56
57 ret = [configurations objectForKey:fntFile];
58 if( ret == nil ) {
59 ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile];
60 [configurations setObject:ret forKey:fntFile];
61 }
62
63 return ret;
64}
65
66void FNTConfigRemoveCache( void )
67{
68 [configurations removeAllObjects];
69}
70
71#pragma mark - Hash Element
72
73// Equal function for targetSet.
74typedef struct _KerningHashElement
75{
76 int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element
77 int amount;
78 UT_hash_handle hh;
79} tKerningHashElement;
80
81#pragma mark -
82#pragma mark BitmapFontConfiguration
83
84
85@interface CCBMFontConfiguration (Private)
86-(void) parseConfigFile:(NSString*)controlFile;
87-(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition;
88-(void) parseInfoArguments:(NSString*)line;
89-(void) parseCommonArguments:(NSString*)line;
90-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile;
91-(void) parseKerningCapacity:(NSString*)line;
92-(void) parseKerningEntry:(NSString*)line;
93-(void) purgeKerningDictionary;
94@end
95
96@implementation CCBMFontConfiguration
97
98+(id) configurationWithFNTFile:(NSString*)FNTfile
99{
100 return [[[self alloc] initWithFNTfile:FNTfile] autorelease];
101}
102
103-(id) initWithFNTfile:(NSString*)fntFile
104{
105 if((self=[super init])) {
106
107 kerningDictionary_ = NULL;
108
109 [self parseConfigFile:fntFile];
110 }
111 return self;
112}
113
114- (void) dealloc
115{
116 CCLOGINFO( @"cocos2d: deallocing %@", self);
117 [self purgeKerningDictionary];
118 [atlasName_ release];
119 [super dealloc];
120}
121
122- (NSString*) description
123{
124 return [NSString stringWithFormat:@"<%@ = %08X | Kernings:%d | Image = %@>", [self class], self,
125 HASH_COUNT(kerningDictionary_),
126 atlasName_];
127}
128
129
130-(void) purgeKerningDictionary
131{
132 tKerningHashElement *current;
133
134 while(kerningDictionary_) {
135 current = kerningDictionary_;
136 HASH_DEL(kerningDictionary_,current);
137 free(current);
138 }
139}
140
141- (void)parseConfigFile:(NSString*)fntFile
142{
143 NSString *fullpath = [CCFileUtils fullPathFromRelativePath:fntFile];
144 NSError *error;
145 NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error];
146
147 NSAssert1( contents, @"cocos2d: Error parsing FNTfile: %@", error);
148
149
150 // Move all lines in the string, which are denoted by \n, into an array
151 NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]];
152
153 // Create an enumerator which we can use to move through the lines read from the control file
154 NSEnumerator *nse = [lines objectEnumerator];
155
156 // Create a holder for each line we are going to work with
157 NSString *line;
158
159 // Loop through all the lines in the lines array processing each one
160 while( (line = [nse nextObject]) ) {
161 // parse spacing / padding
162 if([line hasPrefix:@"info face"]) {
163 // XXX: info parsing is incomplete
164 // Not needed for the Hiero editors, but needed for the AngelCode editor
165// [self parseInfoArguments:line];
166 }
167 // Check to see if the start of the line is something we are interested in
168 else if([line hasPrefix:@"common lineHeight"]) {
169 [self parseCommonArguments:line];
170 }
171 else if([line hasPrefix:@"page id"]) {
172 [self parseImageFileName:line fntFile:fntFile];
173 }
174 else if([line hasPrefix:@"chars c"]) {
175 // Ignore this line
176 }
177 else if([line hasPrefix:@"char"]) {
178 // Parse the current line and create a new CharDef
179 ccBMFontDef characterDefinition;
180 [self parseCharacterDefinition:line charDef:&characterDefinition];
181
182 // Add the CharDef returned to the charArray
183 BMFontArray_[ characterDefinition.charID ] = characterDefinition;
184 }
185 else if([line hasPrefix:@"kernings count"]) {
186 [self parseKerningCapacity:line];
187 }
188 else if([line hasPrefix:@"kerning first"]) {
189 [self parseKerningEntry:line];
190 }
191 }
192 // Finished with lines so release it
193 [lines release];
194}
195
196-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile
197{
198 NSString *propertyValue = nil;
199
200 // Break the values for this line up using =
201 NSArray *values = [line componentsSeparatedByString:@"="];
202
203 // Get the enumerator for the array of components which has been created
204 NSEnumerator *nse = [values objectEnumerator];
205
206 // We need to move past the first entry in the array before we start assigning values
207 [nse nextObject];
208
209 // page ID. Sanity check
210 propertyValue = [nse nextObject];
211 NSAssert( [propertyValue intValue] == 0, @"XXX: LabelBMFont only supports 1 page");
212
213 // file
214 propertyValue = [nse nextObject];
215 NSArray *array = [propertyValue componentsSeparatedByString:@"\""];
216 propertyValue = [array objectAtIndex:1];
217 NSAssert(propertyValue,@"LabelBMFont file could not be found");
218
219 // Supports subdirectories
220 NSString *dir = [fntFile stringByDeletingLastPathComponent];
221 atlasName_ = [dir stringByAppendingPathComponent:propertyValue];
222
223 [atlasName_ retain];
224}
225
226-(void) parseInfoArguments:(NSString*)line
227{
228 //
229 // possible lines to parse:
230 // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0
231 // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
232 //
233 NSArray *values = [line componentsSeparatedByString:@"="];
234 NSEnumerator *nse = [values objectEnumerator];
235 NSString *propertyValue = nil;
236
237 // We need to move past the first entry in the array before we start assigning values
238 [nse nextObject];
239
240 // face (ignore)
241 [nse nextObject];
242
243 // size (ignore)
244 [nse nextObject];
245
246 // bold (ignore)
247 [nse nextObject];
248
249 // italic (ignore)
250 [nse nextObject];
251
252 // charset (ignore)
253 [nse nextObject];
254
255 // unicode (ignore)
256 [nse nextObject];
257
258 // strechH (ignore)
259 [nse nextObject];
260
261 // smooth (ignore)
262 [nse nextObject];
263
264 // aa (ignore)
265 [nse nextObject];
266
267 // padding (ignore)
268 propertyValue = [nse nextObject];
269 {
270
271 NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","];
272 NSEnumerator *paddingEnum = [paddingValues objectEnumerator];
273 // padding top
274 propertyValue = [paddingEnum nextObject];
275 padding_.top = [propertyValue intValue];
276
277 // padding right
278 propertyValue = [paddingEnum nextObject];
279 padding_.right = [propertyValue intValue];
280
281 // padding bottom
282 propertyValue = [paddingEnum nextObject];
283 padding_.bottom = [propertyValue intValue];
284
285 // padding left
286 propertyValue = [paddingEnum nextObject];
287 padding_.left = [propertyValue intValue];
288
289 CCLOG(@"cocos2d: padding: %d,%d,%d,%d", padding_.left, padding_.top, padding_.right, padding_.bottom);
290 }
291
292 // spacing (ignore)
293 [nse nextObject];
294}
295
296-(void) parseCommonArguments:(NSString*)line
297{
298 //
299 // line to parse:
300 // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
301 //
302 NSArray *values = [line componentsSeparatedByString:@"="];
303 NSEnumerator *nse = [values objectEnumerator];
304 NSString *propertyValue = nil;
305
306 // We need to move past the first entry in the array before we start assigning values
307 [nse nextObject];
308
309 // Character ID
310 propertyValue = [nse nextObject];
311 commonHeight_ = [propertyValue intValue];
312
313 // base (ignore)
314 [nse nextObject];
315
316
317 // scaleW. sanity check
318 propertyValue = [nse nextObject];
319 NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
320
321 // scaleH. sanity check
322 propertyValue = [nse nextObject];
323 NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
324
325 // pages. sanity check
326 propertyValue = [nse nextObject];
327 NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page");
328
329 // packed (ignore) What does this mean ??
330}
331- (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition
332{
333 // Break the values for this line up using =
334 NSArray *values = [line componentsSeparatedByString:@"="];
335 NSEnumerator *nse = [values objectEnumerator];
336 NSString *propertyValue;
337
338 // We need to move past the first entry in the array before we start assigning values
339 [nse nextObject];
340
341 // Character ID
342 propertyValue = [nse nextObject];
343 propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location];
344 characterDefinition->charID = [propertyValue intValue];
345 NSAssert(characterDefinition->charID < kCCBMFontMaxChars, @"BitmpaFontAtlas: CharID bigger than supported");
346
347 // Character x
348 propertyValue = [nse nextObject];
349 characterDefinition->rect.origin.x = [propertyValue intValue];
350 // Character y
351 propertyValue = [nse nextObject];
352 characterDefinition->rect.origin.y = [propertyValue intValue];
353 // Character width
354 propertyValue = [nse nextObject];
355 characterDefinition->rect.size.width = [propertyValue intValue];
356 // Character height
357 propertyValue = [nse nextObject];
358 characterDefinition->rect.size.height = [propertyValue intValue];
359 // Character xoffset
360 propertyValue = [nse nextObject];
361 characterDefinition->xOffset = [propertyValue intValue];
362 // Character yoffset
363 propertyValue = [nse nextObject];
364 characterDefinition->yOffset = [propertyValue intValue];
365 // Character xadvance
366 propertyValue = [nse nextObject];
367 characterDefinition->xAdvance = [propertyValue intValue];
368}
369
370-(void) parseKerningCapacity:(NSString*) line
371{
372 // When using uthash there is not need to parse the capacity.
373
374// NSAssert(!kerningDictionary, @"dictionary already initialized");
375//
376// // Break the values for this line up using =
377// NSArray *values = [line componentsSeparatedByString:@"="];
378// NSEnumerator *nse = [values objectEnumerator];
379// NSString *propertyValue;
380//
381// // We need to move past the first entry in the array before we start assigning values
382// [nse nextObject];
383//
384// // count
385// propertyValue = [nse nextObject];
386// int capacity = [propertyValue intValue];
387//
388// if( capacity != -1 )
389// kerningDictionary = ccHashSetNew(capacity, targetSetEql);
390}
391
392-(void) parseKerningEntry:(NSString*) line
393{
394 NSArray *values = [line componentsSeparatedByString:@"="];
395 NSEnumerator *nse = [values objectEnumerator];
396 NSString *propertyValue;
397
398 // We need to move past the first entry in the array before we start assigning values
399 [nse nextObject];
400
401 // first
402 propertyValue = [nse nextObject];
403 int first = [propertyValue intValue];
404
405 // second
406 propertyValue = [nse nextObject];
407 int second = [propertyValue intValue];
408
409 // second
410 propertyValue = [nse nextObject];
411 int amount = [propertyValue intValue];
412
413 tKerningHashElement *element = calloc( sizeof( *element ), 1 );
414 element->amount = amount;
415 element->key = (first<<16) | (second&0xffff);
416 HASH_ADD_INT(kerningDictionary_,key, element);
417}
418
419@end
420
421#pragma mark -
422#pragma mark CCLabelBMFont
423
424@interface CCLabelBMFont (Private)
425-(NSString*) atlasNameFromFntFile:(NSString*)fntFile;
426
427-(int) kerningAmountForFirst:(unichar)first second:(unichar)second;
428
429@end
430
431@implementation CCLabelBMFont
432
433@synthesize opacity = opacity_, color = color_;
434
435#pragma mark LabelBMFont - Purge Cache
436+(void) purgeCachedData
437{
438 FNTConfigRemoveCache();
439}
440
441#pragma mark LabelBMFont - Creation & Init
442
443+(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile
444{
445 return [[[self alloc] initWithString:string fntFile:fntFile] autorelease];
446}
447
448// XXX - deprecated - Will be removed in 1.0.1
449+(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile
450{
451 return [self labelWithString:string fntFile:fntFile];
452}
453
454-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile
455{
456
457 [configuration_ release]; // allow re-init
458
459 configuration_ = FNTConfigLoadFile(fntFile);
460 [configuration_ retain];
461
462 NSAssert( configuration_, @"Error creating config for LabelBMFont");
463
464
465 if ((self=[super initWithFile:configuration_->atlasName_ capacity:[theString length]])) {
466
467 opacity_ = 255;
468 color_ = ccWHITE;
469
470 contentSize_ = CGSizeZero;
471
472 opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha];
473
474 anchorPoint_ = ccp(0.5f, 0.5f);
475
476 [self setString:theString];
477 }
478
479 return self;
480}
481
482-(void) dealloc
483{
484 [string_ release];
485 [configuration_ release];
486 [super dealloc];
487}
488
489#pragma mark LabelBMFont - Atlas generation
490
491-(int) kerningAmountForFirst:(unichar)first second:(unichar)second
492{
493 int ret = 0;
494 unsigned int key = (first<<16) | (second & 0xffff);
495
496 if( configuration_->kerningDictionary_ ) {
497 tKerningHashElement *element = NULL;
498 HASH_FIND_INT(configuration_->kerningDictionary_, &key, element);
499 if(element)
500 ret = element->amount;
501 }
502
503 return ret;
504}
505
506-(void) createFontChars
507{
508 NSInteger nextFontPositionX = 0;
509 NSInteger nextFontPositionY = 0;
510 unichar prev = -1;
511 NSInteger kerningAmount = 0;
512
513 CGSize tmpSize = CGSizeZero;
514
515 NSInteger longestLine = 0;
516 NSUInteger totalHeight = 0;
517
518 NSUInteger quantityOfLines = 1;
519
520 NSUInteger stringLen = [string_ length];
521 if( ! stringLen )
522 return;
523
524 // quantity of lines NEEDS to be calculated before parsing the lines,
525 // since the Y position needs to be calcualted before hand
526 for(NSUInteger i=0; i < stringLen-1;i++) {
527 unichar c = [string_ characterAtIndex:i];
528 if( c=='\n')
529 quantityOfLines++;
530 }
531
532 totalHeight = configuration_->commonHeight_ * quantityOfLines;
533 nextFontPositionY = -(configuration_->commonHeight_ - configuration_->commonHeight_*quantityOfLines);
534
535 for(NSUInteger i=0; i<stringLen; i++) {
536 unichar c = [string_ characterAtIndex:i];
537 NSAssert( c < kCCBMFontMaxChars, @"LabelBMFont: character outside bounds");
538
539 if (c == '\n') {
540 nextFontPositionX = 0;
541 nextFontPositionY -= configuration_->commonHeight_;
542 continue;
543 }
544
545 kerningAmount = [self kerningAmountForFirst:prev second:c];
546
547 ccBMFontDef fontDef = configuration_->BMFontArray_[c];
548
549 CGRect rect = fontDef.rect;
550
551 CCSprite *fontChar;
552
553 fontChar = (CCSprite*) [self getChildByTag:i];
554 if( ! fontChar ) {
555 fontChar = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
556 [self addChild:fontChar z:0 tag:i];
557 [fontChar release];
558 }
559 else {
560 // reusing fonts
561 [fontChar setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
562
563 // restore to default in case they were modified
564 fontChar.visible = YES;
565 fontChar.opacity = 255;
566 }
567
568 float yOffset = configuration_->commonHeight_ - fontDef.yOffset;
569 fontChar.positionInPixels = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount,
570 (float)nextFontPositionY + yOffset - rect.size.height*0.5f );
571
572 // update kerning
573 nextFontPositionX += configuration_->BMFontArray_[c].xAdvance + kerningAmount;
574 prev = c;
575
576 // Apply label properties
577 [fontChar setOpacityModifyRGB:opacityModifyRGB_];
578 // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
579 [fontChar setColor:color_];
580
581 // only apply opacity if it is different than 255 )
582 // to prevent modifying the color too (issue #610)
583 if( opacity_ != 255 )
584 [fontChar setOpacity: opacity_];
585
586 if (longestLine < nextFontPositionX)
587 longestLine = nextFontPositionX;
588 }
589
590 tmpSize.width = longestLine;
591 tmpSize.height = totalHeight;
592
593 [self setContentSizeInPixels:tmpSize];
594}
595
596#pragma mark LabelBMFont - CCLabelProtocol protocol
597- (void) setString:(NSString*) newString
598{
599 [string_ release];
600 string_ = [newString copy];
601
602 CCNode *child;
603 CCARRAY_FOREACH(children_, child)
604 child.visible = NO;
605
606 [self createFontChars];
607}
608
609-(NSString*) string
610{
611 return string_;
612}
613
614-(void) setCString:(char*)label
615{
616 [self setString:[NSString stringWithUTF8String:label]];
617}
618
619#pragma mark LabelBMFont - CCRGBAProtocol protocol
620
621-(void) setColor:(ccColor3B)color
622{
623 color_ = color;
624
625 CCSprite *child;
626 CCARRAY_FOREACH(children_, child)
627 [child setColor:color_];
628}
629
630-(void) setOpacity:(GLubyte)opacity
631{
632 opacity_ = opacity;
633
634 id<CCRGBAProtocol> child;
635 CCARRAY_FOREACH(children_, child)
636 [child setOpacity:opacity_];
637}
638-(void) setOpacityModifyRGB:(BOOL)modify
639{
640 opacityModifyRGB_ = modify;
641
642 id<CCRGBAProtocol> child;
643 CCARRAY_FOREACH(children_, child)
644 [child setOpacityModifyRGB:modify];
645}
646
647-(BOOL) doesOpacityModifyRGB
648{
649 return opacityModifyRGB_;
650}
651
652#pragma mark LabelBMFont - AnchorPoint
653-(void) setAnchorPoint:(CGPoint)point
654{
655 if( ! CGPointEqualToPoint(point, anchorPoint_) ) {
656 [super setAnchorPoint:point];
657 [self createFontChars];
658 }
659}
660
661#pragma mark LabelBMFont - Debug draw
662#if CC_LABELBMFONT_DEBUG_DRAW
663-(void) draw
664{
665 [super draw];
666
667 CGSize s = [self contentSize];
668 CGPoint vertices[4]={
669 ccp(0,0),ccp(s.width,0),
670 ccp(s.width,s.height),ccp(0,s.height),
671 };
672 ccDrawPoly(vertices, 4, YES);
673}
674#endif // CC_LABELBMFONT_DEBUG_DRAW
675@end