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