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