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