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