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