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.Assert(false, "cc.AtlasNode:Abstract updateAtlasValue not overridden"); 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 cc.Assert(tile != null, "title should not be null"); 161 var texture = cc.TextureCache.getInstance().addImage(tile); 162 return this.initWithTexture(texture, tileWidth, tileHeight, itemsToRender); 163 }, 164 165 /** 166 * initializes an CCAtlasNode with a texture the width and height of each item measured in points and the quantity of items to render 167 * @param {cc.Texture2D} texture 168 * @param {Number} tileWidth 169 * @param {Number} tileHeight 170 * @param {Number} itemsToRender 171 * @return {Boolean} 172 */ 173 initWithTexture:null, 174 175 _initWithTextureForCanvas:function(texture, tileWidth, tileHeight, itemsToRender){ 176 this._itemWidth = tileWidth; 177 this._itemHeight = tileHeight; 178 179 this._opacityModifyRGB = true; 180 this._originalTexture = texture; 181 if (!this._originalTexture) { 182 cc.log("cocos2d: Could not initialize cc.AtlasNode. Invalid Texture."); 183 return false; 184 } 185 this._textureForCanvas = this._originalTexture; 186 this._calculateMaxItems(); 187 188 this._quadsToDraw = itemsToRender; 189 return true; 190 }, 191 192 _initWithTextureForWebGL:function(texture, tileWidth, tileHeight, itemsToRender){ 193 this._itemWidth = tileWidth; 194 this._itemHeight = tileHeight; 195 this._colorUnmodified = cc.WHITE; 196 this._opacityModifyRGB = true; 197 198 this._blendFunc.src = cc.BLEND_SRC; 199 this._blendFunc.dst = cc.BLEND_DST; 200 201 var locRealColor = this._realColor; 202 this._colorF32Array = new Float32Array([locRealColor.r / 255.0, locRealColor.g / 255.0, locRealColor.b / 255.0, this._realOpacity / 255.0]); 203 this._textureAtlas = new cc.TextureAtlas(); 204 this._textureAtlas.initWithTexture(texture, itemsToRender); 205 206 if (!this._textureAtlas) { 207 cc.log("cocos2d: Could not initialize cc.AtlasNode. Invalid Texture."); 208 return false; 209 } 210 211 this._updateBlendFunc(); 212 this._updateOpacityModifyRGB(); 213 this._calculateMaxItems(); 214 this._quadsToDraw = itemsToRender; 215 216 //shader stuff 217 this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR)); 218 this._uniformColor = cc.renderContext.getUniformLocation(this.getShaderProgram().getProgram(), "u_color"); 219 return true; 220 }, 221 222 draw:null, 223 224 /** 225 * @param {WebGLRenderingContext} ctx renderContext 226 */ 227 _drawForWebGL:function (ctx) { 228 var context = ctx || cc.renderContext; 229 cc.NODE_DRAW_SETUP(this); 230 cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); 231 context.uniform4fv(this._uniformColor, this._colorF32Array); 232 this._textureAtlas.drawNumberOfQuads(this._quadsToDraw, 0); 233 }, 234 235 /** 236 * @param {cc.Color3B} color3 237 */ 238 setColor:null, 239 240 _setColorForCanvas:function (color3) { 241 var locRealColor = this._realColor; 242 if ((locRealColor.r == color3.r) && (locRealColor.g == color3.g) && (locRealColor.b == color3.b)) 243 return; 244 var temp = new cc.Color3B(color3.r,color3.g,color3.b); 245 this._colorUnmodified = color3; 246 247 if (this._opacityModifyRGB) { 248 var locDisplayedOpacity = this._displayedOpacity; 249 temp.r = temp.r * locDisplayedOpacity / 255; 250 temp.g = temp.g * locDisplayedOpacity / 255; 251 temp.b = temp.b * locDisplayedOpacity / 255; 252 } 253 cc.NodeRGBA.prototype.setColor.call(this, color3); 254 255 if (this.getTexture()) { 256 var element = this._originalTexture.getHtmlElementObj(); 257 if(!element) 258 return; 259 var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(element); 260 if (cacheTextureForColor) { 261 var textureRect = cc.rect(0, 0, element.width, element.height); 262 element = cc.generateTintImage(element, cacheTextureForColor, this._realColor, textureRect); 263 var locTexture = new cc.Texture2D(); 264 locTexture.initWithElement(element); 265 locTexture.handleLoadedTexture(); 266 this.setTexture(locTexture); 267 } 268 } 269 }, 270 271 _setColorForWebGL:function (color3) { 272 var temp = cc.Color3B(color3.r,color3.g,color3.b); 273 this._colorUnmodified = color3; 274 var locDisplayedOpacity = this._displayedOpacity; 275 if (this._opacityModifyRGB) { 276 temp.r = temp.r * locDisplayedOpacity / 255; 277 temp.g = temp.g * locDisplayedOpacity / 255; 278 temp.b = temp.b * locDisplayedOpacity / 255; 279 } 280 cc.NodeRGBA.prototype.setColor.call(this, color3); 281 var locDisplayedColor = this._displayedColor; 282 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 283 locDisplayedColor.b / 255.0, locDisplayedOpacity / 255.0]); 284 }, 285 286 /** 287 * @param {Number} opacity 288 */ 289 setOpacity: null, 290 291 _setOpacityForCanvas: function (opacity) { 292 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 293 // special opacity for premultiplied textures 294 if (this._opacityModifyRGB) { 295 this.setColor(this._colorUnmodified); 296 } 297 }, 298 299 _setOpacityForWebGL: function (opacity) { 300 cc.NodeRGBA.prototype.setOpacity.call(this, opacity); 301 // special opacity for premultiplied textures 302 if (this._opacityModifyRGB) { 303 this.setColor(this._colorUnmodified); 304 } else { 305 var locDisplayedColor = this._displayedColor; 306 this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, 307 locDisplayedColor.b / 255.0, this._displayedOpacity / 255.0]); 308 } 309 }, 310 311 // cc.Texture protocol 312 /** 313 * returns the used texture 314 * @return {cc.Texture2D} 315 */ 316 getTexture: null, 317 318 _getTextureForCanvas: function () { 319 return this._textureForCanvas; 320 }, 321 322 _getTextureForWebGL: function () { 323 return this._textureAtlas.getTexture(); 324 }, 325 326 /** sets a new texture. it will be retained 327 * @param {cc.Texture2D} texture 328 */ 329 setTexture: null, 330 331 _setTextureForCanvas: function (texture) { 332 this._textureForCanvas = texture; 333 }, 334 335 _setTextureForWebGL: function (texture) { 336 this._textureAtlas.setTexture(texture); 337 this._updateBlendFunc(); 338 this._updateOpacityModifyRGB(); 339 }, 340 341 _calculateMaxItems:null, 342 343 _calculateMaxItemsForCanvas:function () { 344 var selTexture = this.getTexture(); 345 var size = selTexture.getContentSize(); 346 347 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 348 this._itemsPerRow = 0 | (size.width / this._itemWidth); 349 }, 350 351 _calculateMaxItemsForWebGL:function () { 352 var selTexture = this.getTexture(); 353 var size = selTexture.getContentSize(); 354 if(this._ignoreContentScaleFactor) 355 size = selTexture.getContentSizeInPixels(); 356 357 this._itemsPerColumn = 0 | (size.height / this._itemHeight); 358 this._itemsPerRow = 0 | (size.width / this._itemWidth); 359 }, 360 361 _updateBlendFunc:function () { 362 if (!this._textureAtlas.getTexture().hasPremultipliedAlpha()) { 363 this._blendFunc.src = gl.SRC_ALPHA; 364 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; 365 } 366 }, 367 368 _updateOpacityModifyRGB:function () { 369 this._opacityModifyRGB = this._textureAtlas.getTexture().hasPremultipliedAlpha(); 370 }, 371 372 _setIgnoreContentScaleFactor:function(ignoreContentScaleFactor){ 373 this._ignoreContentScaleFactor = ignoreContentScaleFactor; 374 } 375 }); 376 377 if(cc.Browser.supportWebGL){ 378 cc.AtlasNode.prototype.initWithTexture = cc.AtlasNode.prototype._initWithTextureForWebGL; 379 cc.AtlasNode.prototype.draw = cc.AtlasNode.prototype._drawForWebGL; 380 cc.AtlasNode.prototype.setColor = cc.AtlasNode.prototype._setColorForWebGL; 381 cc.AtlasNode.prototype.setOpacity = cc.AtlasNode.prototype._setOpacityForWebGL; 382 cc.AtlasNode.prototype.getTexture = cc.AtlasNode.prototype._getTextureForWebGL; 383 cc.AtlasNode.prototype.setTexture = cc.AtlasNode.prototype._setTextureForWebGL; 384 cc.AtlasNode.prototype._calculateMaxItems = cc.AtlasNode.prototype._calculateMaxItemsForWebGL; 385 } else { 386 cc.AtlasNode.prototype.initWithTexture = cc.AtlasNode.prototype._initWithTextureForCanvas; 387 cc.AtlasNode.prototype.draw = cc.Node.prototype.draw; 388 cc.AtlasNode.prototype.setColor = cc.AtlasNode.prototype._setColorForCanvas; 389 cc.AtlasNode.prototype.setOpacity = cc.AtlasNode.prototype._setOpacityForCanvas; 390 cc.AtlasNode.prototype.getTexture = cc.AtlasNode.prototype._getTextureForCanvas; 391 cc.AtlasNode.prototype.setTexture = cc.AtlasNode.prototype._setTextureForCanvas; 392 cc.AtlasNode.prototype._calculateMaxItems = cc.AtlasNode.prototype._calculateMaxItemsForCanvas; 393 } 394 395 /** creates a cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render 396 * @param {String} tile 397 * @param {Number} tileWidth 398 * @param {Number} tileHeight 399 * @param {Number} itemsToRender 400 * @return {cc.AtlasNode} 401 * @example 402 * // example 403 * var node = cc.AtlasNode.create("pathOfTile", 16, 16, 1); 404 */ 405 cc.AtlasNode.create = function (tile, tileWidth, tileHeight, itemsToRender) { 406 var ret = new cc.AtlasNode(); 407 if (ret.initWithTileFile(tile, tileWidth, tileHeight, itemsToRender)) 408 return ret; 409 return null; 410 }; 411 412