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