1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies 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 _addChildForCanvas: function(child, zOrder, tag){ 186 child._lateChild = true; 187 cc.NodeRGBA.prototype.addChild.call(this, child, zOrder, tag); 188 }, 189 190 /** 191 * @function 192 * Atlas generation 193 */ 194 updateAtlasValues: null, 195 196 _updateAtlasValuesForCanvas: function () { 197 var locString = this._string || ""; 198 var n = locString.length; 199 var texture = this.texture; 200 var locItemWidth = this._itemWidth , locItemHeight = this._itemHeight; //needn't multiply cc.contentScaleFactor(), because sprite's draw will do this 201 202 for (var i = 0; i < n; i++) { 203 var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0); 204 var row = parseInt(a % this._itemsPerRow, 10); 205 var col = parseInt(a / this._itemsPerRow, 10); 206 207 var rect = cc.rect(row * locItemWidth, col * locItemHeight, locItemWidth, locItemHeight); 208 var c = locString.charCodeAt(i); 209 var fontChar = this.getChildByTag(i); 210 if (!fontChar) { 211 fontChar = new cc.Sprite(); 212 if (c == 32) { 213 fontChar.init(); 214 fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0)); 215 } else 216 fontChar.initWithTexture(texture, rect); 217 218 cc.NodeRGBA.prototype.addChild.call(this, fontChar, 0, i); 219 } else { 220 if (c == 32) { 221 fontChar.init(); 222 fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0)); 223 } else { 224 // reusing fonts 225 fontChar.initWithTexture(texture, rect); 226 // restore to default in case they were modified 227 fontChar.visible = true; 228 fontChar.opacity = this._displayedOpacity; 229 } 230 } 231 fontChar.setPosition(i * locItemWidth + locItemWidth / 2, locItemHeight / 2); 232 } 233 }, 234 235 _updateAtlasValuesForWebGL: function () { 236 var locString = this._string; 237 var n = locString.length; 238 var locTextureAtlas = this.textureAtlas; 239 240 var texture = locTextureAtlas.texture; 241 var textureWide = texture.pixelsWidth; 242 var textureHigh = texture.pixelsHeight; 243 var itemWidthInPixels = this._itemWidth; 244 var itemHeightInPixels = this._itemHeight; 245 if (!this._ignoreContentScaleFactor) { 246 itemWidthInPixels = this._itemWidth * cc.contentScaleFactor(); 247 itemHeightInPixels = this._itemHeight * cc.contentScaleFactor(); 248 } 249 if (n > locTextureAtlas.getCapacity()) 250 cc.log("cc.LabelAtlas._updateAtlasValues(): Invalid String length"); 251 var quads = locTextureAtlas.quads; 252 var locDisplayedColor = this._displayedColor; 253 var curColor = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: this._displayedOpacity}; 254 var locItemWidth = this._itemWidth; 255 for (var i = 0; i < n; i++) { 256 var a = locString.charCodeAt(i) - this._mapStartChar.charCodeAt(0); 257 var row = a % this._itemsPerRow; 258 var col = 0 | (a / this._itemsPerRow); 259 260 var left, right, top, bottom; 261 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 262 // Issue #938. Don't use texStepX & texStepY 263 left = (2 * row * itemWidthInPixels + 1) / (2 * textureWide); 264 right = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide); 265 top = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh); 266 bottom = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh); 267 } else { 268 left = row * itemWidthInPixels / textureWide; 269 right = left + itemWidthInPixels / textureWide; 270 top = col * itemHeightInPixels / textureHigh; 271 bottom = top + itemHeightInPixels / textureHigh; 272 } 273 var quad = quads[i]; 274 var locQuadTL = quad.tl, locQuadTR = quad.tr, locQuadBL = quad.bl, locQuadBR = quad.br; 275 locQuadTL.texCoords.u = left; 276 locQuadTL.texCoords.v = top; 277 locQuadTR.texCoords.u = right; 278 locQuadTR.texCoords.v = top; 279 locQuadBL.texCoords.u = left; 280 locQuadBL.texCoords.v = bottom; 281 locQuadBR.texCoords.u = right; 282 locQuadBR.texCoords.v = bottom; 283 284 locQuadBL.vertices.x = (i * locItemWidth); 285 locQuadBL.vertices.y = 0; 286 locQuadBL.vertices.z = 0.0; 287 locQuadBR.vertices.x = (i * locItemWidth + locItemWidth); 288 locQuadBR.vertices.y = 0; 289 locQuadBR.vertices.z = 0.0; 290 locQuadTL.vertices.x = i * locItemWidth; 291 locQuadTL.vertices.y = this._itemHeight; 292 locQuadTL.vertices.z = 0.0; 293 locQuadTR.vertices.x = i * locItemWidth + locItemWidth; 294 locQuadTR.vertices.y = this._itemHeight; 295 locQuadTR.vertices.z = 0.0; 296 locQuadTL.colors = curColor; 297 locQuadTR.colors = curColor; 298 locQuadBL.colors = curColor; 299 locQuadBR.colors = curColor; 300 } 301 if (n > 0) { 302 locTextureAtlas.dirty = true; 303 var totalQuads = locTextureAtlas.totalQuads; 304 if (n > totalQuads) 305 locTextureAtlas.increaseTotalQuadsWith(n - totalQuads); 306 } 307 }, 308 309 /** 310 * set the display string 311 * @function 312 * @param {String} label 313 */ 314 setString: null, 315 316 _setStringForCanvas: function (label) { 317 label = String(label); 318 var len = label.length; 319 this._string = label; 320 this.width = len * this._itemWidth; 321 this.height = this._itemHeight; 322 if (this._children) { 323 var locChildren = this._children; 324 len = locChildren.length; 325 for (var i = 0; i < len; i++) { 326 var node = locChildren[i]; 327 if (node && !node._lateChild) 328 node.visible = false; 329 } 330 } 331 332 this.updateAtlasValues(); 333 this.quadsToDraw = len; 334 }, 335 336 _setStringForWebGL: function (label) { 337 label = String(label); 338 var len = label.length; 339 if (len > this.textureAtlas.totalQuads) 340 this.textureAtlas.resizeCapacity(len); 341 342 this._string = label; 343 this.width = len * this._itemWidth; 344 this.height = this._itemHeight; 345 346 this.updateAtlasValues(); 347 this.quadsToDraw = len; 348 }, 349 350 setOpacity: null, 351 352 _setOpacityForCanvas: function (opacity) { 353 if (this._displayedOpacity !== opacity) { 354 cc.AtlasNode.prototype.setOpacity.call(this, opacity); 355 var locChildren = this._children; 356 for (var i = 0, len = locChildren.length; i < len; i++) { 357 if (locChildren[i]) 358 locChildren[i].opacity = opacity; 359 } 360 } 361 }, 362 363 _setOpacityForWebGL: function (opacity) { 364 if (this._opacity !== opacity) 365 cc.AtlasNode.prototype.setOpacity.call(this, opacity); 366 } 367 }); 368 369 var _p = cc.LabelAtlas.prototype; 370 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 371 _p.updateAtlasValues = _p._updateAtlasValuesForWebGL; 372 _p.setString = _p._setStringForWebGL; 373 _p.setOpacity = _p._setOpacityForWebGL; 374 } else { 375 _p.updateAtlasValues = _p._updateAtlasValuesForCanvas; 376 _p.setString = _p._setStringForCanvas; 377 _p.setOpacity = _p._setOpacityForCanvas; 378 _p.addChild = _p._addChildForCanvas; 379 } 380 381 // Override properties 382 cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); 383 384 // Extended properties 385 /** @expose */ 386 _p.string; 387 cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); 388 389 /** 390 * <p> 391 * Create a label atlas. 392 * It accepts two groups of parameters: <br/> 393 * a) string, fntFile <br/> 394 * b) label, textureFilename, width, height, startChar <br/> 395 * </p> 396 * @param {String} strText 397 * @param {String} charMapFile charMapFile or fntFile 398 * @param {Number} [itemWidth=0] 399 * @param {Number} [itemHeight=0] 400 * @param {Number} [startCharMap=""] 401 * @return {cc.LabelAtlas|Null} returns the LabelAtlas object on success 402 * @example 403 * //Example 404 * //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 405 * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapfile.png', 12, 20, ' ') 406 * 407 * //creates the cc.LabelAtlas with a string, a fnt file 408 * var myLabel = cc.LabelAtlas.create('Text to display', 'CharMapFile.plist‘); 409 */ 410 cc.LabelAtlas.create = function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { 411 return new cc.LabelAtlas(strText, charMapFile, itemWidth, itemHeight, startCharMap); 412 }; 413 414