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