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