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