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