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