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