CCLabelBMFont.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. /****************************************************************************
  2. Copyright (c) 2008-2010 Ricardo Quesada
  3. Copyright (c) 2011-2012 cocos2d-x.org
  4. Copyright (c) 2013-2014 Chukong Technologies Inc.
  5. http://www.cocos2d-x.org
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. Use any of these editors to generate BMFonts:
  22. http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
  23. http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
  24. http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
  25. http://www.angelcode.com/products/bmfont/ (Free, Windows only)
  26. ****************************************************************************/
  27. /**
  28. * @constant
  29. * @type Number
  30. */
  31. cc.LABEL_AUTOMATIC_WIDTH = -1;
  32. /**
  33. * <p>cc.LabelBMFont is a subclass of cc.SpriteBatchNode.</p>
  34. *
  35. * <p>Features:<br/>
  36. * <ul><li>- Treats each character like a cc.Sprite. This means that each individual character can be:</li>
  37. * <li>- rotated</li>
  38. * <li>- scaled</li>
  39. * <li>- translated</li>
  40. * <li>- tinted</li>
  41. * <li>- chage the opacity</li>
  42. * <li>- It can be used as part of a menu item.</li>
  43. * <li>- anchorPoint can be used to align the "label"</li>
  44. * <li>- Supports AngelCode text format</li></ul></p>
  45. *
  46. * <p>Limitations:<br/>
  47. * - All inner characters are using an anchorPoint of (0.5, 0.5) and it is not recommend to change it
  48. * because it might affect the rendering</p>
  49. *
  50. * <p>cc.LabelBMFont implements the protocol cc.LabelProtocol, like cc.Label and cc.LabelAtlas.<br/>
  51. * cc.LabelBMFont has the flexibility of cc.Label, the speed of cc.LabelAtlas and all the features of cc.Sprite.<br/>
  52. * If in doubt, use cc.LabelBMFont instead of cc.LabelAtlas / cc.Label.</p>
  53. *
  54. * <p>Supported editors:<br/>
  55. * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)<br/>
  56. * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)<br/>
  57. * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)<br/>
  58. * http://www.angelcode.com/products/bmfont/ (Free, Windows only)</p>
  59. * @class
  60. * @extends cc.SpriteBatchNode
  61. *
  62. * @property {String} string - Content string of label
  63. * @property {Number} textAlign - Horizontal Alignment of label, cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT
  64. * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth
  65. *
  66. * @param {String} str
  67. * @param {String} fntFile
  68. * @param {Number} [width=-1]
  69. * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT]
  70. * @param {cc.Point} [imageOffset=cc.p(0,0)]
  71. *
  72. * @example
  73. * // Example 01
  74. * var label1 = new cc.LabelBMFont("Test case", "test.fnt");
  75. *
  76. * // Example 02
  77. * var label2 = new cc.LabelBMFont("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT);
  78. *
  79. * // Example 03
  80. * var label3 = new cc.LabelBMFont("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.p(0,0));
  81. */
  82. cc.LabelBMFont = cc.SpriteBatchNode.extend(/** @lends cc.LabelBMFont# */{
  83. //property string is Getter and Setter.
  84. //property textAlign is Getter and Setter.
  85. //property boundingWidth is Getter and Setter.
  86. _opacityModifyRGB: false,
  87. _string: "",
  88. _config: null,
  89. // name of fntFile
  90. _fntFile: "",
  91. // initial string without line breaks
  92. _initialString: "",
  93. // alignment of all lines
  94. _alignment: cc.TEXT_ALIGNMENT_CENTER,
  95. // max width until a line break is added
  96. _width: -1,
  97. _lineBreakWithoutSpaces: false,
  98. _imageOffset: null,
  99. _reusedChar: null,
  100. //texture RGBA
  101. _displayedOpacity: 255,
  102. _realOpacity: 255,
  103. _displayedColor: null,
  104. _realColor: null,
  105. _cascadeColorEnabled: true,
  106. _cascadeOpacityEnabled: true,
  107. _textureLoaded: false,
  108. _loadedEventListeners: null,
  109. _className: "LabelBMFont",
  110. _setString: function (newString, needUpdateLabel) {
  111. if (!needUpdateLabel) {
  112. this._string = newString;
  113. } else {
  114. this._initialString = newString;
  115. }
  116. var locChildren = this._children;
  117. if (locChildren) {
  118. for (var i = 0; i < locChildren.length; i++) {
  119. var selNode = locChildren[i];
  120. if (selNode)
  121. selNode.setVisible(false);
  122. }
  123. }
  124. if (this._textureLoaded) {
  125. this.createFontChars();
  126. if (needUpdateLabel)
  127. this.updateLabel();
  128. }
  129. },
  130. /**
  131. * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
  132. * creates a bitmap font atlas with an initial string and the FNT file.
  133. * @param {String} str
  134. * @param {String} fntFile
  135. * @param {Number} [width=-1]
  136. * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT]
  137. * @param {cc.Point} [imageOffset=cc.p(0,0)]
  138. */
  139. ctor: function (str, fntFile, width, alignment, imageOffset) {
  140. var self = this;
  141. cc.SpriteBatchNode.prototype.ctor.call(self);
  142. self._imageOffset = cc.p(0, 0);
  143. self._displayedColor = cc.color(255, 255, 255, 255);
  144. self._realColor = cc.color(255, 255, 255, 255);
  145. self._reusedChar = [];
  146. this.initWithString(str, fntFile, width, alignment, imageOffset);
  147. },
  148. /**
  149. * return texture is loaded
  150. * @returns {boolean}
  151. */
  152. textureLoaded: function () {
  153. return this._textureLoaded;
  154. },
  155. /**
  156. * add texture loaded event listener. <br />
  157. * Will execute the callback in the loaded.
  158. * @param {Function} callback
  159. * @param {Object} target
  160. */
  161. addLoadedEventListener: function (callback, target) {
  162. if (!this._loadedEventListeners)
  163. this._loadedEventListeners = [];
  164. this._loadedEventListeners.push({eventCallback: callback, eventTarget: target});
  165. },
  166. _callLoadedEventCallbacks: function () {
  167. if (!this._loadedEventListeners)
  168. return;
  169. var locListeners = this._loadedEventListeners;
  170. for (var i = 0, len = locListeners.length; i < len; i++) {
  171. var selCallback = locListeners[i];
  172. selCallback.eventCallback.call(selCallback.eventTarget, this);
  173. }
  174. locListeners.length = 0;
  175. },
  176. /**
  177. * Draw this font.
  178. * @param {CanvasRenderingContext2D} ctx
  179. */
  180. draw: function (ctx) {
  181. cc.SpriteBatchNode.prototype.draw.call(this, ctx);
  182. //LabelBMFont - Debug draw
  183. if (cc.LABELBMFONT_DEBUG_DRAW) {
  184. var size = this.getContentSize();
  185. var pos = cc.p(0 | ( -this._anchorPointInPoints.x), 0 | ( -this._anchorPointInPoints.y));
  186. var vertices = [cc.p(pos.x, pos.y), cc.p(pos.x + size.width, pos.y), cc.p(pos.x + size.width, pos.y + size.height), cc.p(pos.x, pos.y + size.height)];
  187. cc._drawingUtil.setDrawColor(0, 255, 0, 255);
  188. cc._drawingUtil.drawPoly(vertices, 4, true);
  189. }
  190. },
  191. //TODO
  192. /**
  193. * tint this label
  194. * @param {cc.Color} color
  195. */
  196. setColor: function (color) {
  197. var locDisplayed = this._displayedColor, locRealColor = this._realColor;
  198. if ((locRealColor.r == color.r) && (locRealColor.g == color.g) && (locRealColor.b == color.b) && (locRealColor.a == color.a))
  199. return;
  200. locDisplayed.r = locRealColor.r = color.r;
  201. locDisplayed.g = locRealColor.g = color.g;
  202. locDisplayed.b = locRealColor.b = color.b;
  203. if (this._textureLoaded) {
  204. if (this._cascadeColorEnabled) {
  205. var parentColor = cc.color.WHITE;
  206. var locParent = this._parent;
  207. if (locParent && locParent.cascadeColor)
  208. parentColor = locParent.getDisplayedColor();
  209. this.updateDisplayedColor(parentColor);
  210. }
  211. }
  212. },
  213. /**
  214. * Conforms to cc.RGBAProtocol protocol.
  215. * @return {Boolean}
  216. */
  217. isOpacityModifyRGB: function () {
  218. return this._opacityModifyRGB;
  219. },
  220. /**
  221. * Set whether to support cc.RGBAProtocol protocol
  222. * @param {Boolean} opacityModifyRGB
  223. */
  224. setOpacityModifyRGB: function (opacityModifyRGB) {
  225. this._opacityModifyRGB = opacityModifyRGB;
  226. var locChildren = this._children;
  227. if (locChildren) {
  228. for (var i = 0; i < locChildren.length; i++) {
  229. var node = locChildren[i];
  230. if (node)
  231. node.opacityModifyRGB = this._opacityModifyRGB;
  232. }
  233. }
  234. },
  235. /**
  236. * Gets the real opacity.
  237. * @returns {number}
  238. */
  239. getOpacity: function () {
  240. return this._realOpacity;
  241. },
  242. /**
  243. * Gets the display opacity.
  244. * @returns {number}
  245. */
  246. getDisplayedOpacity: function () {
  247. return this._displayedOpacity;
  248. },
  249. /**
  250. * Override synthesized setOpacity to recurse items
  251. * @param {Number} opacity
  252. */
  253. setOpacity: function (opacity) {
  254. this._displayedOpacity = this._realOpacity = opacity;
  255. if (this._cascadeOpacityEnabled) {
  256. var parentOpacity = 255;
  257. var locParent = this._parent;
  258. if (locParent && locParent.cascadeOpacity)
  259. parentOpacity = locParent.getDisplayedOpacity();
  260. this.updateDisplayedOpacity(parentOpacity);
  261. }
  262. this._displayedColor.a = this._realColor.a = opacity;
  263. },
  264. /**
  265. * Override synthesized update pacity to recurse items
  266. * @param parentOpacity
  267. */
  268. updateDisplayedOpacity: function (parentOpacity) {
  269. this._displayedOpacity = this._realOpacity * parentOpacity / 255.0;
  270. var locChildren = this._children;
  271. for (var i = 0; i < locChildren.length; i++) {
  272. var locChild = locChildren[i];
  273. if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
  274. locChild.updateDisplayedOpacity(this._displayedOpacity);
  275. } else {
  276. cc.Node.prototype.updateDisplayedOpacity.call(locChild, this._displayedOpacity);
  277. locChild.setNodeDirty();
  278. }
  279. }
  280. this._changeTextureColor();
  281. },
  282. /**
  283. * Checking cascade opacity enabled
  284. * @returns {boolean}
  285. */
  286. isCascadeOpacityEnabled: function () {
  287. return false;
  288. },
  289. /**
  290. * Set cascade opacity enabled
  291. * @param {Boolean} cascadeOpacityEnabled
  292. */
  293. setCascadeOpacityEnabled: function (cascadeOpacityEnabled) {
  294. this._cascadeOpacityEnabled = cascadeOpacityEnabled;
  295. },
  296. /**
  297. * Gets the real color. <br />
  298. * Create a new cc.Color clone in this real color.
  299. * @returns {cc.Color}
  300. */
  301. getColor: function () {
  302. var locRealColor = this._realColor;
  303. return cc.color(locRealColor.r, locRealColor.g, locRealColor.b, locRealColor.a);
  304. },
  305. /**
  306. * Gets the display color. <br />
  307. * Create a new cc.Color clone in this display color.
  308. * @returns {cc.Color}
  309. */
  310. getDisplayedColor: function () {
  311. var dc = this._displayedColor;
  312. return cc.color(dc.r, dc.g, dc.b, dc.a);
  313. },
  314. /**
  315. * Update the display color. <br />
  316. * Only update this label display color.
  317. * @returns {cc.Color}
  318. */
  319. updateDisplayedColor: function (parentColor) {
  320. var locDispColor = this._displayedColor;
  321. var locRealColor = this._realColor;
  322. locDispColor.r = locRealColor.r * parentColor.r / 255.0;
  323. locDispColor.g = locRealColor.g * parentColor.g / 255.0;
  324. locDispColor.b = locRealColor.b * parentColor.b / 255.0;
  325. var locChildren = this._children;
  326. for (var i = 0; i < locChildren.length; i++) {
  327. var locChild = locChildren[i];
  328. if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
  329. locChild.updateDisplayedColor(this._displayedColor);
  330. } else {
  331. cc.Node.prototype.updateDisplayedColor.call(locChild, this._displayedColor);
  332. locChild.setNodeDirty();
  333. }
  334. }
  335. this._changeTextureColor();
  336. },
  337. _changeTextureColor: function () {
  338. if (cc._renderType == cc._RENDER_TYPE_WEBGL)
  339. return;
  340. var locTexture = this.getTexture();
  341. if (locTexture && locTexture.getContentSize().width>0) {
  342. var element = this._originalTexture.getHtmlElementObj();
  343. if(!element)
  344. return;
  345. var locElement = locTexture.getHtmlElementObj();
  346. var textureRect = cc.rect(0, 0, element.width, element.height);
  347. if (locElement instanceof HTMLCanvasElement && !this._rectRotated){
  348. cc.generateTintImageWithMultiply(element, this._displayedColor, textureRect, locElement);
  349. this.setTexture(locTexture);
  350. } else {
  351. locElement = cc.generateTintImageWithMultiply(element, this._displayedColor, textureRect);
  352. locTexture = new cc.Texture2D();
  353. locTexture.initWithElement(locElement);
  354. locTexture.handleLoadedTexture();
  355. this.setTexture(locTexture);
  356. }
  357. }
  358. },
  359. /**
  360. * Checking cascade color enabled
  361. * @returns {boolean}
  362. */
  363. isCascadeColorEnabled: function () {
  364. return false;
  365. },
  366. /**
  367. * Override synthesized setOpacity to recurse items
  368. * @param {Boolean} cascadeColorEnabled
  369. */
  370. setCascadeColorEnabled: function (cascadeColorEnabled) {
  371. this._cascadeColorEnabled = cascadeColorEnabled;
  372. },
  373. /**
  374. * Initialization of the node, please do not call this function by yourself, you should pass the parameters to constructor to initialize it.
  375. */
  376. init: function () {
  377. return this.initWithString(null, null, null, null, null);
  378. },
  379. /**
  380. * init a bitmap font atlas with an initial string and the FNT file
  381. * @param {String} str
  382. * @param {String} fntFile
  383. * @param {Number} [width=-1]
  384. * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT]
  385. * @param {cc.Point} [imageOffset=cc.p(0,0)]
  386. * @return {Boolean}
  387. */
  388. initWithString: function (str, fntFile, width, alignment, imageOffset) {
  389. var self = this, theString = str || "";
  390. if (self._config)
  391. cc.log("cc.LabelBMFont.initWithString(): re-init is no longer supported");
  392. var texture;
  393. if (fntFile) {
  394. var newConf = cc.loader.getRes(fntFile);
  395. if (!newConf) {
  396. cc.log("cc.LabelBMFont.initWithString(): Impossible to create font. Please check file");
  397. return false;
  398. }
  399. self._config = newConf;
  400. self._fntFile = fntFile;
  401. texture = cc.textureCache.addImage(newConf.atlasName);
  402. var locIsLoaded = texture.isLoaded();
  403. self._textureLoaded = locIsLoaded;
  404. if (!locIsLoaded) {
  405. texture.addLoadedEventListener(function (sender) {
  406. var self1 = this;
  407. self1._textureLoaded = true;
  408. //reset the LabelBMFont
  409. self1.initWithTexture(sender, self1._initialString.length);
  410. self1.setString(self1._initialString, true);
  411. self1._callLoadedEventCallbacks();
  412. }, self);
  413. }
  414. } else {
  415. texture = new cc.Texture2D();
  416. var image = new Image();
  417. texture.initWithElement(image);
  418. self._textureLoaded = false;
  419. }
  420. if (self.initWithTexture(texture, theString.length)) {
  421. self._alignment = alignment || cc.TEXT_ALIGNMENT_LEFT;
  422. self._imageOffset = imageOffset || cc.p(0, 0);
  423. self._width = (width == null) ? -1 : width;
  424. self._displayedOpacity = self._realOpacity = 255;
  425. self._displayedColor = cc.color(255, 255, 255, 255);
  426. self._realColor = cc.color(255, 255, 255, 255);
  427. self._cascadeOpacityEnabled = true;
  428. self._cascadeColorEnabled = true;
  429. self._contentSize.width = 0;
  430. self._contentSize.height = 0;
  431. self.setAnchorPoint(0.5, 0.5);
  432. if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
  433. var locTexture = self.textureAtlas.texture;
  434. self._opacityModifyRGB = locTexture.hasPremultipliedAlpha();
  435. var reusedChar = self._reusedChar = new cc.Sprite();
  436. reusedChar.initWithTexture(locTexture, cc.rect(0, 0, 0, 0), false);
  437. reusedChar.batchNode = self;
  438. }
  439. self.setString(theString, true);
  440. return true;
  441. }
  442. return false;
  443. },
  444. /**
  445. * updates the font chars based on the string to render
  446. */
  447. createFontChars: function () {
  448. var self = this;
  449. var locContextType = cc._renderType;
  450. var locTexture = (locContextType === cc._RENDER_TYPE_CANVAS) ? self.texture : self.textureAtlas.texture;
  451. var nextFontPositionX = 0;
  452. var tmpSize = cc.size(0, 0);
  453. var longestLine = 0;
  454. var quantityOfLines = 1;
  455. var locStr = self._string;
  456. var stringLen = locStr ? locStr.length : 0;
  457. if (stringLen === 0)
  458. return;
  459. var i, locCfg = self._config, locKerningDict = locCfg.kerningDict,
  460. locCommonH = locCfg.commonHeight, locFontDict = locCfg.fontDefDictionary;
  461. for (i = 0; i < stringLen - 1; i++) {
  462. if (locStr.charCodeAt(i) == 10) quantityOfLines++;
  463. }
  464. var totalHeight = locCommonH * quantityOfLines;
  465. var nextFontPositionY = -(locCommonH - locCommonH * quantityOfLines);
  466. var prev = -1;
  467. for (i = 0; i < stringLen; i++) {
  468. var key = locStr.charCodeAt(i);
  469. if (key == 0) continue;
  470. if (key === 10) {
  471. //new line
  472. nextFontPositionX = 0;
  473. nextFontPositionY -= locCfg.commonHeight;
  474. continue;
  475. }
  476. var kerningAmount = locKerningDict[(prev << 16) | (key & 0xffff)] || 0;
  477. var fontDef = locFontDict[key];
  478. if (!fontDef) {
  479. cc.log("cocos2d: LabelBMFont: character not found " + locStr[i]);
  480. continue;
  481. }
  482. var rect = cc.rect(fontDef.rect.x, fontDef.rect.y, fontDef.rect.width, fontDef.rect.height);
  483. rect = cc.rectPixelsToPoints(rect);
  484. rect.x += self._imageOffset.x;
  485. rect.y += self._imageOffset.y;
  486. var fontChar = self.getChildByTag(i);
  487. //var hasSprite = true;
  488. if (!fontChar) {
  489. fontChar = new cc.Sprite();
  490. if ((key === 32) && (locContextType === cc._RENDER_TYPE_CANVAS)) rect = cc.rect(0, 0, 0, 0);
  491. fontChar.initWithTexture(locTexture, rect, false);
  492. fontChar._newTextureWhenChangeColor = true;
  493. self.addChild(fontChar, 0, i);
  494. } else {
  495. if ((key === 32) && (locContextType === cc._RENDER_TYPE_CANVAS)) {
  496. fontChar.setTextureRect(rect, false, cc.size(0, 0));
  497. } else {
  498. // updating previous sprite
  499. fontChar.setTextureRect(rect, false);
  500. // restore to default in case they were modified
  501. fontChar.visible = true;
  502. }
  503. }
  504. // Apply label properties
  505. fontChar.opacityModifyRGB = self._opacityModifyRGB;
  506. // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
  507. if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
  508. fontChar.updateDisplayedColor(self._displayedColor);
  509. fontChar.updateDisplayedOpacity(self._displayedOpacity);
  510. } else {
  511. cc.Node.prototype.updateDisplayedColor.call(fontChar, self._displayedColor);
  512. cc.Node.prototype.updateDisplayedOpacity.call(fontChar, self._displayedOpacity);
  513. fontChar.setNodeDirty();
  514. }
  515. var yOffset = locCfg.commonHeight - fontDef.yOffset;
  516. var fontPos = cc.p(nextFontPositionX + fontDef.xOffset + fontDef.rect.width * 0.5 + kerningAmount,
  517. nextFontPositionY + yOffset - rect.height * 0.5 * cc.contentScaleFactor());
  518. fontChar.setPosition(cc.pointPixelsToPoints(fontPos));
  519. // update kerning
  520. nextFontPositionX += fontDef.xAdvance + kerningAmount;
  521. prev = key;
  522. if (longestLine < nextFontPositionX)
  523. longestLine = nextFontPositionX;
  524. }
  525. tmpSize.width = longestLine;
  526. tmpSize.height = totalHeight;
  527. self.setContentSize(cc.sizePixelsToPoints(tmpSize));
  528. },
  529. /**
  530. * Update String. <br />
  531. * Only update this label displa string.
  532. * @param {Boolean} fromUpdate
  533. */
  534. updateString: function (fromUpdate) {
  535. var self = this;
  536. var locChildren = self._children;
  537. if (locChildren) {
  538. for (var i = 0, li = locChildren.length; i < li; i++) {
  539. var node = locChildren[i];
  540. if (node) node.visible = false;
  541. }
  542. }
  543. if (self._config)
  544. self.createFontChars();
  545. if (!fromUpdate)
  546. self.updateLabel();
  547. },
  548. /**
  549. * Gets the text of this label
  550. * @return {String}
  551. */
  552. getString: function () {
  553. return this._initialString;
  554. },
  555. /**
  556. * Set the text
  557. * @param {String} newString
  558. * @param {Boolean|null} needUpdateLabel
  559. */
  560. setString: function (newString, needUpdateLabel) {
  561. newString = String(newString);
  562. if (needUpdateLabel == null)
  563. needUpdateLabel = true;
  564. if (newString == null || typeof(newString) != "string")
  565. newString = newString + "";
  566. this._initialString = newString;
  567. this._setString(newString, needUpdateLabel);
  568. },
  569. _setStringForSetter: function (newString) {
  570. this.setString(newString, false);
  571. },
  572. /**
  573. * Set the text. <br />
  574. * Change this Label display string.
  575. * @deprecated since v3.0 please use .setString
  576. * @param label
  577. */
  578. setCString: function (label) {
  579. this.setString(label, true);
  580. },
  581. /**
  582. * Update Label. <br />
  583. * Update this Label display string and more...
  584. */
  585. updateLabel: function () {
  586. var self = this;
  587. self.string = self._initialString;
  588. // Step 1: Make multiline
  589. if (self._width > 0) {
  590. var stringLength = self._string.length;
  591. var multiline_string = [];
  592. var last_word = [];
  593. var line = 1, i = 0, start_line = false, start_word = false, startOfLine = -1, startOfWord = -1, skip = 0;
  594. var characterSprite;
  595. for (var j = 0, lj = self._children.length; j < lj; j++) {
  596. var justSkipped = 0;
  597. while (!(characterSprite = self.getChildByTag(j + skip + justSkipped)))
  598. justSkipped++;
  599. skip += justSkipped;
  600. if (i >= stringLength)
  601. break;
  602. var character = self._string[i];
  603. if (!start_word) {
  604. startOfWord = self._getLetterPosXLeft(characterSprite);
  605. start_word = true;
  606. }
  607. if (!start_line) {
  608. startOfLine = startOfWord;
  609. start_line = true;
  610. }
  611. // Newline.
  612. if (character.charCodeAt(0) == 10) {
  613. last_word.push('\n');
  614. multiline_string = multiline_string.concat(last_word);
  615. last_word.length = 0;
  616. start_word = false;
  617. start_line = false;
  618. startOfWord = -1;
  619. startOfLine = -1;
  620. //i+= justSkipped;
  621. j--;
  622. skip -= justSkipped;
  623. line++;
  624. if (i >= stringLength)
  625. break;
  626. character = self._string[i];
  627. if (!startOfWord) {
  628. startOfWord = self._getLetterPosXLeft(characterSprite);
  629. start_word = true;
  630. }
  631. if (!startOfLine) {
  632. startOfLine = startOfWord;
  633. start_line = true;
  634. }
  635. i++;
  636. continue;
  637. }
  638. // Whitespace.
  639. if (this._isspace_unicode(character)) {
  640. last_word.push(character);
  641. multiline_string = multiline_string.concat(last_word);
  642. last_word.length = 0;
  643. start_word = false;
  644. startOfWord = -1;
  645. i++;
  646. continue;
  647. }
  648. // Out of bounds.
  649. if (self._getLetterPosXRight(characterSprite) - startOfLine > self._width) {
  650. if (!self._lineBreakWithoutSpaces) {
  651. last_word.push(character);
  652. var found = multiline_string.lastIndexOf(" ");
  653. if (found != -1)
  654. this._utf8_trim_ws(multiline_string);
  655. else
  656. multiline_string = [];
  657. if (multiline_string.length > 0)
  658. multiline_string.push('\n');
  659. line++;
  660. start_line = false;
  661. startOfLine = -1;
  662. i++;
  663. } else {
  664. this._utf8_trim_ws(last_word);
  665. last_word.push('\n');
  666. multiline_string = multiline_string.concat(last_word);
  667. last_word.length = 0;
  668. start_word = false;
  669. start_line = false;
  670. startOfWord = -1;
  671. startOfLine = -1;
  672. line++;
  673. if (i >= stringLength)
  674. break;
  675. if (!startOfWord) {
  676. startOfWord = self._getLetterPosXLeft(characterSprite);
  677. start_word = true;
  678. }
  679. if (!startOfLine) {
  680. startOfLine = startOfWord;
  681. start_line = true;
  682. }
  683. j--;
  684. }
  685. } else {
  686. // Character is normal.
  687. last_word.push(character);
  688. i++;
  689. }
  690. }
  691. multiline_string = multiline_string.concat(last_word);
  692. var len = multiline_string.length;
  693. var str_new = "";
  694. for (i = 0; i < len; ++i)
  695. str_new += multiline_string[i];
  696. str_new = str_new + String.fromCharCode(0);
  697. //this.updateString(true);
  698. self._setString(str_new, false)
  699. }
  700. // Step 2: Make alignment
  701. if (self._alignment != cc.TEXT_ALIGNMENT_LEFT) {
  702. i = 0;
  703. var lineNumber = 0;
  704. var strlen = self._string.length;
  705. var last_line = [];
  706. for (var ctr = 0; ctr < strlen; ctr++) {
  707. if (self._string[ctr].charCodeAt(0) == 10 || self._string[ctr].charCodeAt(0) == 0) {
  708. var lineWidth = 0;
  709. var line_length = last_line.length;
  710. // if last line is empty we must just increase lineNumber and work with next line
  711. if (line_length == 0) {
  712. lineNumber++;
  713. continue;
  714. }
  715. var index = i + line_length - 1 + lineNumber;
  716. if (index < 0) continue;
  717. var lastChar = self.getChildByTag(index);
  718. if (lastChar == null)
  719. continue;
  720. lineWidth = lastChar.getPositionX() + lastChar._getWidth() / 2;
  721. var shift = 0;
  722. switch (self._alignment) {
  723. case cc.TEXT_ALIGNMENT_CENTER:
  724. shift = self.width / 2 - lineWidth / 2;
  725. break;
  726. case cc.TEXT_ALIGNMENT_RIGHT:
  727. shift = self.width - lineWidth;
  728. break;
  729. default:
  730. break;
  731. }
  732. if (shift != 0) {
  733. for (j = 0; j < line_length; j++) {
  734. index = i + j + lineNumber;
  735. if (index < 0) continue;
  736. characterSprite = self.getChildByTag(index);
  737. if (characterSprite)
  738. characterSprite.x += shift;
  739. }
  740. }
  741. i += line_length;
  742. lineNumber++;
  743. last_line.length = 0;
  744. continue;
  745. }
  746. last_line.push(self._string[i]);
  747. }
  748. }
  749. },
  750. /**
  751. * Set text alignment.
  752. * @param {Number} alignment
  753. */
  754. setAlignment: function (alignment) {
  755. this._alignment = alignment;
  756. this.updateLabel();
  757. },
  758. _getAlignment: function () {
  759. return this._alignment;
  760. },
  761. /**
  762. * Set the bounding width. <br />
  763. * max with display width. The exceeding string will be wrapping.
  764. * @param {Number} width
  765. */
  766. setBoundingWidth: function (width) {
  767. this._width = width;
  768. this.updateLabel();
  769. },
  770. _getBoundingWidth: function () {
  771. return this._width;
  772. },
  773. /**
  774. * Set the param to change English word warp according to whether the space. <br />
  775. * default is false.
  776. * @param {Boolean} breakWithoutSpace
  777. */
  778. setLineBreakWithoutSpace: function (breakWithoutSpace) {
  779. this._lineBreakWithoutSpaces = breakWithoutSpace;
  780. this.updateLabel();
  781. },
  782. /**
  783. * Set scale. <br />
  784. * Input a number, will be decrease or increase the font size. <br />
  785. * @param {Number} scale
  786. * @param {Number} [scaleY=null] default is scale
  787. */
  788. setScale: function (scale, scaleY) {
  789. cc.Node.prototype.setScale.call(this, scale, scaleY);
  790. this.updateLabel();
  791. },
  792. /**
  793. * Set scale of x. <br />
  794. * Input a number, will be decrease or increase the font size. <br />
  795. * Horizontal scale.
  796. * @param {Number} scaleX
  797. */
  798. setScaleX: function (scaleX) {
  799. cc.Node.prototype.setScaleX.call(this, scaleX);
  800. this.updateLabel();
  801. },
  802. /**
  803. * Set scale of x. <br />
  804. * Input a number, will be decrease or increase the font size. <br />
  805. * Longitudinal scale.
  806. * @param {Number} scaleY
  807. */
  808. setScaleY: function (scaleY) {
  809. cc.Node.prototype.setScaleY.call(this, scaleY);
  810. this.updateLabel();
  811. },
  812. //TODO
  813. /**
  814. * set fnt file path. <br />
  815. * Change the fnt file path.
  816. * @param {String} fntFile
  817. */
  818. setFntFile: function (fntFile) {
  819. var self = this;
  820. if (fntFile != null && fntFile != self._fntFile) {
  821. var newConf = cc.loader.getRes(fntFile);
  822. if (!newConf) {
  823. cc.log("cc.LabelBMFont.setFntFile() : Impossible to create font. Please check file");
  824. return;
  825. }
  826. self._fntFile = fntFile;
  827. self._config = newConf;
  828. var texture = cc.textureCache.addImage(newConf.atlasName);
  829. var locIsLoaded = texture.isLoaded();
  830. self._textureLoaded = locIsLoaded;
  831. self.texture = texture;
  832. if (cc._renderType === cc._RENDER_TYPE_CANVAS)
  833. self._originalTexture = self.texture;
  834. if (!locIsLoaded) {
  835. texture.addLoadedEventListener(function (sender) {
  836. var self1 = this;
  837. self1._textureLoaded = true;
  838. self1.texture = sender;
  839. self1.createFontChars();
  840. self1._changeTextureColor();
  841. self1.updateLabel();
  842. self1._callLoadedEventCallbacks();
  843. }, self);
  844. } else {
  845. self.createFontChars();
  846. }
  847. }
  848. },
  849. /**
  850. * Return the fnt file path.
  851. * @return {String}
  852. */
  853. getFntFile: function () {
  854. return this._fntFile;
  855. },
  856. /**
  857. * Set the AnchorPoint of the labelBMFont. <br />
  858. * In order to change the location of label.
  859. * @override
  860. * @param {cc.Point|Number} point The anchor point of labelBMFont or The anchor point.x of labelBMFont.
  861. * @param {Number} [y] The anchor point.y of labelBMFont.
  862. */
  863. setAnchorPoint: function (point, y) {
  864. cc.Node.prototype.setAnchorPoint.call(this, point, y);
  865. this.updateLabel();
  866. },
  867. _setAnchor: function (p) {
  868. cc.Node.prototype._setAnchor.call(this, p);
  869. this.updateLabel();
  870. },
  871. _setAnchorX: function (x) {
  872. cc.Node.prototype._setAnchorX.call(this, x);
  873. this.updateLabel();
  874. },
  875. _setAnchorY: function (y) {
  876. cc.Node.prototype._setAnchorY.call(this, y);
  877. this.updateLabel();
  878. },
  879. _atlasNameFromFntFile: function (fntFile) {},
  880. _kerningAmountForFirst: function (first, second) {
  881. var ret = 0;
  882. var key = (first << 16) | (second & 0xffff);
  883. if (this._configuration.kerningDictionary) {
  884. var element = this._configuration.kerningDictionary[key.toString()];
  885. if (element)
  886. ret = element.amount;
  887. }
  888. return ret;
  889. },
  890. _getLetterPosXLeft: function (sp) {
  891. return sp.getPositionX() * this._scaleX - (sp._getWidth() * this._scaleX * sp._getAnchorX());
  892. },
  893. _getLetterPosXRight: function (sp) {
  894. return sp.getPositionX() * this._scaleX + (sp._getWidth() * this._scaleX * sp._getAnchorX());
  895. },
  896. //Checking whether the character is a whitespace
  897. _isspace_unicode: function(ch){
  898. ch = ch.charCodeAt(0);
  899. return ((ch >= 9 && ch <= 13) || ch == 32 || ch == 133 || ch == 160 || ch == 5760
  900. || (ch >= 8192 && ch <= 8202) || ch == 8232 || ch == 8233 || ch == 8239
  901. || ch == 8287 || ch == 12288)
  902. },
  903. _utf8_trim_ws: function(str){
  904. var len = str.length;
  905. if (len <= 0)
  906. return;
  907. var last_index = len - 1;
  908. // Only start trimming if the last character is whitespace..
  909. if (this._isspace_unicode(str[last_index])) {
  910. for (var i = last_index - 1; i >= 0; --i) {
  911. if (this._isspace_unicode(str[i])) {
  912. last_index = i;
  913. }
  914. else {
  915. break;
  916. }
  917. }
  918. this._utf8_trim_from(str, last_index);
  919. }
  920. },
  921. //Trims str st str=[0, index) after the operation.
  922. //Return value: the trimmed string.
  923. _utf8_trim_from: function(str, index){
  924. var len = str.length;
  925. if (index >= len || index < 0)
  926. return;
  927. str.splice(index, len);
  928. }
  929. });
  930. var _p = cc.LabelBMFont.prototype;
  931. if(cc._renderType === cc._RENDER_TYPE_CANVAS && !cc.sys._supportCanvasNewBlendModes)
  932. _p._changeTextureColor = function(){
  933. if(cc._renderType == cc._RENDER_TYPE_WEBGL)
  934. return;
  935. var locElement, locTexture = this.getTexture();
  936. if (locTexture && locTexture.getContentSize().width>0) {
  937. locElement = locTexture.getHtmlElementObj();
  938. if (!locElement)
  939. return;
  940. var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj());
  941. if (cacheTextureForColor) {
  942. if (locElement instanceof HTMLCanvasElement && !this._rectRotated)
  943. cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, null, locElement);
  944. else{
  945. locElement = cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor);
  946. locTexture = new cc.Texture2D();
  947. locTexture.initWithElement(locElement);
  948. locTexture.handleLoadedTexture();
  949. this.setTexture(locTexture);
  950. }
  951. }
  952. }
  953. };
  954. /** @expose */
  955. _p.string;
  956. cc.defineGetterSetter(_p, "string", _p.getString, _p._setStringForSetter);
  957. /** @expose */
  958. _p.boundingWidth;
  959. cc.defineGetterSetter(_p, "boundingWidth", _p._getBoundingWidth, _p.setBoundingWidth);
  960. /** @expose */
  961. _p.textAlign;
  962. cc.defineGetterSetter(_p, "textAlign", _p._getAlignment, _p.setAlignment);
  963. /**
  964. * creates a bitmap font atlas with an initial string and the FNT file
  965. * @deprecated since v3.0 please use new cc.LabelBMFont
  966. * @param {String} str
  967. * @param {String} fntFile
  968. * @param {Number} [width=-1]
  969. * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT]
  970. * @param {cc.Point} [imageOffset=cc.p(0,0)]
  971. * @return {cc.LabelBMFont|Null}
  972. * @example
  973. * // Example 01
  974. * var label1 = cc.LabelBMFont.create("Test case", "test.fnt");
  975. *
  976. * // Example 02
  977. * var label2 = cc.LabelBMFont.create("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT);
  978. *
  979. * // Example 03
  980. * var label3 = cc.LabelBMFont.create("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.p(0,0));
  981. */
  982. cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) {
  983. return new cc.LabelBMFont(str, fntFile, width, alignment, imageOffset);
  984. };
  985. cc._fntLoader = {
  986. INFO_EXP: /info [^\n]*(\n|$)/gi,
  987. COMMON_EXP: /common [^\n]*(\n|$)/gi,
  988. PAGE_EXP: /page [^\n]*(\n|$)/gi,
  989. CHAR_EXP: /char [^\n]*(\n|$)/gi,
  990. KERNING_EXP: /kerning [^\n]*(\n|$)/gi,
  991. ITEM_EXP: /\w+=[^ \r\n]+/gi,
  992. INT_EXP: /^[\-]?\d+$/,
  993. _parseStrToObj: function (str) {
  994. var arr = str.match(this.ITEM_EXP);
  995. var obj = {};
  996. if (arr) {
  997. for (var i = 0, li = arr.length; i < li; i++) {
  998. var tempStr = arr[i];
  999. var index = tempStr.indexOf("=");
  1000. var key = tempStr.substring(0, index);
  1001. var value = tempStr.substring(index + 1);
  1002. if (value.match(this.INT_EXP)) value = parseInt(value);
  1003. else if (value[0] == '"') value = value.substring(1, value.length - 1);
  1004. obj[key] = value;
  1005. }
  1006. }
  1007. return obj;
  1008. },
  1009. /**
  1010. * Parse Fnt string.
  1011. * @param fntStr
  1012. * @param url
  1013. * @returns {{}}
  1014. */
  1015. parseFnt: function (fntStr, url) {
  1016. var self = this, fnt = {};
  1017. //padding
  1018. var infoObj = self._parseStrToObj(fntStr.match(self.INFO_EXP)[0]);
  1019. var paddingArr = infoObj["padding"].split(",");
  1020. var padding = {
  1021. left: parseInt(paddingArr[0]),
  1022. top: parseInt(paddingArr[1]),
  1023. right: parseInt(paddingArr[2]),
  1024. bottom: parseInt(paddingArr[3])
  1025. };
  1026. //common
  1027. var commonObj = self._parseStrToObj(fntStr.match(self.COMMON_EXP)[0]);
  1028. fnt.commonHeight = commonObj["lineHeight"];
  1029. if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
  1030. var texSize = cc.configuration.getMaxTextureSize();
  1031. if (commonObj["scaleW"] > texSize.width || commonObj["scaleH"] > texSize.height)
  1032. cc.log("cc.LabelBMFont._parseCommonArguments(): page can't be larger than supported");
  1033. }
  1034. if (commonObj["pages"] !== 1) cc.log("cc.LabelBMFont._parseCommonArguments(): only supports 1 page");
  1035. //page
  1036. var pageObj = self._parseStrToObj(fntStr.match(self.PAGE_EXP)[0]);
  1037. if (pageObj["id"] !== 0) cc.log("cc.LabelBMFont._parseImageFileName() : file could not be found");
  1038. fnt.atlasName = cc.path.changeBasename(url, pageObj["file"]);
  1039. //char
  1040. var charLines = fntStr.match(self.CHAR_EXP);
  1041. var fontDefDictionary = fnt.fontDefDictionary = {};
  1042. for (var i = 0, li = charLines.length; i < li; i++) {
  1043. var charObj = self._parseStrToObj(charLines[i]);
  1044. var charId = charObj["id"];
  1045. fontDefDictionary[charId] = {
  1046. rect: {x: charObj["x"], y: charObj["y"], width: charObj["width"], height: charObj["height"]},
  1047. xOffset: charObj["xoffset"],
  1048. yOffset: charObj["yoffset"],
  1049. xAdvance: charObj["xadvance"]
  1050. };
  1051. }
  1052. //kerning
  1053. var kerningDict = fnt.kerningDict = {};
  1054. var kerningLines = fntStr.match(self.KERNING_EXP);
  1055. if (kerningLines) {
  1056. for (var i = 0, li = kerningLines.length; i < li; i++) {
  1057. var kerningObj = self._parseStrToObj(kerningLines[i]);
  1058. kerningDict[(kerningObj["first"] << 16) | (kerningObj["second"] & 0xffff)] = kerningObj["amount"];
  1059. }
  1060. }
  1061. return fnt;
  1062. },
  1063. /**
  1064. * load the fnt
  1065. * @param realUrl
  1066. * @param url
  1067. * @param res
  1068. * @param cb
  1069. */
  1070. load: function (realUrl, url, res, cb) {
  1071. var self = this;
  1072. cc.loader.loadTxt(realUrl, function (err, txt) {
  1073. if (err) return cb(err);
  1074. cb(null, self.parseFnt(txt, url));
  1075. });
  1076. }
  1077. };
  1078. cc.loader.register(["fnt"], cc._fntLoader);