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  * using image file to print text label on the screen, might be a bit slower than cc.Label, similar to cc.LabelBMFont
 29  * @class
 30  * @extends cc.AtlasNode
 31  *
 32  * @property {String}   string  - Content string of label
 33  */
 34 cc.LabelAtlas = cc.AtlasNode.extend(/** @lends cc.LabelAtlas# */{
 35     // string to render
 36     _string:null,
 37     // the first char in the charmap
 38     _mapStartChar:null,
 39 
 40     _textureLoaded:false,
 41     _loadedEventListeners: null,
 42     _className:"LabelAtlas",
 43 
 44     ctor:function(){
 45         cc.AtlasNode.prototype.ctor.call(this);
 46     },
 47 
 48     /**
 49      * return  texture is loaded
 50      * @returns {boolean}
 51      */
 52     textureLoaded:function(){
 53         return this._textureLoaded;
 54     },
 55 
 56     /**
 57      * add texture loaded event listener
 58      * @param {Function} callback
 59      * @param {Object} target
 60      */
 61     addLoadedEventListener:function(callback, target){
 62         if(!this._loadedEventListeners)
 63             this._loadedEventListeners = [];
 64         this._loadedEventListeners.push({eventCallback:callback, eventTarget:target});
 65     },
 66 
 67     _callLoadedEventCallbacks:function(){
 68         if(!this._loadedEventListeners)
 69             return;
 70         this._textureLoaded = true;
 71         var locListeners = this._loadedEventListeners;
 72         for(var i = 0, len = locListeners.length;  i < len; i++){
 73             var selCallback = locListeners[i];
 74             selCallback.eventCallback.call(selCallback.eventTarget, this);
 75         }
 76         locListeners.length = 0;
 77     },
 78     /**
 79      * <p>
 80      * initializes the cc.LabelAtlas with a string, a char map file(the atlas),                     <br/>
 81      * the width and height of each element and the starting char of the atlas                      <br/>
 82      *  It accepts two groups of parameters:                                                        <br/>
 83      * a) string, fntFile                                                                           <br/>
 84      * b) label, textureFilename, width, height, startChar                                          <br/>
 85      * </p>
 86      * @param {String} strText
 87      * @param {String|cc.Texture2D} charMapFile  charMapFile or fntFile or texture file
 88      * @param {Number} [itemWidth=0]
 89      * @param {Number} [itemHeight=0]
 90      * @param {Number} [startCharMap=""]
 91      * @return {Boolean} returns true on success
 92      */
 93     initWithString:function (strText, charMapFile, itemWidth, itemHeight, startCharMap) {
 94         var label = strText + "", textureFilename, width, height, startChar;
 95         if (arguments.length === 2) {
 96             var dict = cc.loader.getRes(charMapFile);
 97             if(parseInt(dict["version"], 10) !== 1) {
 98                 cc.log("cc.LabelAtlas.initWithString(): Unsupported version. Upgrade cocos2d version");
 99                 return false;
100             }
101 
102             textureFilename = cc.path.changeBasename(charMapFile, dict["textureFilename"]);
103             var locScaleFactor = cc.CONTENT_SCALE_FACTOR();
104             width = parseInt(dict["itemWidth"], 10) / locScaleFactor;
105             height = parseInt(dict["itemHeight"], 10) / locScaleFactor;
106             startChar = String.fromCharCode(parseInt(dict["firstChar"], 10));
107         } else {
108             textureFilename = charMapFile;
109             width = itemWidth || 0;
110             height = itemHeight || 0;
111             startChar = startCharMap || " ";
112         }
113 
114         var texture = null;
115         if(textureFilename instanceof cc.Texture2D)
116             texture = textureFilename;
117         else
118             texture = cc.textureCache.addImage(textureFilename);
119         var locLoaded = texture.isLoaded();
120         this._textureLoaded = locLoaded;
121         if(!locLoaded){
122             texture.addLoadedEventListener(function(sender){
123                 this.initWithTexture(texture, width, height, label.length);
124                 this.string = label;
125                 this._callLoadedEventCallbacks();
126             },this);
127         }
128         if (this.initWithTexture(texture, width, height, label.length)) {
129             this._mapStartChar = startChar;
130             this.string = label;
131             return true;
132         }
133         return false;
134     },
135 
136     /**
137      * @param {cc.Color} color3
138      */
139     setColor:function (color3) {
140         cc.AtlasNode.prototype.setColor.call(this, color3);
141         this.updateAtlasValues();
142     },
143     /**
144      * return the text of this label
145      * @return {String}
146      */
147     getString:function () {
148         return this._string;
149     },
150 
151     /**
152      * draw the label
153      */
154     draw:function (ctx) {
155         cc.AtlasNode.prototype.draw.call(this,ctx);
156         if (cc.LABELATLAS_DEBUG_DRAW) {
157             var s = this.size;
158             var vertices = [cc.p(0, 0), cc.p(s.width, 0),
159                 cc.p(s.width, s.height), cc.p(0, s.height)];
160             cc._drawingUtil.drawPoly(vertices, 4, true);
161         }
162     },
163 
164     /**
165      * @function
166      * Atlas generation
167      */
168     updateAtlasValues: null,
169 
170     _updateAtlasValuesForCanvas: function () {
171         var locString = this._string;
172         var n = locString.length;
173         var texture = this.texture;
174         var locItemWidth = this._itemWidth , locItemHeight = this._itemHeight ;     //needn't multiply cc.CONTENT_SCALE_FACTOR(), because sprite's draw will do this
175 
176         for (var i = 0; i < n; i++) {
177             var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0);
178             var row = parseInt(a % this._itemsPerRow, 10);
179             var col = parseInt(a / this._itemsPerRow, 10);
180 
181             var rect = cc.rect(row * locItemWidth, col * locItemHeight, locItemWidth, locItemHeight);
182             var c = locString.charCodeAt(i);
183             var fontChar = this.getChildByTag(i);
184             if (!fontChar) {
185                 fontChar = new cc.Sprite();
186                 if (c == 32) {
187                     fontChar.init();
188                     fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0));
189                 } else
190                     fontChar.initWithTexture(texture, rect);
191 
192                 this.addChild(fontChar, 0, i);
193             } else {
194                 if (c == 32) {
195                     fontChar.init();
196                     fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0));
197                 } else {
198                     // reusing fonts
199                     fontChar.initWithTexture(texture, rect);
200                     // restore to default in case they were modified
201                     fontChar.visible = true;
202                     fontChar.opacity = this._displayedOpacity;
203                 }
204             }
205             fontChar.setPosition(i * locItemWidth + locItemWidth / 2, locItemHeight / 2);
206         }
207     },
208 
209     _updateAtlasValuesForWebGL: function () {
210         var locString = this._string;
211         var n = locString.length;
212         var locTextureAtlas = this.textureAtlas;
213 
214         var texture = locTextureAtlas.texture;
215         var textureWide = texture.pixelsWidth;
216         var textureHigh = texture.pixelsHeight;
217         var itemWidthInPixels = this._itemWidth;
218         var itemHeightInPixels = this._itemHeight;
219         if (!this._ignoreContentScaleFactor) {
220             itemWidthInPixels = this._itemWidth * cc.CONTENT_SCALE_FACTOR();
221             itemHeightInPixels = this._itemHeight * cc.CONTENT_SCALE_FACTOR();
222         }
223         if(n > locTextureAtlas.getCapacity())
224             cc.log("cc.LabelAtlas._updateAtlasValues(): Invalid String length");
225         var quads = locTextureAtlas.quads;
226         var locDisplayedColor = this._displayedColor;
227         var curColor = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: this._displayedOpacity};
228         var locItemWidth = this._itemWidth;
229         for (var i = 0; i < n; i++) {
230             var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0);
231             var row = a % this._itemsPerRow;
232             var col = 0 | (a / this._itemsPerRow);
233 
234             var left, right, top, bottom;
235             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
236                 // Issue #938. Don't use texStepX & texStepY
237                 left = (2 * row * itemWidthInPixels + 1) / (2 * textureWide);
238                 right = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide);
239                 top = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh);
240                 bottom = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh);
241             } else {
242                 left = row * itemWidthInPixels / textureWide;
243                 right = left + itemWidthInPixels / textureWide;
244                 top = col * itemHeightInPixels / textureHigh;
245                 bottom = top + itemHeightInPixels / textureHigh;
246             }
247             var quad = quads[i];
248             var locQuadTL = quad.tl, locQuadTR = quad.tr, locQuadBL = quad.bl, locQuadBR = quad.br;
249             locQuadTL.texCoords.u = left;
250             locQuadTL.texCoords.v = top;
251             locQuadTR.texCoords.u = right;
252             locQuadTR.texCoords.v = top;
253             locQuadBL.texCoords.u = left;
254             locQuadBL.texCoords.v = bottom;
255             locQuadBR.texCoords.u = right;
256             locQuadBR.texCoords.v = bottom;
257 
258             locQuadBL.vertices.x = (i * locItemWidth);
259             locQuadBL.vertices.y = 0;
260             locQuadBL.vertices.z = 0.0;
261             locQuadBR.vertices.x = (i * locItemWidth + locItemWidth);
262             locQuadBR.vertices.y = 0;
263             locQuadBR.vertices.z = 0.0;
264             locQuadTL.vertices.x = i * locItemWidth;
265             locQuadTL.vertices.y = this._itemHeight;
266             locQuadTL.vertices.z = 0.0;
267             locQuadTR.vertices.x = i * locItemWidth + locItemWidth;
268             locQuadTR.vertices.y = this._itemHeight;
269             locQuadTR.vertices.z = 0.0;
270             locQuadTL.colors = curColor;
271             locQuadTR.colors = curColor;
272             locQuadBL.colors = curColor;
273             locQuadBR.colors = curColor;
274         }
275         if (n > 0) {
276             locTextureAtlas.dirty = true;
277             var totalQuads = locTextureAtlas.totalQuads;
278             if (n > totalQuads)
279                 locTextureAtlas.increaseTotalQuadsWith(n - totalQuads);
280         }
281     },
282 
283     /**
284      * set the display string
285      * @function
286      * @param {String} label
287      */
288     setString: null,
289 
290     _setStringForCanvas: function (label) {
291         label = String(label);
292         var len = label.length;
293         this._string = label;
294         this.width = len * this._itemWidth;
295         this.height = this._itemHeight;
296         if (this._children) {
297             var locChildren = this._children;
298             len = locChildren.length;
299             for (var i = 0; i < len; i++) {
300                 var node = locChildren[i];
301                 if (node)
302                     node.visible = false;
303             }
304         }
305 
306         this.updateAtlasValues();
307         this.quadsToDraw = len;
308     },
309 
310     _setStringForWebGL: function (label) {
311         label = String(label);
312         var len = label.length;
313         if (len > this.textureAtlas.totalQuads)
314             this.textureAtlas.resizeCapacity(len);
315 
316         this._string = label;
317         this.width = len * this._itemWidth;
318         this.height = this._itemHeight;
319 
320         this.updateAtlasValues();
321         this.quadsToDraw = len;
322     },
323 
324     setOpacity: null,
325 
326     _setOpacityForCanvas: function (opacity) {
327         if (this._displayedOpacity !== opacity) {
328             cc.AtlasNode.prototype.setOpacity.call(this, opacity);
329             var locChildren = this._children;
330             for (var i = 0, len = locChildren.length; i < len; i++) {
331                 if (locChildren[i])
332                     locChildren[i].opacity = opacity;
333             }
334         }
335     },
336 
337     _setOpacityForWebGL: function (opacity) {
338         if (this._opacity !== opacity)
339             cc.AtlasNode.prototype.setOpacity.call(this, opacity);
340     }
341 });
342 
343 window._p = cc.LabelAtlas.prototype;
344 if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
345     _p.updateAtlasValues =  _p._updateAtlasValuesForWebGL;
346     _p.setString =  _p._setStringForWebGL;
347     _p.setOpacity =  _p._setOpacityForWebGL;
348 } else {
349     _p.updateAtlasValues =  _p._updateAtlasValuesForCanvas;
350     _p.setString =  _p._setStringForCanvas;
351     _p.setOpacity =  _p._setOpacityForCanvas;
352 }
353 
354 // Override properties
355 cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity);
356 
357 // Extended properties
358 /** @expose */
359 _p.string;
360 cc.defineGetterSetter(_p, "string", _p.getString, _p.setString);
361 
362 delete window._p;
363 
364 /**
365  * <p>
366  *  It accepts two groups of parameters:                                                            <br/>
367  * a) string, fntFile                                                                               <br/>
368  * b) label, textureFilename, width, height, startChar                                              <br/>
369  * </p>
370  * @param {String} strText
371  * @param {String} charMapFile  charMapFile or fntFile
372  * @param {Number} [itemWidth=0]
373  * @param {Number} [itemHeight=0]
374  * @param {Number} [startCharMap=""]
375  * @return {cc.LabelAtlas|Null} returns the LabelAtlas object on success
376  * @example
377  * //Example
378  * //creates the cc.LabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas
379  * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapfile.png', 12, 20, ' ')
380  *
381  * //creates the cc.LabelAtlas with a string, a fnt file
382  * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapFile.plist‘);
383  */
384 cc.LabelAtlas.create = function (strText, charMapFile, itemWidth, itemHeight, startCharMap) {
385     var ret = new cc.LabelAtlas();
386     if (ret && cc.LabelAtlas.prototype.initWithString.apply(ret,arguments)) {
387         return ret;
388     }
389     return null;
390 };
391 
392