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