1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies 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  * @property {String}       string          - Content string of label
 36  * @property {Number}       textAlign       - Horizontal Alignment of label: cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT
 37  * @property {Number}       verticalAlign   - Vertical Alignment of label: cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM
 38  * @property {Number}       fontSize        - Font size of label
 39  * @property {String}       fontName        - Font name of label
 40  * @property {String}       font            - The label font with a style string: e.g. "18px Verdana"
 41  * @property {Number}       boundingWidth   - Width of the bounding box of label, the real content width is limited by boundingWidth
 42  * @property {Number}       boundingHeight  - Height of the bounding box of label, the real content height is limited by boundingHeight
 43  * @property {cc.Color}     fillStyle       - The fill color
 44  * @property {cc.Color}     strokeStyle     - The stroke color
 45  * @property {Number}       lineWidth       - The line width for stroke
 46  * @property {Number}       shadowOffsetX   - The x axis offset of shadow
 47  * @property {Number}       shadowOffsetY   - The y axis offset of shadow
 48  * @property {Number}       shadowOpacity   - The opacity of shadow
 49  * @property {Number}       shadowBlur      - The blur size of shadow
 50  *
 51  */
 52 cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{
 53     _dimensions: null,
 54     _hAlignment: cc.TEXT_ALIGNMENT_CENTER,
 55     _vAlignment: cc.VERTICAL_TEXT_ALIGNMENT_TOP,
 56     _fontName: null,
 57     _fontSize: 0.0,
 58     _string: "",
 59     _originalText: null,
 60     _isMultiLine: false,
 61     _fontStyleStr: null,
 62 
 63     // font shadow
 64     _shadowEnabled: false,
 65     _shadowOffset: null,
 66     _shadowOpacity: 0,
 67     _shadowBlur: 0,
 68     _shadowColorStr: null,
 69 
 70     // font stroke
 71     _strokeEnabled: false,
 72     _strokeColor: null,
 73     _strokeSize: 0,
 74     _strokeColorStr: null,
 75 
 76     // font tint
 77     _textFillColor: null,
 78     _fillColorStr: null,
 79 
 80     _strokeShadowOffsetX: 0,
 81     _strokeShadowOffsetY: 0,
 82     _needUpdateTexture: false,
 83 
 84     _labelCanvas: null,
 85     _labelContext: null,
 86     _lineWidths: null,
 87     _className: "LabelTTF",
 88 
 89 
 90     /**
 91      * creates a cc.LabelTTF from a font name, alignment, dimension and font size
 92      * Constructor of cc.LabelTTF
 93      * @param {String} text
 94      * @param {String|cc.FontDefinition} [fontName="Arial"]
 95      * @param {Number} [fontSize=16]
 96      * @param {cc.Size} [dimensions=cc.size(0,0)]
 97      * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT]
 98      * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP]
 99      * @example
100      * var myLabel = new cc.LabelTTF('label text',  'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT);
101      *
102      * var fontDef = new cc.FontDefinition();
103      * fontDef.fontName = "Arial";
104      * fontDef.fontSize = "32";
105      * var myLabel = new cc.LabelTTF('label text',  fontDef);
106      */
107     ctor: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) {
108         cc.Sprite.prototype.ctor.call(this);
109 
110         this._dimensions = cc.size(0, 0);
111         this._hAlignment = cc.TEXT_ALIGNMENT_LEFT;
112         this._vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP;
113         this._opacityModifyRGB = false;
114         this._fontStyleStr = "";
115         this._fontName = "Arial";
116         this._isMultiLine = false;
117 
118         this._shadowEnabled = false;
119         this._shadowOffset = cc.p(0, 0);
120         this._shadowOpacity = 0;
121         this._shadowBlur = 0;
122         this._shadowColorStr = "rgba(128, 128, 128, 0.5)";
123 
124         this._strokeEnabled = false;
125         this._strokeColor = cc.color(255, 255, 255, 255);
126         this._strokeSize = 0;
127         this._strokeColorStr = "";
128 
129         this._textFillColor = cc.color(255, 255, 255, 255);
130         this._fillColorStr = "rgba(255,255,255,1)";
131         this._strokeShadowOffsetX = 0;
132         this._strokeShadowOffsetY = 0;
133         this._needUpdateTexture = false;
134 
135         this._lineWidths = [];
136 
137         this._setColorsString();
138 
139         if (fontName && fontName instanceof cc.FontDefinition) {
140             this.initWithStringAndTextDefinition(text, fontName);
141         }
142         else {
143             cc.LabelTTF.prototype.initWithString.call(this, text, fontName, fontSize, dimensions, hAlignment, vAlignment);
144         }
145     },
146 
147     init: function () {
148         return this.initWithString(" ", this._fontName, this._fontSize);
149     },
150 
151     _measureConfig: function () {
152         this._getLabelContext().font = this._fontStyleStr;
153     },
154     _measure: function (text) {
155         return this._getLabelContext().measureText(text).width;
156     },
157     _checkNextline: function (text, width) {
158         var tWidth = this._measure(text);
159         // Estimated word number per line
160         var baseNb = Math.floor(text.length * width / tWidth);
161         // Next line is a line with line break
162         var nextlinebreak = text.indexOf('\n');
163         if (baseNb * 0.8 >= nextlinebreak && nextlinebreak > 0) return nextlinebreak + 1;
164         // Text width smaller than requested width
165         if (tWidth < width) return text.length;
166 
167         var found = false, l = width + 1, idfound = -1, index = baseNb, result,
168             re = cc.LabelTTF._checkRegEx,
169             reversre = cc.LabelTTF._reverseCheckRegEx,
170             enre = cc.LabelTTF._checkEnRegEx,
171             substr = text.substr(baseNb);
172 
173         // Forward check
174         // Find next special caracter or chinese caracters
175         while (result = re.exec(substr)) {
176             index += result[0].length;
177             var tem = text.substr(0, index);
178             l = this._measure(tem);
179             if (result[2] == '\n' && l < width) {
180                 found = true;
181                 idfound = index;
182                 break;
183             }
184             if (l > width) {
185                 if (idfound != -1)
186                     found = true;
187                 break;
188             }
189             idfound = index;
190             substr = text.substr(index);
191         }
192         if (found) return idfound;
193 
194         // Backward check when forward check failed
195         substr = text.substr(0, baseNb);
196         idfound = baseNb;
197         while (result = reversre.exec(substr)) {
198             // BUG: Not secured if check with result[0]
199             idfound = result[1].length;
200             substr = result[1];
201             l = this._measure(substr);
202             if (l < width) {
203                 if (enre.test(result[2]))
204                     idfound++;
205                 break;
206             }
207         }
208 
209         // Avoid when idfound == 0, the process may enter in a infinite loop
210         return idfound || 1;
211     },
212 
213     /**
214      * Prints out a description of this class
215      * @return {String}
216      */
217     description: function () {
218         return "<cc.LabelTTF | FontName =" + this._fontName + " FontSize = " + this._fontSize.toFixed(1) + ">";
219     },
220 
221     setColor: null,
222 
223     _setColorsString: null,
224 
225     updateDisplayedColor: null,
226     setOpacity: null,
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         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._renderType === cc._RENDER_TYPE_CANVAS) ? fontSize : fontSize * cc.contentScaleFactor();
311         this._fontSize = fontSize;
312         this._fontStyleStr = this._fontSize + "px '" + fontName + "'";
313         this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize);
314         this.string = strInfo;
315         this._setColorsString();
316         this._updateTexture();
317         this._needUpdateTexture = false;
318         return true;
319     },
320 
321     /**
322      * initializes the CCLabelTTF with a font name, alignment, dimension and font size
323      * @param {String} text
324      * @param {cc.FontDefinition} textDefinition
325      * @return {Boolean}
326      */
327     initWithStringAndTextDefinition: null,
328 
329     /**
330      * set the text definition used by this label
331      * @param {cc.FontDefinition} theDefinition
332      */
333     setTextDefinition: function (theDefinition) {
334         if (theDefinition)
335             this._updateWithTextDefinition(theDefinition, true);
336     },
337 
338     /**
339      * get the text definition used by this label
340      * @return {cc.FontDefinition}
341      */
342     getTextDefinition: function () {
343         return this._prepareTextDefinition(false);
344     },
345 
346     /**
347      * enable or disable shadow for the label
348      * @param {cc.Point} shadowOffset
349      * @param {Number} shadowOpacity (0 to 1)
350      * @param {Number} shadowBlur
351      */
352     enableShadow: function (shadowOffsetX, shadowOffsetY, shadowOpacity, shadowBlur) {
353         shadowOpacity = shadowOpacity || 0.5;
354         if (false === this._shadowEnabled)
355             this._shadowEnabled = true;
356 
357         var locShadowOffset = this._shadowOffset;
358         if (locShadowOffset && (locShadowOffset.x != shadowOffsetX) || (locShadowOffset._y != shadowOffsetY)) {
359             locShadowOffset.x = shadowOffsetX;
360             locShadowOffset.y = shadowOffsetY;
361         }
362 
363         if (this._shadowOpacity != shadowOpacity) {
364             this._shadowOpacity = shadowOpacity;
365         }
366         this._setColorsString();
367 
368         if (this._shadowBlur != shadowBlur)
369             this._shadowBlur = shadowBlur;
370 
371         this._needUpdateTexture = true;
372     },
373 
374     _getShadowOffsetX: function () {
375         return this._shadowOffset.x;
376     },
377     _setShadowOffsetX: function (x) {
378         if (false === this._shadowEnabled)
379             this._shadowEnabled = true;
380 
381         if (this._shadowOffset.x != x) {
382             this._shadowOffset.x = x;
383             this._needUpdateTexture = true;
384         }
385     },
386 
387     _getShadowOffsetY: function () {
388         return this._shadowOffset._y;
389     },
390     _setShadowOffsetY: function (y) {
391         if (false === this._shadowEnabled)
392             this._shadowEnabled = true;
393 
394         if (this._shadowOffset._y != y) {
395             this._shadowOffset._y = y;
396             this._needUpdateTexture = true;
397         }
398     },
399 
400     _getShadowOffset: function () {
401         return cc.p(this._shadowOffset.x, this._shadowOffset.y);
402     },
403     _setShadowOffset: function (offset) {
404         if (false === this._shadowEnabled)
405             this._shadowEnabled = true;
406 
407         if (this._shadowOffset.x != offset.x || this._shadowOffset.y != offset.y) {
408             this._shadowOffset.x = offset.x;
409             this._shadowOffset.y = offset.y;
410             this._needUpdateTexture = true;
411         }
412     },
413 
414     _getShadowOpacity: function () {
415         return this._shadowOpacity;
416     },
417     _setShadowOpacity: function (shadowOpacity) {
418         if (false === this._shadowEnabled)
419             this._shadowEnabled = true;
420 
421         if (this._shadowOpacity != shadowOpacity) {
422             this._shadowOpacity = shadowOpacity;
423             this._setColorsString();
424             this._needUpdateTexture = true;
425         }
426     },
427 
428     _getShadowBlur: function () {
429         return this._shadowBlur;
430     },
431     _setShadowBlur: function (shadowBlur) {
432         if (false === this._shadowEnabled)
433             this._shadowEnabled = true;
434 
435         if (this._shadowBlur != shadowBlur) {
436             this._shadowBlur = shadowBlur;
437             this._needUpdateTexture = true;
438         }
439     },
440 
441     /**
442      * disable shadow rendering
443      */
444     disableShadow: function () {
445         if (this._shadowEnabled) {
446             this._shadowEnabled = false;
447             this._needUpdateTexture = true;
448         }
449     },
450 
451     /**
452      * enable or disable stroke
453      * @param {cc.Color} strokeColor
454      * @param {Number} strokeSize
455      */
456     enableStroke: function (strokeColor, strokeSize) {
457         if (this._strokeEnabled === false)
458             this._strokeEnabled = true;
459 
460         var locStrokeColor = this._strokeColor;
461         if ((locStrokeColor.r !== strokeColor.r) || (locStrokeColor.g !== strokeColor.g) || (locStrokeColor.b !== strokeColor.b)) {
462             locStrokeColor.r = strokeColor.r;
463             locStrokeColor.g = strokeColor.g;
464             locStrokeColor.b = strokeColor.b;
465             this._setColorsString();
466         }
467 
468         if (this._strokeSize !== strokeSize)
469             this._strokeSize = strokeSize || 0;
470 
471         this._needUpdateTexture = true;
472     },
473 
474     _getStrokeStyle: function () {
475         return this._strokeColor;
476     },
477     _setStrokeStyle: function (strokeStyle) {
478         if (this._strokeEnabled === false)
479             this._strokeEnabled = true;
480 
481         var locStrokeColor = this._strokeColor;
482         if ((locStrokeColor.r !== strokeStyle.r) || (locStrokeColor.g !== strokeStyle.g) || (locStrokeColor.b !== strokeStyle.b)) {
483             locStrokeColor.r = strokeStyle.r;
484             locStrokeColor.g = strokeStyle.g;
485             locStrokeColor.b = strokeStyle.b;
486             this._setColorsString();
487 
488             this._needUpdateTexture = true;
489         }
490     },
491 
492     _getLineWidth: function () {
493         return this._strokeSize;
494     },
495     _setLineWidth: function (lineWidth) {
496         if (this._strokeEnabled === false)
497             this._strokeEnabled = true;
498 
499         if (this._strokeSize !== lineWidth) {
500             this._strokeSize = lineWidth || 0;
501             this._needUpdateTexture = true;
502         }
503     },
504 
505     /**
506      * disable stroke
507      */
508     disableStroke: function () {
509         if (this._strokeEnabled) {
510             this._strokeEnabled = false;
511             this._needUpdateTexture = true;
512         }
513     },
514 
515     /**
516      * set text tinting
517      * @function
518      * @param {cc.Color} tintColor
519      */
520     setFontFillColor: null,
521 
522     _getFillStyle: function () {
523         return this._textFillColor;
524     },
525 
526     //set the text definition for this label
527     _updateWithTextDefinition: function (textDefinition, mustUpdateTexture) {
528         if (textDefinition.fontDimensions) {
529             this._dimensions.width = textDefinition.boundingWidth;
530             this._dimensions.height = textDefinition.boundingHeight;
531         } else {
532             this._dimensions.width = 0;
533             this._dimensions.height = 0;
534         }
535 
536         this._hAlignment = textDefinition.textAlign;
537         this._vAlignment = textDefinition.verticalAlign;
538 
539         this._fontName = textDefinition.fontName;
540         this._fontSize = textDefinition.fontSize || 12;
541         this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'";
542         this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize);
543 
544         // shadow
545         if (textDefinition.shadowEnabled)
546             this.enableShadow(textDefinition.shadowOffsetX,
547                 textDefinition.shadowOffsetY,
548                 textDefinition.shadowOpacity,
549                 textDefinition.shadowBlur);
550 
551         // stroke
552         if (textDefinition.strokeEnabled)
553             this.enableStroke(textDefinition.strokeStyle, textDefinition.lineWidth);
554 
555         // fill color
556         this.setFontFillColor(textDefinition.fillStyle);
557 
558         if (mustUpdateTexture)
559             this._updateTexture();
560     },
561 
562     _prepareTextDefinition: function (adjustForResolution) {
563         var texDef = new cc.FontDefinition();
564 
565         if (adjustForResolution) {
566             //texDef.fontSize = (cc._renderType === cc._RENDER_TYPE_CANVAS) ? this._fontSize : this._fontSize * cc.contentScaleFactor();
567             texDef.fontSize = this._fontSize;
568             texDef.boundingWidth = cc.contentScaleFactor() * this._dimensions.width;
569             texDef.boundingHeight = cc.contentScaleFactor() * this._dimensions.height;
570         } else {
571             texDef.fontSize = this._fontSize;
572             texDef.boundingWidth = this._dimensions.width;
573             texDef.boundingHeight = this._dimensions.height;
574         }
575 
576         texDef.fontName = this._fontName;
577         texDef.textAlign = this._hAlignment;
578         texDef.verticalAlign = this._vAlignment;
579 
580         // stroke
581         if (this._strokeEnabled) {
582             texDef.strokeEnabled = true;
583             var locStrokeColor = this._strokeColor;
584             texDef.strokeStyle = cc.color(locStrokeColor.r, locStrokeColor.g, locStrokeColor.b);
585             texDef.lineWidth = this._strokeSize;
586         } else
587             texDef.strokeEnabled = false;
588 
589         // shadow
590         if (this._shadowEnabled) {
591             texDef.shadowEnabled = true;
592             texDef.shadowBlur = this._shadowBlur;
593             texDef.shadowOpacity = this._shadowOpacity;
594 
595             texDef.shadowOffsetX = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.x;
596             texDef.shadowOffsetY = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.y;
597         } else
598             texDef._shadowEnabled = false;
599 
600         // text tint
601         var locTextFillColor = this._textFillColor;
602         texDef.fillStyle = cc.color(locTextFillColor.r, locTextFillColor.g, locTextFillColor.b);
603         return texDef;
604     },
605 
606     _fontClientHeight: 18,
607     /**
608      * changes the string to render
609      * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas
610      * @param {String} text text for the label
611      */
612     setString: function (text) {
613         text = String(text);
614         if (this._originalText != text) {
615             this._originalText = text + "";
616 
617             this._updateString();
618 
619             // Force update
620             this._needUpdateTexture = true;
621         }
622     },
623     _updateString: function () {
624         this._string = this._originalText;
625     },
626     /**
627      * set Horizontal Alignment of cc.LabelTTF
628      * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment
629      */
630     setHorizontalAlignment: function (alignment) {
631         if (alignment !== this._hAlignment) {
632             this._hAlignment = alignment;
633 
634             // Force update
635             this._needUpdateTexture = true;
636         }
637     },
638 
639     /**
640      * set Vertical Alignment of cc.LabelTTF
641      * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment
642      */
643     setVerticalAlignment: function (verticalAlignment) {
644         if (verticalAlignment != this._vAlignment) {
645             this._vAlignment = verticalAlignment;
646 
647             // Force update
648             this._needUpdateTexture = true;
649         }
650     },
651 
652     /**
653      * set Dimensions of cc.LabelTTF
654      * @param {cc.Size} dim
655      */
656     setDimensions: function (dim) {
657         if (dim.width != this._dimensions.width || dim.height != this._dimensions.height) {
658             this._dimensions = dim;
659             this._updateString();
660             // Force udpate
661             this._needUpdateTexture = true;
662         }
663     },
664 
665     _getBoundingWidth: function () {
666         return this._dimensions.width;
667     },
668     _setBoundingWidth: function (width) {
669         if (width != this._dimensions.width) {
670             this._dimensions.width = width;
671             this._updateString();
672             // Force udpate
673             this._needUpdateTexture = true;
674         }
675     },
676 
677     _getBoundingHeight: function () {
678         return this._dimensions.height;
679     },
680     _setBoundingHeight: function (height) {
681         if (height != this._dimensions.height) {
682             this._dimensions.height = height;
683             this._updateString();
684             // Force udpate
685             this._needUpdateTexture = true;
686         }
687     },
688 
689     /**
690      * set font size of cc.LabelTTF
691      * @param {Number} fontSize
692      */
693     setFontSize: function (fontSize) {
694         if (this._fontSize !== fontSize) {
695             this._fontSize = fontSize;
696             this._fontStyleStr = fontSize + "px '" + this._fontName + "'";
697             this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, fontSize);
698             // Force update
699             this._needUpdateTexture = true;
700         }
701     },
702 
703     /**
704      * set font name of cc.LabelTTF
705      * @param {String} fontName
706      */
707     setFontName: function (fontName) {
708         if (this._fontName && this._fontName != fontName) {
709             this._fontName = fontName;
710             this._fontStyleStr = this._fontSize + "px '" + fontName + "'";
711             this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize);
712             // Force update
713             this._needUpdateTexture = true;
714         }
715     },
716 
717     _getFont: function () {
718         return this._fontStyleStr;
719     },
720     _setFont: function (fontStyle) {
721         var res = cc.LabelTTF._fontStyleRE.exec(fontStyle);
722         if (res) {
723             this._fontSize = parseInt(res[1]);
724             this._fontName = res[2];
725             this._fontStyleStr = fontStyle;
726             this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize);
727             // Force update
728             this._needUpdateTexture = true;
729         }
730     },
731 
732     _drawTTFInCanvas: function (context) {
733         if (!context)
734             return;
735         var locStrokeShadowOffsetX = this._strokeShadowOffsetX, locStrokeShadowOffsetY = this._strokeShadowOffsetY;
736         var locContentSizeHeight = this._contentSize.height - locStrokeShadowOffsetY, locVAlignment = this._vAlignment, locHAlignment = this._hAlignment,
737             locFontHeight = this._fontClientHeight, locStrokeSize = this._strokeSize;
738 
739         context.setTransform(1, 0, 0, 1, 0 + locStrokeShadowOffsetX * 0.5, locContentSizeHeight + locStrokeShadowOffsetY * 0.5);
740 
741         //this is fillText for canvas
742         if (context.font != this._fontStyleStr)
743             context.font = this._fontStyleStr;
744         context.fillStyle = this._fillColorStr;
745 
746         var xOffset = 0, yOffset = 0;
747         //stroke style setup
748         var locStrokeEnabled = this._strokeEnabled;
749         if (locStrokeEnabled) {
750             context.lineWidth = locStrokeSize * 2;
751             context.strokeStyle = this._strokeColorStr;
752         }
753 
754         //shadow style setup
755         if (this._shadowEnabled) {
756             var locShadowOffset = this._shadowOffset;
757             context.shadowColor = this._shadowColorStr;
758             context.shadowOffsetX = locShadowOffset.x;
759             context.shadowOffsetY = -locShadowOffset.y;
760             context.shadowBlur = this._shadowBlur;
761         }
762 
763         context.textBaseline = cc.LabelTTF._textBaseline[locVAlignment];
764         context.textAlign = cc.LabelTTF._textAlign[locHAlignment];
765 
766         var locContentWidth = this._contentSize.width - locStrokeShadowOffsetX;
767         if (locHAlignment === cc.TEXT_ALIGNMENT_RIGHT)
768             xOffset += locContentWidth;
769         else if (locHAlignment === cc.TEXT_ALIGNMENT_CENTER)
770             xOffset += locContentWidth / 2;
771         else
772             xOffset += 0;
773         if (this._isMultiLine) {
774             var locStrLen = this._strings.length;
775             if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM)
776                 yOffset = locFontHeight + locContentSizeHeight - locFontHeight * locStrLen;
777             else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER)
778                 yOffset = locFontHeight / 2 + (locContentSizeHeight - locFontHeight * locStrLen) / 2;
779 
780             for (var i = 0; i < locStrLen; i++) {
781                 var line = this._strings[i];
782                 var tmpOffsetY = -locContentSizeHeight + (locFontHeight * i) + yOffset;
783                 if (locStrokeEnabled)
784                     context.strokeText(line, xOffset, tmpOffsetY);
785                 context.fillText(line, xOffset, tmpOffsetY);
786             }
787         } else {
788             if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) {
789                 if (locStrokeEnabled)
790                     context.strokeText(this._string, xOffset, yOffset);
791                 context.fillText(this._string, xOffset, yOffset);
792             } else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP) {
793                 yOffset -= locContentSizeHeight;
794                 if (locStrokeEnabled)
795                     context.strokeText(this._string, xOffset, yOffset);
796                 context.fillText(this._string, xOffset, yOffset);
797             } else {
798                 yOffset -= locContentSizeHeight * 0.5;
799                 if (locStrokeEnabled)
800                     context.strokeText(this._string, xOffset, yOffset);
801                 context.fillText(this._string, xOffset, yOffset);
802             }
803         }
804     },
805 
806     _getLabelContext: function () {
807         if (this._labelContext)
808             return this._labelContext;
809 
810         if (!this._labelCanvas) {
811             var locCanvas = cc.newElement("canvas");
812             var labelTexture = new cc.Texture2D();
813             labelTexture.initWithElement(locCanvas);
814             this.texture = labelTexture;
815             this._labelCanvas = locCanvas;
816         }
817         this._labelContext = this._labelCanvas.getContext("2d");
818         return this._labelContext;
819     },
820 
821     _updateTTF: function () {
822         var locDimensionsWidth = this._dimensions.width, i, strLength;
823         var locLineWidth = this._lineWidths;
824         locLineWidth.length = 0;
825 
826         this._isMultiLine = false;
827         this._measureConfig();
828         if (locDimensionsWidth !== 0) {
829             // Content processing
830             var text = this._string;
831             this._strings = [];
832             for (i = 0, strLength = this._string.length; i < strLength;) {
833                 // Find the index of next line
834                 var next = this._checkNextline(text.substr(i), locDimensionsWidth);
835                 var append = text.substr(i, next);
836                 this._strings.push(append);
837                 i += next;
838             }
839         } else {
840             this._strings = this._string.split('\n');
841             for (i = 0, strLength = this._strings.length; i < strLength; i++) {
842                 locLineWidth.push(this._measure(this._strings[i]));
843             }
844         }
845 
846         if (this._strings.length > 0)
847             this._isMultiLine = true;
848 
849         var locSize, locStrokeShadowOffsetX = 0, locStrokeShadowOffsetY = 0;
850         if (this._strokeEnabled)
851             locStrokeShadowOffsetX = locStrokeShadowOffsetY = this._strokeSize * 2;
852         if (this._shadowEnabled) {
853             var locOffsetSize = this._shadowOffset;
854             locStrokeShadowOffsetX += Math.abs(locOffsetSize.x) * 2;
855             locStrokeShadowOffsetY += Math.abs(locOffsetSize.y) * 2;
856         }
857 
858         //get offset for stroke and shadow
859         if (locDimensionsWidth === 0) {
860             if (this._isMultiLine)
861                 locSize = cc.size(0 | (Math.max.apply(Math, locLineWidth) + locStrokeShadowOffsetX),
862                     0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY));
863             else
864                 locSize = cc.size(0 | (this._measure(this._string) + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY));
865         } else {
866             if (this._dimensions.height === 0) {
867                 if (this._isMultiLine)
868                     locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY));
869                 else
870                     locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY));
871             } else {
872                 //dimension is already set, contentSize must be same as dimension
873                 locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._dimensions.height + locStrokeShadowOffsetY));
874             }
875         }
876         this.setContentSize(locSize);
877         this._strokeShadowOffsetX = locStrokeShadowOffsetX;
878         this._strokeShadowOffsetY = locStrokeShadowOffsetY;
879 
880         // need computing _anchorPointInPoints
881         var locAP = this._anchorPoint;
882         this._anchorPointInPoints.x = (locStrokeShadowOffsetX * 0.5) + ((locSize.width - locStrokeShadowOffsetX) * locAP.x);
883         this._anchorPointInPoints.y = (locStrokeShadowOffsetY * 0.5) + ((locSize.height - locStrokeShadowOffsetY) * locAP.y);
884     },
885 
886     getContentSize: function () {
887         if (this._needUpdateTexture)
888             this._updateTTF();
889         return cc.Sprite.prototype.getContentSize.call(this);
890     },
891 
892     _getWidth: function () {
893         if (this._needUpdateTexture)
894             this._updateTTF();
895         return cc.Sprite.prototype._getWidth.call(this);
896     },
897     _getHeight: function () {
898         if (this._needUpdateTexture)
899             this._updateTTF();
900         return cc.Sprite.prototype._getHeight.call(this);
901     },
902 
903     _updateTexture: function () {
904         var locContext = this._getLabelContext(), locLabelCanvas = this._labelCanvas;
905         var locContentSize = this._contentSize;
906 
907         if (this._string.length === 0) {
908             locLabelCanvas.width = 1;
909             locLabelCanvas.height = locContentSize.height;
910             this.setTextureRect(cc.rect(0, 0, 1, locContentSize.height));
911             return true;
912         }
913 
914         //set size for labelCanvas
915         locContext.font = this._fontStyleStr;
916         this._updateTTF();
917         var width = locContentSize.width, height = locContentSize.height;
918         var flag = locLabelCanvas.width == width && locLabelCanvas.height == height;
919         locLabelCanvas.width = width;
920         locLabelCanvas.height = height;
921         if (flag) locContext.clearRect(0, 0, width, height);
922 
923         //draw text to labelCanvas
924         this._drawTTFInCanvas(locContext);
925         this._texture && this._texture.handleLoadedTexture();
926 
927         this.setTextureRect(cc.rect(0, 0, width, height));
928         return true;
929     },
930 
931     visit: function (ctx) {
932         if (!this._string || this._string == "")
933             return;
934         if (this._needUpdateTexture) {
935             this._needUpdateTexture = false;
936             this._updateTexture();
937         }
938         var context = ctx || cc._renderContext;
939         cc.Sprite.prototype.visit.call(this, context);
940     },
941 
942     /**
943      * Draw sprite to canvas
944      * @function
945      * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx Render context of canvas, 2d or 3d
946      */
947     draw: null,
948 
949     _setTextureCoords: function (rect) {
950         var tex = this._batchNode ? this.textureAtlas.texture : this._texture;
951         if (!tex)
952             return;
953 
954         var atlasWidth = tex.pixelsWidth;
955         var atlasHeight = tex.pixelsHeight;
956 
957         var left, right, top, bottom, tempSwap, locQuad = this._quad;
958         if (this._rectRotated) {
959             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
960                 left = (2 * rect.x + 1) / (2 * atlasWidth);
961                 right = left + (rect.height * 2 - 2) / (2 * atlasWidth);
962                 top = (2 * rect.y + 1) / (2 * atlasHeight);
963                 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight);
964             } else {
965                 left = rect.x / atlasWidth;
966                 right = (rect.x + rect.height) / atlasWidth;
967                 top = rect.y / atlasHeight;
968                 bottom = (rect.y + rect.width) / atlasHeight;
969             }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
970 
971             if (this._flippedX) {
972                 tempSwap = top;
973                 top = bottom;
974                 bottom = tempSwap;
975             }
976 
977             if (this._flippedY) {
978                 tempSwap = left;
979                 left = right;
980                 right = tempSwap;
981             }
982 
983             locQuad.bl.texCoords.u = left;
984             locQuad.bl.texCoords.v = top;
985             locQuad.br.texCoords.u = left;
986             locQuad.br.texCoords.v = bottom;
987             locQuad.tl.texCoords.u = right;
988             locQuad.tl.texCoords.v = top;
989             locQuad.tr.texCoords.u = right;
990             locQuad.tr.texCoords.v = bottom;
991         } else {
992             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
993                 left = (2 * rect.x + 1) / (2 * atlasWidth);
994                 right = left + (rect.width * 2 - 2) / (2 * atlasWidth);
995                 top = (2 * rect.y + 1) / (2 * atlasHeight);
996                 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight);
997             } else {
998                 left = rect.x / atlasWidth;
999                 right = (rect.x + rect.width) / atlasWidth;
1000                 top = rect.y / atlasHeight;
1001                 bottom = (rect.y + rect.height) / atlasHeight;
1002             } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
1003 
1004             if (this._flippedX) {
1005                 tempSwap = left;
1006                 left = right;
1007                 right = tempSwap;
1008             }
1009 
1010             if (this._flippedY) {
1011                 tempSwap = top;
1012                 top = bottom;
1013                 bottom = tempSwap;
1014             }
1015 
1016             locQuad.bl.texCoords.u = left;
1017             locQuad.bl.texCoords.v = bottom;
1018             locQuad.br.texCoords.u = right;
1019             locQuad.br.texCoords.v = bottom;
1020             locQuad.tl.texCoords.u = left;
1021             locQuad.tl.texCoords.v = top;
1022             locQuad.tr.texCoords.u = right;
1023             locQuad.tr.texCoords.v = top;
1024         }
1025         this._quadDirty = true;
1026     }
1027 });
1028 
1029 if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1030 
1031     var _p = cc.LabelTTF.prototype;
1032 
1033     _p.setColor = function (color3) {
1034         cc.NodeRGBA.prototype.setColor.call(this, color3);
1035 
1036         this._setColorsString();
1037     };
1038 
1039     _p._setColorsString = function () {
1040         this._needUpdateTexture = true;
1041 
1042         var locDisplayColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity;
1043         var locStrokeColor = this._strokeColor, locFontFillColor = this._textFillColor;
1044 
1045         this._shadowColorStr = "rgba(" + (0 | (locDisplayColor.r * 0.5)) + "," + (0 | (locDisplayColor.g * 0.5)) + "," + (0 | (locDisplayColor.b * 0.5)) + "," + this._shadowOpacity + ")";
1046         this._fillColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locFontFillColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locFontFillColor.g)) + ","
1047             + (0 | (locDisplayColor.b / 255 * locFontFillColor.b)) + ", " + locDisplayedOpacity / 255 + ")";
1048         this._strokeColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locStrokeColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locStrokeColor.g)) + ","
1049             + (0 | (locDisplayColor.b / 255 * locStrokeColor.b)) + ", " + locDisplayedOpacity / 255 + ")";
1050     };
1051 
1052     _p.updateDisplayedColor = function (parentColor) {
1053         cc.NodeRGBA.prototype.updateDisplayedColor.call(this, parentColor);
1054         this._setColorsString();
1055     };
1056 
1057     _p.setOpacity = function (opacity) {
1058         if (this._opacity === opacity)
1059             return;
1060         cc.Sprite.prototype.setOpacity.call(this, opacity);
1061         this._setColorsString();
1062         this._needUpdateTexture = true;
1063     };
1064 
1065     //TODO: _p._updateDisplayedOpacityForCanvas
1066     _p.updateDisplayedOpacity = cc.Sprite.prototype.updateDisplayedOpacity;
1067 
1068     _p.initWithStringAndTextDefinition = function (text, textDefinition) {
1069         // prepare everything needed to render the label
1070         this._updateWithTextDefinition(textDefinition, false);
1071 
1072         // set the string
1073         this.string = text;
1074 
1075         return true;
1076     };
1077 
1078     _p.setFontFillColor = function (tintColor) {
1079         var locTextFillColor = this._textFillColor;
1080         if (locTextFillColor.r != tintColor.r || locTextFillColor.g != tintColor.g || locTextFillColor.b != tintColor.b) {
1081             locTextFillColor.r = tintColor.r;
1082             locTextFillColor.g = tintColor.g;
1083             locTextFillColor.b = tintColor.b;
1084 
1085             this._setColorsString();
1086             this._needUpdateTexture = true;
1087         }
1088     };
1089 
1090     _p.draw = cc.Sprite.prototype.draw;
1091 
1092     _p.setTextureRect = function (rect, rotated, untrimmedSize) {
1093         this._rectRotated = rotated || false;
1094         untrimmedSize = untrimmedSize || rect;
1095 
1096         this.setContentSize(untrimmedSize);
1097         this.setVertexRect(rect);
1098 
1099         var locTextureCoordRect = this._textureRect_Canvas;
1100         locTextureCoordRect.x = rect.x;
1101         locTextureCoordRect.y = rect.y;
1102         locTextureCoordRect.width = rect.width;
1103         locTextureCoordRect.height = rect.height;
1104         locTextureCoordRect.validRect = !(locTextureCoordRect.width === 0 || locTextureCoordRect.height === 0
1105             || locTextureCoordRect.x < 0 || locTextureCoordRect.y < 0);
1106 
1107         var relativeOffset = this._unflippedOffsetPositionFromCenter;
1108         if (this._flippedX)
1109             relativeOffset.x = -relativeOffset.x;
1110         if (this._flippedY)
1111             relativeOffset.y = -relativeOffset.y;
1112         this._offsetPosition.x = relativeOffset.x + (this._contentSize.width - this._rect.width) / 2;
1113         this._offsetPosition.y = relativeOffset.y + (this._contentSize.height - this._rect.height) / 2;
1114 
1115         // rendering using batch node
1116         if (this._batchNode) {
1117             this.dirty = true;
1118         }
1119     };
1120     _p = null;
1121 
1122 } else {
1123     cc.assert(typeof cc._tmp.WebGLLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFWebGL.js");
1124     cc._tmp.WebGLLabelTTF();
1125     delete cc._tmp.WebGLLabelTTF;
1126 }
1127 
1128 cc.assert(typeof cc._tmp.PrototypeLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFPropertyDefine.js");
1129 cc._tmp.PrototypeLabelTTF();
1130 delete cc._tmp.PrototypeLabelTTF;
1131 
1132 cc.LabelTTF._textAlign = ["left", "center", "right"];
1133 
1134 cc.LabelTTF._textBaseline = ["top", "middle", "bottom"];
1135 
1136 // Class static properties for measure util
1137 cc.LabelTTF._checkRegEx = /(.+?)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/;
1138 cc.LabelTTF._reverseCheckRegEx = /(.*)([\s\n\r\-\/\\\:]|[\u4E00-\u9FA5]|[\uFE30-\uFFA0])/;
1139 cc.LabelTTF._checkEnRegEx = /[\s\-\/\\\:]/;
1140 
1141 // Only support style in this format: "18px Verdana" or "18px 'Helvetica Neue'"
1142 cc.LabelTTF._fontStyleRE = /^(\d+)px\s+['"]?([\w\s\d]+)['"]?$/;
1143 
1144 /**
1145  * creates a cc.LabelTTF from a font name, alignment, dimension and font size
1146  * @param {String} text
1147  * @param {String|cc.FontDefinition} [fontName="Arial"]
1148  * @param {Number} [fontSize=16]
1149  * @param {cc.Size} [dimensions=cc.size(0,0)]
1150  * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT]
1151  * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP]
1152  * @return {cc.LabelTTF|Null}
1153  * @example
1154  * // Example
1155  * 1.
1156  * var myLabel = cc.LabelTTF.create('label text',  'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT);
1157  * 2.
1158  * var fontDef = new cc.FontDefinition();
1159  * fontDef.fontName = "Arial";
1160  * fontDef.fontSize = "32";
1161  * var myLabel = cc.LabelTTF.create('label text',  fontDef);
1162  */
1163 cc.LabelTTF.create = function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) {
1164     return new cc.LabelTTF(text, fontName, fontSize, dimensions, hAlignment, vAlignment);
1165 };
1166 
1167 
1168 if (cc.USE_LA88_LABELS)
1169     cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTURECOLOR;
1170 else
1171     cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTUREA8COLOR;
1172 
1173 cc.LabelTTF.__labelHeightDiv = cc.newElement("div");
1174 cc.LabelTTF.__labelHeightDiv.style.fontFamily = "Arial";
1175 cc.LabelTTF.__labelHeightDiv.style.position = "absolute";
1176 cc.LabelTTF.__labelHeightDiv.style.left = "-100px";
1177 cc.LabelTTF.__labelHeightDiv.style.top = "-100px";
1178 cc.LabelTTF.__labelHeightDiv.style.lineHeight = "normal";
1179 
1180 document.body ?
1181     document.body.appendChild(cc.LabelTTF.__labelHeightDiv) :
1182     cc._addEventListener(window, 'load', function () {
1183         this.removeEventListener('load', arguments.callee, false);
1184         document.body.appendChild(cc.LabelTTF.__labelHeightDiv);
1185     }, false);
1186 
1187 
1188 cc.LabelTTF.__getFontHeightByDiv = function (fontName, fontSize) {
1189     var clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize];
1190     if (clientHeight > 0) return clientHeight;
1191     var labelDiv = cc.LabelTTF.__labelHeightDiv;
1192     labelDiv.innerHTML = "ajghl~!";
1193     labelDiv.style.fontFamily = fontName;
1194     labelDiv.style.fontSize = fontSize + "px";
1195     clientHeight = labelDiv.clientHeight;
1196     cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize] = clientHeight;
1197     labelDiv.innerHTML = "";
1198     return clientHeight;
1199 };
1200 
1201 cc.LabelTTF.__fontHeightCache = {};