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