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 _textureLoaded:false, 39 _loadedEventListeners: null, 40 41 ctor:function(){ 42 cc.AtlasNode.prototype.ctor.call(this); 43 }, 44 45 /** 46 * return texture is loaded 47 * @returns {boolean} 48 */ 49 textureLoaded:function(){ 50 return this._textureLoaded; 51 }, 52 53 /** 54 * add texture loaded event listener 55 * @param {Function} callback 56 * @param {Object} target 57 */ 58 addLoadedEventListener:function(callback, target){ 59 if(!this._loadedEventListeners) 60 this._loadedEventListeners = []; 61 this._loadedEventListeners.push({eventCallback:callback, eventTarget:target}); 62 }, 63 64 _callLoadedEventCallbacks:function(){ 65 if(!this._loadedEventListeners) 66 return; 67 this._textureLoaded = true; 68 var locListeners = this._loadedEventListeners; 69 for(var i = 0, len = locListeners.length; i < len; i++){ 70 var selCallback = locListeners[i]; 71 cc.doCallback(selCallback.eventCallback, selCallback.eventTarget, this); 72 } 73 locListeners.length = 0; 74 }, 75 /** 76 * <p> 77 * initializes the cc.LabelAtlas with a string, a char map file(the atlas), <br/> 78 * the width and height of each element and the starting char of the atlas <br/> 79 * It accepts two groups of parameters: <br/> 80 * a) string, fntFile <br/> 81 * b) label, textureFilename, width, height, startChar <br/> 82 * </p> 83 * @param {String} strText 84 * @param {String|cc.Texture2D} charMapFile charMapFile or fntFile or texture file 85 * @param {Number} [itemWidth=0] 86 * @param {Number} [itemHeight=0] 87 * @param {Number} [startCharMap=""] 88 * @return {Boolean} returns true on success 89 */ 90 initWithString:function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { 91 var label = strText + "", textureFilename, width, height, startChar; 92 if (arguments.length === 2) { 93 var fileUtils = cc.FileUtils.getInstance(); 94 var pathStr = fileUtils.fullPathForFilename(charMapFile); 95 var relPathStr = pathStr.substr(0, pathStr.lastIndexOf('/')) + '/'; 96 97 var dict = fileUtils.dictionaryWithContentsOfFileThreadSafe(pathStr); 98 if(parseInt(dict["version"], 10) !== 1) { 99 cc.log("cc.LabelAtlas.initWithString(): Unsupported version. Upgrade cocos2d version"); 100 return false; 101 } 102 103 textureFilename = relPathStr + dict["textureFilename"]; 104 var locScaleFactor = cc.CONTENT_SCALE_FACTOR(); 105 width = parseInt(dict["itemWidth"], 10) / locScaleFactor; 106 height = parseInt(dict["itemHeight"], 10) / locScaleFactor; 107 startChar = String.fromCharCode(parseInt(dict["firstChar"], 10)); 108 } else { 109 textureFilename = charMapFile; 110 width = itemWidth || 0; 111 height = itemHeight || 0; 112 startChar = startCharMap || " "; 113 } 114 115 var texture = null; 116 if(textureFilename instanceof cc.Texture2D) 117 texture = textureFilename; 118 else 119 texture = cc.TextureCache.getInstance().addImage(textureFilename); 120 var locLoaded = texture.isLoaded(); 121 this._textureLoaded = locLoaded; 122 if(!locLoaded){ 123 texture.addLoadedEventListener(function(sender){ 124 this.initWithTexture(texture, width, height, label.length); 125 this.setString(label); 126 this._callLoadedEventCallbacks(); 127 },this); 128 } 129 if (this.initWithTexture(texture, width, height, label.length)) { 130 this._mapStartChar = startChar; 131 this.setString(label); 132 return true; 133 } 134 return false; 135 }, 136 137 /** 138 * @param {cc.Color3B} color3 139 */ 140 setColor:function (color3) { 141 cc.AtlasNode.prototype.setColor.call(this, color3); 142 this.updateAtlasValues(); 143 }, 144 /** 145 * return the text of this label 146 * @return {String} 147 */ 148 getString:function () { 149 return this._string; 150 }, 151 152 /** 153 * draw the label 154 */ 155 draw:function (ctx) { 156 cc.AtlasNode.prototype.draw.call(this,ctx); 157 if (cc.LABELATLAS_DEBUG_DRAW) { 158 var s = this.getContentSize(); 159 var vertices = [cc.p(0, 0), cc.p(s.width, 0), 160 cc.p(s.width, s.height), cc.p(0, s.height)]; 161 cc.drawingUtil.drawPoly(vertices, 4, true); 162 } 163 }, 164 165 /** 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.getTexture(); 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.SizeZero()); 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.SizeZero()); 197 } else { 198 // reusing fonts 199 fontChar.initWithTexture(texture, rect); 200 // restore to default in case they were modified 201 fontChar.setVisible(true); 202 fontChar.setOpacity(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.getTexture(); 215 var textureWide = texture.getPixelsWide(); 216 var textureHigh = texture.getPixelsHigh(); 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.getQuads(); 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.setDirty(true); 277 var totalQuads = locTextureAtlas.getTotalQuads(); 278 if (n > totalQuads) 279 locTextureAtlas.increaseTotalQuadsWith(n - totalQuads); 280 } 281 }, 282 283 /** 284 * set the display string 285 * @param {String} label 286 */ 287 setString: null, 288 289 _setStringForCanvas: function (label) { 290 label = String(label); 291 var len = label.length; 292 this._string = label; 293 this.setContentSize(len * this._itemWidth, this._itemHeight); 294 if (this._children) { 295 var locChildren = this._children; 296 len = locChildren.length; 297 for (var i = 0; i < len; i++) { 298 var node = locChildren[i]; 299 if (node) 300 node.setVisible(false); 301 } 302 } 303 304 this.updateAtlasValues(); 305 this._quadsToDraw = len; 306 }, 307 308 _setStringForWebGL: function (label) { 309 label = String(label); 310 var len = label.length; 311 if (len > this._textureAtlas.getTotalQuads()) 312 this._textureAtlas.resizeCapacity(len); 313 314 this._string = label; 315 this.setContentSize(len * this._itemWidth, this._itemHeight); 316 317 this.updateAtlasValues(); 318 this._quadsToDraw = len; 319 }, 320 321 setOpacity: null, 322 323 _setOpacityForCanvas: function (opacity) { 324 if (this._displayedOpacity !== opacity) { 325 cc.AtlasNode.prototype.setOpacity.call(this, opacity); 326 var locChildren = this._children; 327 for (var i = 0, len = locChildren.length; i < len; i++) { 328 if (locChildren[i]) 329 locChildren[i].setOpacity(opacity); 330 } 331 } 332 }, 333 334 _setOpacityForWebGL: function (opacity) { 335 if (this._opacity !== opacity) 336 cc.AtlasNode.prototype.setOpacity.call(this, opacity); 337 } 338 }); 339 340 if(cc.Browser.supportWebGL){ 341 cc.LabelAtlas.prototype.updateAtlasValues = cc.LabelAtlas.prototype._updateAtlasValuesForWebGL; 342 cc.LabelAtlas.prototype.setString = cc.LabelAtlas.prototype._setStringForWebGL; 343 cc.LabelAtlas.prototype.setOpacity = cc.LabelAtlas.prototype._setOpacityForWebGL; 344 } else { 345 cc.LabelAtlas.prototype.updateAtlasValues = cc.LabelAtlas.prototype._updateAtlasValuesForCanvas; 346 cc.LabelAtlas.prototype.setString = cc.LabelAtlas.prototype._setStringForCanvas; 347 cc.LabelAtlas.prototype.setOpacity = cc.LabelAtlas.prototype._setOpacityForCanvas; 348 } 349 350 /** 351 * <p> 352 * It accepts two groups of parameters: <br/> 353 * a) string, fntFile <br/> 354 * b) label, textureFilename, width, height, startChar <br/> 355 * </p> 356 * @param {String} strText 357 * @param {String} charMapFile charMapFile or fntFile 358 * @param {Number} [itemWidth=0] 359 * @param {Number} [itemHeight=0] 360 * @param {Number} [startCharMap=""] 361 * @return {cc.LabelAtlas|Null} returns the LabelAtlas object on success 362 * @example 363 * //Example 364 * //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 365 * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapfile.png', 12, 20, ' ') 366 * 367 * //creates the cc.LabelAtlas with a string, a fnt file 368 * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapFile.plist‘); 369 */ 370 cc.LabelAtlas.create = function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { 371 var ret = new cc.LabelAtlas(); 372 if (ret && cc.LabelAtlas.prototype.initWithString.apply(ret,arguments)) { 373 return ret; 374 } 375 return null; 376 }; 377 378