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 if(this._uniformColor && this._colorF32Array){ 251 context.uniform4fv(this._uniformColor, this._colorF32Array); 252 this.textureAtlas.drawNumberOfQuads(this.quadsToDraw, 0); 253 } 254 }, 255 256 /** 257 * @function 258 * @param {cc.Color} color3 259 */ 260 setColor: null, 261 262 _setColorForCanvas: function (color3) { 263 var locRealColor = this._realColor; 264 if ((locRealColor.r == color3.r) && (locRealColor.g == color3.g) && (locRealColor.b == color3.b)) 265 return; 266 var temp = cc.color(color3.r, color3.g, color3.b); 267 this._colorUnmodified = color3; 268 269 if (this._opacityModifyRGB) { 270 var locDisplayedOpacity = this._displayedOpacity; 271 temp.r = temp.r * locDisplayedOpacity / 255; 272 temp.g = temp.g * locDisplayedOpacity / 255; 273 temp.b = temp.b * locDisplayedOpacity / 255; 274 } 275 cc.NodeRGBA.prototype.setColor.call(this, color3); 276 277 if (this.texture) { 278 var element = this._originalTexture.getHtmlElementObj(); 279 if (!element) 280 return; 281 var cacheTextureForColor = cc.textureCache.getTextureColors(element); 282 if (cacheTextureForColor) { 283 var textureRect = cc.rect(0, 0, element.width, element.height); 284 element = cc.generateTintImage(element, cacheTextureForColor, this._realColor, textureRect); 285 var locTexture = new cc.Texture2D(); 286 locTexture.initWithElement(element); 287 locTexture.handleLoadedTexture(); 288 this.texture = locTexture; 289 } 290 } 291 }, 292 293 _setColorForWebGL: function (color3) { 294 var temp = cc.color(color3.r, color3.g, color3.b); 295 this._colorUnmodified = color3; 296 var locDisplayedOpacity = this._displayedOpacity; 297 if (this._opacityModifyRGB) { 298 temp.r = temp.r * locDisplayedOpacity / 255; 299 temp.g = temp.g * locDisplayedOpacity / 255; 300 temp.b = temp.b * locDisplayedOpacity / 255; 301 } 302 cc.NodeRGBA.prototype.setColor.call(this, color3); 303 var locDisplayedColor = this._displayedColor; 304 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 305 locDisplayedColor.b / 255.0, locDisplayedOpacity / 255.0]); 306 }, 307 308 /** 309 * @function 310 * @param {Number} opacity 311 */ 312 setOpacity: function (opacity) { 313 }, 314 315 _setOpacityForCanvas: function (opacity) { 316 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 317 // special opacity for premultiplied textures 318 if (this._opacityModifyRGB) { 319 this.color = this._colorUnmodified; 320 } 321 }, 322 323 _setOpacityForWebGL: function (opacity) { 324 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 325 // special opacity for premultiplied textures 326 if (this._opacityModifyRGB) { 327 this.color = this._colorUnmodified; 328 } else { 329 var locDisplayedColor = this._displayedColor; 330 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 331 locDisplayedColor.b / 255.0, this._displayedOpacity / 255.0]); 332 } 333 }, 334 335 // cc.Texture protocol 336 /** 337 * returns the used texture 338 * @function 339 * @return {cc.Texture2D} 340 */ 341 getTexture: null, 342 343 _getTextureForCanvas: function () { 344 return this._textureForCanvas; 345 }, 346 347 _getTextureForWebGL: function () { 348 return this.textureAtlas.texture; 349 }, 350 351 /** 352 * sets a new texture. it will be retained 353 * @function 354 * @param {cc.Texture2D} texture 355 */ 356 setTexture: null, 357 358 _setTextureForCanvas: function (texture) { 359 this._textureForCanvas = texture; 360 }, 361 362 _setTextureForWebGL: function (texture) { 363 this.textureAtlas.texture = texture; 364 this._updateBlendFunc(); 365 this._updateOpacityModifyRGB(); 366 }, 367 368 _calculateMaxItems: null, 369 370 _calculateMaxItemsForCanvas: function () { 371 var selTexture = this.texture; 372 var size = selTexture.getContentSize(); 373 374 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 375 this._itemsPerRow = 0 | (size.width / this._itemWidth); 376 }, 377 378 _calculateMaxItemsForWebGL: function () { 379 var selTexture = this.texture; 380 var size = selTexture.getContentSize(); 381 if (this._ignoreContentScaleFactor) 382 size = selTexture.getContentSizeInPixels(); 383 384 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 385 this._itemsPerRow = 0 | (size.width / this._itemWidth); 386 }, 387 388 _updateBlendFunc: function () { 389 if (!this.textureAtlas.texture.hasPremultipliedAlpha()) { 390 this._blendFunc.src = cc.SRC_ALPHA; 391 this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; 392 } 393 }, 394 395 _updateOpacityModifyRGB: function () { 396 this._opacityModifyRGB = this.textureAtlas.texture.hasPremultipliedAlpha(); 397 }, 398 399 _setIgnoreContentScaleFactor: function (ignoreContentScaleFactor) { 400 this._ignoreContentScaleFactor = ignoreContentScaleFactor; 401 } 402 }); 403 404 var _p = cc.AtlasNode.prototype; 405 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 406 _p.initWithTexture = _p._initWithTextureForWebGL; 407 _p.draw = _p._drawForWebGL; 408 _p.setColor = _p._setColorForWebGL; 409 _p.setOpacity = _p._setOpacityForWebGL; 410 _p.getTexture = _p._getTextureForWebGL; 411 _p.setTexture = _p._setTextureForWebGL; 412 _p._calculateMaxItems = _p._calculateMaxItemsForWebGL; 413 } else { 414 _p.initWithTexture = _p._initWithTextureForCanvas; 415 _p.draw = cc.Node.prototype.draw; 416 _p.setColor = _p._setColorForCanvas; 417 _p.setOpacity = _p._setOpacityForCanvas; 418 _p.getTexture = _p._getTextureForCanvas; 419 _p.setTexture = _p._setTextureForCanvas; 420 _p._calculateMaxItems = _p._calculateMaxItemsForCanvas; 421 } 422 423 // Override properties 424 cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); 425 cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); 426 427 // Extended properties 428 /** @expose */ 429 _p.texture; 430 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); 431 /** @expose */ 432 _p.textureAtlas; 433 /** @expose */ 434 _p.quadsToDraw; 435 436 437 /** creates a cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render 438 * @param {String} tile 439 * @param {Number} tileWidth 440 * @param {Number} tileHeight 441 * @param {Number} itemsToRender 442 * @return {cc.AtlasNode} 443 * @example 444 * // example 445 * var node = cc.AtlasNode.create("pathOfTile", 16, 16, 1); 446 */ 447 cc.AtlasNode.create = function (tile, tileWidth, tileHeight, itemsToRender) { 448 return new cc.AtlasNode(tile, tileWidth, tileHeight, itemsToRender); 449 }; 450 451