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