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 /** <p> cc.AtlasNode is a subclass of cc.Node that implements the cc.RGBAProtocol and<br/> 28 * cc.TextureProtocol protocol</p> 29 * 30 * <p> It knows how to render a TextureAtlas object. <br/> 31 * If you are going to render a TextureAtlas consider subclassing cc.AtlasNode (or a subclass of cc.AtlasNode)</p> 32 * 33 * <p> All features from cc.Node are valid, plus the following features: <br/> 34 * - opacity and RGB colors </p> 35 * @class 36 * @extends cc.NodeRGBA 37 * 38 * @property {cc.Texture2D} texture - Current used texture 39 * @property {cc.TextureAtlas} textureAtlas - Texture atlas for cc.AtlasNode 40 * @property {Number} quadsToDraw - Number of quads to draw 41 * 42 */ 43 cc.AtlasNode = cc.NodeRGBA.extend(/** @lends cc.AtlasNode# */{ 44 textureAtlas: null, 45 quadsToDraw: 0, 46 47 RGBAProtocol: true, 48 //! chars per row 49 _itemsPerRow: 0, 50 //! chars per column 51 _itemsPerColumn: 0, 52 //! width of each char 53 _itemWidth: 0, 54 //! height of each char 55 _itemHeight: 0, 56 57 _colorUnmodified: null, 58 59 // protocol variables 60 _opacityModifyRGB: false, 61 _blendFunc: null, 62 63 _ignoreContentScaleFactor: false, // This variable is only used for CCLabelAtlas FPS display. So plz don't modify its value. 64 _className: "AtlasNode", 65 66 /** 67 * Creates a cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render 68 * Constructor of cc.AtlasNode 69 * @param {String} tile 70 * @param {Number} tileWidth 71 * @param {Number} tileHeight 72 * @param {Number} itemsToRender 73 * @example 74 * var node = new cc.AtlasNode("pathOfTile", 16, 16, 1); 75 */ 76 ctor: function (tile, tileWidth, tileHeight, itemsToRender) { 77 cc.NodeRGBA.prototype.ctor.call(this); 78 this._colorUnmodified = cc.color.WHITE; 79 this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 80 this._ignoreContentScaleFactor = false; 81 82 itemsToRender !== undefined && this.initWithTileFile(tile, tileWidth, tileHeight, itemsToRender); 83 }, 84 85 /** updates the Atlas (indexed vertex array). 86 * Shall be overridden in subclasses 87 */ 88 updateAtlasValues: function () { 89 cc.log(cc._LogInfos.AtlasNode_updateAtlasValues); 90 }, 91 92 /** cc.AtlasNode - RGBA protocol 93 * @return {cc.Color} 94 */ 95 getColor: function () { 96 if (this._opacityModifyRGB) 97 return this._colorUnmodified; 98 return cc.NodeRGBA.prototype.getColor.call(this); 99 }, 100 101 /** 102 * @param {Boolean} value 103 */ 104 setOpacityModifyRGB: function (value) { 105 var oldColor = this.color; 106 this._opacityModifyRGB = value; 107 this.color = oldColor; 108 }, 109 110 /** 111 * @return {Boolean} 112 */ 113 isOpacityModifyRGB: function () { 114 return this._opacityModifyRGB; 115 }, 116 117 /** cc.AtlasNode - CocosNodeTexture protocol 118 * @return {cc.BlendFunc} 119 */ 120 getBlendFunc: function () { 121 return this._blendFunc; 122 }, 123 124 /** 125 * BlendFunc setter 126 * @param {Number | cc.BlendFunc} src 127 * @param {Number} dst 128 */ 129 setBlendFunc: function (src, dst) { 130 if (dst === undefined) 131 this._blendFunc = src; 132 else 133 this._blendFunc = {src: src, dst: dst}; 134 }, 135 136 /** 137 * @param {cc.TextureAtlas} value 138 */ 139 setTextureAtlas: function (value) { 140 this.textureAtlas = value; 141 }, 142 143 /** 144 * @return {cc.TextureAtlas} 145 */ 146 getTextureAtlas: function () { 147 return this.textureAtlas; 148 }, 149 150 /** 151 * @return {Number} 152 */ 153 getQuadsToDraw: function () { 154 return this.quadsToDraw; 155 }, 156 157 /** 158 * @param {Number} quadsToDraw 159 */ 160 setQuadsToDraw: function (quadsToDraw) { 161 this.quadsToDraw = quadsToDraw; 162 }, 163 164 _textureForCanvas: null, 165 _originalTexture: null, 166 167 _uniformColor: null, 168 _colorF32Array: null, 169 170 /** initializes an cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render 171 * @param {String} tile 172 * @param {Number} tileWidth 173 * @param {Number} tileHeight 174 * @param {Number} itemsToRender 175 * @return {Boolean} 176 */ 177 initWithTileFile: function (tile, tileWidth, tileHeight, itemsToRender) { 178 if (!tile) 179 throw "cc.AtlasNode.initWithTileFile(): title should not be null"; 180 var texture = cc.textureCache.addImage(tile); 181 return this.initWithTexture(texture, tileWidth, tileHeight, itemsToRender); 182 }, 183 184 /** 185 * initializes an CCAtlasNode with a texture the width and height of each item measured in points and the quantity of items to render 186 * @param {cc.Texture2D} texture 187 * @param {Number} tileWidth 188 * @param {Number} tileHeight 189 * @param {Number} itemsToRender 190 * @return {Boolean} 191 */ 192 initWithTexture: null, 193 194 _initWithTextureForCanvas: function (texture, tileWidth, tileHeight, itemsToRender) { 195 this._itemWidth = tileWidth; 196 this._itemHeight = tileHeight; 197 198 this._opacityModifyRGB = true; 199 this._originalTexture = texture; 200 if (!this._originalTexture) { 201 cc.log(cc._LogInfos.AtlasNode__initWithTexture); 202 return false; 203 } 204 this._textureForCanvas = this._originalTexture; 205 this._calculateMaxItems(); 206 207 this.quadsToDraw = itemsToRender; 208 return true; 209 }, 210 211 _initWithTextureForWebGL: function (texture, tileWidth, tileHeight, itemsToRender) { 212 this._itemWidth = tileWidth; 213 this._itemHeight = tileHeight; 214 this._colorUnmodified = cc.color.WHITE; 215 this._opacityModifyRGB = true; 216 217 this._blendFunc.src = cc.BLEND_SRC; 218 this._blendFunc.dst = cc.BLEND_DST; 219 220 var locRealColor = this._realColor; 221 this._colorF32Array = new Float32Array([locRealColor.r / 255.0, locRealColor.g / 255.0, locRealColor.b / 255.0, this._realOpacity / 255.0]); 222 this.textureAtlas = new cc.TextureAtlas(); 223 this.textureAtlas.initWithTexture(texture, itemsToRender); 224 225 if (!this.textureAtlas) { 226 cc.log(cc._LogInfos.AtlasNode__initWithTexture); 227 return false; 228 } 229 230 this._updateBlendFunc(); 231 this._updateOpacityModifyRGB(); 232 this._calculateMaxItems(); 233 this.quadsToDraw = itemsToRender; 234 235 //shader stuff 236 this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR); 237 this._uniformColor = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), "u_color"); 238 return true; 239 }, 240 241 draw: null, 242 243 /** 244 * @param {WebGLRenderingContext} ctx renderContext 245 */ 246 _drawForWebGL: function (ctx) { 247 var context = ctx || cc._renderContext; 248 cc.nodeDrawSetup(this); 249 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 250 context.uniform4fv(this._uniformColor, this._colorF32Array); 251 this.textureAtlas.drawNumberOfQuads(this.quadsToDraw, 0); 252 }, 253 254 /** 255 * @function 256 * @param {cc.Color} color3 257 */ 258 setColor: null, 259 260 _setColorForCanvas: function (color3) { 261 var locRealColor = this._realColor; 262 if ((locRealColor.r == color3.r) && (locRealColor.g == color3.g) && (locRealColor.b == color3.b)) 263 return; 264 var temp = cc.color(color3.r, color3.g, color3.b); 265 this._colorUnmodified = color3; 266 267 if (this._opacityModifyRGB) { 268 var locDisplayedOpacity = this._displayedOpacity; 269 temp.r = temp.r * locDisplayedOpacity / 255; 270 temp.g = temp.g * locDisplayedOpacity / 255; 271 temp.b = temp.b * locDisplayedOpacity / 255; 272 } 273 cc.NodeRGBA.prototype.setColor.call(this, color3); 274 275 if (this.texture) { 276 var element = this._originalTexture.getHtmlElementObj(); 277 if (!element) 278 return; 279 var cacheTextureForColor = cc.textureCache.getTextureColors(element); 280 if (cacheTextureForColor) { 281 var textureRect = cc.rect(0, 0, element.width, element.height); 282 element = cc.generateTintImage(element, cacheTextureForColor, this._realColor, textureRect); 283 var locTexture = new cc.Texture2D(); 284 locTexture.initWithElement(element); 285 locTexture.handleLoadedTexture(); 286 this.texture = locTexture; 287 } 288 } 289 }, 290 291 _setColorForWebGL: function (color3) { 292 var temp = cc.color(color3.r, color3.g, color3.b); 293 this._colorUnmodified = color3; 294 var locDisplayedOpacity = this._displayedOpacity; 295 if (this._opacityModifyRGB) { 296 temp.r = temp.r * locDisplayedOpacity / 255; 297 temp.g = temp.g * locDisplayedOpacity / 255; 298 temp.b = temp.b * locDisplayedOpacity / 255; 299 } 300 cc.NodeRGBA.prototype.setColor.call(this, color3); 301 var locDisplayedColor = this._displayedColor; 302 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 303 locDisplayedColor.b / 255.0, locDisplayedOpacity / 255.0]); 304 }, 305 306 /** 307 * @function 308 * @param {Number} opacity 309 */ 310 setOpacity: function (opacity) { 311 }, 312 313 _setOpacityForCanvas: function (opacity) { 314 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 315 // special opacity for premultiplied textures 316 if (this._opacityModifyRGB) { 317 this.color = this._colorUnmodified; 318 } 319 }, 320 321 _setOpacityForWebGL: function (opacity) { 322 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 323 // special opacity for premultiplied textures 324 if (this._opacityModifyRGB) { 325 this.color = this._colorUnmodified; 326 } else { 327 var locDisplayedColor = this._displayedColor; 328 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 329 locDisplayedColor.b / 255.0, this._displayedOpacity / 255.0]); 330 } 331 }, 332 333 // cc.Texture protocol 334 /** 335 * returns the used texture 336 * @function 337 * @return {cc.Texture2D} 338 */ 339 getTexture: null, 340 341 _getTextureForCanvas: function () { 342 return this._textureForCanvas; 343 }, 344 345 _getTextureForWebGL: function () { 346 return this.textureAtlas.texture; 347 }, 348 349 /** 350 * sets a new texture. it will be retained 351 * @function 352 * @param {cc.Texture2D} texture 353 */ 354 setTexture: null, 355 356 _setTextureForCanvas: function (texture) { 357 this._textureForCanvas = texture; 358 }, 359 360 _setTextureForWebGL: function (texture) { 361 this.textureAtlas.texture = texture; 362 this._updateBlendFunc(); 363 this._updateOpacityModifyRGB(); 364 }, 365 366 _calculateMaxItems: null, 367 368 _calculateMaxItemsForCanvas: function () { 369 var selTexture = this.texture; 370 var size = selTexture.getContentSize(); 371 372 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 373 this._itemsPerRow = 0 | (size.width / this._itemWidth); 374 }, 375 376 _calculateMaxItemsForWebGL: function () { 377 var selTexture = this.texture; 378 var size = selTexture.getContentSize(); 379 if (this._ignoreContentScaleFactor) 380 size = selTexture.getContentSizeInPixels(); 381 382 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 383 this._itemsPerRow = 0 | (size.width / this._itemWidth); 384 }, 385 386 _updateBlendFunc: function () { 387 if (!this.textureAtlas.texture.hasPremultipliedAlpha()) { 388 this._blendFunc.src = cc.SRC_ALPHA; 389 this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; 390 } 391 }, 392 393 _updateOpacityModifyRGB: function () { 394 this._opacityModifyRGB = this.textureAtlas.texture.hasPremultipliedAlpha(); 395 }, 396 397 _setIgnoreContentScaleFactor: function (ignoreContentScaleFactor) { 398 this._ignoreContentScaleFactor = ignoreContentScaleFactor; 399 } 400 }); 401 402 var _p = cc.AtlasNode.prototype; 403 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 404 _p.initWithTexture = _p._initWithTextureForWebGL; 405 _p.draw = _p._drawForWebGL; 406 _p.setColor = _p._setColorForWebGL; 407 _p.setOpacity = _p._setOpacityForWebGL; 408 _p.getTexture = _p._getTextureForWebGL; 409 _p.setTexture = _p._setTextureForWebGL; 410 _p._calculateMaxItems = _p._calculateMaxItemsForWebGL; 411 } else { 412 _p.initWithTexture = _p._initWithTextureForCanvas; 413 _p.draw = cc.Node.prototype.draw; 414 _p.setColor = _p._setColorForCanvas; 415 _p.setOpacity = _p._setOpacityForCanvas; 416 _p.getTexture = _p._getTextureForCanvas; 417 _p.setTexture = _p._setTextureForCanvas; 418 _p._calculateMaxItems = _p._calculateMaxItemsForCanvas; 419 } 420 421 // Override properties 422 cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); 423 cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); 424 425 // Extended properties 426 /** @expose */ 427 _p.texture; 428 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); 429 /** @expose */ 430 _p.textureAtlas; 431 /** @expose */ 432 _p.quadsToDraw; 433 434 435 /** creates a cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render 436 * @param {String} tile 437 * @param {Number} tileWidth 438 * @param {Number} tileHeight 439 * @param {Number} itemsToRender 440 * @return {cc.AtlasNode} 441 * @example 442 * // example 443 * var node = cc.AtlasNode.create("pathOfTile", 16, 16, 1); 444 */ 445 cc.AtlasNode.create = function (tile, tileWidth, tileHeight, itemsToRender) { 446 return new cc.AtlasNode(tile, tileWidth, tileHeight, itemsToRender); 447 }; 448 449