PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/FlappyBird.spritebuilder/Source/libs/cocos2d-iphone/cocos2d/CCLabelBMFont.m

https://gitlab.com/fractalspace/FlappyBirdTemplate-Spritebuilder
Objective C | 973 lines | 614 code | 196 blank | 163 comment | 83 complexity | 7361800516322aadd51317ace9459db8 MD5 | raw file
Possible License(s): BSD-3-Clause
  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. * Copyright (c) 2013-2014 Cocos2D Authors
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. * Portions of this code are based and inspired on:
  27. * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
  28. * by Michael Daley
  29. *
  30. *
  31. * Use any of these editors to generate BMFonts:
  32. * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
  33. * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
  34. * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
  35. * http://www.angelcode.com/products/bmfont/ (Free, Windows only)
  36. */
  37. #import "ccConfig.h"
  38. #import "ccMacros.h"
  39. #import "CCLabelBMFont.h"
  40. #import "CCSprite.h"
  41. #import "CCConfiguration.h"
  42. #import "CCTexture.h"
  43. #import "CCTextureCache.h"
  44. #import "Support/CCFileUtils.h"
  45. #import "Support/CGPointExtension.h"
  46. #import "Support/uthash.h"
  47. #import "CCLabelBMFont_Private.h"
  48. #import "CCSprite_Private.h"
  49. #pragma mark -
  50. #pragma mark FNTConfig Cache - free functions
  51. NSMutableDictionary *configurations = nil;
  52. CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile)
  53. {
  54. CCBMFontConfiguration *ret = nil;
  55. if( configurations == nil )
  56. configurations = [NSMutableDictionary dictionaryWithCapacity:3];
  57. ret = [configurations objectForKey:fntFile];
  58. if( ret == nil ) {
  59. ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile];
  60. if( ret )
  61. [configurations setObject:ret forKey:fntFile];
  62. }
  63. return ret;
  64. }
  65. void FNTConfigRemoveCache( void )
  66. {
  67. [configurations removeAllObjects];
  68. }
  69. #pragma mark -
  70. #pragma mark BitmapFontConfiguration
  71. @interface CCBMFontConfiguration ()
  72. -(NSMutableString *) parseConfigFile:(NSString*)controlFile;
  73. -(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition;
  74. -(void) parseInfoArguments:(NSString*)line;
  75. -(void) parseCommonArguments:(NSString*)line;
  76. -(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile;
  77. -(void) parseKerningEntry:(NSString*)line;
  78. -(void) purgeKerningDictionary;
  79. -(void) purgeFontDefDictionary;
  80. @end
  81. #pragma mark -
  82. #pragma mark CCBMFontConfiguration
  83. @implementation CCBMFontConfiguration
  84. @synthesize characterSet=_characterSet;
  85. @synthesize atlasName=_atlasName;
  86. +(id) configurationWithFNTFile:(NSString*)FNTfile
  87. {
  88. return [[self alloc] initWithFNTfile:FNTfile];
  89. }
  90. -(id) initWithFNTfile:(NSString*)fntFile
  91. {
  92. if((self=[super init])) {
  93. _kerningDictionary = NULL;
  94. _fontDefDictionary = NULL;
  95. NSMutableString *validCharsString = [self parseConfigFile:fntFile];
  96. if( ! validCharsString ) {
  97. return nil;
  98. }
  99. _characterSet = [NSCharacterSet characterSetWithCharactersInString:validCharsString];
  100. }
  101. return self;
  102. }
  103. - (void) dealloc
  104. {
  105. CCLOGINFO( @"cocos2d: deallocing %@", self);
  106. [self purgeFontDefDictionary];
  107. [self purgeKerningDictionary];
  108. }
  109. - (NSString*) description
  110. {
  111. return [NSString stringWithFormat:@"<%@ = %p | Glphys:%d Kernings:%d | Image = %@>", [self class], self,
  112. HASH_COUNT(_fontDefDictionary),
  113. HASH_COUNT(_kerningDictionary),
  114. _atlasName];
  115. }
  116. -(void) purgeFontDefDictionary
  117. {
  118. tCCFontDefHashElement *current;
  119. tCCFontDefHashElement *tmp;
  120. HASH_ITER(hh, _fontDefDictionary, current, tmp) {
  121. HASH_DEL(_fontDefDictionary, current);
  122. free(current);
  123. }
  124. }
  125. -(void) purgeKerningDictionary
  126. {
  127. tCCKerningHashElement *current;
  128. while(_kerningDictionary) {
  129. current = _kerningDictionary;
  130. HASH_DEL(_kerningDictionary,current);
  131. free(current);
  132. }
  133. }
  134. - (NSMutableString *)parseConfigFile:(NSString*)fntFile
  135. {
  136. NSString *fullpath = [[CCFileUtils sharedFileUtils] fullPathForFilename:fntFile];
  137. NSError *error;
  138. NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error];
  139. NSMutableString *validCharsString = [[NSMutableString alloc] initWithCapacity:512];
  140. if( ! contents ) {
  141. NSLog(@"cocos2d: Error parsing FNTfile %@: %@", fntFile, error);
  142. return nil;
  143. }
  144. // Move all lines in the string, which are denoted by \n, into an array
  145. NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]];
  146. // Create an enumerator which we can use to move through the lines read from the control file
  147. NSEnumerator *nse = [lines objectEnumerator];
  148. // Create a holder for each line we are going to work with
  149. NSString *line;
  150. // Loop through all the lines in the lines array processing each one
  151. while( (line = [nse nextObject]) ) {
  152. // parse spacing / padding
  153. if([line hasPrefix:@"info face"]) {
  154. // XXX: info parsing is incomplete
  155. // Not needed for the Hiero editors, but needed for the AngelCode editor
  156. // [self parseInfoArguments:line];
  157. }
  158. // Check to see if the start of the line is something we are interested in
  159. else if([line hasPrefix:@"common lineHeight"]) {
  160. [self parseCommonArguments:line];
  161. }
  162. else if([line hasPrefix:@"page id"]) {
  163. [self parseImageFileName:line fntFile:fntFile];
  164. }
  165. else if([line hasPrefix:@"chars c"]) {
  166. // Ignore this line
  167. }
  168. else if([line hasPrefix:@"char"]) {
  169. // Parse the current line and create a new CharDef
  170. tCCFontDefHashElement *element = malloc( sizeof(*element) );
  171. [self parseCharacterDefinition:line charDef:&element->fontDef];
  172. element->key = element->fontDef.charID;
  173. HASH_ADD_INT(_fontDefDictionary, key, element);
  174. [validCharsString appendFormat:@"%C", element->fontDef.charID];
  175. }
  176. // else if([line hasPrefix:@"kernings count"]) {
  177. // [self parseKerningCapacity:line];
  178. // }
  179. else if([line hasPrefix:@"kerning first"]) {
  180. [self parseKerningEntry:line];
  181. }
  182. }
  183. // Finished with lines so release it
  184. return validCharsString;
  185. }
  186. -(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile
  187. {
  188. NSString *propertyValue = nil;
  189. // Break the values for this line up using =
  190. NSArray *values = [line componentsSeparatedByString:@"="];
  191. // Get the enumerator for the array of components which has been created
  192. NSEnumerator *nse = [values objectEnumerator];
  193. // We need to move past the first entry in the array before we start assigning values
  194. [nse nextObject];
  195. // page ID. Sanity check
  196. propertyValue = [nse nextObject];
  197. NSAssert( [propertyValue intValue] == 0, @"XXX: LabelBMFont only supports 1 page");
  198. // file
  199. propertyValue = [nse nextObject];
  200. NSArray *array = [propertyValue componentsSeparatedByString:@"\""];
  201. propertyValue = [array objectAtIndex:1];
  202. NSAssert(propertyValue,@"LabelBMFont file could not be found");
  203. // Supports subdirectories
  204. NSString *dir = [fntFile stringByDeletingLastPathComponent];
  205. _atlasName = [dir stringByAppendingPathComponent:propertyValue];
  206. }
  207. -(void) parseInfoArguments:(NSString*)line
  208. {
  209. //
  210. // possible lines to parse:
  211. // 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
  212. // 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
  213. //
  214. NSArray *values = [line componentsSeparatedByString:@"="];
  215. NSEnumerator *nse = [values objectEnumerator];
  216. NSString *propertyValue = nil;
  217. // We need to move past the first entry in the array before we start assigning values
  218. [nse nextObject];
  219. // face (ignore)
  220. [nse nextObject];
  221. // size (ignore)
  222. [nse nextObject];
  223. // bold (ignore)
  224. [nse nextObject];
  225. // italic (ignore)
  226. [nse nextObject];
  227. // charset (ignore)
  228. [nse nextObject];
  229. // unicode (ignore)
  230. [nse nextObject];
  231. // strechH (ignore)
  232. [nse nextObject];
  233. // smooth (ignore)
  234. [nse nextObject];
  235. // aa (ignore)
  236. [nse nextObject];
  237. // padding (ignore)
  238. propertyValue = [nse nextObject];
  239. {
  240. NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","];
  241. NSEnumerator *paddingEnum = [paddingValues objectEnumerator];
  242. // padding top
  243. propertyValue = [paddingEnum nextObject];
  244. _padding.top = [propertyValue intValue];
  245. // padding right
  246. propertyValue = [paddingEnum nextObject];
  247. _padding.right = [propertyValue intValue];
  248. // padding bottom
  249. propertyValue = [paddingEnum nextObject];
  250. _padding.bottom = [propertyValue intValue];
  251. // padding left
  252. propertyValue = [paddingEnum nextObject];
  253. _padding.left = [propertyValue intValue];
  254. CCLOG(@"cocos2d: padding: %d,%d,%d,%d", _padding.left, _padding.top, _padding.right, _padding.bottom);
  255. }
  256. // spacing (ignore)
  257. [nse nextObject];
  258. }
  259. -(void) parseCommonArguments:(NSString*)line
  260. {
  261. //
  262. // line to parse:
  263. // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
  264. //
  265. NSArray *values = [line componentsSeparatedByString:@"="];
  266. NSEnumerator *nse = [values objectEnumerator];
  267. NSString *propertyValue = nil;
  268. // We need to move past the first entry in the array before we start assigning values
  269. [nse nextObject];
  270. // Character ID
  271. propertyValue = [nse nextObject];
  272. _commonHeight = [propertyValue intValue];
  273. // base (ignore)
  274. [nse nextObject];
  275. // scaleW. sanity check
  276. propertyValue = [nse nextObject];
  277. NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
  278. // scaleH. sanity check
  279. propertyValue = [nse nextObject];
  280. NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
  281. // pages. sanity check
  282. propertyValue = [nse nextObject];
  283. NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page");
  284. // packed (ignore) What does this mean ??
  285. }
  286. - (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition
  287. {
  288. // Break the values for this line up using =
  289. NSArray *values = [line componentsSeparatedByString:@"="];
  290. NSEnumerator *nse = [values objectEnumerator];
  291. NSString *propertyValue;
  292. // We need to move past the first entry in the array before we start assigning values
  293. [nse nextObject];
  294. // Character ID
  295. propertyValue = [nse nextObject];
  296. propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location];
  297. characterDefinition->charID = [propertyValue intValue];
  298. // Character x
  299. propertyValue = [nse nextObject];
  300. characterDefinition->rect.origin.x = [propertyValue intValue];
  301. // Character y
  302. propertyValue = [nse nextObject];
  303. characterDefinition->rect.origin.y = [propertyValue intValue];
  304. // Character width
  305. propertyValue = [nse nextObject];
  306. characterDefinition->rect.size.width = [propertyValue intValue];
  307. // Character height
  308. propertyValue = [nse nextObject];
  309. characterDefinition->rect.size.height = [propertyValue intValue];
  310. // Character xoffset
  311. propertyValue = [nse nextObject];
  312. characterDefinition->xOffset = [propertyValue intValue];
  313. // Character yoffset
  314. propertyValue = [nse nextObject];
  315. characterDefinition->yOffset = [propertyValue intValue];
  316. // Character xadvance
  317. propertyValue = [nse nextObject];
  318. characterDefinition->xAdvance = [propertyValue intValue];
  319. }
  320. -(void) parseKerningEntry:(NSString*) line
  321. {
  322. NSArray *values = [line componentsSeparatedByString:@"="];
  323. NSEnumerator *nse = [values objectEnumerator];
  324. NSString *propertyValue;
  325. // We need to move past the first entry in the array before we start assigning values
  326. [nse nextObject];
  327. // first
  328. propertyValue = [nse nextObject];
  329. int first = [propertyValue intValue];
  330. // second
  331. propertyValue = [nse nextObject];
  332. int second = [propertyValue intValue];
  333. // second
  334. propertyValue = [nse nextObject];
  335. int amount = [propertyValue intValue];
  336. tCCKerningHashElement *element = calloc( sizeof( *element ), 1 );
  337. element->amount = amount;
  338. element->key = (first<<16) | (second&0xffff);
  339. HASH_ADD_INT(_kerningDictionary,key, element);
  340. }
  341. @end
  342. #pragma mark -
  343. #pragma mark CCLabelBMFont
  344. @interface CCLabelBMFont ()
  345. -(int) kerningAmountForFirst:(unichar)first second:(unichar)second;
  346. -(void) updateLabel;
  347. -(void) setString:(NSString*) newString updateLabel:(BOOL)update;
  348. @end
  349. #pragma mark -
  350. #pragma mark CCLabelBMFont
  351. @implementation CCLabelBMFont {
  352. // The text displayed by the label.
  353. NSString *_string;
  354. // The font file of the text.
  355. NSString *_fntFile;
  356. // The original text excluding line breaks.
  357. NSString *_initialString;
  358. // The maximum width allowed before a line break will be inserted.
  359. float _width;
  360. // The technique used for horizontal aligning of the text.
  361. CCTextAlignment _alignment;
  362. // Parsed configuration of the font file.
  363. CCBMFontConfiguration *_configuration;
  364. // Offset of the texture atlas.
  365. CGPoint _imageOffset;
  366. // Reused char.
  367. CCSprite *_reusedChar;
  368. // Replacement for the old CCNode.tag property which was
  369. // used heavily in the original code.
  370. NSMutableArray *_childForTag;
  371. }
  372. @synthesize alignment = _alignment;
  373. #pragma mark LabelBMFont - Purge Cache
  374. +(void) purgeCachedData
  375. {
  376. FNTConfigRemoveCache();
  377. }
  378. #pragma mark LabelBMFont - Creation & Init
  379. +(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile
  380. {
  381. return [[self alloc] initWithString:string fntFile:fntFile width:kCCLabelAutomaticWidth alignment:CCTextAlignmentLeft imageOffset:CGPointZero];
  382. }
  383. +(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment
  384. {
  385. return [[self alloc] initWithString:string fntFile:fntFile width:width alignment:alignment imageOffset:CGPointZero];
  386. }
  387. +(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset
  388. {
  389. return [[self alloc] initWithString:string fntFile:fntFile width:width alignment:alignment imageOffset:offset];
  390. }
  391. -(id) init
  392. {
  393. return [self initWithString:nil fntFile:nil width:kCCLabelAutomaticWidth alignment:CCTextAlignmentLeft imageOffset:CGPointZero];
  394. }
  395. -(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile
  396. {
  397. return [self initWithString:theString fntFile:fntFile width:kCCLabelAutomaticWidth alignment:CCTextAlignmentLeft];
  398. }
  399. -(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment
  400. {
  401. return [self initWithString:theString fntFile:fntFile width:width alignment:alignment imageOffset:CGPointZero];
  402. }
  403. // designated initializer
  404. -(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset
  405. {
  406. NSAssert(!_configuration, @"re-init is no longer supported");
  407. // if theString && fntfile are both nil, then it is OK
  408. NSAssert( (theString && fntFile) || (theString==nil && fntFile==nil), @"Invalid params for CCLabelBMFont");
  409. CCTexture *texture = nil;
  410. CCBMFontConfiguration *newConf = nil;
  411. if( fntFile ) {
  412. newConf = FNTConfigLoadFile(fntFile);
  413. if(!newConf) {
  414. CCLOGWARN(@"cocos2d: WARNING. CCLabelBMFont: Impossible to create font. Please check file: '%@'", fntFile );
  415. return nil;
  416. }
  417. texture = [[CCTextureCache sharedTextureCache] addImage:newConf.atlasName];
  418. } else {
  419. texture = [[CCTexture alloc] init];
  420. }
  421. if((self = [super init])){
  422. if (fntFile){
  423. _configuration = newConf;
  424. _fntFile = [fntFile copy];
  425. }
  426. self.texture = texture;
  427. _width = width;
  428. _alignment = alignment;
  429. _displayColor = _color = [CCColor whiteColor].ccColor4f;
  430. _cascadeOpacityEnabled = YES;
  431. _cascadeColorEnabled = YES;
  432. _contentSize = CGSizeZero;
  433. _anchorPoint = ccp(0.5f, 0.5f);
  434. _imageOffset = offset;
  435. _reusedChar = [[CCSprite alloc] initWithTexture:self.texture rect:CGRectMake(0, 0, 0, 0) rotated:NO];
  436. _childForTag = [NSMutableArray array];
  437. [self setString:theString updateLabel:YES];
  438. }
  439. return self;
  440. }
  441. -(CCSprite *)childForTag:(NSUInteger)tag
  442. {
  443. if(tag < _childForTag.count){
  444. id child = _childForTag[tag];
  445. return (child == [NSNull null] ? nil : child);
  446. } else {
  447. return nil;
  448. }
  449. }
  450. -(void)setTag:(NSUInteger)tag forChild:(CCSprite *)child
  451. {
  452. if(tag < _childForTag.count){
  453. // Replace the value normally.
  454. [_childForTag replaceObjectAtIndex:tag withObject:child];
  455. } else {
  456. // The array is expanding.
  457. // Insert NSNull to fill holes if necessary since NSArray cannot be sparse.
  458. while(_childForTag.count < tag) [_childForTag addObject:[NSNull null]];
  459. [_childForTag addObject:child];
  460. }
  461. }
  462. #pragma mark LabelBMFont - Alignment
  463. - (void)updateLabel
  464. {
  465. [self setString:_initialString updateLabel:NO];
  466. if (_width > 0){
  467. //Step 1: Make multiline
  468. NSString *multilineString = @"", *lastWord = @"";
  469. int line = 1, i = 0;
  470. NSUInteger stringLength = [self.string length];
  471. float startOfLine = -1, startOfWord = -1;
  472. int skip = 0;
  473. //Go through each character and insert line breaks as necessary
  474. for (int j = 0; j < [_children count]; j++) {
  475. CCSprite *characterSprite;
  476. int justSkipped = 0;
  477. while(!(characterSprite = [self childForTag:j+skip+justSkipped]))
  478. justSkipped++;
  479. skip += justSkipped;
  480. if (!characterSprite.visible)
  481. continue;
  482. if (i >= stringLength || i < 0)
  483. break;
  484. unichar character = [self.string characterAtIndex:i];
  485. if (startOfWord == -1)
  486. startOfWord = characterSprite.position.x - characterSprite.contentSize.width/2;
  487. if (startOfLine == -1)
  488. startOfLine = startOfWord;
  489. //Character is a line break
  490. //Put lastWord on the current line and start a new line
  491. //Reset lastWord
  492. if ([[NSCharacterSet newlineCharacterSet] characterIsMember:character]) {
  493. lastWord = [lastWord stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  494. lastWord = [lastWord stringByPaddingToLength:[lastWord length] + justSkipped withString:[NSString stringWithFormat:@"%C", character] startingAtIndex:0];
  495. multilineString = [multilineString stringByAppendingString:lastWord];
  496. lastWord = @"";
  497. startOfWord = -1;
  498. line++;
  499. startOfLine = -1;
  500. i+=justSkipped;
  501. //CCLabelBMFont do not have a character for new lines, so do NOT "continue;" in the for loop. Process the next character
  502. if (i >= stringLength || i < 0)
  503. break;
  504. character = [self.string characterAtIndex:i];
  505. if (startOfWord == -1)
  506. startOfWord = characterSprite.position.x - characterSprite.contentSize.width/2;
  507. if (startOfLine == -1)
  508. startOfLine = startOfWord;
  509. }
  510. //Character is a whitespace
  511. //Put lastWord on current line and continue on current line
  512. //Reset lastWord
  513. if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember:character]) {
  514. lastWord = [lastWord stringByAppendingFormat:@"%C", character];
  515. multilineString = [multilineString stringByAppendingString:lastWord];
  516. lastWord = @"";
  517. startOfWord = -1;
  518. i++;
  519. continue;
  520. }
  521. //Character is out of bounds
  522. //Do not put lastWord on current line. Add "\n" to current line to start a new line
  523. //Append to lastWord
  524. if (characterSprite.position.x + characterSprite.contentSize.width/2 - startOfLine > _width) {
  525. NSString *trimmedString = [lastWord stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  526. lastWord = [[trimmedString stringByAppendingString:@"\n"] stringByAppendingFormat:@"%C", character];
  527. line++;
  528. startOfLine = -1;
  529. startOfWord = -1;
  530. i++;
  531. continue;
  532. } else {
  533. //Character is normal
  534. //Append to lastWord
  535. lastWord = [lastWord stringByAppendingFormat:@"%C", character];
  536. i++;
  537. continue;
  538. }
  539. }
  540. multilineString = [multilineString stringByAppendingFormat:@"%@", lastWord];
  541. [self setString:multilineString updateLabel:NO];
  542. }
  543. //Step 2: Make alignment
  544. if (self.alignment != CCTextAlignmentLeft) {
  545. int i = 0;
  546. //Number of spaces skipped
  547. int lineNumber = 0;
  548. //Go through line by line
  549. for (NSString *lineString in [_string componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]) {
  550. int lineWidth = 0;
  551. //Find index of last character in this line
  552. NSInteger index = i + [lineString length] - 1 + lineNumber;
  553. if (index < 0)
  554. continue;
  555. //Find position of last character on the line
  556. CCSprite *lastChar = [self childForTag:index];
  557. lineWidth = lastChar.position.x + lastChar.contentSize.width/2;
  558. //Figure out how much to shift each character in this line horizontally
  559. float shift = 0;
  560. switch (self.alignment) {
  561. case CCTextAlignmentCenter:
  562. shift = self.contentSize.width/2 - lineWidth/2;
  563. break;
  564. case CCTextAlignmentRight:
  565. shift = self.contentSize.width - lineWidth;
  566. default:
  567. break;
  568. }
  569. if (shift != 0) {
  570. int j = 0;
  571. //For each character, shift it so that the line is center aligned
  572. for (j = 0; j < [lineString length]; j++) {
  573. index = i + j + lineNumber;
  574. if (index < 0)
  575. continue;
  576. CCSprite *characterSprite = [self childForTag:index];
  577. characterSprite.position = ccpAdd(characterSprite.position, ccp(shift, 0));
  578. }
  579. }
  580. i += [lineString length];
  581. lineNumber++;
  582. }
  583. }
  584. }
  585. #pragma mark LabelBMFont - Atlas generation
  586. -(int) kerningAmountForFirst:(unichar)first second:(unichar)second
  587. {
  588. int ret = 0;
  589. unsigned int key = (first<<16) | (second & 0xffff);
  590. if( _configuration->_kerningDictionary ) {
  591. tCCKerningHashElement *element = NULL;
  592. HASH_FIND_INT(_configuration->_kerningDictionary, &key, element);
  593. if(element)
  594. ret = element->amount;
  595. }
  596. return ret;
  597. }
  598. -(void) createFontChars
  599. {
  600. NSInteger nextFontPositionX = 0;
  601. NSInteger nextFontPositionY = 0;
  602. unichar prev = -1;
  603. NSInteger kerningAmount = 0;
  604. CGSize tmpSize = CGSizeZero;
  605. NSInteger longestLine = 0;
  606. NSUInteger totalHeight = 0;
  607. NSUInteger quantityOfLines = 1;
  608. NSCharacterSet *charSet = _configuration.characterSet;
  609. NSUInteger stringLen = [_string length];
  610. if( ! stringLen )
  611. return;
  612. // quantity of lines NEEDS to be calculated before parsing the lines,
  613. // since the Y position needs to be calcualted before hand
  614. for(NSUInteger i=0; i < stringLen-1;i++) {
  615. unichar c = [_string characterAtIndex:i];
  616. if([[NSCharacterSet newlineCharacterSet] characterIsMember:c])
  617. quantityOfLines++;
  618. }
  619. totalHeight = _configuration->_commonHeight * quantityOfLines;
  620. nextFontPositionY = -(_configuration->_commonHeight - _configuration->_commonHeight*quantityOfLines);
  621. CGRect rect;
  622. ccBMFontDef fontDef = (ccBMFontDef){};
  623. CGFloat contentScale = 1.0/self.texture.contentScale;
  624. for(NSUInteger i = 0; i<stringLen; i++) {
  625. unichar c = [_string characterAtIndex:i];
  626. if ([[NSCharacterSet newlineCharacterSet] characterIsMember:c]) {
  627. nextFontPositionX = 0;
  628. nextFontPositionY -= _configuration->_commonHeight;
  629. continue;
  630. }
  631. if(![charSet characterIsMember:c]){
  632. CCLOGWARN(@"cocos2d: CCLabelBMFont: Attempted to use character not defined in this bitmap: %C", c);
  633. continue;
  634. }
  635. kerningAmount = [self kerningAmountForFirst:prev second:c];
  636. tCCFontDefHashElement *element = NULL;
  637. // unichar is a short, and an int is needed on HASH_FIND_INT
  638. NSUInteger key = (NSUInteger)c;
  639. HASH_FIND_INT(_configuration->_fontDefDictionary , &key, element);
  640. if( ! element ) {
  641. CCLOGWARN(@"cocos2d: CCLabelBMFont: characer not found %c", c);
  642. continue;
  643. }
  644. fontDef = element->fontDef;
  645. rect = CC_RECT_SCALE(fontDef.rect, contentScale);
  646. rect.origin.x += _imageOffset.x;
  647. rect.origin.y += _imageOffset.y;
  648. BOOL hasSprite = YES;
  649. CCSprite *fontChar = [self childForTag:i];
  650. if( fontChar )
  651. {
  652. // Reusing previous Sprite
  653. fontChar.visible = YES;
  654. }
  655. else
  656. {
  657. // New Sprite ? Set correct color, opacity, etc...
  658. // if( 0 ) {
  659. // /* WIP: Doesn't support many features yet.
  660. // But this code is super fast. It doesn't create any sprite.
  661. // Ideal for big labels.
  662. // */
  663. // fontChar = _reusedChar;
  664. // hasSprite = NO;
  665. // } else {
  666. fontChar = [[CCSprite alloc] initWithTexture:self.texture rect:rect];
  667. [self addChild:fontChar z:i];
  668. [self setTag:i forChild:fontChar];
  669. // }
  670. // Color MUST be set before opacity due to premultiplied alpha.
  671. [fontChar updateDisplayedColor:_displayColor];
  672. [fontChar updateDisplayedOpacity:_displayColor.a];
  673. }
  674. // updating previous sprite
  675. [fontChar setTextureRect:rect rotated:NO untrimmedSize:rect.size];
  676. // See issue 1343. cast( signed short + unsigned integer ) == unsigned integer (sign is lost!)
  677. NSInteger yOffset = _configuration->_commonHeight - fontDef.yOffset;
  678. CGPoint fontPos = ccp( (CGFloat)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount,
  679. (CGFloat)nextFontPositionY + yOffset - rect.size.height*0.5f * self.texture.contentScale );
  680. fontChar.position = ccpMult(fontPos, contentScale);
  681. // update kerning
  682. nextFontPositionX += fontDef.xAdvance + kerningAmount;
  683. prev = c;
  684. if (longestLine < nextFontPositionX)
  685. longestLine = nextFontPositionX;
  686. }
  687. // If the last character processed has an xAdvance which is less that the width of the characters image, then we need
  688. // to adjust the width of the string to take this into account, or the character will overlap the end of the bounding
  689. // box
  690. if (fontDef.xAdvance < fontDef.rect.size.width) {
  691. tmpSize.width = longestLine + fontDef.rect.size.width - fontDef.xAdvance;
  692. } else {
  693. tmpSize.width = longestLine;
  694. }
  695. tmpSize.height = totalHeight;
  696. [self setContentSize:CC_SIZE_SCALE(tmpSize, contentScale)];
  697. }
  698. #pragma mark LabelBMFont - CCLabelProtocol protocol
  699. -(NSString*) string
  700. {
  701. return _string;
  702. }
  703. -(void) setCString:(char*)label
  704. {
  705. [self setString:[NSString stringWithUTF8String:label] ];
  706. }
  707. - (void) setString:(NSString*)newString
  708. {
  709. [self setString:newString updateLabel:YES];
  710. }
  711. - (void) setString:(NSString*) newString updateLabel:(BOOL)update
  712. {
  713. if( !update ) {
  714. _string = [newString copy];
  715. } else {
  716. _initialString = [newString copy];
  717. }
  718. for (CCSprite* child in _children)
  719. child.visible = NO;
  720. [self createFontChars];
  721. if (update)
  722. [self updateLabel];
  723. }
  724. #pragma mark LabelBMFont - AnchorPoint
  725. -(void) setAnchorPoint:(CGPoint)point
  726. {
  727. if( ! CGPointEqualToPoint(point, _anchorPoint) ) {
  728. [super setAnchorPoint:point];
  729. [self createFontChars];
  730. }
  731. }
  732. #pragma mark LabelBMFont - Alignment
  733. - (void)setWidth:(float)width {
  734. _width = width;
  735. [self updateLabel];
  736. }
  737. - (void)setAlignment:(CCTextAlignment)alignment {
  738. _alignment = alignment;
  739. [self updateLabel];
  740. }
  741. #pragma mark LabelBMFont - FntFile
  742. - (void) setFntFile:(NSString*) fntFile
  743. {
  744. if( fntFile != _fntFile ) {
  745. CCBMFontConfiguration *newConf = FNTConfigLoadFile(fntFile);
  746. // Always throw this exception instead of NSAssert to let a consumer handle
  747. // errors gracefully in environments with disabled assertions(e.g. release builds).
  748. // Otherwise createFontChars can crash with a nasty segmentation fault.
  749. if (!newConf)
  750. {
  751. [NSException raise:@"Invalid font file" format:@"CCLabelBMFont: Impossible to create font. Please check file: '%@'", fntFile];
  752. }
  753. _fntFile = fntFile;
  754. _configuration = newConf;
  755. _childForTag = [NSMutableArray array];
  756. self.texture = [CCTexture textureWithFile:_configuration.atlasName];
  757. [self createFontChars];
  758. }
  759. }
  760. - (NSString*) fntFile
  761. {
  762. return _fntFile;
  763. }
  764. #pragma mark LabelBMFont - Debug draw
  765. #if CC_LABELBMFONT_DEBUG_DRAW
  766. -(void) draw
  767. {
  768. [super draw];
  769. CGSize s = [self contentSize];
  770. CGPoint vertices[4]={
  771. ccp(0,0),ccp(s.width,0),
  772. ccp(s.width,s.height),ccp(0,s.height),
  773. };
  774. ccDrawPoly(vertices, 4, YES);
  775. }
  776. #endif // CC_LABELBMFONT_DEBUG_DRAW
  777. @end