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 /**
 28  * @constant
 29  * @type Number
 30  */
 31 cc.DEFAULT_SPRITE_BATCH_CAPACITY = 29;
 32 
 33 /**
 34  * <p>
 35  *     In Canvas render mode ,cc.SpriteBatchNodeCanvas is like a normal node: if it contains children.             <br/>
 36  *     If its _useCache is set to true, it can cache the result that all children of SpriteBatchNode to a canvas <br/>
 37  *     (often known as "batch draw").<br/>
 38  *     <br/>
 39  *     A cc.SpriteBatchNode can reference one and only one texture (one image file, one texture atlas).<br/>
 40  *     Only the cc.Sprites that are contained in that texture can be added to the cc.SpriteBatchNode.<br/>
 41  *     All cc.Sprites added to a cc.SpriteBatchNode are drawn in one WebGL draw call. <br/>
 42  *     If the cc.Sprites are not added to a cc.SpriteBatchNode then an WebGL draw call will be needed for each one, which is less efficient. <br/>
 43  *     <br/>
 44  *     Limitations:<br/>
 45  *       - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is cc.Sprite or any subclass of cc.Sprite. <br/>
 46  *          eg: particles, labels and layer can't be added to a cc.SpriteBatchNode. <br/>
 47  *       - Either all its children are Aliased or Antialiased. It can't be a mix. <br/>
 48  *          This is because "alias" is a property of the texture, and all the sprites share the same texture. </br>
 49  * </p>
 50  * @class
 51  * @extends cc.Node
 52  *
 53  * @property {cc.TextureAtlas}  textureAtlas    - The texture atlas
 54  * @property {Array}            descendants     - <@readonly> Descendants of sprite batch node
 55  *
 56  * @example
 57  * //create a SpriteBatchNode
 58  * var parent2 = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
 59  */
 60 cc.SpriteBatchNode = cc.Node.extend(/** @lends cc.SpriteBatchNode# */{
 61     textureAtlas: null,
 62 
 63     _blendFunc: null,
 64     // all descendants: chlidren, gran children, etc...
 65     _descendants: null,
 66     _className: "SpriteBatchNode",
 67 
 68     /**
 69      * <p>
 70      *    This is the opposite of "addQuadFromSprite.<br/>
 71      *    It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas<br/>
 72      * </p>
 73      * @param {cc.Sprite} child
 74      * @param {Number} z zOrder
 75      * @param {Number} aTag
 76      * @return {cc.SpriteBatchNode}
 77      */
 78     addSpriteWithoutQuad: function (child, z, aTag) {
 79 
 80         cc.assert(child, cc._LogInfos.SpriteBatchNode_addSpriteWithoutQuad_2);
 81 
 82         if (!(child instanceof cc.Sprite)) {
 83             cc.log(cc._LogInfos.SpriteBatchNode_addSpriteWithoutQuad);
 84             return null;
 85         }
 86 
 87         // quad index is Z
 88         child.atlasIndex = z;
 89 
 90         // XXX: optimize with a binary search
 91         var i = 0, locDescendants = this._descendants;
 92         if (locDescendants && locDescendants.length > 0) {
 93             for (var index = 0; index < locDescendants.length; index++) {
 94                 var obj = locDescendants[index];
 95                 if (obj && (obj.atlasIndex >= z))
 96                     ++i;
 97             }
 98         }
 99         locDescendants.splice(i, 0, child);
100 
101         // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
102         cc.Node.prototype.addChild.call(this, child, z, aTag);
103 
104         //#issue 1262 don't use lazy sorting, tiles are added as quads not as sprites, so sprites need to be added in order
105         this.reorderBatch(false);
106         return this;
107     },
108 
109     // property
110     /**
111      * Return TextureAtlas of cc.SpriteBatchNode
112      * @return {cc.TextureAtlas}
113      */
114     getTextureAtlas: function () {
115         return this.textureAtlas;
116     },
117 
118     /**
119      * TextureAtlas of cc.SpriteBatchNode setter
120      * @param {cc.TextureAtlas} textureAtlas
121      */
122     setTextureAtlas: function (textureAtlas) {
123         if (textureAtlas != this.textureAtlas) {
124             this.textureAtlas = textureAtlas;
125         }
126     },
127 
128     /**
129      * Return Descendants of cc.SpriteBatchNode
130      * @return {Array}
131      */
132     getDescendants: function () {
133         return  this._descendants;
134     },
135 
136     /**
137      * <p>
138      *    initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
139      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
140      *    The file will be loaded using the TextureMgr.
141      * </p>
142      * @param {String} fileImage
143      * @param {Number} capacity
144      * @return {Boolean}
145      */
146     initWithFile: function (fileImage, capacity) {
147         var texture2D = cc.textureCache.textureForKey(fileImage);
148         if (!texture2D)
149             texture2D = cc.textureCache.addImage(fileImage);
150         return this.initWithTexture(texture2D, capacity);
151     },
152 
153     _setNodeDirtyForCache: function () {
154         this._cacheDirty = true;
155     },
156 
157     /**
158      * <p>
159      *    initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
160      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
161      *    The file will be loaded using the TextureMgr.
162      * </p>
163      * @param {String} fileImage
164      * @param {Number} capacity
165      * @return {Boolean}
166      */
167     init: function (fileImage, capacity) {
168         var texture2D = cc.textureCache.textureForKey(fileImage);
169         if (!texture2D)
170             texture2D = cc.textureCache.addImage(fileImage);
171         return this.initWithTexture(texture2D, capacity);
172     },
173 
174     /**
175      * increase Atlas Capacity
176      */
177     increaseAtlasCapacity: function () {
178         // if we're going beyond the current TextureAtlas's capacity,
179         // all the previously initialized sprites will need to redo their texture coords
180         // this is likely computationally expensive
181         var locCapacity = this.textureAtlas.capacity;
182         var quantity = Math.floor((locCapacity + 1) * 4 / 3);
183 
184         cc.log(cc._LogInfos.SpriteBatchNode_increaseAtlasCapacity, locCapacity, quantity);
185 
186         if (!this.textureAtlas.resizeCapacity(quantity)) {
187             // serious problems
188             cc.log(cc._LogInfos.SpriteBatchNode_increaseAtlasCapacity_2);
189         }
190     },
191 
192     /**
193      * removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter.
194      * @warning Removing a child from a cc.SpriteBatchNode is very slow
195      * @param {Number} index
196      * @param {Boolean} doCleanup
197      */
198     removeChildAtIndex: function (index, doCleanup) {
199         this.removeChild(this._children[index], doCleanup);
200     },
201 
202     /**
203      * rebuild index in order for child
204      * @param {cc.Sprite} pobParent
205      * @param {Number} index
206      * @return {Number}
207      */
208     rebuildIndexInOrder: function (pobParent, index) {
209         var children = pobParent.children;
210         if (children && children.length > 0) {
211             for (var i = 0; i < children.length; i++) {
212                 var obj = children[i];
213                 if (obj && (obj.zIndex < 0)) {
214                     index = this.rebuildIndexInOrder(obj, index);
215                 }
216             }
217         }
218         // ignore self (batch node)
219         if (!pobParent == this) {
220             pobParent.atlasIndex = index;
221             index++;
222         }
223         if (children && children.length > 0) {
224             for (i = 0; i < children.length; i++) {
225                 obj = children[i];
226                 if (obj && (obj.zIndex >= 0)) {
227                     index = this.rebuildIndexInOrder(obj, index);
228                 }
229             }
230         }
231         return index;
232     },
233 
234     /**
235      * get highest atlas index in child
236      * @param {cc.Sprite} sprite
237      * @return {Number}
238      */
239     highestAtlasIndexInChild: function (sprite) {
240         var children = sprite.children;
241 
242         if (!children || children.length == 0)
243             return sprite.atlasIndex;
244         else
245             return this.highestAtlasIndexInChild(children[children.length - 1]);
246     },
247 
248     /**
249      * get lowest atlas index in child
250      * @param {cc.Sprite} sprite
251      * @return {Number}
252      */
253     lowestAtlasIndexInChild: function (sprite) {
254         var children = sprite.children;
255 
256         if (!children || children.length == 0)
257             return sprite.atlasIndex;
258         else
259             return this.lowestAtlasIndexInChild(children[children.length - 1]);
260     },
261 
262     /**
263      * get atlas index for child
264      * @param {cc.Sprite} sprite
265      * @param {Number} nZ
266      * @return {Number}
267      */
268     atlasIndexForChild: function (sprite, nZ) {
269         var selParent = sprite.parent;
270         var brothers = selParent.children;
271         var childIndex = brothers.indexOf(sprite);
272 
273         // ignore parent Z if parent is spriteSheet
274         var ignoreParent = selParent == this;
275         var previous = null;
276         if (childIndex > 0 && childIndex < cc.UINT_MAX)
277             previous = brothers[childIndex - 1];
278 
279         // first child of the sprite sheet
280         if (ignoreParent) {
281             if (childIndex == 0)
282                 return 0;
283             return this.highestAtlasIndexInChild(previous) + 1;
284         }
285 
286         // parent is a cc.Sprite, so, it must be taken into account
287         // first child of an cc.Sprite ?
288         if (childIndex == 0) {
289             // less than parent and brothers
290             if (nZ < 0)
291                 return selParent.atlasIndex;
292             else
293                 return selParent.atlasIndex + 1;
294         } else {
295             // previous & sprite belong to the same branch
296             if ((previous.zIndex < 0 && nZ < 0) || (previous.zIndex >= 0 && nZ >= 0))
297                 return this.highestAtlasIndexInChild(previous) + 1;
298 
299             // else (previous < 0 and sprite >= 0 )
300             return selParent.atlasIndex + 1;
301         }
302     },
303 
304     /**
305      * Sprites use this to start sortChildren, don't call this manually
306      * @param {Boolean} reorder
307      */
308     reorderBatch: function (reorder) {
309         this._reorderChildDirty = reorder;
310     },
311 
312     /**
313      * set the source blending function for the texture
314      * @param {Number | cc.BlendFunc} src
315      * @param {Number} dst
316      */
317     setBlendFunc: function (src, dst) {
318         if (dst === undefined)
319             this._blendFunc = src;
320         else
321             this._blendFunc = {src: src, dst: dst};
322     },
323 
324     /**
325      * returns the blending function used for the texture
326      * @return {cc.BlendFunc}
327      */
328     getBlendFunc: function () {
329         return this._blendFunc;
330     },
331 
332     /**
333      *  (override reorderChild of cc.Node)
334      * @override
335      * @param {cc.Sprite} child
336      * @param {Number} zOrder
337      */
338     reorderChild: function (child, zOrder) {
339 
340         cc.assert(child, cc._LogInfos.SpriteBatchNode_reorderChild_2);
341 
342         if (this._children.indexOf(child) === -1) {
343             cc.log(cc._LogInfos.SpriteBatchNode_reorderChild);
344             return;
345         }
346 
347         if (zOrder === child.zIndex)
348             return;
349 
350         //set the z-order and sort later
351         cc.Node.prototype.reorderChild.call(this, child, zOrder);
352         this.setNodeDirty();
353     },
354 
355     /**
356      * remove child from cc.SpriteBatchNode (override removeChild of cc.Node)
357      * @param {cc.Sprite} child
358      * @param cleanup
359      */
360     removeChild: function (child, cleanup) {
361         // explicit null handling
362         if (child == null)
363             return;
364         if (this._children.indexOf(child) === -1) {
365             cc.log(cc._LogInfos.SpriteBatchNode_removeChild);
366             return;
367         }
368 
369         // cleanup before removing
370         this.removeSpriteFromAtlas(child);
371         cc.Node.prototype.removeChild.call(this, child, cleanup);
372     },
373 
374     _mvpMatrix: null,
375     _textureForCanvas: null,
376     _useCache: false,
377     _originalTexture: null,
378 
379     /**
380      * <p>
381      *    Constructor
382      *    creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.<br/>
383      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
384      *    The file will be loaded using the TextureMgr.<br/>
385      * </p>
386      * @function
387      * @constructor
388      * @param {String} fileImage
389      * @param {Number} capacity
390      * @example
391      * 1.
392      * //create a SpriteBatchNode with image path
393      * var spriteBatchNode = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
394      * 2.
395      * //create a SpriteBatchNode with texture
396      * var texture = cc.textureCache.addImage("res/animations/grossini.png");
397      * var spriteBatchNode = cc.SpriteBatchNode.create(texture,50);
398      */
399     ctor: null,
400 
401     _ctorForCanvas: function (fileImage, capacity) {
402         cc.Node.prototype.ctor.call(this);
403 
404         var texture2D;
405         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
406         if (typeof(fileImage) == "string") {
407             texture2D = cc.textureCache.textureForKey(fileImage);
408             if (!texture2D)
409                 texture2D = cc.textureCache.addImage(fileImage);
410         }
411         else if (fileImage instanceof cc.Texture2D)
412             texture2D = fileImage;
413 
414         texture2D && this.initWithTexture(texture2D, capacity);
415     },
416 
417     _ctorForWebGL: function (fileImage, capacity) {
418         cc.Node.prototype.ctor.call(this);
419         this._mvpMatrix = new cc.kmMat4();
420 
421         var texture2D;
422         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
423         if (typeof(fileImage) == "string") {
424             texture2D = cc.textureCache.textureForKey(fileImage);
425             if (!texture2D)
426                 texture2D = cc.textureCache.addImage(fileImage);
427         }
428         else if (fileImage instanceof cc.Texture2D)
429             texture2D = fileImage;
430 
431         texture2D && this.initWithTexture(texture2D, capacity);
432     },
433 
434 
435     /**
436      * <p>
437      *   Updates a quad at a certain index into the texture atlas. The CCSprite won't be added into the children array.                 <br/>
438      *   This method should be called only when you are dealing with very big AtlasSrite and when most of the cc.Sprite won't be updated.<br/>
439      *   For example: a tile map (cc.TMXMap) or a label with lots of characters (BitmapFontAtlas)<br/>
440      * </p>
441      * @function
442      * @param {cc.Sprite} sprite
443      * @param {Number} index
444      */
445     updateQuadFromSprite: null,
446 
447     _updateQuadFromSpriteForCanvas: function (sprite, index) {
448 
449         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite_2);
450 
451         if (!(sprite instanceof cc.Sprite)) {
452             cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
453             return;
454         }
455 
456         //
457         // update the quad directly. Don't add the sprite to the scene graph
458         //
459         sprite.batchNode = this;
460         sprite.atlasIndex = index;
461 
462         sprite.dirty = true;
463         // UpdateTransform updates the textureAtlas quad
464         sprite.updateTransform();
465     },
466 
467     _updateQuadFromSpriteForWebGL: function (sprite, index) {
468 
469         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
470 
471         if (!(sprite instanceof cc.Sprite)) {
472             cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
473             return;
474         }
475 
476         // make needed room
477         var locCapacity = this.textureAtlas.capacity;
478         while (index >= locCapacity || locCapacity == this.textureAtlas.totalQuads) {
479             this.increaseAtlasCapacity();
480         }
481 
482         //
483         // update the quad directly. Don't add the sprite to the scene graph
484         //
485         sprite.batchNode = this;
486         sprite.atlasIndex = index;
487 
488         sprite.dirty = true;
489         // UpdateTransform updates the textureAtlas quad
490         sprite.updateTransform();
491     },
492 
493     _swap: function (oldIndex, newIndex) {
494         var locDescendants = this._descendants;
495         var locTextureAtlas = this.textureAtlas;
496         var quads = locTextureAtlas.quads;
497         var tempItem = locDescendants[oldIndex];
498         var tempIteQuad = cc.V3F_C4B_T2F_QuadCopy(quads[oldIndex]);
499 
500         //update the index of other swapped item
501         locDescendants[newIndex].atlasIndex = oldIndex;
502         locDescendants[oldIndex] = locDescendants[newIndex];
503 
504         locTextureAtlas.updateQuad(quads[newIndex], oldIndex);
505         locDescendants[newIndex] = tempItem;
506         locTextureAtlas.updateQuad(tempIteQuad, newIndex);
507     },
508 
509     /**
510      * <p>
511      *    Inserts a quad at a certain index into the texture atlas. The cc.Sprite won't be added into the children array.                    <br/>
512      *    This method should be called only when you are dealing with very big AtlasSprite and when most of the cc.Sprite won't be updated.  <br/>
513      *    For example: a tile map (cc.TMXMap) or a label with lots of characters (cc.LabelBMFont)
514      * </p>
515      * @function
516      * @param {cc.Sprite} sprite
517      * @param {Number} index
518      */
519     insertQuadFromSprite: null,
520 
521     _insertQuadFromSpriteForCanvas: function (sprite, index) {
522 
523         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_insertQuadFromSprite_2);
524 
525         if (!(sprite instanceof cc.Sprite)) {
526             cc.log(cc._LogInfos.CCSpriteBatchNode_insertQuadFromSprite);
527             return;
528         }
529 
530         //
531         // update the quad directly. Don't add the sprite to the scene graph
532         //
533         sprite.batchNode = this;
534         sprite.atlasIndex = index;
535 
536         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
537         // XXX: so, it should be AFTER the insertQuad
538         sprite.dirty = true;
539         sprite.updateTransform();
540         this._children.splice(index, 0, sprite);
541     },
542 
543     _insertQuadFromSpriteForWebGL: function (sprite, index) {
544 
545         cc.assert(sprite, cc._LogInfos.Sprite_insertQuadFromSprite_2);
546 
547         if (!(sprite instanceof cc.Sprite)) {
548             cc.log(cc._LogInfos.Sprite_insertQuadFromSprite);
549             return;
550         }
551 
552         // make needed room
553         var locTextureAtlas = this.textureAtlas;
554         while (index >= locTextureAtlas.capacity || locTextureAtlas.capacity === locTextureAtlas.totalQuads)
555             this.increaseAtlasCapacity();
556 
557         //
558         // update the quad directly. Don't add the sprite to the scene graph
559         //
560         sprite.batchNode = this;
561         sprite.atlasIndex = index;
562         locTextureAtlas.insertQuad(sprite.quad, index);
563 
564         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
565         // XXX: so, it should be AFTER the insertQuad
566         sprite.dirty = true;
567         sprite.updateTransform();
568     },
569 
570     _updateAtlasIndex: function (sprite, curIndex) {
571         var count = 0;
572         var pArray = sprite.children;
573         if (pArray)
574             count = pArray.length;
575 
576         var oldIndex = 0;
577         if (count === 0) {
578             oldIndex = sprite.atlasIndex;
579             sprite.atlasIndex = curIndex;
580             sprite.arrivalOrder = 0;
581             if (oldIndex != curIndex)
582                 this._swap(oldIndex, curIndex);
583             curIndex++;
584         } else {
585             var needNewIndex = true;
586             if (pArray[0].zIndex >= 0) {
587                 //all children are in front of the parent
588                 oldIndex = sprite.atlasIndex;
589                 sprite.atlasIndex = curIndex;
590                 sprite.arrivalOrder = 0;
591                 if (oldIndex != curIndex)
592                     this._swap(oldIndex, curIndex);
593                 curIndex++;
594                 needNewIndex = false;
595             }
596             for (var i = 0; i < pArray.length; i++) {
597                 var child = pArray[i];
598                 if (needNewIndex && child.zIndex >= 0) {
599                     oldIndex = sprite.atlasIndex;
600                     sprite.atlasIndex = curIndex;
601                     sprite.arrivalOrder = 0;
602                     if (oldIndex != curIndex) {
603                         this._swap(oldIndex, curIndex);
604                     }
605                     curIndex++;
606                     needNewIndex = false;
607                 }
608                 curIndex = this._updateAtlasIndex(child, curIndex);
609             }
610 
611             if (needNewIndex) {
612                 //all children have a zOrder < 0)
613                 oldIndex = sprite.atlasIndex;
614                 sprite.atlasIndex = curIndex;
615                 sprite.arrivalOrder = 0;
616                 if (oldIndex != curIndex) {
617                     this._swap(oldIndex, curIndex);
618                 }
619                 curIndex++;
620             }
621         }
622 
623         return curIndex;
624     },
625 
626     _updateBlendFunc: function () {
627         if (!this.textureAtlas.texture.hasPremultipliedAlpha()) {
628             this._blendFunc.src = cc.SRC_ALPHA;
629             this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
630         }
631     },
632 
633     /**
634      * <p>
635      *    initializes a CCSpriteBatchNode with a texture2d and capacity of children.<br/>
636      *    The capacity will be increased in 33% in runtime if it run out of space.
637      * </p>
638      * @function
639      * @param {cc.Texture2D} tex
640      * @param {Number} [capacity]
641      * @return {Boolean}
642      */
643     initWithTexture: null,
644 
645     _initWithTextureForCanvas: function (tex, capacity) {
646         this._children = [];
647         this._descendants = [];
648 
649         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
650 
651         this._originalTexture = tex;
652         this._textureForCanvas = tex;
653         return true;
654     },
655 
656     _initWithTextureForWebGL: function (tex, capacity) {
657         this._children = [];
658         this._descendants = [];
659 
660         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
661         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
662         this.textureAtlas = new cc.TextureAtlas();
663         this.textureAtlas.initWithTexture(tex, capacity);
664         this._updateBlendFunc();
665         this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
666         return true;
667     },
668 
669     /**
670      * add child helper
671      * @param {cc.Sprite} sprite
672      * @param {Number} index
673      */
674     insertChild: function (sprite, index) {
675         sprite.batchNode = this;
676         sprite.atlasIndex = index;
677         sprite.dirty = true;
678 
679         var locTextureAtlas = this.textureAtlas;
680         if (locTextureAtlas.totalQuads >= locTextureAtlas.capacity)
681             this.increaseAtlasCapacity();
682 
683         locTextureAtlas.insertQuad(sprite.quad, index);
684         this._descendants.splice(index, 0, sprite);
685 
686         // update indices
687         var i = index + 1, locDescendant = this._descendants;
688         if (locDescendant && locDescendant.length > 0) {
689             for (; i < locDescendant.length; i++)
690                 locDescendant[i].atlasIndex++;
691         }
692 
693         // add children recursively
694         var locChildren = sprite.children, child;
695         if (locChildren) {
696             for (i = 0, l = locChildren.length || 0; i < l; i++) {
697                 child = locChildren[i];
698                 if (child) {
699                     var getIndex = this.atlasIndexForChild(child, child.zIndex);
700                     this.insertChild(child, getIndex);
701                 }
702             }
703         }
704     },
705 
706     /**
707      * addChild helper, faster than insertChild
708      * @function
709      * @param {cc.Sprite} sprite
710      */
711     appendChild: null,
712 
713     _appendChildForCanvas: function (sprite) {
714         this._reorderChildDirty = true;
715         sprite.batchNode = this;
716         sprite.dirty = true;
717 
718         this._descendants.push(sprite);
719         var index = this._descendants.length - 1;
720         sprite.atlasIndex = index;
721 
722         // add children recursively
723         var children = sprite.children;
724         for (var i = 0, l = children.length || 0; i < l; i++)
725             this.appendChild(children[i]);
726     },
727 
728     _appendChildForWebGL: function (sprite) {
729         this._reorderChildDirty = true;
730         sprite.batchNode = this;
731         sprite.dirty = true;
732 
733         this._descendants.push(sprite);
734         var index = this._descendants.length - 1;
735         sprite.atlasIndex = index;
736 
737         var locTextureAtlas = this.textureAtlas;
738         if (locTextureAtlas.totalQuads == locTextureAtlas.capacity)
739             this.increaseAtlasCapacity();
740         locTextureAtlas.insertQuad(sprite.quad, index);
741 
742         // add children recursively
743         var children = sprite.children;
744         for (var i = 0, l = children.length || 0; i < l; i++)
745             this.appendChild(children[i]);
746     },
747 
748     /**
749      * remove sprite from TextureAtlas
750      * @function
751      * @param {cc.Sprite} sprite
752      */
753     removeSpriteFromAtlas: null,
754 
755     _removeSpriteFromAtlasForCanvas: function (sprite) {
756         // Cleanup sprite. It might be reused (issue #569)
757         sprite.batchNode = null;
758         var locDescendants = this._descendants;
759         var index = locDescendants.indexOf(sprite);
760         if (index != -1) {
761             locDescendants.splice(index, 1)
762 
763             // update all sprites beyond this one
764             var len = locDescendants.length;
765             for (; index < len; ++index) {
766                 var s = locDescendants[index];
767                 s.atlasIndex--;
768             }
769         }
770 
771         // remove children recursively
772         var children = sprite.children;
773         if (children) {
774             for (var i = 0, l = children.length || 0; i < l; i++)
775                 children[i] && this.removeSpriteFromAtlas(children[i]);
776         }
777     },
778 
779     _removeSpriteFromAtlasForWebGL: function (sprite) {
780         this.textureAtlas.removeQuadAtIndex(sprite.atlasIndex);   // remove from TextureAtlas
781 
782         // Cleanup sprite. It might be reused (issue #569)
783         sprite.batchNode = null;
784 
785         var locDescendants = this._descendants;
786         var index = locDescendants.indexOf(sprite);
787         if (index != -1) {
788             locDescendants.splice(index, 1);
789 
790             // update all sprites beyond this one
791 
792             var len = locDescendants.length;
793             for (; index < len; ++index) {
794                 var s = locDescendants[index];
795                 s.atlasIndex--;
796             }
797         }
798 
799         // remove children recursively
800         var children = sprite.children;
801         if (children) {
802             for (var i = 0, l = children.length || 0; i < l; i++)
803                 children[i] && this.removeSpriteFromAtlas(children[i]);
804         }
805     },
806     // CCTextureProtocol
807     /**
808      * Return texture of cc.SpriteBatchNode
809      * @function
810      * @return {cc.Texture2D|HTMLImageElement|HTMLCanvasElement}
811      */
812     getTexture: null,
813 
814     _getTextureForCanvas: function () {
815         return this._textureForCanvas;
816     },
817 
818     _getTextureForWebGL: function () {
819         return this.textureAtlas.texture;
820     },
821 
822     /**
823      * Texture of cc.SpriteBatchNode setter
824      * @function
825      * @param {cc.Texture2D} texture
826      */
827     setTexture: null,
828 
829     _setTextureForCanvas: function (texture) {
830         this._textureForCanvas = texture;
831         var locChildren = this._children;
832         for (var i = 0; i < locChildren.length; i++)
833             locChildren[i].texture = texture;
834     },
835 
836     _setTextureForWebGL: function (texture) {
837         this.textureAtlas.texture = texture;
838         this._updateBlendFunc();
839     },
840 
841     /**
842      * Don't call visit on it's children ( override visit of cc.Node )
843      * @function
844      * @override
845      * @param {CanvasRenderingContext2D} ctx
846      */
847     visit: null,
848 
849     _visitForCanvas: function (ctx) {
850         var context = ctx || cc._renderContext;
851         // quick return if not visible
852         if (!this._visible)
853             return;
854 
855         context.save();
856         this.transform(ctx);
857         var i, locChildren = this._children;
858 
859         if (locChildren) {
860             this.sortAllChildren();
861             for (i = 0; i < locChildren.length; i++) {
862                 if (locChildren[i])
863                     locChildren[i].visit(context);
864             }
865         }
866 
867         context.restore();
868     },
869 
870     _visitForWebGL: function (ctx) {
871         var gl = ctx || cc._renderContext;
872 
873         // CAREFUL:
874         // This visit is almost identical to CocosNode#visit
875         // with the exception that it doesn't call visit on it's children
876         //
877         // The alternative is to have a void CCSprite#visit, but
878         // although this is less mantainable, is faster
879         //
880         if (!this._visible)
881             return;
882         cc.kmGLPushMatrix();
883         var locGrid = this.grid;
884         if (locGrid && locGrid.isActive()) {
885             locGrid.beforeDraw();
886             this.transformAncestors();
887         }
888         this.sortAllChildren();
889         this.transform(gl);
890         this.draw(gl);
891         if (locGrid && locGrid.isActive())
892             locGrid.afterDraw(this);
893         cc.kmGLPopMatrix();
894         this.arrivalOrder = 0;
895     },
896 
897     /**
898      * Add child to cc.SpriteBatchNode (override addChild of cc.Node)
899      * @function
900      * @override
901      * @param {cc.Sprite} child
902      * @param {Number} [zOrder]
903      * @param {Number} [tag]
904      */
905     addChild: null,
906 
907     _addChildForCanvas: function (child, zOrder, tag) {
908 
909         cc.assert(child != null, cc._LogInfos.CCSpriteBatchNode_addChild_3);
910 
911         if (!(child instanceof cc.Sprite)) {
912             cc.log(cc._LogInfos.CCSpriteBatchNode_addChild);
913             return;
914         }
915 
916         zOrder = (zOrder == null) ? child.zIndex : zOrder;
917         tag = (tag == null) ? child.tag : tag;
918 
919         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
920         this.appendChild(child);
921         this.setNodeDirty();
922     },
923 
924     _addChildForWebGL: function (child, zOrder, tag) {
925 
926         cc.assert(child != null, cc._LogInfos.Sprite_addChild_6);
927 
928         if (!(child instanceof cc.Sprite)) {
929             cc.log(cc._LogInfos.Sprite_addChild_4);
930             return;
931         }
932         if (child.texture != this.textureAtlas.texture) {                    // check cc.Sprite is using the same texture id
933             cc.log(cc._LogInfos.Sprite_addChild_5);
934             return;
935         }
936 
937         zOrder = (zOrder == null) ? child.zIndex : zOrder;
938         tag = (tag == null) ? child.tag : tag;
939 
940         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
941         this.appendChild(child);
942         this.setNodeDirty();
943     },
944 
945     /**
946      * <p>Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter. <br/>
947      * (override removeAllChildren of cc.Node)</p>
948      * @function
949      * @param {Boolean} cleanup
950      */
951     removeAllChildren: null,
952 
953     _removeAllChildrenForCanvas: function (cleanup) {
954         // Invalidate atlas index. issue #569
955         // useSelfRender should be performed on all descendants. issue #1216
956         var locDescendants = this._descendants;
957         if (locDescendants && locDescendants.length > 0) {
958             for (var i = 0, len = locDescendants.length; i < len; i++) {
959                 if (locDescendants[i])
960                     locDescendants[i].batchNode = null;
961             }
962         }
963 
964         cc.Node.prototype.removeAllChildren.call(this, cleanup);
965         this._descendants.length = 0;
966     },
967 
968     _removeAllChildrenForWebGL: function (cleanup) {
969         // Invalidate atlas index. issue #569
970         // useSelfRender should be performed on all descendants. issue #1216
971         var locDescendants = this._descendants;
972         if (locDescendants && locDescendants.length > 0) {
973             for (var i = 0, len = locDescendants.length; i < len; i++) {
974                 if (locDescendants[i])
975                     locDescendants[i].batchNode = null;
976             }
977         }
978         cc.Node.prototype.removeAllChildren.call(this, cleanup);
979         this._descendants.length = 0;
980         this.textureAtlas.removeAllQuads();
981     },
982 
983     sortAllChildren: null,
984 
985     _sortAllChildrenForCanvas: function () {
986         if (this._reorderChildDirty) {
987             var i, j = 0, locChildren = this._children;
988             var length = locChildren.length, tempChild;
989             //insertion sort
990             for (i = 1; i < length; i++) {
991                 var tempItem = locChildren[i];
992                 j = i - 1;
993                 tempChild = locChildren[j];
994 
995                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
996                 while (j >= 0 && ( tempItem._localZOrder < tempChild._localZOrder ||
997                     ( tempItem._localZOrder == tempChild._localZOrder && tempItem.arrivalOrder < tempChild.arrivalOrder ))) {
998                     locChildren[j + 1] = tempChild;
999                     j = j - 1;
1000                     tempChild = locChildren[j];
1001                 }
1002                 locChildren[j + 1] = tempItem;
1003             }
1004 
1005             //sorted now check all children
1006             if (locChildren.length > 0) {
1007                 //first sort all children recursively based on zOrder
1008                 this._arrayMakeObjectsPerformSelector(locChildren, cc.Node.StateCallbackType.sortAllChildren);
1009             }
1010             this._reorderChildDirty = false;
1011         }
1012     },
1013 
1014     _sortAllChildrenForWebGL: function () {
1015         if (this._reorderChildDirty) {
1016             var childrenArr = this._children;
1017             var i, j = 0, length = childrenArr.length, tempChild;
1018             //insertion sort
1019             for (i = 1; i < length; i++) {
1020                 var tempItem = childrenArr[i];
1021                 j = i - 1;
1022                 tempChild = childrenArr[j];
1023 
1024                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
1025                 while (j >= 0 && ( tempItem._localZOrder < tempChild._localZOrder ||
1026                     ( tempItem._localZOrder == tempChild._localZOrder && tempItem.arrivalOrder < tempChild.arrivalOrder ))) {
1027                     childrenArr[j + 1] = tempChild;
1028                     j = j - 1;
1029                     tempChild = childrenArr[j];
1030                 }
1031                 childrenArr[j + 1] = tempItem;
1032             }
1033 
1034             //sorted now check all children
1035             if (childrenArr.length > 0) {
1036                 //first sort all children recursively based on zOrder
1037                 this._arrayMakeObjectsPerformSelector(childrenArr, cc.Node.StateCallbackType.sortAllChildren);
1038 
1039                 var index = 0;
1040                 //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact)
1041                 // and at the same time reorder descedants and the quads to the right index
1042                 for (i = 0; i < childrenArr.length; i++)
1043                     index = this._updateAtlasIndex(childrenArr[i], index);
1044             }
1045             this._reorderChildDirty = false;
1046         }
1047     },
1048     /**
1049      * draw cc.SpriteBatchNode (override draw of cc.Node)
1050      * @function
1051      */
1052     draw: null,
1053 
1054     _drawForWebGL: function () {
1055         // Optimization: Fast Dispatch
1056         if (this.textureAtlas.totalQuads === 0)
1057             return;
1058 
1059         //cc.nodeDrawSetup(this);
1060         this._shaderProgram.use();
1061         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
1062         this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.updateTransform);
1063         cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst);
1064 
1065         this.textureAtlas.drawQuads();
1066     }
1067 });
1068 
1069 var _p = cc.SpriteBatchNode.prototype;
1070 
1071 if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
1072     _p.ctor = _p._ctorForWebGL;
1073     _p.updateQuadFromSprite = _p._updateQuadFromSpriteForWebGL;
1074     _p.insertQuadFromSprite = _p._insertQuadFromSpriteForWebGL;
1075     _p.initWithTexture = _p._initWithTextureForWebGL;
1076     _p.appendChild = _p._appendChildForWebGL;
1077     _p.removeSpriteFromAtlas = _p._removeSpriteFromAtlasForWebGL;
1078     _p.getTexture = _p._getTextureForWebGL;
1079     _p.setTexture = _p._setTextureForWebGL;
1080     _p.visit = _p._visitForWebGL;
1081     _p.addChild = _p._addChildForWebGL;
1082     _p.removeAllChildren = _p._removeAllChildrenForWebGL;
1083     _p.sortAllChildren = _p._sortAllChildrenForWebGL;
1084     _p.draw = _p._drawForWebGL;
1085 } else {
1086     _p.ctor = _p._ctorForCanvas;
1087     _p.updateQuadFromSprite = _p._updateQuadFromSpriteForCanvas;
1088     _p.insertQuadFromSprite = _p._insertQuadFromSpriteForCanvas;
1089     _p.initWithTexture = _p._initWithTextureForCanvas;
1090     _p.appendChild = _p._appendChildForCanvas;
1091     _p.removeSpriteFromAtlas = _p._removeSpriteFromAtlasForCanvas;
1092     _p.getTexture = _p._getTextureForCanvas;
1093     _p.setTexture = _p._setTextureForCanvas;
1094     _p.visit = _p._visitForCanvas;
1095     _p.removeAllChildren = _p._removeAllChildrenForCanvas;
1096     _p.addChild = _p._addChildForCanvas;
1097     _p.sortAllChildren = _p._sortAllChildrenForCanvas;
1098     _p.draw = cc.Node.prototype.draw;
1099 }
1100 
1101 // Override properties
1102 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
1103 
1104 // Extended properties
1105 /** @expose */
1106 _p.descendants;
1107 cc.defineGetterSetter(_p, "descendants", _p.getDescendants);
1108 
1109 
1110 /**
1111  * <p>
1112  *    creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.<br/>
1113  *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
1114  *    The file will be loaded using the TextureMgr.<br/>
1115  * </p>
1116  * @param {String|cc.Texture2D} fileImage
1117  * @param {Number} capacity
1118  * @return {cc.SpriteBatchNode}
1119  * @example
1120  * 1.
1121  * //create a SpriteBatchNode with image path
1122  * var spriteBatchNode = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
1123  * 2.
1124  * //create a SpriteBatchNode with texture
1125  * var texture = cc.textureCache.addImage("res/animations/grossini.png");
1126  * var spriteBatchNode = cc.SpriteBatchNode.create(texture,50);
1127  */
1128 cc.SpriteBatchNode.create = function (fileImage, capacity) {
1129     return new cc.SpriteBatchNode(fileImage, capacity);
1130 };
1131