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 cc.LabelAtlas = cc.AtlasNode.extend(/** @lends cc.LabelAtlas# */{
 33     // string to render
 34     _string:null,
 35     // the first char in the charmap
 36     _mapStartChar:null,
 37 
 38     /**
 39      * <p>
 40      * initializes the cc.LabelAtlas with a string, a char map file(the atlas),                     <br/>
 41      * the width and height of each element and the starting char of the atlas                      <br/>
 42      *  It accepts two groups of parameters:                                                        <br/>
 43      * a) string, fntFile                                                                           <br/>
 44      * b) label, textureFilename, width, height, startChar                                          <br/>
 45      * </p>
 46      * @param {String} strText
 47      * @param {String|cc.Texture2D} charMapFile  charMapFile or fntFile or texture file
 48      * @param {Number} [itemWidth=0]
 49      * @param {Number} [itemHeight=0]
 50      * @param {Number} [startCharMap=""]
 51      * @return {Boolean} returns true on success
 52      */
 53     initWithString:function (strText, charMapFile, itemWidth, itemHeight, startCharMap) {
 54         var label = strText + "", textureFilename, width, height, startChar;
 55         if (arguments.length === 2) {
 56             var fileUtils = cc.FileUtils.getInstance();
 57             var pathStr = fileUtils.fullPathForFilename(charMapFile);
 58             var relPathStr = pathStr.substr(0, pathStr.lastIndexOf('/')) + '/';
 59 
 60             var dict = fileUtils.dictionaryWithContentsOfFileThreadSafe(pathStr);
 61             if(parseInt(dict["version"], 10) !== 1) {
 62                 cc.log("cc.LabelAtlas.initWithString(): Unsupported version. Upgrade cocos2d version");
 63                 return false;
 64             }
 65 
 66             textureFilename = relPathStr + dict["textureFilename"];
 67             var locScaleFactor = cc.CONTENT_SCALE_FACTOR();
 68             width = parseInt(dict["itemWidth"], 10) / locScaleFactor;
 69             height = parseInt(dict["itemHeight"], 10) / locScaleFactor;
 70             startChar = String.fromCharCode(parseInt(dict["firstChar"], 10));
 71         } else {
 72             textureFilename = charMapFile;
 73             width = itemWidth || 0;
 74             height = itemHeight || 0;
 75             startChar = startCharMap || " ";
 76         }
 77 
 78         var texture = null;
 79         if(textureFilename instanceof cc.Texture2D)
 80             texture = textureFilename;
 81         else
 82             texture = cc.TextureCache.getInstance().addImage(textureFilename);
 83 
 84         if (this.initWithTexture(texture, width, height, label.length)) {
 85             this._mapStartChar = startChar;
 86             this.setString(label);
 87             return true;
 88         }
 89         return false;
 90     },
 91 
 92     /**
 93      * @param {cc.Color3B} color3
 94      */
 95     setColor:function (color3) {
 96         cc.AtlasNode.prototype.setColor.call(this, color3);
 97         this.updateAtlasValues();
 98     },
 99     /**
100      * return the text of this label
101      * @return {String}
102      */
103     getString:function () {
104         return this._string;
105     },
106 
107     /**
108      * draw the label
109      */
110     draw:function (ctx) {
111         cc.AtlasNode.prototype.draw.call(this,ctx);
112         if (cc.LABELATLAS_DEBUG_DRAW) {
113             var s = this.getContentSize();
114             var vertices = [cc.p(0, 0), cc.p(s.width, 0),
115                 cc.p(s.width, s.height), cc.p(0, s.height)];
116             cc.drawingUtil.drawPoly(vertices, 4, true);
117         }
118     },
119 
120     /**
121      *  Atlas generation
122      */
123     updateAtlasValues: null,
124 
125     _updateAtlasValuesForCanvas: function () {
126         var locString = this._string;
127         var n = locString.length;
128         var texture = this.getTexture();
129         var locItemWidth = this._itemWidth, locItemHeight = this._itemHeight;
130         var locScaleFactor = cc.CONTENT_SCALE_FACTOR();
131         for (var i = 0; i < n; i++) {
132             var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0);
133             var row = parseInt(a % this._itemsPerRow, 10) * locScaleFactor;
134             var col = parseInt(a / this._itemsPerRow, 10) * locScaleFactor;
135 
136             var rect = cc.rect(row * locItemWidth, col * locItemHeight, locItemWidth, locItemHeight);
137             var c = locString.charCodeAt(i);
138             var fontChar = this.getChildByTag(i);
139             if (!fontChar) {
140                 fontChar = new cc.Sprite();
141                 if (c == 32) {
142                     fontChar.init();
143                     fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.SizeZero());
144                 } else
145                     fontChar.initWithTexture(texture, rect);
146 
147                 this.addChild(fontChar, 0, i);
148             } else {
149                 if (c == 32) {
150                     fontChar.init();
151                     fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.SizeZero());
152                 } else {
153                     // reusing fonts
154                     fontChar.initWithTexture(texture, rect);
155                     // restore to default in case they were modified
156                     fontChar.setVisible(true);
157                     fontChar.setOpacity(this._displayedOpacity);
158                 }
159             }
160             fontChar.setPosition(i * locItemWidth + locItemWidth / 2, locItemHeight / 2);
161         }
162     },
163 
164     _updateAtlasValuesForWebGL: function () {
165         var locString = this._string;
166         var n = locString.length;
167         var locTextureAtlas = this._textureAtlas;
168 
169         var texture = locTextureAtlas.getTexture();
170         var textureWide = texture.getPixelsWide();
171         var textureHigh = texture.getPixelsHigh();
172         var itemWidthInPixels = this._itemWidth;
173         var itemHeightInPixels = this._itemHeight;
174         if (!this._ignoreContentScaleFactor) {
175             itemWidthInPixels = this._itemWidth * cc.CONTENT_SCALE_FACTOR();
176             itemHeightInPixels = this._itemHeight * cc.CONTENT_SCALE_FACTOR();
177         }
178         if(n > locTextureAtlas.getCapacity())
179             cc.log("cc.LabelAtlas._updateAtlasValues(): Invalid String length");
180         var quads = locTextureAtlas.getQuads();
181         var locDisplayedColor = this._displayedColor;
182         var curColor = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: this._displayedOpacity};
183         var locItemWidth = this._itemWidth;
184         for (var i = 0; i < n; i++) {
185             var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0);
186             var row = a % this._itemsPerRow;
187             var col = 0 | (a / this._itemsPerRow);
188 
189             var left, right, top, bottom;
190             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
191                 // Issue #938. Don't use texStepX & texStepY
192                 left = (2 * row * itemWidthInPixels + 1) / (2 * textureWide);
193                 right = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide);
194                 top = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh);
195                 bottom = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh);
196             } else {
197                 left = row * itemWidthInPixels / textureWide;
198                 right = left + itemWidthInPixels / textureWide;
199                 top = col * itemHeightInPixels / textureHigh;
200                 bottom = top + itemHeightInPixels / textureHigh;
201             }
202             var quad = quads[i];
203             var locQuadTL = quad.tl, locQuadTR = quad.tr, locQuadBL = quad.bl, locQuadBR = quad.br;
204             locQuadTL.texCoords.u = left;
205             locQuadTL.texCoords.v = top;
206             locQuadTR.texCoords.u = right;
207             locQuadTR.texCoords.v = top;
208             locQuadBL.texCoords.u = left;
209             locQuadBL.texCoords.v = bottom;
210             locQuadBR.texCoords.u = right;
211             locQuadBR.texCoords.v = bottom;
212 
213             locQuadBL.vertices.x = (i * locItemWidth);
214             locQuadBL.vertices.y = 0;
215             locQuadBL.vertices.z = 0.0;
216             locQuadBR.vertices.x = (i * locItemWidth + locItemWidth);
217             locQuadBR.vertices.y = 0;
218             locQuadBR.vertices.z = 0.0;
219             locQuadTL.vertices.x = i * locItemWidth;
220             locQuadTL.vertices.y = this._itemHeight;
221             locQuadTL.vertices.z = 0.0;
222             locQuadTR.vertices.x = i * locItemWidth + locItemWidth;
223             locQuadTR.vertices.y = this._itemHeight;
224             locQuadTR.vertices.z = 0.0;
225             locQuadTL.colors = curColor;
226             locQuadTR.colors = curColor;
227             locQuadBL.colors = curColor;
228             locQuadBR.colors = curColor;
229         }
230         if (n > 0) {
231             locTextureAtlas.setDirty(true);
232             var totalQuads = locTextureAtlas.getTotalQuads();
233             if (n > totalQuads)
234                 locTextureAtlas.increaseTotalQuadsWith(n - totalQuads);
235         }
236     },
237 
238     /**
239      * set the display string
240      * @param {String} label
241      */
242     setString: null,
243 
244     _setStringForCanvas: function (label) {
245         label = String(label);
246         var len = label.length;
247         this._string = label;
248         this.setContentSize(cc.size(len * this._itemWidth, this._itemHeight));
249         if (this._children) {
250             var locChildren = this._children;
251             len = locChildren.length;
252             for (var i = 0; i < len; i++) {
253                 var node = locChildren[i];
254                 if (node)
255                     node.setVisible(false);
256             }
257         }
258 
259         this.updateAtlasValues();
260         this._quadsToDraw = len;
261     },
262 
263     _setStringForWebGL: function (label) {
264         label = String(label);
265         var len = label.length;
266         if (len > this._textureAtlas.getTotalQuads())
267             this._textureAtlas.resizeCapacity(len);
268 
269         this._string = label;
270         this.setContentSize(cc.size(len * this._itemWidth, this._itemHeight));
271 
272         this.updateAtlasValues();
273         this._quadsToDraw = len;
274     },
275 
276     setOpacity: null,
277 
278     _setOpacityForCanvas: function (opacity) {
279         if (this._displayedOpacity !== opacity) {
280             cc.AtlasNode.prototype.setOpacity.call(this, opacity);
281             var locChildren = this._children;
282             for (var i = 0, len = locChildren.length; i < len; i++) {
283                 if (locChildren[i])
284                     locChildren[i].setOpacity(opacity);
285             }
286         }
287     },
288 
289     _setOpacityForWebGL: function (opacity) {
290         if (this._opacity !== opacity)
291             cc.AtlasNode.prototype.setOpacity.call(this, opacity);
292     }
293 });
294 
295 if(cc.Browser.supportWebGL){
296     cc.LabelAtlas.prototype.updateAtlasValues =  cc.LabelAtlas.prototype._updateAtlasValuesForWebGL;
297     cc.LabelAtlas.prototype.setString =  cc.LabelAtlas.prototype._setStringForWebGL;
298     cc.LabelAtlas.prototype.setOpacity =  cc.LabelAtlas.prototype._setOpacityForWebGL;
299 } else {
300     cc.LabelAtlas.prototype.updateAtlasValues =  cc.LabelAtlas.prototype._updateAtlasValuesForCanvas;
301     cc.LabelAtlas.prototype.setString =  cc.LabelAtlas.prototype._setStringForCanvas;
302     cc.LabelAtlas.prototype.setOpacity =  cc.LabelAtlas.prototype._setOpacityForCanvas;
303 }
304 
305 /**
306  * <p>
307  *  It accepts two groups of parameters:                                                            <br/>
308  * a) string, fntFile                                                                               <br/>
309  * b) label, textureFilename, width, height, startChar                                              <br/>
310  * </p>
311  * @param {String} strText
312  * @param {String} charMapFile  charMapFile or fntFile
313  * @param {Number} [itemWidth=0]
314  * @param {Number} [itemHeight=0]
315  * @param {Number} [startCharMap=""]
316  * @return {cc.LabelAtlas|Null} returns the LabelAtlas object on success
317  * @example
318  * //Example
319  * //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
320  * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapfile.png', 12, 20, ' ')
321  *
322  * //creates the cc.LabelAtlas with a string, a fnt file
323  * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapFile.plist‘);
324  */
325 cc.LabelAtlas.create = function (strText, charMapFile, itemWidth, itemHeight, startCharMap) {
326     var ret = new cc.LabelAtlas();
327     if (ret && cc.LabelAtlas.prototype.initWithString.apply(ret,arguments)) {
328         return ret;
329     }
330     return null;
331 };
332 
333