1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga 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 cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{ 36 _dimensions:null, 37 _hAlignment:cc.TEXT_ALIGNMENT_CENTER, 38 _vAlignment:cc.VERTICAL_TEXT_ALIGNMENT_TOP, 39 _fontName: null, 40 _fontSize:0.0, 41 _string:"", 42 _originalText: null, 43 _isMultiLine:false, 44 _fontStyleStr:null, 45 46 // font shadow 47 _shadowEnabled:false, 48 _shadowOffset:null, 49 _shadowOpacity:0, 50 _shadowBlur:0, 51 _shadowColorStr:null, 52 53 // font stroke 54 _strokeEnabled:false, 55 _strokeColor:null, 56 _strokeSize:0, 57 _strokeColorStr:null, 58 59 // font tint 60 _textFillColor:null, 61 _fillColorStr:null, 62 63 _strokeShadowOffsetX:0, 64 _strokeShadowOffsetY:0, 65 _needUpdateTexture:false, 66 67 _labelCanvas:null, 68 _labelContext:null, 69 _lineWidths:null, 70 71 /** 72 * Constructor 73 */ 74 ctor:function () { 75 cc.Sprite.prototype.ctor.call(this); 76 77 this._dimensions = cc.SizeZero(); 78 this._hAlignment = cc.TEXT_ALIGNMENT_LEFT; 79 this._vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP; 80 this._opacityModifyRGB = false; 81 this._fontStyleStr = ""; 82 this._fontName = "Arial"; 83 this._isMultiLine = false; 84 85 this._shadowEnabled = false; 86 this._shadowOffset = cc.SizeZero(); 87 this._shadowOpacity = 0; 88 this._shadowBlur = 0; 89 this._shadowColorStr = "rgba(128, 128, 128, 0.5)"; 90 91 this._strokeEnabled = false; 92 this._strokeColor = cc.white(); 93 this._strokeSize = 0; 94 this._strokeColorStr = ""; 95 96 this._textFillColor = cc.white(); 97 this._fillColorStr = "rgba(255,255,255,1)"; 98 this._strokeShadowOffsetX = 0; 99 this._strokeShadowOffsetY = 0; 100 this._needUpdateTexture = false; 101 102 this._lineWidths = []; 103 104 this._setColorsString(); 105 }, 106 107 init:function () { 108 return this.initWithString(" ", this._fontName, this._fontSize); 109 }, 110 111 _measureConfig: function() { 112 this._getLabelContext().font = this._fontStyleStr; 113 }, 114 _measure: function(text) { 115 return this._getLabelContext().measureText(text).width; 116 }, 117 _checkNextline: function( text, width){ 118 var tWidth = this._measure(text); 119 // Estimated word number per line 120 var baseNb = Math.floor( text.length * width / tWidth ); 121 // Next line is a line with line break 122 var nextlinebreak = text.indexOf('\n'); 123 if(baseNb*0.8 >= nextlinebreak && nextlinebreak > 0) return nextlinebreak+1; 124 // Text width smaller than requested width 125 if(tWidth < width) return text.length; 126 127 var found = false, l = width + 1, idfound = -1, index = baseNb, result, 128 re = cc.LabelTTF._checkRegEx, 129 reversre = cc.LabelTTF._reverseCheckRegEx, 130 enre = cc.LabelTTF._checkEnRegEx, 131 substr = text.substr(baseNb); 132 133 // Forward check 134 // Find next special caracter or chinese caracters 135 while (result = re.exec(substr)) { 136 index += result[0].length; 137 var tem = text.substr(0, index); 138 l = this._measure(tem); 139 if(result[2] == '\n' && l < width) { 140 found = true; 141 idfound = index; 142 break; 143 } 144 if(l > width) { 145 if(idfound != -1) 146 found = true; 147 break; 148 } 149 idfound = index; 150 substr = text.substr(index); 151 } 152 if(found) return idfound; 153 154 // Backward check when forward check failed 155 substr = text.substr(0, baseNb); 156 idfound = baseNb; 157 while (result = reversre.exec(substr)) { 158 // BUG: Not secured if check with result[0] 159 idfound = result[1].length; 160 substr = result[1]; 161 l = this._measure(substr); 162 if(l < width) { 163 if(enre.test(result[2])) 164 idfound++; 165 break; 166 } 167 } 168 169 // Avoid when idfound == 0, the process may enter in a infinite loop 170 return idfound || 1; 171 }, 172 173 /** 174 * Prints out a description of this class 175 * @return {String} 176 */ 177 description:function () { 178 return "<cc.LabelTTF | FontName =" + this._fontName + " FontSize = " + this._fontSize.toFixed(1) + ">"; 179 }, 180 181 setColor: null, 182 183 _setColorForCanvas: function (color3) { 184 cc.NodeRGBA.prototype.setColor.call(this, color3); 185 186 this._setColorsStringForCanvas(); 187 }, 188 189 _setColorsString: null, 190 191 _setColorsStringForCanvas: function () { 192 this._needUpdateTexture = true; 193 194 var locDisplayColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity; 195 var locStrokeColor = this._strokeColor, locFontFillColor = this._textFillColor; 196 197 this._shadowColorStr = "rgba(" + (0 | (locDisplayColor.r * 0.5)) + "," + (0 | (locDisplayColor.g * 0.5)) + "," + (0 | (locDisplayColor.b * 0.5)) + "," + this._shadowOpacity + ")"; 198 this._fillColorStr = "rgba(" + (0 | (locDisplayColor.r /255 * locFontFillColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locFontFillColor.g)) + "," 199 + (0 | (locDisplayColor.b / 255 * locFontFillColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; 200 this._strokeColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locStrokeColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locStrokeColor.g)) + "," 201 + (0 | (locDisplayColor.b / 255 * locStrokeColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; 202 }, 203 204 _setColorsStringForWebGL:function(){ 205 this._needUpdateTexture = true; 206 var locStrokeColor = this._strokeColor, locFontFillColor = this._textFillColor; 207 this._shadowColorStr = "rgba(128,128,128," + this._shadowOpacity + ")"; 208 this._fillColorStr = "rgba(" + (0 | locFontFillColor.r) + "," + (0 | locFontFillColor.g) + "," + (0 | locFontFillColor.b) + ", 1)"; 209 this._strokeColorStr = "rgba(" + (0 | locStrokeColor.r) + "," + (0 | locStrokeColor.g) + "," + (0 | locStrokeColor.b) + ", 1)"; 210 }, 211 212 updateDisplayedColor:null, 213 _updateDisplayedColorForCanvas:function(parentColor){ 214 cc.NodeRGBA.prototype.updateDisplayedColor.call(this,parentColor); 215 this._setColorsString(); 216 }, 217 218 setOpacity: null, 219 220 _setOpacityForCanvas: function (opacity) { 221 if (this._opacity === opacity) 222 return; 223 cc.Sprite.prototype.setOpacity.call(this, opacity); 224 this._setColorsString(); 225 this._needUpdateTexture = true; 226 }, 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 if (cc.Sprite.prototype.init.call(this)) { 305 this._opacityModifyRGB = false; 306 this._dimensions = cc.size(dimensions.width, dimensions.height); 307 this._fontName = fontName || "Arial"; 308 this._hAlignment = hAlignment; 309 this._vAlignment = vAlignment; 310 311 //this._fontSize = (cc.renderContextType === cc.CANVAS) ? fontSize : fontSize * cc.CONTENT_SCALE_FACTOR(); 312 this._fontSize = fontSize; 313 this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; 314 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName,this._fontSize); 315 this.setString(strInfo); 316 this._setColorsString(); 317 this._updateTexture(); 318 this._needUpdateTexture = false; 319 return true; 320 } 321 return false; 322 }, 323 324 /** 325 * initializes the CCLabelTTF with a font name, alignment, dimension and font size 326 * @param {String} text 327 * @param {cc.FontDefinition} textDefinition 328 * @return {Boolean} 329 */ 330 initWithStringAndTextDefinition:null, 331 332 _initWithStringAndTextDefinitionForCanvas:function(text, textDefinition){ 333 if(!cc.Sprite.prototype.init.call(this)) 334 return false; 335 336 // prepare everything needed to render the label 337 this._updateWithTextDefinition(textDefinition, false); 338 339 // set the string 340 this.setString(text); 341 342 return true; 343 }, 344 345 _initWithStringAndTextDefinitionForWebGL:function(text, textDefinition){ 346 if(!cc.Sprite.prototype.init.call(this)) 347 return false; 348 349 // shader program 350 this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.LabelTTF._SHADER_PROGRAM)); 351 352 // prepare everything needed to render the label 353 this._updateWithTextDefinition(textDefinition, false); 354 355 // set the string 356 this.setString(text); 357 358 return true; 359 }, 360 361 /** 362 * set the text definition used by this label 363 * @param {cc.FontDefinition} theDefinition 364 */ 365 setTextDefinition:function(theDefinition){ 366 if (theDefinition) 367 this._updateWithTextDefinition(theDefinition, true); 368 }, 369 370 /** 371 * get the text definition used by this label 372 * @return {cc.FontDefinition} 373 */ 374 getTextDefinition:function(){ 375 return this._prepareTextDefinition(false); 376 }, 377 378 /** 379 * enable or disable shadow for the label 380 * @param {cc.Size} shadowOffset 381 * @param {Number} shadowOpacity (0 to 1) 382 * @param {Number} shadowBlur 383 * @param {Boolean} [mustUpdateTexture=false] This parameter is not used. It's kept for cocos2d-x JSB compatibility 384 */ 385 enableShadow:function(shadowOffset, shadowOpacity, shadowBlur, mustUpdateTexture){ 386 shadowOpacity = shadowOpacity || 0.5; 387 if (false === this._shadowEnabled) 388 this._shadowEnabled = true; 389 390 var locShadowOffset = this._shadowOffset; 391 if (locShadowOffset && (locShadowOffset.width != shadowOffset.width) || (locShadowOffset.height != shadowOffset.height)) { 392 locShadowOffset.width = shadowOffset.width; 393 locShadowOffset.height = shadowOffset.height; 394 } 395 396 if (this._shadowOpacity != shadowOpacity ){ 397 this._shadowOpacity = shadowOpacity; 398 } 399 this._setColorsString(); 400 401 if (this._shadowBlur != shadowBlur) 402 this._shadowBlur = shadowBlur; 403 404 this._needUpdateTexture = true; 405 }, 406 407 /** 408 * disable shadow rendering 409 * @param {Boolean} [mustUpdateTexture=false] This parameter is not used. It's kept for cocos2d-x JSB compatibility 410 */ 411 disableShadow:function(mustUpdateTexture){ 412 if (this._shadowEnabled) { 413 this._shadowEnabled = false; 414 this._needUpdateTexture = true; 415 } 416 }, 417 418 /** 419 * enable or disable stroke 420 * @param {cc.Color3B} strokeColor 421 * @param {Number} strokeSize 422 * @param {Boolean} [mustUpdateTexture=false] This parameter is not used. It's kept for cocos2d-x JSB compatibility 423 */ 424 enableStroke:function(strokeColor, strokeSize, mustUpdateTexture){ 425 if(this._strokeEnabled === false) 426 this._strokeEnabled = true; 427 428 var locStrokeColor = this._strokeColor; 429 if ( (locStrokeColor.r !== strokeColor.r) || (locStrokeColor.g !== strokeColor.g) || (locStrokeColor.b !== strokeColor.b) ) { 430 locStrokeColor.r = strokeColor.r; 431 locStrokeColor.g = strokeColor.g; 432 locStrokeColor.b = strokeColor.b; 433 this._setColorsString(); 434 } 435 436 if (this._strokeSize!== strokeSize) 437 this._strokeSize = strokeSize || 0; 438 439 this._needUpdateTexture = true; 440 }, 441 442 /** 443 * disable stroke 444 * @param {Boolean} [mustUpdateTexture=false] This parameter is not used. It's kept for cocos2d-x JSB compatibility 445 */ 446 disableStroke:function(mustUpdateTexture){ 447 if (this._strokeEnabled){ 448 this._strokeEnabled = false; 449 this._needUpdateTexture = true; 450 } 451 }, 452 453 /** 454 * set text tinting 455 * @param {cc.Color3B} tintColor 456 * @param {Boolean} [mustUpdateTexture=false] This parameter is not used. It's kept for cocos2d-x JSB compatibility 457 */ 458 setFontFillColor:null, 459 460 _setFontFillColorForCanvas: function (tintColor, mustUpdateTexture) { 461 //mustUpdateTexture = (mustUpdateTexture == null) ? true : mustUpdateTexture; 462 var locTextFillColor = this._textFillColor; 463 if (locTextFillColor.r != tintColor.r || locTextFillColor.g != tintColor.g || locTextFillColor.b != tintColor.b) { 464 locTextFillColor.r = tintColor.r; 465 locTextFillColor.g = tintColor.g; 466 locTextFillColor.b = tintColor.b; 467 468 this._setColorsString(); 469 this._needUpdateTexture = true; 470 } 471 }, 472 473 _setFontFillColorForWebGL: function (tintColor, mustUpdateTexture) { 474 var locTextFillColor = this._textFillColor; 475 if (locTextFillColor.r != tintColor.r || locTextFillColor.g != tintColor.g || locTextFillColor.b != tintColor.b) { 476 locTextFillColor.r = tintColor.r; 477 locTextFillColor.g = tintColor.g; 478 locTextFillColor.b = tintColor.b; 479 this._setColorsString(); 480 this._needUpdateTexture = true; 481 } 482 }, 483 484 //set the text definition for this label 485 _updateWithTextDefinition:function(textDefinition, mustUpdateTexture){ 486 if(textDefinition.fontDimensions){ 487 this._dimensions.width = textDefinition.fontDimensions.width; 488 this._dimensions.height = textDefinition.fontDimensions.height; 489 } else { 490 this._dimensions.width = 0; 491 this._dimensions.height = 0; 492 } 493 494 this._hAlignment = textDefinition.fontAlignmentH; 495 this._vAlignment = textDefinition.fontAlignmentV; 496 497 this._fontName = textDefinition.fontName; 498 this._fontSize = textDefinition.fontSize||12; 499 this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'"; 500 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName,this._fontSize); 501 502 // shadow 503 if ( textDefinition.shadowEnabled) 504 this.enableShadow(textDefinition.shadowOffset, textDefinition.shadowOpacity, textDefinition.shadowBlur, false); 505 506 // stroke 507 if ( textDefinition.strokeEnabled ) 508 this.enableStroke(textDefinition.strokeColor, textDefinition.strokeSize, false); 509 510 // fill color 511 this.setFontFillColor(textDefinition.fontFillColor, false); 512 513 if (mustUpdateTexture) 514 this._updateTexture(); 515 }, 516 517 _prepareTextDefinition:function(adjustForResolution){ 518 var texDef = new cc.FontDefinition(); 519 520 if (adjustForResolution){ 521 //texDef.fontSize = (cc.renderContextType === cc.CANVAS) ? this._fontSize : this._fontSize * cc.CONTENT_SCALE_FACTOR(); 522 texDef.fontSize = this._fontSize; 523 texDef.fontDimensions = cc.SIZE_POINTS_TO_PIXELS(this._dimensions); 524 } else { 525 texDef.fontSize = this._fontSize; 526 texDef.fontDimensions = cc.size(this._dimensions.width, this._dimensions.height); 527 } 528 529 texDef.fontName = this._fontName; 530 texDef.fontAlignmentH = this._hAlignment; 531 texDef.fontAlignmentV = this._vAlignment; 532 533 // stroke 534 if ( this._strokeEnabled ){ 535 texDef.strokeEnabled = true; 536 var locStrokeColor = this._strokeColor; 537 texDef.strokeColor = new cc.Color3B(locStrokeColor.r, locStrokeColor.g, locStrokeColor.b); 538 texDef.strokeSize = this._strokeSize; 539 }else 540 texDef.strokeEnabled = false; 541 542 // shadow 543 if ( this._shadowEnabled ){ 544 texDef.shadowEnabled = true; 545 texDef.shadowBlur = this._shadowBlur; 546 texDef.shadowOpacity = this._shadowOpacity; 547 548 texDef.shadowOffset = adjustForResolution ? cc.SIZE_POINTS_TO_PIXELS(this._shadowOffset) 549 : cc.size(this._shadowOffset.width,this._shadowOffset.height); 550 }else 551 texDef._shadowEnabled = false; 552 553 // text tint 554 var locTextFillColor = this._textFillColor; 555 texDef.fontFillColor = new cc.Color3B(locTextFillColor.r, locTextFillColor.g, locTextFillColor.b); 556 return texDef; 557 }, 558 559 _fontClientHeight:18, 560 /** 561 * changes the string to render 562 * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas 563 * @param {String} text text for the label 564 */ 565 setString:function (text) { 566 text = String(text); 567 if (this._originalText != text) { 568 this._originalText = text + ""; 569 570 this._updateString(); 571 572 // Force update 573 this._needUpdateTexture = true; 574 } 575 }, 576 _updateString: function() { 577 this._string = this._originalText; 578 }, 579 /** 580 * set Horizontal Alignment of cc.LabelTTF 581 * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment 582 */ 583 setHorizontalAlignment:function (alignment) { 584 if (alignment !== this._hAlignment) { 585 this._hAlignment = alignment; 586 587 // Force update 588 this._needUpdateTexture = true; 589 } 590 }, 591 592 /** 593 * set Vertical Alignment of cc.LabelTTF 594 * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment 595 */ 596 setVerticalAlignment:function (verticalAlignment) { 597 if (verticalAlignment != this._vAlignment) { 598 this._vAlignment = verticalAlignment; 599 600 // Force update 601 this._needUpdateTexture = true; 602 } 603 }, 604 605 /** 606 * set Dimensions of cc.LabelTTF 607 * @param {cc.Size} dim 608 */ 609 setDimensions:function (dim) { 610 if (dim.width != this._dimensions.width || dim.height != this._dimensions.height) { 611 this._dimensions = dim; 612 this._updateString(); 613 // Force udpate 614 this._needUpdateTexture = true; 615 } 616 }, 617 618 /** 619 * set font size of cc.LabelTTF 620 * @param {Number} fontSize 621 */ 622 setFontSize:function (fontSize) { 623 if (this._fontSize !== fontSize) { 624 this._fontSize = fontSize; 625 this._fontStyleStr = fontSize + "px '" + this._fontName + "'"; 626 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName,fontSize); 627 // Force update 628 this._needUpdateTexture = true; 629 } 630 }, 631 632 /** 633 * set font name of cc.LabelTTF 634 * @param {String} fontName 635 */ 636 setFontName:function (fontName) { 637 if (this._fontName && this._fontName != fontName ) { 638 this._fontName = fontName; 639 this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; 640 this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName,this._fontSize); 641 // Force update 642 this._needUpdateTexture = true; 643 } 644 }, 645 646 _drawTTFInCanvas: function (context) { 647 if (!context) 648 return; 649 var locStrokeShadowOffsetX = this._strokeShadowOffsetX, locStrokeShadowOffsetY = this._strokeShadowOffsetY; 650 var locContentSizeHeight = this._contentSize.height - locStrokeShadowOffsetY, locVAlignment = this._vAlignment, locHAlignment = this._hAlignment, 651 locFontHeight = this._fontClientHeight, locStrokeSize = this._strokeSize; 652 653 context.setTransform(1, 0, 0, 1, 0 + locStrokeShadowOffsetX * 0.5 , locContentSizeHeight + locStrokeShadowOffsetY * 0.5); 654 655 //this is fillText for canvas 656 if (context.font != this._fontStyleStr) 657 context.font = this._fontStyleStr; 658 context.fillStyle = this._fillColorStr; 659 660 var xOffset = 0, yOffset = 0; 661 //stroke style setup 662 var locStrokeEnabled = this._strokeEnabled; 663 if (locStrokeEnabled) { 664 context.lineWidth = locStrokeSize * 2; 665 context.strokeStyle = this._strokeColorStr; 666 } 667 668 //shadow style setup 669 if (this._shadowEnabled) { 670 var locShadowOffset = this._shadowOffset; 671 context.shadowColor = this._shadowColorStr; 672 context.shadowOffsetX = locShadowOffset.width; 673 context.shadowOffsetY = -locShadowOffset.height; 674 context.shadowBlur = this._shadowBlur; 675 } 676 677 context.textBaseline = cc.LabelTTF._textBaseline[locVAlignment]; 678 context.textAlign = cc.LabelTTF._textAlign[locHAlignment]; 679 680 var locContentWidth = this._contentSize.width - locStrokeShadowOffsetX; 681 if (locHAlignment === cc.TEXT_ALIGNMENT_RIGHT) 682 xOffset += locContentWidth; 683 else if (locHAlignment === cc.TEXT_ALIGNMENT_CENTER) 684 xOffset += locContentWidth / 2; 685 else 686 xOffset += 0; 687 if (this._isMultiLine) { 688 var locStrLen = this._strings.length; 689 if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) 690 yOffset = locFontHeight + locContentSizeHeight - locFontHeight * locStrLen; 691 else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER) 692 yOffset = locFontHeight / 2 + (locContentSizeHeight - locFontHeight * locStrLen) / 2; 693 694 for (var i = 0; i < locStrLen; i++) { 695 var line = this._strings[i]; 696 var tmpOffsetY = -locContentSizeHeight + (locFontHeight * i) + yOffset; 697 if (locStrokeEnabled) 698 context.strokeText(line, xOffset, tmpOffsetY); 699 context.fillText(line, xOffset, tmpOffsetY); 700 } 701 } else { 702 if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) { 703 if (locStrokeEnabled) 704 context.strokeText(this._string, xOffset, yOffset); 705 context.fillText(this._string, xOffset, yOffset); 706 } else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP) { 707 yOffset -= locContentSizeHeight ; 708 if (locStrokeEnabled) 709 context.strokeText(this._string, xOffset, yOffset); 710 context.fillText(this._string, xOffset, yOffset); 711 } else { 712 yOffset -= locContentSizeHeight * 0.5; 713 if (locStrokeEnabled) 714 context.strokeText(this._string, xOffset, yOffset); 715 context.fillText(this._string, xOffset, yOffset); 716 } 717 } 718 }, 719 720 _getLabelContext:function () { 721 if (this._labelContext) 722 return this._labelContext; 723 724 if (!this._labelCanvas) { 725 var locCanvas = document.createElement("canvas"); 726 var labelTexture = new cc.Texture2D(); 727 labelTexture.initWithElement(locCanvas); 728 this.setTexture(labelTexture); 729 this._labelCanvas = locCanvas; 730 } 731 this._labelContext = this._labelCanvas.getContext("2d"); 732 return this._labelContext; 733 }, 734 735 _updateTTF: function () { 736 var locDimensionsWidth = this._dimensions.width, i, strLength; 737 var locLineWidth = this._lineWidths; 738 locLineWidth.length = 0; 739 740 this._isMultiLine = false ; 741 this._measureConfig(); 742 if (locDimensionsWidth !== 0) { 743 // Content processing 744 var text = this._string; 745 this._strings = []; 746 for (i = 0, strLength = this._string.length; i < strLength;) { 747 // Find the index of next line 748 var next = this._checkNextline(text.substr(i), locDimensionsWidth); 749 var append = text.substr(i, next); 750 this._strings.push(append); 751 i += next; 752 } 753 } else { 754 this._strings = this._string.split('\n'); 755 for (i = 0, strLength = this._strings.length; i < strLength; i++) { 756 locLineWidth.push(this._measure(this._strings[i])); 757 } 758 } 759 760 if (this._strings.length > 0) 761 this._isMultiLine = true; 762 763 var locSize, locStrokeShadowOffsetX = 0, locStrokeShadowOffsetY = 0; 764 if (this._strokeEnabled) 765 locStrokeShadowOffsetX = locStrokeShadowOffsetY = this._strokeSize * 2; 766 if (this._shadowEnabled) { 767 var locOffsetSize = this._shadowOffset; 768 locStrokeShadowOffsetX += Math.abs(locOffsetSize.width) * 2; 769 locStrokeShadowOffsetY += Math.abs(locOffsetSize.height) * 2; 770 } 771 772 //get offset for stroke and shadow 773 if (locDimensionsWidth === 0) { 774 if (this._isMultiLine) 775 locSize = cc.size(0 | (Math.max.apply(Math, locLineWidth) + locStrokeShadowOffsetX), 776 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); 777 else 778 locSize = cc.size(0 | (this._measure(this._string) + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); 779 } else { 780 if (this._dimensions.height === 0) { 781 if (this._isMultiLine) 782 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); 783 else 784 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); 785 } else { 786 //dimension is already set, contentSize must be same as dimension 787 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._dimensions.height + locStrokeShadowOffsetY)); 788 } 789 } 790 this.setContentSize(locSize); 791 this._strokeShadowOffsetX = locStrokeShadowOffsetX; 792 this._strokeShadowOffsetY = locStrokeShadowOffsetY; 793 794 // need computing _anchorPointInPoints 795 var locAP = this._anchorPoint; 796 this._anchorPointInPoints.x = (locStrokeShadowOffsetX * 0.5) + ((locSize.width - locStrokeShadowOffsetX) * locAP.x); 797 this._anchorPointInPoints.y = (locStrokeShadowOffsetY * 0.5) + ((locSize.height - locStrokeShadowOffsetY) * locAP.y); 798 }, 799 800 getContentSize:function(){ 801 if(this._needUpdateTexture) 802 this._updateTTF(); 803 return cc.Sprite.prototype.getContentSize.call(this); 804 }, 805 806 _updateTexture:function () { 807 var locContext = this._getLabelContext(), locLabelCanvas = this._labelCanvas; 808 var locContentSize = this._contentSize; 809 810 if(this._string.length === 0){ 811 locLabelCanvas.width = 1; 812 locLabelCanvas.height = locContentSize.height; 813 this.setTextureRect(cc.rect(0, 0, 1, locContentSize.height)); 814 return true; 815 } 816 817 //set size for labelCanvas 818 locContext.font = this._fontStyleStr; 819 this._updateTTF(); 820 var width = locContentSize.width, height = locContentSize.height; 821 var flag = locLabelCanvas.width == width && locLabelCanvas.height == height; 822 locLabelCanvas.width = width; 823 locLabelCanvas.height = height; 824 if(flag) locContext.clearRect(0, 0, width, height); 825 826 //draw text to labelCanvas 827 this._drawTTFInCanvas(locContext); 828 this._texture.handleLoadedTexture(); 829 830 this.setTextureRect(cc.rect(0, 0, width, height)); 831 return true; 832 }, 833 834 visit:function(ctx){ 835 if(!this._string || this._string == "") 836 return; 837 if(this._needUpdateTexture ){ 838 this._needUpdateTexture = false; 839 this._updateTexture(); 840 } 841 var context = ctx || cc.renderContext; 842 cc.Sprite.prototype.visit.call(this,context); 843 }, 844 845 draw: null, 846 847 /** 848 * draw sprite to canvas 849 * @param {WebGLRenderingContext} ctx 3d context of canvas 850 */ 851 _drawForWebGL: function (ctx) { 852 if (!this._string || this._string == "") 853 return; 854 855 var gl = ctx || cc.renderContext, locTexture = this._texture; 856 857 if (locTexture && locTexture._isLoaded) { 858 this._shaderProgram.use(); 859 this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4(); 860 861 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 862 cc.glBindTexture2D(locTexture); 863 864 cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); 865 866 gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); 867 if (this._quadDirty) { 868 gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.STATIC_DRAW); 869 this._quadDirty = false; 870 } 871 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); 872 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16); 873 gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); 874 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 875 } 876 877 if (cc.SPRITE_DEBUG_DRAW === 1) { 878 // draw bounding box 879 var locQuad = this._quad; 880 var verticesG1 = [ 881 cc.p(locQuad.tl.vertices.x, locQuad.tl.vertices.y), 882 cc.p(locQuad.bl.vertices.x, locQuad.bl.vertices.y), 883 cc.p(locQuad.br.vertices.x, locQuad.br.vertices.y), 884 cc.p(locQuad.tr.vertices.x, locQuad.tr.vertices.y) 885 ]; 886 cc.drawingUtil.drawPoly(verticesG1, 4, true); 887 } else if (cc.SPRITE_DEBUG_DRAW === 2) { 888 // draw texture box 889 var drawSizeG2 = this.getTextureRect()._size; 890 var offsetPixG2 = this.getOffsetPosition(); 891 var verticesG2 = [cc.p(offsetPixG2.x, offsetPixG2.y), cc.p(offsetPixG2.x + drawSizeG2.width, offsetPixG2.y), 892 cc.p(offsetPixG2.x + drawSizeG2.width, offsetPixG2.y + drawSizeG2.height), cc.p(offsetPixG2.x, offsetPixG2.y + drawSizeG2.height)]; 893 cc.drawingUtil.drawPoly(verticesG2, 4, true); 894 } // CC_SPRITE_DEBUG_DRAW 895 cc.g_NumberOfDraws++; 896 }, 897 898 _setTextureRectForCanvas: function (rect, rotated, untrimmedSize) { 899 this._rectRotated = rotated || false; 900 untrimmedSize = untrimmedSize || rect._size; 901 902 this.setContentSize(untrimmedSize); 903 this.setVertexRect(rect); 904 905 var locTextureCoordRect = this._textureRect_Canvas; 906 locTextureCoordRect.x = rect.x; 907 locTextureCoordRect.y = rect.y; 908 locTextureCoordRect.width = rect.width; 909 locTextureCoordRect.height = rect.height; 910 locTextureCoordRect.validRect = !(locTextureCoordRect.width === 0 || locTextureCoordRect.height === 0 911 || locTextureCoordRect.x < 0 || locTextureCoordRect.y < 0); 912 913 var relativeOffset = this._unflippedOffsetPositionFromCenter; 914 if (this._flippedX) 915 relativeOffset.x = -relativeOffset.x; 916 if (this._flippedY) 917 relativeOffset.y = -relativeOffset.y; 918 this._offsetPosition.x = relativeOffset.x + (this._contentSize.width - this._rect.width) / 2; 919 this._offsetPosition.y = relativeOffset.y + (this._contentSize.height - this._rect.height) / 2; 920 921 // rendering using batch node 922 if (this._batchNode) { 923 this._dirty = true; 924 } 925 }, 926 927 _setTextureCoords:function (rect) { 928 var tex = this._batchNode ? this._textureAtlas.getTexture() : this._texture; 929 if (!tex) 930 return; 931 932 var atlasWidth = tex.getPixelsWide(); 933 var atlasHeight = tex.getPixelsHigh(); 934 935 var left, right, top, bottom, tempSwap, locQuad = this._quad; 936 if (this._rectRotated) { 937 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 938 left = (2 * rect.x + 1) / (2 * atlasWidth); 939 right = left + (rect.height * 2 - 2) / (2 * atlasWidth); 940 top = (2 * rect.y + 1) / (2 * atlasHeight); 941 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); 942 } else { 943 left = rect.x / atlasWidth; 944 right = (rect.x + rect.height) / atlasWidth; 945 top = rect.y / atlasHeight; 946 bottom = (rect.y + rect.width) / atlasHeight; 947 }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 948 949 if (this._flippedX) { 950 tempSwap = top; 951 top = bottom; 952 bottom = tempSwap; 953 } 954 955 if (this._flippedY) { 956 tempSwap = left; 957 left = right; 958 right = tempSwap; 959 } 960 961 locQuad.bl.texCoords.u = left; 962 locQuad.bl.texCoords.v = top; 963 locQuad.br.texCoords.u = left; 964 locQuad.br.texCoords.v = bottom; 965 locQuad.tl.texCoords.u = right; 966 locQuad.tl.texCoords.v = top; 967 locQuad.tr.texCoords.u = right; 968 locQuad.tr.texCoords.v = bottom; 969 } else { 970 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 971 left = (2 * rect.x + 1) / (2 * atlasWidth); 972 right = left + (rect.width * 2 - 2) / (2 * atlasWidth); 973 top = (2 * rect.y + 1) / (2 * atlasHeight); 974 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); 975 } else { 976 left = rect.x / atlasWidth; 977 right = (rect.x + rect.width) / atlasWidth; 978 top = rect.y / atlasHeight; 979 bottom = (rect.y + rect.height) / atlasHeight; 980 } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 981 982 if (this._flippedX) { 983 tempSwap = left; 984 left = right; 985 right = tempSwap; 986 } 987 988 if (this._flippedY) { 989 tempSwap = top; 990 top = bottom; 991 bottom = tempSwap; 992 } 993 994 locQuad.bl.texCoords.u = left; 995 locQuad.bl.texCoords.v = bottom; 996 locQuad.br.texCoords.u = right; 997 locQuad.br.texCoords.v = bottom; 998 locQuad.tl.texCoords.u = left; 999 locQuad.tl.texCoords.v = top; 1000 locQuad.tr.texCoords.u = right; 1001 locQuad.tr.texCoords.v = top; 1002 } 1003 this._quadDirty = true; 1004 } 1005 }); 1006 1007 if(cc.Browser.supportWebGL){ 1008 cc.LabelTTF.prototype.setColor = cc.Sprite.prototype.setColor; 1009 cc.LabelTTF.prototype._setColorsString = cc.LabelTTF.prototype._setColorsStringForWebGL; 1010 cc.LabelTTF.prototype.updateDisplayedColor = cc.Sprite.prototype.updateDisplayedColor; 1011 cc.LabelTTF.prototype.setOpacity = cc.Sprite.prototype.setOpacity; 1012 cc.LabelTTF.prototype.updateDisplayedOpacity = cc.Sprite.prototype.updateDisplayedOpacity; 1013 cc.LabelTTF.prototype.initWithStringAndTextDefinition = cc.LabelTTF.prototype._initWithStringAndTextDefinitionForWebGL; 1014 cc.LabelTTF.prototype.setFontFillColor = cc.LabelTTF.prototype._setFontFillColorForWebGL; 1015 cc.LabelTTF.prototype.draw = cc.LabelTTF.prototype._drawForWebGL; 1016 cc.LabelTTF.prototype.setTextureRect = cc.Sprite.prototype._setTextureRectForWebGL; 1017 } else { 1018 cc.LabelTTF.prototype.setColor = cc.LabelTTF.prototype._setColorForCanvas; 1019 cc.LabelTTF.prototype._setColorsString = cc.LabelTTF.prototype._setColorsStringForCanvas; 1020 cc.LabelTTF.prototype.updateDisplayedColor = cc.LabelTTF.prototype._updateDisplayedColorForCanvas; 1021 cc.LabelTTF.prototype.setOpacity = cc.LabelTTF.prototype._setOpacityForCanvas; 1022 cc.LabelTTF.prototype.updateDisplayedOpacity = cc.LabelTTF.prototype._updateDisplayedOpacityForCanvas; 1023 cc.LabelTTF.prototype.initWithStringAndTextDefinition = cc.LabelTTF.prototype._initWithStringAndTextDefinitionForCanvas; 1024 cc.LabelTTF.prototype.setFontFillColor = cc.LabelTTF.prototype._setFontFillColorForCanvas; 1025 cc.LabelTTF.prototype.draw = cc.Sprite.prototype.draw; 1026 cc.LabelTTF.prototype.setTextureRect = cc.LabelTTF.prototype._setTextureRectForCanvas; 1027 } 1028 1029 cc.LabelTTF._textAlign = ["left", "center", "right"]; 1030 1031 cc.LabelTTF._textBaseline = ["top", "middle", "bottom"]; 1032 1033 // Class static properties for measure util 1034 cc.LabelTTF._checkRegEx = /(.+?)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/; 1035 cc.LabelTTF._reverseCheckRegEx = /(.*)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/; 1036 cc.LabelTTF._checkEnRegEx = /[\s\-\/\\\:]/; 1037 1038 /** 1039 * creates a cc.LabelTTF from a font name, alignment, dimension and font size 1040 * @param {String} label 1041 * @param {String} fontName 1042 * @param {Number} fontSize 1043 * @param {cc.Size} [dimensions=cc.SIZE_ZERO] 1044 * @param {Number} [hAlignment=] 1045 * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] 1046 * @return {cc.LabelTTF|Null} 1047 * @example 1048 * // Example 1049 * var myLabel = cc.LabelTTF.create('label text', 'Times New Roman', 32, cc.size(32,16), cc.TEXT_ALIGNMENT_LEFT); 1050 */ 1051 cc.LabelTTF.create = function (label, fontName, fontSize, dimensions, hAlignment, vAlignment) { 1052 var ret = new cc.LabelTTF(); 1053 if (ret.initWithString(label, fontName, fontSize, dimensions, hAlignment, vAlignment)) 1054 return ret; 1055 return null; 1056 }; 1057 1058 /** 1059 * Create a label with string and a font definition 1060 * @param {String} text 1061 * @param {cc.FontDefinition} textDefinition 1062 * @return {cc.LabelTTF|Null} 1063 */ 1064 cc.LabelTTF.createWithFontDefinition = function(text, textDefinition){ 1065 var ret = new cc.LabelTTF(); 1066 if(ret && ret.initWithStringAndTextDefinition(text, textDefinition)) 1067 return ret; 1068 return null; 1069 }; 1070 1071 if(cc.USE_LA88_LABELS) 1072 cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTURECOLOR; 1073 else 1074 cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTUREA8COLOR; 1075 1076 cc.LabelTTF.__labelHeightDiv = document.createElement("div"); 1077 cc.LabelTTF.__labelHeightDiv.style.fontFamily = "Arial"; 1078 cc.LabelTTF.__labelHeightDiv.style.position = "absolute"; 1079 cc.LabelTTF.__labelHeightDiv.style.left = "-100px"; 1080 cc.LabelTTF.__labelHeightDiv.style.top = "-100px"; 1081 cc.LabelTTF.__labelHeightDiv.style.lineHeight = "normal"; 1082 document.body.appendChild(cc.LabelTTF.__labelHeightDiv); 1083 1084 cc.LabelTTF.__getFontHeightByDiv = function(fontName, fontSize){ 1085 var clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize]; 1086 if (clientHeight > 0) return clientHeight; 1087 var labelDiv = cc.LabelTTF.__labelHeightDiv; 1088 labelDiv.innerHTML = "ajghl~!"; 1089 labelDiv.style.fontFamily = fontName; 1090 labelDiv.style.fontSize = fontSize + "px"; 1091 clientHeight = labelDiv.clientHeight ; 1092 cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize] = clientHeight; 1093 labelDiv.innerHTML = ""; 1094 return clientHeight; 1095 }; 1096 1097 cc.LabelTTF.__fontHeightCache = {}; 1098 1099 1100 1101