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 6 http://www.cocos2d-x.org 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 27 /** 28 * cc.LabelTTF is a subclass of cc.TextureNode that knows how to render text labels<br/> 29 * All features from cc.TextureNode are valid in cc.LabelTTF<br/> 30 * cc.LabelTTF objects are slow for js-binding on mobile devices.Consider using cc.LabelAtlas or cc.LabelBMFont instead. <br/> 31 * Consider using cc.LabelAtlas or cc.LabelBMFont instead.<br/> 32 * @class 33 * @extends cc.Sprite 34 * 35 * @property {String} string - Content string of label 36 * @property {Number} textAlign - Horizontal Alignment of label: cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT 37 * @property {Number} verticalAlign - Vertical Alignment of label: cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM 38 * @property {Number} fontSize - Font size of label 39 * @property {String} fontName - Font name of label 40 * @property {String} font - The label font with a style string: e.g. "18px Verdana" 41 * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth 42 * @property {Number} boundingHeight - Height of the bounding box of label, the real content height is limited by boundingHeight 43 * @property {cc.Color} fillStyle - The fill color 44 * @property {cc.Color} strokeStyle - The stroke color 45 * @property {Number} lineWidth - The line width for stroke 46 * @property {Number} shadowOffsetX - The x axis offset of shadow 47 * @property {Number} shadowOffsetY - The y axis offset of shadow 48 * @property {Number} shadowOpacity - The opacity of shadow 49 * @property {Number} shadowBlur - The blur size of shadow 50 * 51 */ 52 cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{ 53 _dimensions: null, 54 _hAlignment: cc.TEXT_ALIGNMENT_CENTER, 55 _vAlignment: cc.VERTICAL_TEXT_ALIGNMENT_TOP, 56 _fontName: null, 57 _fontSize: 0.0, 58 _string: "", 59 _originalText: null, 60 _isMultiLine: false, 61 _fontStyleStr: null, 62 63 // font shadow 64 _shadowEnabled: false, 65 _shadowOffset: null, 66 _shadowOpacity: 0, 67 _shadowBlur: 0, 68 _shadowColorStr: null, 69 70 // font stroke 71 _strokeEnabled: false, 72 _strokeColor: null, 73 _strokeSize: 0, 74 _strokeColorStr: null, 75 76 // font tint 77 _textFillColor: null, 78 _fillColorStr: null, 79 80 _strokeShadowOffsetX: 0, 81 _strokeShadowOffsetY: 0, 82 _needUpdateTexture: false, 83 84 _labelCanvas: null, 85 _labelContext: null, 86 _lineWidths: null, 87 _className: "LabelTTF", 88 89 90 /** 91 * creates a cc.LabelTTF from a font name, alignment, dimension and font size 92 * Constructor of cc.LabelTTF 93 * @param {String} text 94 * @param {String|cc.FontDefinition} [fontName="Arial"] 95 * @param {Number} [fontSize=16] 96 * @param {cc.Size} [dimensions=cc.size(0,0)] 97 * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] 98 * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] 99 * @example 100 * var myLabel = new cc.LabelTTF('label text', 'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT); 101 * 102 * var fontDef = new cc.FontDefinition(); 103 * fontDef.fontName = "Arial"; 104 * fontDef.fontSize = "32"; 105 * var myLabel = new cc.LabelTTF('label text', fontDef); 106 */ 107 ctor: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { 108 cc.Sprite.prototype.ctor.call(this); 109 110 this._dimensions = cc.size(0, 0); 111 this._hAlignment = cc.TEXT_ALIGNMENT_LEFT; 112 this._vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP; 113 this._opacityModifyRGB = false; 114 this._fontStyleStr = ""; 115 this._fontName = "Arial"; 116 this._isMultiLine = false; 117 118 this._shadowEnabled = false; 119 this._shadowOffset = cc.p(0, 0); 120 this._shadowOpacity = 0; 121 this._shadowBlur = 0; 122 this._shadowColorStr = "rgba(128, 128, 128, 0.5)"; 123 124 this._strokeEnabled = false; 125 this._strokeColor = cc.color(255, 255, 255, 255); 126 this._strokeSize = 0; 127 this._strokeColorStr = ""; 128 129 this._textFillColor = cc.color(255, 255, 255, 255); 130 this._fillColorStr = "rgba(255,255,255,1)"; 131 this._strokeShadowOffsetX = 0; 132 this._strokeShadowOffsetY = 0; 133 this._needUpdateTexture = false; 134 135 this._lineWidths = []; 136 137 this._setColorsString(); 138 139 if (fontName && fontName instanceof cc.FontDefinition) { 140 this.initWithStringAndTextDefinition(text, fontName); 141 } 142 else { 143 cc.LabelTTF.prototype.initWithString.call(this, text, fontName, fontSize, dimensions, hAlignment, vAlignment); 144 } 145 }, 146 147 init: function () { 148 return this.initWithString(" ", this._fontName, this._fontSize); 149 }, 150 151 _measureConfig: function () { 152 this._getLabelContext().font = this._fontStyleStr; 153 }, 154 _measure: function (text) { 155 return this._getLabelContext().measureText(text).width; 156 }, 157 _checkNextline: function (text, width) { 158 var tWidth = this._measure(text); 159 // Estimated word number per line 160 var baseNb = Math.floor(text.length * width / tWidth); 161 // Next line is a line with line break 162 var nextlinebreak = text.indexOf('\n'); 163 if (baseNb * 0.8 >= nextlinebreak && nextlinebreak > 0) return nextlinebreak + 1; 164 // Text width smaller than requested width 165 if (tWidth < width) return text.length; 166 167 var found = false, l = width + 1, idfound = -1, index = baseNb, result, 168 re = cc.LabelTTF._checkRegEx, 169 reversre = cc.LabelTTF._reverseCheckRegEx, 170 enre = cc.LabelTTF._checkEnRegEx, 171 substr = text.substr(baseNb); 172 173 // Forward check 174 // Find next special caracter or chinese caracters 175 while (result = re.exec(substr)) { 176 index += result[0].length; 177 var tem = text.substr(0, index); 178 l = this._measure(tem); 179 if (result[2] == '\n' && l < width) { 180 found = true; 181 idfound = index; 182 break; 183 } 184 if (l > width) { 185 if (idfound != -1) 186 found = true; 187 break; 188 } 189 idfound = index; 190 substr = text.substr(index); 191 } 192 if (found) return idfound; 193 194 // Backward check when forward check failed 195 substr = text.substr(0, baseNb); 196 idfound = baseNb; 197 while (result = reversre.exec(substr)) { 198 // BUG: Not secured if check with result[0] 199 idfound = result[1].length; 200 substr = result[1]; 201 l = this._measure(substr); 202 if (l < width) { 203 if (enre.test(result[2])) 204 idfound++; 205 break; 206 } 207 } 208 209 // Avoid when idfound == 0, the process may enter in a infinite loop 210 return idfound || 1; 211 }, 212 213 /** 214 * Prints out a description of this class 215 * @return {String} 216 */ 217 description: function () { 218 return "<cc.LabelTTF | FontName =" + this._fontName + " FontSize = " + this._fontSize.toFixed(1) + ">"; 219 }, 220 221 setColor: null, 222 223 _setColorsString: null, 224 225 updateDisplayedColor: null, 226 setOpacity: null, 227 228 updateDisplayedOpacity: null, 229 updateDisplayedOpacityForCanvas: function (parentOpacity) { 230 cc.NodeRGBA.prototype.updateDisplayedOpacity.call(this, parentOpacity); 231 this._setColorsString(); 232 }, 233 234 /** 235 * returns the text of the label 236 * @return {String} 237 */ 238 getString: function () { 239 return this._string; 240 }, 241 242 /** 243 * return Horizontal Alignment of cc.LabelTTF 244 * @return {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} 245 */ 246 getHorizontalAlignment: function () { 247 return this._hAlignment; 248 }, 249 250 /** 251 * return Vertical Alignment of cc.LabelTTF 252 * @return {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} 253 */ 254 getVerticalAlignment: function () { 255 return this._vAlignment; 256 }, 257 258 /** 259 * return Dimensions of cc.LabelTTF 260 * @return {cc.Size} 261 */ 262 getDimensions: function () { 263 return cc.size(this._dimensions.width, this._dimensions.height); 264 }, 265 266 /** 267 * return font size of cc.LabelTTF 268 * @return {Number} 269 */ 270 getFontSize: function () { 271 return this._fontSize; 272 }, 273 274 /** 275 * return font name of cc.LabelTTF 276 * @return {String} 277 */ 278 getFontName: function () { 279 return this._fontName; 280 }, 281 282 /** 283 * initializes the cc.LabelTTF with a font name, alignment, dimension and font size 284 * @param {String} label string 285 * @param {String} fontName 286 * @param {Number} fontSize 287 * @param {cc.Size} [dimensions=] 288 * @param {Number} [hAlignment=] 289 * @param {Number} [vAlignment=] 290 * @return {Boolean} return false on error 291 */ 292 initWithString: function (label, fontName, fontSize, dimensions, hAlignment, vAlignment) { 293 var strInfo; 294 if (label) 295 strInfo = label + ""; 296 else 297 strInfo = ""; 298 299 fontSize = fontSize || 16; 300 dimensions = dimensions || cc.size(0, fontSize); 301 hAlignment = hAlignment || cc.TEXT_ALIGNMENT_LEFT; 302 vAlignment = vAlignment || cc.VERTICAL_TEXT_ALIGNMENT_TOP; 303 304 this._opacityModifyRGB = false; 305 this._dimensions = cc.size(dimensions.width, dimensions.height); 306 this._fontName = fontName || "Arial"; 307 this._hAlignment = hAlignment; 308 this._vAlignment = vAlignment; 309 310 //this._fontSize = (cc._renderType === cc._RENDER_TYPE_CANVAS) ? fontSize : fontSize * cc.contentScaleFactor(); 311 this._fontSize = fontSize; 312 this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; 313 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize); 314 this.string = strInfo; 315 this._setColorsString(); 316 this._updateTexture(); 317 this._needUpdateTexture = false; 318 return true; 319 }, 320 321 /** 322 * initializes the CCLabelTTF with a font name, alignment, dimension and font size 323 * @param {String} text 324 * @param {cc.FontDefinition} textDefinition 325 * @return {Boolean} 326 */ 327 initWithStringAndTextDefinition: null, 328 329 /** 330 * set the text definition used by this label 331 * @param {cc.FontDefinition} theDefinition 332 */ 333 setTextDefinition: function (theDefinition) { 334 if (theDefinition) 335 this._updateWithTextDefinition(theDefinition, true); 336 }, 337 338 /** 339 * get the text definition used by this label 340 * @return {cc.FontDefinition} 341 */ 342 getTextDefinition: function () { 343 return this._prepareTextDefinition(false); 344 }, 345 346 /** 347 * enable or disable shadow for the label 348 * @param {cc.Point} shadowOffset 349 * @param {Number} shadowOpacity (0 to 1) 350 * @param {Number} shadowBlur 351 */ 352 enableShadow: function (shadowOffsetX, shadowOffsetY, shadowOpacity, shadowBlur) { 353 shadowOpacity = shadowOpacity || 0.5; 354 if (false === this._shadowEnabled) 355 this._shadowEnabled = true; 356 357 var locShadowOffset = this._shadowOffset; 358 if (locShadowOffset && (locShadowOffset.x != shadowOffsetX) || (locShadowOffset._y != shadowOffsetY)) { 359 locShadowOffset.x = shadowOffsetX; 360 locShadowOffset.y = shadowOffsetY; 361 } 362 363 if (this._shadowOpacity != shadowOpacity) { 364 this._shadowOpacity = shadowOpacity; 365 } 366 this._setColorsString(); 367 368 if (this._shadowBlur != shadowBlur) 369 this._shadowBlur = shadowBlur; 370 371 this._needUpdateTexture = true; 372 }, 373 374 _getShadowOffsetX: function () { 375 return this._shadowOffset.x; 376 }, 377 _setShadowOffsetX: function (x) { 378 if (false === this._shadowEnabled) 379 this._shadowEnabled = true; 380 381 if (this._shadowOffset.x != x) { 382 this._shadowOffset.x = x; 383 this._needUpdateTexture = true; 384 } 385 }, 386 387 _getShadowOffsetY: function () { 388 return this._shadowOffset._y; 389 }, 390 _setShadowOffsetY: function (y) { 391 if (false === this._shadowEnabled) 392 this._shadowEnabled = true; 393 394 if (this._shadowOffset._y != y) { 395 this._shadowOffset._y = y; 396 this._needUpdateTexture = true; 397 } 398 }, 399 400 _getShadowOffset: function () { 401 return cc.p(this._shadowOffset.x, this._shadowOffset.y); 402 }, 403 _setShadowOffset: function (offset) { 404 if (false === this._shadowEnabled) 405 this._shadowEnabled = true; 406 407 if (this._shadowOffset.x != offset.x || this._shadowOffset.y != offset.y) { 408 this._shadowOffset.x = offset.x; 409 this._shadowOffset.y = offset.y; 410 this._needUpdateTexture = true; 411 } 412 }, 413 414 _getShadowOpacity: function () { 415 return this._shadowOpacity; 416 }, 417 _setShadowOpacity: function (shadowOpacity) { 418 if (false === this._shadowEnabled) 419 this._shadowEnabled = true; 420 421 if (this._shadowOpacity != shadowOpacity) { 422 this._shadowOpacity = shadowOpacity; 423 this._setColorsString(); 424 this._needUpdateTexture = true; 425 } 426 }, 427 428 _getShadowBlur: function () { 429 return this._shadowBlur; 430 }, 431 _setShadowBlur: function (shadowBlur) { 432 if (false === this._shadowEnabled) 433 this._shadowEnabled = true; 434 435 if (this._shadowBlur != shadowBlur) { 436 this._shadowBlur = shadowBlur; 437 this._needUpdateTexture = true; 438 } 439 }, 440 441 /** 442 * disable shadow rendering 443 */ 444 disableShadow: function () { 445 if (this._shadowEnabled) { 446 this._shadowEnabled = false; 447 this._needUpdateTexture = true; 448 } 449 }, 450 451 /** 452 * enable or disable stroke 453 * @param {cc.Color} strokeColor 454 * @param {Number} strokeSize 455 */ 456 enableStroke: function (strokeColor, strokeSize) { 457 if (this._strokeEnabled === false) 458 this._strokeEnabled = true; 459 460 var locStrokeColor = this._strokeColor; 461 if ((locStrokeColor.r !== strokeColor.r) || (locStrokeColor.g !== strokeColor.g) || (locStrokeColor.b !== strokeColor.b)) { 462 locStrokeColor.r = strokeColor.r; 463 locStrokeColor.g = strokeColor.g; 464 locStrokeColor.b = strokeColor.b; 465 this._setColorsString(); 466 } 467 468 if (this._strokeSize !== strokeSize) 469 this._strokeSize = strokeSize || 0; 470 471 this._needUpdateTexture = true; 472 }, 473 474 _getStrokeStyle: function () { 475 return this._strokeColor; 476 }, 477 _setStrokeStyle: function (strokeStyle) { 478 if (this._strokeEnabled === false) 479 this._strokeEnabled = true; 480 481 var locStrokeColor = this._strokeColor; 482 if ((locStrokeColor.r !== strokeStyle.r) || (locStrokeColor.g !== strokeStyle.g) || (locStrokeColor.b !== strokeStyle.b)) { 483 locStrokeColor.r = strokeStyle.r; 484 locStrokeColor.g = strokeStyle.g; 485 locStrokeColor.b = strokeStyle.b; 486 this._setColorsString(); 487 488 this._needUpdateTexture = true; 489 } 490 }, 491 492 _getLineWidth: function () { 493 return this._strokeSize; 494 }, 495 _setLineWidth: function (lineWidth) { 496 if (this._strokeEnabled === false) 497 this._strokeEnabled = true; 498 499 if (this._strokeSize !== lineWidth) { 500 this._strokeSize = lineWidth || 0; 501 this._needUpdateTexture = true; 502 } 503 }, 504 505 /** 506 * disable stroke 507 */ 508 disableStroke: function () { 509 if (this._strokeEnabled) { 510 this._strokeEnabled = false; 511 this._needUpdateTexture = true; 512 } 513 }, 514 515 /** 516 * set text tinting 517 * @function 518 * @param {cc.Color} tintColor 519 */ 520 setFontFillColor: null, 521 522 _getFillStyle: function () { 523 return this._textFillColor; 524 }, 525 526 //set the text definition for this label 527 _updateWithTextDefinition: function (textDefinition, mustUpdateTexture) { 528 if (textDefinition.fontDimensions) { 529 this._dimensions.width = textDefinition.boundingWidth; 530 this._dimensions.height = textDefinition.boundingHeight; 531 } else { 532 this._dimensions.width = 0; 533 this._dimensions.height = 0; 534 } 535 536 this._hAlignment = textDefinition.textAlign; 537 this._vAlignment = textDefinition.verticalAlign; 538 539 this._fontName = textDefinition.fontName; 540 this._fontSize = textDefinition.fontSize || 12; 541 this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'"; 542 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize); 543 544 // shadow 545 if (textDefinition.shadowEnabled) 546 this.enableShadow(textDefinition.shadowOffsetX, 547 textDefinition.shadowOffsetY, 548 textDefinition.shadowOpacity, 549 textDefinition.shadowBlur); 550 551 // stroke 552 if (textDefinition.strokeEnabled) 553 this.enableStroke(textDefinition.strokeStyle, textDefinition.lineWidth); 554 555 // fill color 556 this.setFontFillColor(textDefinition.fillStyle); 557 558 if (mustUpdateTexture) 559 this._updateTexture(); 560 }, 561 562 _prepareTextDefinition: function (adjustForResolution) { 563 var texDef = new cc.FontDefinition(); 564 565 if (adjustForResolution) { 566 //texDef.fontSize = (cc._renderType === cc._RENDER_TYPE_CANVAS) ? this._fontSize : this._fontSize * cc.contentScaleFactor(); 567 texDef.fontSize = this._fontSize; 568 texDef.boundingWidth = cc.contentScaleFactor() * this._dimensions.width; 569 texDef.boundingHeight = cc.contentScaleFactor() * this._dimensions.height; 570 } else { 571 texDef.fontSize = this._fontSize; 572 texDef.boundingWidth = this._dimensions.width; 573 texDef.boundingHeight = this._dimensions.height; 574 } 575 576 texDef.fontName = this._fontName; 577 texDef.textAlign = this._hAlignment; 578 texDef.verticalAlign = this._vAlignment; 579 580 // stroke 581 if (this._strokeEnabled) { 582 texDef.strokeEnabled = true; 583 var locStrokeColor = this._strokeColor; 584 texDef.strokeStyle = cc.color(locStrokeColor.r, locStrokeColor.g, locStrokeColor.b); 585 texDef.lineWidth = this._strokeSize; 586 } else 587 texDef.strokeEnabled = false; 588 589 // shadow 590 if (this._shadowEnabled) { 591 texDef.shadowEnabled = true; 592 texDef.shadowBlur = this._shadowBlur; 593 texDef.shadowOpacity = this._shadowOpacity; 594 595 texDef.shadowOffsetX = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.x; 596 texDef.shadowOffsetY = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.y; 597 } else 598 texDef._shadowEnabled = false; 599 600 // text tint 601 var locTextFillColor = this._textFillColor; 602 texDef.fillStyle = cc.color(locTextFillColor.r, locTextFillColor.g, locTextFillColor.b); 603 return texDef; 604 }, 605 606 _fontClientHeight: 18, 607 /** 608 * changes the string to render 609 * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas 610 * @param {String} text text for the label 611 */ 612 setString: function (text) { 613 text = String(text); 614 if (this._originalText != text) { 615 this._originalText = text + ""; 616 617 this._updateString(); 618 619 // Force update 620 this._needUpdateTexture = true; 621 } 622 }, 623 _updateString: function () { 624 this._string = this._originalText; 625 }, 626 /** 627 * set Horizontal Alignment of cc.LabelTTF 628 * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment 629 */ 630 setHorizontalAlignment: function (alignment) { 631 if (alignment !== this._hAlignment) { 632 this._hAlignment = alignment; 633 634 // Force update 635 this._needUpdateTexture = true; 636 } 637 }, 638 639 /** 640 * set Vertical Alignment of cc.LabelTTF 641 * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment 642 */ 643 setVerticalAlignment: function (verticalAlignment) { 644 if (verticalAlignment != this._vAlignment) { 645 this._vAlignment = verticalAlignment; 646 647 // Force update 648 this._needUpdateTexture = true; 649 } 650 }, 651 652 /** 653 * set Dimensions of cc.LabelTTF 654 * @param {cc.Size} dim 655 */ 656 setDimensions: function (dim) { 657 if (dim.width != this._dimensions.width || dim.height != this._dimensions.height) { 658 this._dimensions = dim; 659 this._updateString(); 660 // Force udpate 661 this._needUpdateTexture = true; 662 } 663 }, 664 665 _getBoundingWidth: function () { 666 return this._dimensions.width; 667 }, 668 _setBoundingWidth: function (width) { 669 if (width != this._dimensions.width) { 670 this._dimensions.width = width; 671 this._updateString(); 672 // Force udpate 673 this._needUpdateTexture = true; 674 } 675 }, 676 677 _getBoundingHeight: function () { 678 return this._dimensions.height; 679 }, 680 _setBoundingHeight: function (height) { 681 if (height != this._dimensions.height) { 682 this._dimensions.height = height; 683 this._updateString(); 684 // Force udpate 685 this._needUpdateTexture = true; 686 } 687 }, 688 689 /** 690 * set font size of cc.LabelTTF 691 * @param {Number} fontSize 692 */ 693 setFontSize: function (fontSize) { 694 if (this._fontSize !== fontSize) { 695 this._fontSize = fontSize; 696 this._fontStyleStr = fontSize + "px '" + this._fontName + "'"; 697 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, fontSize); 698 // Force update 699 this._needUpdateTexture = true; 700 } 701 }, 702 703 /** 704 * set font name of cc.LabelTTF 705 * @param {String} fontName 706 */ 707 setFontName: function (fontName) { 708 if (this._fontName && this._fontName != fontName) { 709 this._fontName = fontName; 710 this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; 711 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize); 712 // Force update 713 this._needUpdateTexture = true; 714 } 715 }, 716 717 _getFont: function () { 718 return this._fontStyleStr; 719 }, 720 _setFont: function (fontStyle) { 721 var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); 722 if (res) { 723 this._fontSize = parseInt(res[1]); 724 this._fontName = res[2]; 725 this._fontStyleStr = fontStyle; 726 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize); 727 // Force update 728 this._needUpdateTexture = true; 729 } 730 }, 731 732 _drawTTFInCanvas: function (context) { 733 if (!context) 734 return; 735 var locStrokeShadowOffsetX = this._strokeShadowOffsetX, locStrokeShadowOffsetY = this._strokeShadowOffsetY; 736 var locContentSizeHeight = this._contentSize.height - locStrokeShadowOffsetY, locVAlignment = this._vAlignment, locHAlignment = this._hAlignment, 737 locFontHeight = this._fontClientHeight, locStrokeSize = this._strokeSize; 738 739 context.setTransform(1, 0, 0, 1, 0 + locStrokeShadowOffsetX * 0.5, locContentSizeHeight + locStrokeShadowOffsetY * 0.5); 740 741 //this is fillText for canvas 742 if (context.font != this._fontStyleStr) 743 context.font = this._fontStyleStr; 744 context.fillStyle = this._fillColorStr; 745 746 var xOffset = 0, yOffset = 0; 747 //stroke style setup 748 var locStrokeEnabled = this._strokeEnabled; 749 if (locStrokeEnabled) { 750 context.lineWidth = locStrokeSize * 2; 751 context.strokeStyle = this._strokeColorStr; 752 } 753 754 //shadow style setup 755 if (this._shadowEnabled) { 756 var locShadowOffset = this._shadowOffset; 757 context.shadowColor = this._shadowColorStr; 758 context.shadowOffsetX = locShadowOffset.x; 759 context.shadowOffsetY = -locShadowOffset.y; 760 context.shadowBlur = this._shadowBlur; 761 } 762 763 context.textBaseline = cc.LabelTTF._textBaseline[locVAlignment]; 764 context.textAlign = cc.LabelTTF._textAlign[locHAlignment]; 765 766 var locContentWidth = this._contentSize.width - locStrokeShadowOffsetX; 767 if (locHAlignment === cc.TEXT_ALIGNMENT_RIGHT) 768 xOffset += locContentWidth; 769 else if (locHAlignment === cc.TEXT_ALIGNMENT_CENTER) 770 xOffset += locContentWidth / 2; 771 else 772 xOffset += 0; 773 if (this._isMultiLine) { 774 var locStrLen = this._strings.length; 775 if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) 776 yOffset = locFontHeight + locContentSizeHeight - locFontHeight * locStrLen; 777 else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER) 778 yOffset = locFontHeight / 2 + (locContentSizeHeight - locFontHeight * locStrLen) / 2; 779 780 for (var i = 0; i < locStrLen; i++) { 781 var line = this._strings[i]; 782 var tmpOffsetY = -locContentSizeHeight + (locFontHeight * i) + yOffset; 783 if (locStrokeEnabled) 784 context.strokeText(line, xOffset, tmpOffsetY); 785 context.fillText(line, xOffset, tmpOffsetY); 786 } 787 } else { 788 if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) { 789 if (locStrokeEnabled) 790 context.strokeText(this._string, xOffset, yOffset); 791 context.fillText(this._string, xOffset, yOffset); 792 } else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP) { 793 yOffset -= locContentSizeHeight; 794 if (locStrokeEnabled) 795 context.strokeText(this._string, xOffset, yOffset); 796 context.fillText(this._string, xOffset, yOffset); 797 } else { 798 yOffset -= locContentSizeHeight * 0.5; 799 if (locStrokeEnabled) 800 context.strokeText(this._string, xOffset, yOffset); 801 context.fillText(this._string, xOffset, yOffset); 802 } 803 } 804 }, 805 806 _getLabelContext: function () { 807 if (this._labelContext) 808 return this._labelContext; 809 810 if (!this._labelCanvas) { 811 var locCanvas = cc.newElement("canvas"); 812 var labelTexture = new cc.Texture2D(); 813 labelTexture.initWithElement(locCanvas); 814 this.texture = labelTexture; 815 this._labelCanvas = locCanvas; 816 } 817 this._labelContext = this._labelCanvas.getContext("2d"); 818 return this._labelContext; 819 }, 820 821 _updateTTF: function () { 822 var locDimensionsWidth = this._dimensions.width, i, strLength; 823 var locLineWidth = this._lineWidths; 824 locLineWidth.length = 0; 825 826 this._isMultiLine = false; 827 this._measureConfig(); 828 if (locDimensionsWidth !== 0) { 829 // Content processing 830 var text = this._string; 831 this._strings = []; 832 for (i = 0, strLength = this._string.length; i < strLength;) { 833 // Find the index of next line 834 var next = this._checkNextline(text.substr(i), locDimensionsWidth); 835 var append = text.substr(i, next); 836 this._strings.push(append); 837 i += next; 838 } 839 } else { 840 this._strings = this._string.split('\n'); 841 for (i = 0, strLength = this._strings.length; i < strLength; i++) { 842 locLineWidth.push(this._measure(this._strings[i])); 843 } 844 } 845 846 if (this._strings.length > 0) 847 this._isMultiLine = true; 848 849 var locSize, locStrokeShadowOffsetX = 0, locStrokeShadowOffsetY = 0; 850 if (this._strokeEnabled) 851 locStrokeShadowOffsetX = locStrokeShadowOffsetY = this._strokeSize * 2; 852 if (this._shadowEnabled) { 853 var locOffsetSize = this._shadowOffset; 854 locStrokeShadowOffsetX += Math.abs(locOffsetSize.x) * 2; 855 locStrokeShadowOffsetY += Math.abs(locOffsetSize.y) * 2; 856 } 857 858 //get offset for stroke and shadow 859 if (locDimensionsWidth === 0) { 860 if (this._isMultiLine) 861 locSize = cc.size(0 | (Math.max.apply(Math, locLineWidth) + locStrokeShadowOffsetX), 862 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); 863 else 864 locSize = cc.size(0 | (this._measure(this._string) + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); 865 } else { 866 if (this._dimensions.height === 0) { 867 if (this._isMultiLine) 868 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); 869 else 870 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); 871 } else { 872 //dimension is already set, contentSize must be same as dimension 873 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._dimensions.height + locStrokeShadowOffsetY)); 874 } 875 } 876 this.setContentSize(locSize); 877 this._strokeShadowOffsetX = locStrokeShadowOffsetX; 878 this._strokeShadowOffsetY = locStrokeShadowOffsetY; 879 880 // need computing _anchorPointInPoints 881 var locAP = this._anchorPoint; 882 this._anchorPointInPoints.x = (locStrokeShadowOffsetX * 0.5) + ((locSize.width - locStrokeShadowOffsetX) * locAP.x); 883 this._anchorPointInPoints.y = (locStrokeShadowOffsetY * 0.5) + ((locSize.height - locStrokeShadowOffsetY) * locAP.y); 884 }, 885 886 getContentSize: function () { 887 if (this._needUpdateTexture) 888 this._updateTTF(); 889 return cc.Sprite.prototype.getContentSize.call(this); 890 }, 891 892 _getWidth: function () { 893 if (this._needUpdateTexture) 894 this._updateTTF(); 895 return cc.Sprite.prototype._getWidth.call(this); 896 }, 897 _getHeight: function () { 898 if (this._needUpdateTexture) 899 this._updateTTF(); 900 return cc.Sprite.prototype._getHeight.call(this); 901 }, 902 903 _updateTexture: function () { 904 var locContext = this._getLabelContext(), locLabelCanvas = this._labelCanvas; 905 var locContentSize = this._contentSize; 906 907 if (this._string.length === 0) { 908 locLabelCanvas.width = 1; 909 locLabelCanvas.height = locContentSize.height; 910 this.setTextureRect(cc.rect(0, 0, 1, locContentSize.height)); 911 return true; 912 } 913 914 //set size for labelCanvas 915 locContext.font = this._fontStyleStr; 916 this._updateTTF(); 917 var width = locContentSize.width, height = locContentSize.height; 918 var flag = locLabelCanvas.width == width && locLabelCanvas.height == height; 919 locLabelCanvas.width = width; 920 locLabelCanvas.height = height; 921 if (flag) locContext.clearRect(0, 0, width, height); 922 923 //draw text to labelCanvas 924 this._drawTTFInCanvas(locContext); 925 this._texture && this._texture.handleLoadedTexture(); 926 927 this.setTextureRect(cc.rect(0, 0, width, height)); 928 return true; 929 }, 930 931 visit: function (ctx) { 932 if (!this._string || this._string == "") 933 return; 934 if (this._needUpdateTexture) { 935 this._needUpdateTexture = false; 936 this._updateTexture(); 937 } 938 var context = ctx || cc._renderContext; 939 cc.Sprite.prototype.visit.call(this, context); 940 }, 941 942 /** 943 * Draw sprite to canvas 944 * @function 945 * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx Render context of canvas, 2d or 3d 946 */ 947 draw: null, 948 949 _setTextureCoords: function (rect) { 950 var tex = this._batchNode ? this.textureAtlas.texture : this._texture; 951 if (!tex) 952 return; 953 954 var atlasWidth = tex.pixelsWidth; 955 var atlasHeight = tex.pixelsHeight; 956 957 var left, right, top, bottom, tempSwap, locQuad = this._quad; 958 if (this._rectRotated) { 959 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 960 left = (2 * rect.x + 1) / (2 * atlasWidth); 961 right = left + (rect.height * 2 - 2) / (2 * atlasWidth); 962 top = (2 * rect.y + 1) / (2 * atlasHeight); 963 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); 964 } else { 965 left = rect.x / atlasWidth; 966 right = (rect.x + rect.height) / atlasWidth; 967 top = rect.y / atlasHeight; 968 bottom = (rect.y + rect.width) / atlasHeight; 969 }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 970 971 if (this._flippedX) { 972 tempSwap = top; 973 top = bottom; 974 bottom = tempSwap; 975 } 976 977 if (this._flippedY) { 978 tempSwap = left; 979 left = right; 980 right = tempSwap; 981 } 982 983 locQuad.bl.texCoords.u = left; 984 locQuad.bl.texCoords.v = top; 985 locQuad.br.texCoords.u = left; 986 locQuad.br.texCoords.v = bottom; 987 locQuad.tl.texCoords.u = right; 988 locQuad.tl.texCoords.v = top; 989 locQuad.tr.texCoords.u = right; 990 locQuad.tr.texCoords.v = bottom; 991 } else { 992 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 993 left = (2 * rect.x + 1) / (2 * atlasWidth); 994 right = left + (rect.width * 2 - 2) / (2 * atlasWidth); 995 top = (2 * rect.y + 1) / (2 * atlasHeight); 996 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); 997 } else { 998 left = rect.x / atlasWidth; 999 right = (rect.x + rect.width) / atlasWidth; 1000 top = rect.y / atlasHeight; 1001 bottom = (rect.y + rect.height) / atlasHeight; 1002 } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 1003 1004 if (this._flippedX) { 1005 tempSwap = left; 1006 left = right; 1007 right = tempSwap; 1008 } 1009 1010 if (this._flippedY) { 1011 tempSwap = top; 1012 top = bottom; 1013 bottom = tempSwap; 1014 } 1015 1016 locQuad.bl.texCoords.u = left; 1017 locQuad.bl.texCoords.v = bottom; 1018 locQuad.br.texCoords.u = right; 1019 locQuad.br.texCoords.v = bottom; 1020 locQuad.tl.texCoords.u = left; 1021 locQuad.tl.texCoords.v = top; 1022 locQuad.tr.texCoords.u = right; 1023 locQuad.tr.texCoords.v = top; 1024 } 1025 this._quadDirty = true; 1026 } 1027 }); 1028 1029 if (cc._renderType === cc._RENDER_TYPE_CANVAS) { 1030 1031 var _p = cc.LabelTTF.prototype; 1032 1033 _p.setColor = function (color3) { 1034 cc.NodeRGBA.prototype.setColor.call(this, color3); 1035 1036 this._setColorsString(); 1037 }; 1038 1039 _p._setColorsString = function () { 1040 this._needUpdateTexture = true; 1041 1042 var locDisplayColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity; 1043 var locStrokeColor = this._strokeColor, locFontFillColor = this._textFillColor; 1044 1045 this._shadowColorStr = "rgba(" + (0 | (locDisplayColor.r * 0.5)) + "," + (0 | (locDisplayColor.g * 0.5)) + "," + (0 | (locDisplayColor.b * 0.5)) + "," + this._shadowOpacity + ")"; 1046 this._fillColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locFontFillColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locFontFillColor.g)) + "," 1047 + (0 | (locDisplayColor.b / 255 * locFontFillColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; 1048 this._strokeColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locStrokeColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locStrokeColor.g)) + "," 1049 + (0 | (locDisplayColor.b / 255 * locStrokeColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; 1050 }; 1051 1052 _p.updateDisplayedColor = function (parentColor) { 1053 cc.NodeRGBA.prototype.updateDisplayedColor.call(this, parentColor); 1054 this._setColorsString(); 1055 }; 1056 1057 _p.setOpacity = function (opacity) { 1058 if (this._opacity === opacity) 1059 return; 1060 cc.Sprite.prototype.setOpacity.call(this, opacity); 1061 this._setColorsString(); 1062 this._needUpdateTexture = true; 1063 }; 1064 1065 //TODO: _p._updateDisplayedOpacityForCanvas 1066 _p.updateDisplayedOpacity = cc.Sprite.prototype.updateDisplayedOpacity; 1067 1068 _p.initWithStringAndTextDefinition = function (text, textDefinition) { 1069 // prepare everything needed to render the label 1070 this._updateWithTextDefinition(textDefinition, false); 1071 1072 // set the string 1073 this.string = text; 1074 1075 return true; 1076 }; 1077 1078 _p.setFontFillColor = function (tintColor) { 1079 var locTextFillColor = this._textFillColor; 1080 if (locTextFillColor.r != tintColor.r || locTextFillColor.g != tintColor.g || locTextFillColor.b != tintColor.b) { 1081 locTextFillColor.r = tintColor.r; 1082 locTextFillColor.g = tintColor.g; 1083 locTextFillColor.b = tintColor.b; 1084 1085 this._setColorsString(); 1086 this._needUpdateTexture = true; 1087 } 1088 }; 1089 1090 _p.draw = cc.Sprite.prototype.draw; 1091 1092 _p.setTextureRect = function (rect, rotated, untrimmedSize) { 1093 this._rectRotated = rotated || false; 1094 untrimmedSize = untrimmedSize || rect; 1095 1096 this.setContentSize(untrimmedSize); 1097 this.setVertexRect(rect); 1098 1099 var locTextureCoordRect = this._textureRect_Canvas; 1100 locTextureCoordRect.x = rect.x; 1101 locTextureCoordRect.y = rect.y; 1102 locTextureCoordRect.width = rect.width; 1103 locTextureCoordRect.height = rect.height; 1104 locTextureCoordRect.validRect = !(locTextureCoordRect.width === 0 || locTextureCoordRect.height === 0 1105 || locTextureCoordRect.x < 0 || locTextureCoordRect.y < 0); 1106 1107 var relativeOffset = this._unflippedOffsetPositionFromCenter; 1108 if (this._flippedX) 1109 relativeOffset.x = -relativeOffset.x; 1110 if (this._flippedY) 1111 relativeOffset.y = -relativeOffset.y; 1112 this._offsetPosition.x = relativeOffset.x + (this._contentSize.width - this._rect.width) / 2; 1113 this._offsetPosition.y = relativeOffset.y + (this._contentSize.height - this._rect.height) / 2; 1114 1115 // rendering using batch node 1116 if (this._batchNode) { 1117 this.dirty = true; 1118 } 1119 }; 1120 _p = null; 1121 1122 } else { 1123 cc.assert(typeof cc._tmp.WebGLLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFWebGL.js"); 1124 cc._tmp.WebGLLabelTTF(); 1125 delete cc._tmp.WebGLLabelTTF; 1126 } 1127 1128 cc.assert(typeof cc._tmp.PrototypeLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFPropertyDefine.js"); 1129 cc._tmp.PrototypeLabelTTF(); 1130 delete cc._tmp.PrototypeLabelTTF; 1131 1132 cc.LabelTTF._textAlign = ["left", "center", "right"]; 1133 1134 cc.LabelTTF._textBaseline = ["top", "middle", "bottom"]; 1135 1136 // Class static properties for measure util 1137 cc.LabelTTF._checkRegEx = /(.+?)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/; 1138 cc.LabelTTF._reverseCheckRegEx = /(.*)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/; 1139 cc.LabelTTF._checkEnRegEx = /[\s\-\/\\\:]/; 1140 1141 // Only support style in this format: "18px Verdana" or "18px 'Helvetica Neue'" 1142 cc.LabelTTF._fontStyleRE = /^(\d+)px\s+['"]?([\w\s\d]+)['"]?$/; 1143 1144 /** 1145 * creates a cc.LabelTTF from a font name, alignment, dimension and font size 1146 * @param {String} text 1147 * @param {String|cc.FontDefinition} [fontName="Arial"] 1148 * @param {Number} [fontSize=16] 1149 * @param {cc.Size} [dimensions=cc.size(0,0)] 1150 * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] 1151 * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] 1152 * @return {cc.LabelTTF|Null} 1153 * @example 1154 * // Example 1155 * 1. 1156 * var myLabel = cc.LabelTTF.create('label text', 'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT); 1157 * 2. 1158 * var fontDef = new cc.FontDefinition(); 1159 * fontDef.fontName = "Arial"; 1160 * fontDef.fontSize = "32"; 1161 * var myLabel = cc.LabelTTF.create('label text', fontDef); 1162 */ 1163 cc.LabelTTF.create = function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { 1164 return new cc.LabelTTF(text, fontName, fontSize, dimensions, hAlignment, vAlignment); 1165 }; 1166 1167 1168 if (cc.USE_LA88_LABELS) 1169 cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTURECOLOR; 1170 else 1171 cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTUREA8COLOR; 1172 1173 cc.LabelTTF.__labelHeightDiv = cc.newElement("div"); 1174 cc.LabelTTF.__labelHeightDiv.style.fontFamily = "Arial"; 1175 cc.LabelTTF.__labelHeightDiv.style.position = "absolute"; 1176 cc.LabelTTF.__labelHeightDiv.style.left = "-100px"; 1177 cc.LabelTTF.__labelHeightDiv.style.top = "-100px"; 1178 cc.LabelTTF.__labelHeightDiv.style.lineHeight = "normal"; 1179 1180 document.body ? 1181 document.body.appendChild(cc.LabelTTF.__labelHeightDiv) : 1182 cc._addEventListener(window, 'load', function () { 1183 this.removeEventListener('load', arguments.callee, false); 1184 document.body.appendChild(cc.LabelTTF.__labelHeightDiv); 1185 }, false); 1186 1187 1188 cc.LabelTTF.__getFontHeightByDiv = function (fontName, fontSize) { 1189 var clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize]; 1190 if (clientHeight > 0) return clientHeight; 1191 var labelDiv = cc.LabelTTF.__labelHeightDiv; 1192 labelDiv.innerHTML = "ajghl~!"; 1193 labelDiv.style.fontFamily = fontName; 1194 labelDiv.style.fontSize = fontSize + "px"; 1195 clientHeight = labelDiv.clientHeight; 1196 cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize] = clientHeight; 1197 labelDiv.innerHTML = ""; 1198 return clientHeight; 1199 }; 1200 1201 cc.LabelTTF.__fontHeightCache = {};