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