1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies 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      *    Constructor of cc.SpriteBatchNode
386      * </p>
387      * @function
388      *
389      * @param {String} fileImage
390      * @param {Number} capacity
391      * @example
392      * 1.
393      * //create a SpriteBatchNode with image path
394      * var spriteBatchNode = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
395      * 2.
396      * //create a SpriteBatchNode with texture
397      * var texture = cc.textureCache.addImage("res/animations/grossini.png");
398      * var spriteBatchNode = cc.SpriteBatchNode.create(texture,50);
399      */
400     ctor: null,
401 
402     _ctorForCanvas: function (fileImage, capacity) {
403         cc.Node.prototype.ctor.call(this);
404 
405         var texture2D;
406         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
407         if (typeof(fileImage) == "string") {
408             texture2D = cc.textureCache.textureForKey(fileImage);
409             if (!texture2D)
410                 texture2D = cc.textureCache.addImage(fileImage);
411         }
412         else if (fileImage instanceof cc.Texture2D)
413             texture2D = fileImage;
414 
415         texture2D && this.initWithTexture(texture2D, capacity);
416     },
417 
418     _ctorForWebGL: function (fileImage, capacity) {
419         cc.Node.prototype.ctor.call(this);
420         this._mvpMatrix = new cc.kmMat4();
421 
422         var texture2D;
423         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
424         if (typeof(fileImage) == "string") {
425             texture2D = cc.textureCache.textureForKey(fileImage);
426             if (!texture2D)
427                 texture2D = cc.textureCache.addImage(fileImage);
428         }
429         else if (fileImage instanceof cc.Texture2D)
430             texture2D = fileImage;
431 
432         texture2D && this.initWithTexture(texture2D, capacity);
433     },
434 
435 
436     /**
437      * <p>
438      *   Updates a quad at a certain index into the texture atlas. The CCSprite won't be added into the children array.                 <br/>
439      *   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/>
440      *   For example: a tile map (cc.TMXMap) or a label with lots of characters (BitmapFontAtlas)<br/>
441      * </p>
442      * @function
443      * @param {cc.Sprite} sprite
444      * @param {Number} index
445      */
446     updateQuadFromSprite: null,
447 
448     _updateQuadFromSpriteForCanvas: function (sprite, index) {
449 
450         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite_2);
451 
452         if (!(sprite instanceof cc.Sprite)) {
453             cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
454             return;
455         }
456 
457         //
458         // update the quad directly. Don't add the sprite to the scene graph
459         //
460         sprite.batchNode = this;
461         sprite.atlasIndex = index;
462 
463         sprite.dirty = true;
464         // UpdateTransform updates the textureAtlas quad
465         sprite.updateTransform();
466     },
467 
468     _updateQuadFromSpriteForWebGL: function (sprite, index) {
469 
470         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
471 
472         if (!(sprite instanceof cc.Sprite)) {
473             cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
474             return;
475         }
476 
477         // make needed room
478         var locCapacity = this.textureAtlas.capacity;
479         while (index >= locCapacity || locCapacity == this.textureAtlas.totalQuads) {
480             this.increaseAtlasCapacity();
481         }
482 
483         //
484         // update the quad directly. Don't add the sprite to the scene graph
485         //
486         sprite.batchNode = this;
487         sprite.atlasIndex = index;
488 
489         sprite.dirty = true;
490         // UpdateTransform updates the textureAtlas quad
491         sprite.updateTransform();
492     },
493 
494     _swap: function (oldIndex, newIndex) {
495         var locDescendants = this._descendants;
496         var locTextureAtlas = this.textureAtlas;
497         var quads = locTextureAtlas.quads;
498         var tempItem = locDescendants[oldIndex];
499         var tempIteQuad = cc.V3F_C4B_T2F_QuadCopy(quads[oldIndex]);
500 
501         //update the index of other swapped item
502         locDescendants[newIndex].atlasIndex = oldIndex;
503         locDescendants[oldIndex] = locDescendants[newIndex];
504 
505         locTextureAtlas.updateQuad(quads[newIndex], oldIndex);
506         locDescendants[newIndex] = tempItem;
507         locTextureAtlas.updateQuad(tempIteQuad, newIndex);
508     },
509 
510     /**
511      * <p>
512      *    Inserts a quad at a certain index into the texture atlas. The cc.Sprite won't be added into the children array.                    <br/>
513      *    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/>
514      *    For example: a tile map (cc.TMXMap) or a label with lots of characters (cc.LabelBMFont)
515      * </p>
516      * @function
517      * @param {cc.Sprite} sprite
518      * @param {Number} index
519      */
520     insertQuadFromSprite: null,
521 
522     _insertQuadFromSpriteForCanvas: function (sprite, index) {
523 
524         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_insertQuadFromSprite_2);
525 
526         if (!(sprite instanceof cc.Sprite)) {
527             cc.log(cc._LogInfos.CCSpriteBatchNode_insertQuadFromSprite);
528             return;
529         }
530 
531         //
532         // update the quad directly. Don't add the sprite to the scene graph
533         //
534         sprite.batchNode = this;
535         sprite.atlasIndex = index;
536 
537         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
538         // XXX: so, it should be AFTER the insertQuad
539         sprite.dirty = true;
540         sprite.updateTransform();
541         this._children.splice(index, 0, sprite);
542     },
543 
544     _insertQuadFromSpriteForWebGL: function (sprite, index) {
545 
546         cc.assert(sprite, cc._LogInfos.Sprite_insertQuadFromSprite_2);
547 
548         if (!(sprite instanceof cc.Sprite)) {
549             cc.log(cc._LogInfos.Sprite_insertQuadFromSprite);
550             return;
551         }
552 
553         // make needed room
554         var locTextureAtlas = this.textureAtlas;
555         while (index >= locTextureAtlas.capacity || locTextureAtlas.capacity === locTextureAtlas.totalQuads)
556             this.increaseAtlasCapacity();
557 
558         //
559         // update the quad directly. Don't add the sprite to the scene graph
560         //
561         sprite.batchNode = this;
562         sprite.atlasIndex = index;
563         locTextureAtlas.insertQuad(sprite.quad, index);
564 
565         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
566         // XXX: so, it should be AFTER the insertQuad
567         sprite.dirty = true;
568         sprite.updateTransform();
569     },
570 
571     _updateAtlasIndex: function (sprite, curIndex) {
572         var count = 0;
573         var pArray = sprite.children;
574         if (pArray)
575             count = pArray.length;
576 
577         var oldIndex = 0;
578         if (count === 0) {
579             oldIndex = sprite.atlasIndex;
580             sprite.atlasIndex = curIndex;
581             sprite.arrivalOrder = 0;
582             if (oldIndex != curIndex)
583                 this._swap(oldIndex, curIndex);
584             curIndex++;
585         } else {
586             var needNewIndex = true;
587             if (pArray[0].zIndex >= 0) {
588                 //all children are in front of the parent
589                 oldIndex = sprite.atlasIndex;
590                 sprite.atlasIndex = curIndex;
591                 sprite.arrivalOrder = 0;
592                 if (oldIndex != curIndex)
593                     this._swap(oldIndex, curIndex);
594                 curIndex++;
595                 needNewIndex = false;
596             }
597             for (var i = 0; i < pArray.length; i++) {
598                 var child = pArray[i];
599                 if (needNewIndex && child.zIndex >= 0) {
600                     oldIndex = sprite.atlasIndex;
601                     sprite.atlasIndex = curIndex;
602                     sprite.arrivalOrder = 0;
603                     if (oldIndex != curIndex) {
604                         this._swap(oldIndex, curIndex);
605                     }
606                     curIndex++;
607                     needNewIndex = false;
608                 }
609                 curIndex = this._updateAtlasIndex(child, curIndex);
610             }
611 
612             if (needNewIndex) {
613                 //all children have a zOrder < 0)
614                 oldIndex = sprite.atlasIndex;
615                 sprite.atlasIndex = curIndex;
616                 sprite.arrivalOrder = 0;
617                 if (oldIndex != curIndex) {
618                     this._swap(oldIndex, curIndex);
619                 }
620                 curIndex++;
621             }
622         }
623 
624         return curIndex;
625     },
626 
627     _updateBlendFunc: function () {
628         if (!this.textureAtlas.texture.hasPremultipliedAlpha()) {
629             this._blendFunc.src = cc.SRC_ALPHA;
630             this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
631         }
632     },
633 
634     /**
635      * <p>
636      *    initializes a CCSpriteBatchNode with a texture2d and capacity of children.<br/>
637      *    The capacity will be increased in 33% in runtime if it run out of space.
638      * </p>
639      * @function
640      * @param {cc.Texture2D} tex
641      * @param {Number} [capacity]
642      * @return {Boolean}
643      */
644     initWithTexture: null,
645 
646     _initWithTextureForCanvas: function (tex, capacity) {
647         this._children = [];
648         this._descendants = [];
649 
650         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
651 
652         this._originalTexture = tex;
653         this._textureForCanvas = tex;
654         return true;
655     },
656 
657     _initWithTextureForWebGL: function (tex, capacity) {
658         this._children = [];
659         this._descendants = [];
660 
661         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
662         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
663         this.textureAtlas = new cc.TextureAtlas();
664         this.textureAtlas.initWithTexture(tex, capacity);
665         this._updateBlendFunc();
666         this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
667         return true;
668     },
669 
670     /**
671      * add child helper
672      * @param {cc.Sprite} sprite
673      * @param {Number} index
674      */
675     insertChild: function (sprite, index) {
676         sprite.batchNode = this;
677         sprite.atlasIndex = index;
678         sprite.dirty = true;
679 
680         var locTextureAtlas = this.textureAtlas;
681         if (locTextureAtlas.totalQuads >= locTextureAtlas.capacity)
682             this.increaseAtlasCapacity();
683 
684         locTextureAtlas.insertQuad(sprite.quad, index);
685         this._descendants.splice(index, 0, sprite);
686 
687         // update indices
688         var i = index + 1, locDescendant = this._descendants;
689         if (locDescendant && locDescendant.length > 0) {
690             for (; i < locDescendant.length; i++)
691                 locDescendant[i].atlasIndex++;
692         }
693 
694         // add children recursively
695         var locChildren = sprite.children, child;
696         if (locChildren) {
697             for (i = 0, l = locChildren.length || 0; i < l; i++) {
698                 child = locChildren[i];
699                 if (child) {
700                     var getIndex = this.atlasIndexForChild(child, child.zIndex);
701                     this.insertChild(child, getIndex);
702                 }
703             }
704         }
705     },
706 
707     /**
708      * addChild helper, faster than insertChild
709      * @function
710      * @param {cc.Sprite} sprite
711      */
712     appendChild: null,
713 
714     _appendChildForCanvas: function (sprite) {
715         this._reorderChildDirty = true;
716         sprite.batchNode = this;
717         sprite.dirty = true;
718 
719         this._descendants.push(sprite);
720         var index = this._descendants.length - 1;
721         sprite.atlasIndex = index;
722 
723         // add children recursively
724         var children = sprite.children;
725         for (var i = 0, l = children.length || 0; i < l; i++)
726             this.appendChild(children[i]);
727     },
728 
729     _appendChildForWebGL: function (sprite) {
730         this._reorderChildDirty = true;
731         sprite.batchNode = this;
732         sprite.dirty = true;
733 
734         this._descendants.push(sprite);
735         var index = this._descendants.length - 1;
736         sprite.atlasIndex = index;
737 
738         var locTextureAtlas = this.textureAtlas;
739         if (locTextureAtlas.totalQuads == locTextureAtlas.capacity)
740             this.increaseAtlasCapacity();
741         locTextureAtlas.insertQuad(sprite.quad, index);
742 
743         // add children recursively
744         var children = sprite.children;
745         for (var i = 0, l = children.length || 0; i < l; i++)
746             this.appendChild(children[i]);
747     },
748 
749     /**
750      * remove sprite from TextureAtlas
751      * @function
752      * @param {cc.Sprite} sprite
753      */
754     removeSpriteFromAtlas: null,
755 
756     _removeSpriteFromAtlasForCanvas: function (sprite) {
757         // Cleanup sprite. It might be reused (issue #569)
758         sprite.batchNode = null;
759         var locDescendants = this._descendants;
760         var index = locDescendants.indexOf(sprite);
761         if (index != -1) {
762             locDescendants.splice(index, 1)
763 
764             // update all sprites beyond this one
765             var len = locDescendants.length;
766             for (; index < len; ++index) {
767                 var s = locDescendants[index];
768                 s.atlasIndex--;
769             }
770         }
771 
772         // remove children recursively
773         var children = sprite.children;
774         if (children) {
775             for (var i = 0, l = children.length || 0; i < l; i++)
776                 children[i] && this.removeSpriteFromAtlas(children[i]);
777         }
778     },
779 
780     _removeSpriteFromAtlasForWebGL: function (sprite) {
781         this.textureAtlas.removeQuadAtIndex(sprite.atlasIndex);   // remove from TextureAtlas
782 
783         // Cleanup sprite. It might be reused (issue #569)
784         sprite.batchNode = null;
785 
786         var locDescendants = this._descendants;
787         var index = locDescendants.indexOf(sprite);
788         if (index != -1) {
789             locDescendants.splice(index, 1);
790 
791             // update all sprites beyond this one
792 
793             var len = locDescendants.length;
794             for (; index < len; ++index) {
795                 var s = locDescendants[index];
796                 s.atlasIndex--;
797             }
798         }
799 
800         // remove children recursively
801         var children = sprite.children;
802         if (children) {
803             for (var i = 0, l = children.length || 0; i < l; i++)
804                 children[i] && this.removeSpriteFromAtlas(children[i]);
805         }
806     },
807     // CCTextureProtocol
808     /**
809      * Return texture of cc.SpriteBatchNode
810      * @function
811      * @return {cc.Texture2D|HTMLImageElement|HTMLCanvasElement}
812      */
813     getTexture: null,
814 
815     _getTextureForCanvas: function () {
816         return this._textureForCanvas;
817     },
818 
819     _getTextureForWebGL: function () {
820         return this.textureAtlas.texture;
821     },
822 
823     /**
824      * Texture of cc.SpriteBatchNode setter
825      * @function
826      * @param {cc.Texture2D} texture
827      */
828     setTexture: null,
829 
830     _setTextureForCanvas: function (texture) {
831         this._textureForCanvas = texture;
832         var locChildren = this._children;
833         for (var i = 0; i < locChildren.length; i++)
834             locChildren[i].texture = texture;
835     },
836 
837     _setTextureForWebGL: function (texture) {
838         this.textureAtlas.texture = texture;
839         this._updateBlendFunc();
840     },
841 
842     /**
843      * Don't call visit on it's children ( override visit of cc.Node )
844      * @function
845      * @override
846      * @param {CanvasRenderingContext2D} ctx
847      */
848     visit: null,
849 
850     _visitForCanvas: function (ctx) {
851         var context = ctx || cc._renderContext;
852         // quick return if not visible
853         if (!this._visible)
854             return;
855 
856         context.save();
857         this.transform(ctx);
858         var i, locChildren = this._children;
859 
860         if (locChildren) {
861             this.sortAllChildren();
862             for (i = 0; i < locChildren.length; i++) {
863                 if (locChildren[i])
864                     locChildren[i].visit(context);
865             }
866         }
867 
868         context.restore();
869     },
870 
871     _visitForWebGL: function (ctx) {
872         var gl = ctx || cc._renderContext;
873 
874         // CAREFUL:
875         // This visit is almost identical to CocosNode#visit
876         // with the exception that it doesn't call visit on it's children
877         //
878         // The alternative is to have a void CCSprite#visit, but
879         // although this is less mantainable, is faster
880         //
881         if (!this._visible)
882             return;
883         cc.kmGLPushMatrix();
884         var locGrid = this.grid;
885         if (locGrid && locGrid.isActive()) {
886             locGrid.beforeDraw();
887             this.transformAncestors();
888         }
889         this.sortAllChildren();
890         this.transform(gl);
891         this.draw(gl);
892         if (locGrid && locGrid.isActive())
893             locGrid.afterDraw(this);
894         cc.kmGLPopMatrix();
895         this.arrivalOrder = 0;
896     },
897 
898     /**
899      * Add child to cc.SpriteBatchNode (override addChild of cc.Node)
900      * @function
901      * @override
902      * @param {cc.Sprite} child
903      * @param {Number} [zOrder]
904      * @param {Number} [tag]
905      */
906     addChild: null,
907 
908     _addChildForCanvas: function (child, zOrder, tag) {
909 
910         cc.assert(child != null, cc._LogInfos.CCSpriteBatchNode_addChild_3);
911 
912         if (!(child instanceof cc.Sprite)) {
913             cc.log(cc._LogInfos.CCSpriteBatchNode_addChild);
914             return;
915         }
916 
917         zOrder = (zOrder == null) ? child.zIndex : zOrder;
918         tag = (tag == null) ? child.tag : tag;
919 
920         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
921         this.appendChild(child);
922         this.setNodeDirty();
923     },
924 
925     _addChildForWebGL: function (child, zOrder, tag) {
926 
927         cc.assert(child != null, cc._LogInfos.Sprite_addChild_6);
928 
929         if (!(child instanceof cc.Sprite)) {
930             cc.log(cc._LogInfos.Sprite_addChild_4);
931             return;
932         }
933         if (child.texture != this.textureAtlas.texture) {                    // check cc.Sprite is using the same texture id
934             cc.log(cc._LogInfos.Sprite_addChild_5);
935             return;
936         }
937 
938         zOrder = (zOrder == null) ? child.zIndex : zOrder;
939         tag = (tag == null) ? child.tag : tag;
940 
941         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
942         this.appendChild(child);
943         this.setNodeDirty();
944     },
945 
946     /**
947      * <p>Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter. <br/>
948      * (override removeAllChildren of cc.Node)</p>
949      * @function
950      * @param {Boolean} cleanup
951      */
952     removeAllChildren: null,
953 
954     _removeAllChildrenForCanvas: function (cleanup) {
955         // Invalidate atlas index. issue #569
956         // useSelfRender should be performed on all descendants. issue #1216
957         var locDescendants = this._descendants;
958         if (locDescendants && locDescendants.length > 0) {
959             for (var i = 0, len = locDescendants.length; i < len; i++) {
960                 if (locDescendants[i])
961                     locDescendants[i].batchNode = null;
962             }
963         }
964 
965         cc.Node.prototype.removeAllChildren.call(this, cleanup);
966         this._descendants.length = 0;
967     },
968 
969     _removeAllChildrenForWebGL: function (cleanup) {
970         // Invalidate atlas index. issue #569
971         // useSelfRender should be performed on all descendants. issue #1216
972         var locDescendants = this._descendants;
973         if (locDescendants && locDescendants.length > 0) {
974             for (var i = 0, len = locDescendants.length; i < len; i++) {
975                 if (locDescendants[i])
976                     locDescendants[i].batchNode = null;
977             }
978         }
979         cc.Node.prototype.removeAllChildren.call(this, cleanup);
980         this._descendants.length = 0;
981         this.textureAtlas.removeAllQuads();
982     },
983 
984     sortAllChildren: null,
985 
986     _sortAllChildrenForCanvas: function () {
987         if (this._reorderChildDirty) {
988             var i, j = 0, locChildren = this._children;
989             var length = locChildren.length, tempChild;
990             //insertion sort
991             for (i = 1; i < length; i++) {
992                 var tempItem = locChildren[i];
993                 j = i - 1;
994                 tempChild = locChildren[j];
995 
996                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
997                 while (j >= 0 && ( tempItem._localZOrder < tempChild._localZOrder ||
998                     ( tempItem._localZOrder == tempChild._localZOrder && tempItem.arrivalOrder < tempChild.arrivalOrder ))) {
999                     locChildren[j + 1] = tempChild;
1000                     j = j - 1;
1001                     tempChild = locChildren[j];
1002                 }
1003                 locChildren[j + 1] = tempItem;
1004             }
1005 
1006             //sorted now check all children
1007             if (locChildren.length > 0) {
1008                 //first sort all children recursively based on zOrder
1009                 this._arrayMakeObjectsPerformSelector(locChildren, cc.Node.StateCallbackType.sortAllChildren);
1010             }
1011             this._reorderChildDirty = false;
1012         }
1013     },
1014 
1015     _sortAllChildrenForWebGL: function () {
1016         if (this._reorderChildDirty) {
1017             var childrenArr = this._children;
1018             var i, j = 0, length = childrenArr.length, tempChild;
1019             //insertion sort
1020             for (i = 1; i < length; i++) {
1021                 var tempItem = childrenArr[i];
1022                 j = i - 1;
1023                 tempChild = childrenArr[j];
1024 
1025                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
1026                 while (j >= 0 && ( tempItem._localZOrder < tempChild._localZOrder ||
1027                     ( tempItem._localZOrder == tempChild._localZOrder && tempItem.arrivalOrder < tempChild.arrivalOrder ))) {
1028                     childrenArr[j + 1] = tempChild;
1029                     j = j - 1;
1030                     tempChild = childrenArr[j];
1031                 }
1032                 childrenArr[j + 1] = tempItem;
1033             }
1034 
1035             //sorted now check all children
1036             if (childrenArr.length > 0) {
1037                 //first sort all children recursively based on zOrder
1038                 this._arrayMakeObjectsPerformSelector(childrenArr, cc.Node.StateCallbackType.sortAllChildren);
1039 
1040                 var index = 0;
1041                 //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact)
1042                 // and at the same time reorder descedants and the quads to the right index
1043                 for (i = 0; i < childrenArr.length; i++)
1044                     index = this._updateAtlasIndex(childrenArr[i], index);
1045             }
1046             this._reorderChildDirty = false;
1047         }
1048     },
1049     /**
1050      * draw cc.SpriteBatchNode (override draw of cc.Node)
1051      * @function
1052      */
1053     draw: null,
1054 
1055     _drawForWebGL: function () {
1056         // Optimization: Fast Dispatch
1057         if (this.textureAtlas.totalQuads === 0)
1058             return;
1059 
1060         //cc.nodeDrawSetup(this);
1061         this._shaderProgram.use();
1062         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
1063         this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.updateTransform);
1064         cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst);
1065 
1066         this.textureAtlas.drawQuads();
1067     }
1068 });
1069 
1070 var _p = cc.SpriteBatchNode.prototype;
1071 
1072 if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
1073     _p.ctor = _p._ctorForWebGL;
1074     _p.updateQuadFromSprite = _p._updateQuadFromSpriteForWebGL;
1075     _p.insertQuadFromSprite = _p._insertQuadFromSpriteForWebGL;
1076     _p.initWithTexture = _p._initWithTextureForWebGL;
1077     _p.appendChild = _p._appendChildForWebGL;
1078     _p.removeSpriteFromAtlas = _p._removeSpriteFromAtlasForWebGL;
1079     _p.getTexture = _p._getTextureForWebGL;
1080     _p.setTexture = _p._setTextureForWebGL;
1081     _p.visit = _p._visitForWebGL;
1082     _p.addChild = _p._addChildForWebGL;
1083     _p.removeAllChildren = _p._removeAllChildrenForWebGL;
1084     _p.sortAllChildren = _p._sortAllChildrenForWebGL;
1085     _p.draw = _p._drawForWebGL;
1086 } else {
1087     _p.ctor = _p._ctorForCanvas;
1088     _p.updateQuadFromSprite = _p._updateQuadFromSpriteForCanvas;
1089     _p.insertQuadFromSprite = _p._insertQuadFromSpriteForCanvas;
1090     _p.initWithTexture = _p._initWithTextureForCanvas;
1091     _p.appendChild = _p._appendChildForCanvas;
1092     _p.removeSpriteFromAtlas = _p._removeSpriteFromAtlasForCanvas;
1093     _p.getTexture = _p._getTextureForCanvas;
1094     _p.setTexture = _p._setTextureForCanvas;
1095     _p.visit = _p._visitForCanvas;
1096     _p.removeAllChildren = _p._removeAllChildrenForCanvas;
1097     _p.addChild = _p._addChildForCanvas;
1098     _p.sortAllChildren = _p._sortAllChildrenForCanvas;
1099     _p.draw = cc.Node.prototype.draw;
1100 }
1101 
1102 // Override properties
1103 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
1104 
1105 // Extended properties
1106 /** @expose */
1107 _p.descendants;
1108 cc.defineGetterSetter(_p, "descendants", _p.getDescendants);
1109 
1110 
1111 /**
1112  * <p>
1113  *    creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.<br/>
1114  *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
1115  *    The file will be loaded using the TextureMgr.<br/>
1116  * </p>
1117  * @param {String|cc.Texture2D} fileImage
1118  * @param {Number} capacity
1119  * @return {cc.SpriteBatchNode}
1120  * @example
1121  * 1.
1122  * //create a SpriteBatchNode with image path
1123  * var spriteBatchNode = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
1124  * 2.
1125  * //create a SpriteBatchNode with texture
1126  * var texture = cc.textureCache.addImage("res/animations/grossini.png");
1127  * var spriteBatchNode = cc.SpriteBatchNode.create(texture,50);
1128  */
1129 cc.SpriteBatchNode.create = function (fileImage, capacity) {
1130     return new cc.SpriteBatchNode(fileImage, capacity);
1131 };
1132