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  * Tint a texture using the "multiply" operation
 29  * @param {HTMLImageElement} image
 30  * @param {cc.Color} color
 31  * @param {cc.Rect} [rect]
 32  * @param {HTMLCanvasElement} renderCanvas
 33  * @returns {HTMLCanvasElement}
 34  */
 35 cc.generateTintImageWithMultiply = function(image, color, rect, renderCanvas){
 36     renderCanvas = renderCanvas || cc.newElement("canvas");
 37 
 38     rect = rect || cc.rect(0,0, image.width, image.height);
 39 
 40     var context = renderCanvas.getContext( "2d" );
 41     if(renderCanvas.width != rect.width || renderCanvas.height != rect.height){
 42         renderCanvas.width = rect.width;
 43         renderCanvas.height = rect.height;
 44     }else{
 45         context.globalCompositeOperation = "source-over";
 46     }
 47 
 48     context.fillStyle = "rgb(" + (0|color.r) + "," + (0|color.g) + "," + (0|color.b) + ")";
 49     context.fillRect(0, 0, rect.width, rect.height);
 50     context.globalCompositeOperation = "multiply";
 51     context.drawImage(image,
 52         rect.x,
 53         rect.y,
 54         rect.width,
 55         rect.height,
 56         0,
 57         0,
 58         rect.width,
 59         rect.height);
 60     context.globalCompositeOperation = "destination-atop";
 61     context.drawImage(image,
 62         rect.x,
 63         rect.y,
 64         rect.width,
 65         rect.height,
 66         0,
 67         0,
 68         rect.width,
 69         rect.height);
 70     return renderCanvas;
 71 };
 72 
 73 /**
 74  * Generate tinted texture with lighter.
 75  * lighter:    The source and destination colors are added to each other, resulting in brighter colors,
 76  * moving towards color values of 1 (maximum brightness for that color).
 77  * @function
 78  * @param {HTMLImageElement} texture
 79  * @param {Array} tintedImgCache
 80  * @param {cc.Color} color
 81  * @param {cc.Rect} rect
 82  * @param {HTMLCanvasElement} [renderCanvas]
 83  * @return {HTMLCanvasElement}
 84  */
 85 cc.generateTintImage = function (texture, tintedImgCache, color, rect, renderCanvas) {
 86     if (!rect)
 87         rect = cc.rect(0, 0, texture.width, texture.height);
 88 
 89     var r = color.r / 255;
 90     var g = color.g / 255;
 91     var b = color.b / 255;
 92 
 93     var w = Math.min(rect.width, tintedImgCache[0].width);
 94     var h = Math.min(rect.height, tintedImgCache[0].height);
 95     var buff = renderCanvas;
 96     var ctx;
 97 
 98     // Create a new buffer if required
 99     if (!buff) {
100         buff = cc.newElement("canvas");
101         buff.width = w;
102         buff.height = h;
103         ctx = buff.getContext("2d");
104     } else {
105         ctx = buff.getContext("2d");
106         ctx.clearRect(0, 0, w, h);
107     }
108 
109     ctx.save();
110     ctx.globalCompositeOperation = 'lighter';
111 
112     // Make sure to keep the renderCanvas alpha in mind in case of overdraw
113     var a = ctx.globalAlpha;
114     if (r > 0) {
115         ctx.globalAlpha = r * a;
116         ctx.drawImage(tintedImgCache[0], rect.x, rect.y, w, h, 0, 0, w, h);
117     }
118     if (g > 0) {
119         ctx.globalAlpha = g * a;
120         ctx.drawImage(tintedImgCache[1], rect.x, rect.y, w, h, 0, 0, w, h);
121     }
122     if (b > 0) {
123         ctx.globalAlpha = b * a;
124         ctx.drawImage(tintedImgCache[2], rect.x, rect.y, w, h, 0, 0, w, h);
125     }
126 
127     if (r + g + b < 1) {
128         ctx.globalAlpha = a;
129         ctx.drawImage(tintedImgCache[3], rect.x, rect.y, w, h, 0, 0, w, h);
130     }
131 
132     ctx.restore();
133     return buff;
134 };
135 
136 /**
137  * Generates texture's cache for texture tint
138  * @function
139  * @param {HTMLImageElement} texture
140  * @return {Array}
141  */
142 cc.generateTextureCacheForColor = function (texture) {
143     if (texture.channelCache) {
144         return texture.channelCache;
145     }
146 
147     var textureCache = [
148         cc.newElement("canvas"),
149         cc.newElement("canvas"),
150         cc.newElement("canvas"),
151         cc.newElement("canvas")
152     ];
153 
154     function renderToCache() {
155         var ref = cc.generateTextureCacheForColor;
156 
157         var w = texture.width;
158         var h = texture.height;
159 
160         textureCache[0].width = w;
161         textureCache[0].height = h;
162         textureCache[1].width = w;
163         textureCache[1].height = h;
164         textureCache[2].width = w;
165         textureCache[2].height = h;
166         textureCache[3].width = w;
167         textureCache[3].height = h;
168 
169         ref.canvas.width = w;
170         ref.canvas.height = h;
171 
172         var ctx = ref.canvas.getContext("2d");
173         ctx.drawImage(texture, 0, 0);
174 
175         ref.tempCanvas.width = w;
176         ref.tempCanvas.height = h;
177 
178         var pixels = ctx.getImageData(0, 0, w, h).data;
179 
180         for (var rgbI = 0; rgbI < 4; rgbI++) {
181             var cacheCtx = textureCache[rgbI].getContext('2d');
182             cacheCtx.getImageData(0, 0, w, h).data;
183             ref.tempCtx.drawImage(texture, 0, 0);
184 
185             var to = ref.tempCtx.getImageData(0, 0, w, h);
186             var toData = to.data;
187 
188             for (var i = 0; i < pixels.length; i += 4) {
189                 toData[i  ] = (rgbI === 0) ? pixels[i  ] : 0;
190                 toData[i + 1] = (rgbI === 1) ? pixels[i + 1] : 0;
191                 toData[i + 2] = (rgbI === 2) ? pixels[i + 2] : 0;
192                 toData[i + 3] = pixels[i + 3];
193             }
194             cacheCtx.putImageData(to, 0, 0);
195         }
196         texture.onload = null;
197     }
198 
199     try {
200         renderToCache();
201     } catch (e) {
202         texture.onload = renderToCache;
203     }
204 
205     texture.channelCache = textureCache;
206     return textureCache;
207 };
208 
209 cc.generateTextureCacheForColor.canvas = cc.newElement('canvas');
210 cc.generateTextureCacheForColor.tempCanvas = cc.newElement('canvas');
211 cc.generateTextureCacheForColor.tempCtx = cc.generateTextureCacheForColor.tempCanvas.getContext('2d');
212 
213 cc.cutRotateImageToCanvas = function (texture, rect) {
214     if (!texture)
215         return null;
216 
217     if (!rect)
218         return texture;
219 
220     var nCanvas = cc.newElement("canvas");
221     nCanvas.width = rect.width;
222     nCanvas.height = rect.height;
223     var ctx = nCanvas.getContext("2d");
224     ctx.translate(nCanvas.width / 2, nCanvas.height / 2);
225     ctx.rotate(-1.5707963267948966);
226     ctx.drawImage(texture, rect.x, rect.y, rect.height, rect.width, -rect.height / 2, -rect.width / 2, rect.height, rect.width);
227     return nCanvas;
228 };
229 
230 cc._getCompositeOperationByBlendFunc = function(blendFunc){
231     if(!blendFunc)
232         return "source";
233     else{
234         if(( blendFunc.src == cc.SRC_ALPHA && blendFunc.dst == cc.ONE) || (blendFunc.src == cc.ONE && blendFunc.dst == cc.ONE))
235             return "lighter";
236         else if(blendFunc.src == cc.ZERO && blendFunc.dst == cc.SRC_ALPHA)
237             return "destination-in";
238         else if(blendFunc.src == cc.ZERO && blendFunc.dst == cc.ONE_MINUS_SRC_ALPHA)
239             return "destination-out";
240         else
241             return "source";
242     }
243 };
244 
245 /**
246  * <p>cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) )  <br/>
247  *
248  * cc.Sprite can be created with an image, or with a sub-rectangle of an image.  <br/>
249  *
250  * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid   <br/>
251  *    - Features when the parent is a cc.BatchNode: <br/>
252  *        - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch.  <br/>
253  *
254  *    - Limitations   <br/>
255  *        - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)  <br/>
256  *        - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) <br/>
257  *        - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.  <br/>
258  *        - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. <br/>
259  *        - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.        <br/>
260  *
261  *  If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node:      <br/>
262  *    - It supports blending functions    <br/>
263  *    - It supports aliasing / antialiasing    <br/>
264  *    - But the rendering will be slower: 1 draw per children.   <br/>
265  *
266  * The default anchorPoint in cc.Sprite is (0.5, 0.5). </p>
267  * @class
268  * @extends cc.Node
269  *
270  * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName  The string which indicates a path to image file, e.g., "scene1/monster.png".
271  * @param {cc.Rect} rect  Only the contents inside rect of pszFileName's texture will be applied for this sprite.
272  * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
273  * @example
274  *
275  * 1.Create a sprite with image path and rect
276  * var sprite1 = new cc.Sprite("res/HelloHTML5World.png");
277  * var sprite2 = new cc.Sprite("res/HelloHTML5World.png",cc.rect(0,0,480,320));
278  *
279  * 2.Create a sprite with a sprite frame name. Must add "#" before frame name.
280  * var sprite = new cc.Sprite('#grossini_dance_01.png');
281  *
282  * 3.Create a sprite with a sprite frame
283  * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png");
284  * var sprite = new cc.Sprite(spriteFrame);
285  *
286  * 4.Create a sprite with an existing texture contained in a CCTexture2D object
287  *      After creation, the rect will be the size of the texture, and the offset will be (0,0).
288  * var texture = cc.textureCache.addImage("HelloHTML5World.png");
289  * var sprite1 = new cc.Sprite(texture);
290  * var sprite2 = new cc.Sprite(texture, cc.rect(0,0,480,320));
291  *
292  * @property {Boolean}              dirty               - Indicates whether the sprite needs to be updated.
293  * @property {Boolean}              flippedX            - Indicates whether or not the spirte is flipped on x axis.
294  * @property {Boolean}              flippedY            - Indicates whether or not the spirte is flipped on y axis.
295  * @property {Number}               offsetX             - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex.
296  * @property {Number}               offsetY             - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex.
297  * @property {Number}               atlasIndex          - The index used on the TextureAtlas.
298  * @property {cc.Texture2D}         texture             - Texture used to render the sprite.
299  * @property {Boolean}              textureRectRotated  - <@readonly> Indicate whether the texture rectangle is rotated.
300  * @property {cc.TextureAtlas}      textureAtlas        - The weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode.
301  * @property {cc.SpriteBatchNode}   batchNode           - The batch node object if this sprite is rendered by cc.SpriteBatchNode.
302  * @property {cc.V3F_C4B_T2F_Quad}  quad                - <@readonly> The quad (tex coords, vertex coords and color) information.
303  */
304 cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{
305 	dirty:false,
306 	atlasIndex:0,
307     textureAtlas:null,
308 
309     _batchNode:null,
310     _recursiveDirty:null, //Whether all of the sprite's children needs to be updated
311     _hasChildren:null, //Whether the sprite contains children
312     _shouldBeHidden:false, //should not be drawn because one of the ancestors is not visible
313     _transformToBatch:null,
314 
315     //
316     // Data used when the sprite is self-rendered
317     //
318     _blendFunc:null, //It's required for CCTextureProtocol inheritance
319     _texture:null, //cc.Texture2D object that is used to render the sprite
320 
321     //
322     // Shared data
323     //
324     // texture
325     _rect:null, //Retangle of cc.Texture2D
326     _rectRotated:false, //Whether the texture is rotated
327 
328     // Offset Position (used by Zwoptex)
329     _offsetPosition:null, // absolute
330     _unflippedOffsetPositionFromCenter:null,
331 
332     _opacityModifyRGB:false,
333 
334     // image is flipped
335     _flippedX:false, //Whether the sprite is flipped horizontally or not.
336     _flippedY:false, //Whether the sprite is flipped vertically or not.
337 
338     _textureLoaded:false,
339     _loadedEventListeners: null,
340     _newTextureWhenChangeColor: null,         //hack property for LabelBMFont
341     _className:"Sprite",
342 
343     //Only for texture update judgment
344     _oldDisplayColor: cc.color.WHITE,
345 
346     /**
347      * Returns whether the texture have been loaded
348      * @returns {boolean}
349      */
350     textureLoaded:function(){
351         return this._textureLoaded;
352     },
353 
354     /**
355      * Add a event listener for texture loaded event.
356      * @param {Function} callback
357      * @param {Object} target
358      */
359     addLoadedEventListener:function(callback, target){
360         if(!this._loadedEventListeners)
361             this._loadedEventListeners = [];
362         this._loadedEventListeners.push({eventCallback:callback, eventTarget:target});
363     },
364 
365     _callLoadedEventCallbacks:function(){
366         if(!this._loadedEventListeners)
367             return;
368         var locListeners = this._loadedEventListeners;
369         for(var i = 0, len = locListeners.length;  i < len; i++){
370             var selCallback = locListeners[i];
371             selCallback.eventCallback.call(selCallback.eventTarget, this);
372         }
373         locListeners.length = 0;
374     },
375 
376     /**
377      * Returns whether or not the Sprite needs to be updated in the Atlas
378      * @return {Boolean} True if the sprite needs to be updated in the Atlas, false otherwise.
379      */
380     isDirty:function () {
381         return this.dirty;
382     },
383 
384     /**
385      * Makes the sprite to be updated in the Atlas.
386      * @param {Boolean} bDirty
387      */
388     setDirty:function (bDirty) {
389         this.dirty = bDirty;
390     },
391 
392     /**
393      * Returns whether or not the texture rectangle is rotated.
394      * @return {Boolean}
395      */
396     isTextureRectRotated:function () {
397         return this._rectRotated;
398     },
399 
400     /**
401      * Returns the index used on the TextureAtlas.
402      * @return {Number}
403      */
404     getAtlasIndex:function () {
405         return this.atlasIndex;
406     },
407 
408     /**
409      * Sets the index used on the TextureAtlas.
410      * @warning Don't modify this value unless you know what you are doing
411      * @param {Number} atlasIndex
412      */
413     setAtlasIndex:function (atlasIndex) {
414         this.atlasIndex = atlasIndex;
415     },
416 
417     /**
418      * Returns the rect of the cc.Sprite in points
419      * @return {cc.Rect}
420      */
421     getTextureRect:function () {
422         return cc.rect(this._rect.x, this._rect.y, this._rect.width, this._rect.height);
423     },
424 
425     /**
426      * Returns the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode
427      * @return {cc.TextureAtlas}
428      */
429     getTextureAtlas:function () {
430         return this.textureAtlas;
431     },
432 
433     /**
434      * Sets the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode
435      * @param {cc.TextureAtlas} textureAtlas
436      */
437     setTextureAtlas:function (textureAtlas) {
438         this.textureAtlas = textureAtlas;
439     },
440 
441     /**
442      * Returns the offset position of the sprite. Calculated automatically by editors like Zwoptex.
443      * @return {cc.Point}
444      */
445     getOffsetPosition:function () {
446         return cc.p(this._offsetPosition);
447     },
448 
449 	_getOffsetX: function () {
450 		return this._offsetPosition.x;
451 	},
452 	_getOffsetY: function () {
453 		return this._offsetPosition.y;
454 	},
455 
456     /**
457      * Returns the blend function
458      * @return {cc.BlendFunc}
459      */
460     getBlendFunc:function () {
461         return this._blendFunc;
462     },
463 
464     /**
465      * Initializes a sprite with an SpriteFrame. The texture and rect in SpriteFrame will be applied on this sprite.<br/>
466      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself,
467      * @param {cc.SpriteFrame} spriteFrame A CCSpriteFrame object. It should includes a valid texture and a rect
468      * @return {Boolean}  true if the sprite is initialized properly, false otherwise.
469      */
470     initWithSpriteFrame:function (spriteFrame) {
471 
472         cc.assert(spriteFrame, cc._LogInfos.Sprite_initWithSpriteFrame);
473 
474         if(!spriteFrame.textureLoaded()){
475             //add event listener
476             this._textureLoaded = false;
477             spriteFrame.addLoadedEventListener(this._spriteFrameLoadedCallback, this);
478         }
479 
480         var rotated = cc._renderType === cc._RENDER_TYPE_CANVAS ? false : spriteFrame._rotated;
481         var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect(), rotated);
482         this.setSpriteFrame(spriteFrame);
483 
484         return ret;
485     },
486 
487     _spriteFrameLoadedCallback:null,
488 
489     /**
490      * Initializes a sprite with a sprite frame name. <br/>
491      * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name.  <br/>
492      * If the cc.SpriteFrame doesn't exist it will raise an exception. <br/>
493      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
494      * @param {String} spriteFrameName A key string that can fected a volid cc.SpriteFrame from cc.SpriteFrameCache
495      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
496      * @example
497      * var sprite = new cc.Sprite();
498      * sprite.initWithSpriteFrameName("grossini_dance_01.png");
499      */
500     initWithSpriteFrameName:function (spriteFrameName) {
501         cc.assert(spriteFrameName, cc._LogInfos.Sprite_initWithSpriteFrameName);
502         var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName);
503         cc.assert(frame, spriteFrameName + cc._LogInfos.Sprite_initWithSpriteFrameName1);
504         return this.initWithSpriteFrame(frame);
505     },
506 
507     /**
508      * Tell the sprite to use batch node render.
509      * @param {cc.SpriteBatchNode} batchNode
510      */
511     useBatchNode:function (batchNode) {
512         this.textureAtlas = batchNode.textureAtlas; // weak ref
513         this._batchNode = batchNode;
514     },
515 
516     /**
517      * <p>
518      *    set the vertex rect.<br/>
519      *    It will be called internally by setTextureRect.                           <br/>
520      *    Useful if you want to create 2x images from SD images in Retina Display.  <br/>
521      *    Do not call it manually. Use setTextureRect instead.  <br/>
522      *    (override this method to generate "double scale" sprites)
523      * </p>
524      * @param {cc.Rect} rect
525      */
526     setVertexRect:function (rect) {
527         this._rect.x = rect.x;
528         this._rect.y = rect.y;
529         this._rect.width = rect.width;
530         this._rect.height = rect.height;
531     },
532 
533     /**
534      * Sort all children of this sprite node.
535      * @override
536      */
537     sortAllChildren:function () {
538         if (this._reorderChildDirty) {
539             var _children = this._children;
540 
541             // insertion sort
542             var len = _children.length, i, j, tmp;
543             for(i=1; i<len; i++){
544                 tmp = _children[i];
545                 j = i - 1;
546 
547                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
548                 while(j >= 0){
549                     if(tmp._localZOrder < _children[j]._localZOrder){
550                         _children[j+1] = _children[j];
551                     }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){
552                         _children[j+1] = _children[j];
553                     }else{
554                         break;
555                     }
556                     j--;
557                 }
558                 _children[j+1] = tmp;
559             }
560 
561             if (this._batchNode) {
562                 this._arrayMakeObjectsPerformSelector(_children, cc.Node._StateCallbackType.sortAllChildren);
563             }
564 
565             //don't need to check children recursively, that's done in visit of each child
566             this._reorderChildDirty = false;
567         }
568 
569     },
570 
571     /**
572      * Reorders a child according to a new z value.  (override cc.Node )
573      * @param {cc.Node} child
574      * @param {Number} zOrder
575      * @override
576      */
577     reorderChild:function (child, zOrder) {
578         cc.assert(child, cc._LogInfos.Sprite_reorderChild_2);
579         if(this._children.indexOf(child) === -1){
580             cc.log(cc._LogInfos.Sprite_reorderChild);
581             return;
582         }
583 
584         if (zOrder === child.zIndex)
585             return;
586 
587         if (this._batchNode && !this._reorderChildDirty) {
588             this._setReorderChildDirtyRecursively();
589             this._batchNode.reorderBatch(true);
590         }
591         cc.Node.prototype.reorderChild.call(this, child, zOrder);
592     },
593 
594     /**
595      * Removes a child from the sprite.
596      * @param child
597      * @param cleanup  whether or not cleanup all running actions
598      * @override
599      */
600     removeChild:function (child, cleanup) {
601         if (this._batchNode)
602             this._batchNode.removeSpriteFromAtlas(child);
603         cc.Node.prototype.removeChild.call(this, child, cleanup);
604     },
605 
606     /**
607      * Sets whether the sprite is visible or not.
608      * @param {Boolean} visible
609      * @override
610      */
611     setVisible:function (visible) {
612         cc.Node.prototype.setVisible.call(this, visible);
613         this.setDirtyRecursively(true);
614     },
615 
616     /**
617      * Removes all children from the container.
618      * @param cleanup whether or not cleanup all running actions
619      * @override
620      */
621     removeAllChildren:function (cleanup) {
622         var locChildren = this._children, locBatchNode = this._batchNode;
623         if (locBatchNode && locChildren != null) {
624             for (var i = 0, len = locChildren.length; i < len; i++)
625                 locBatchNode.removeSpriteFromAtlas(locChildren[i]);
626         }
627 
628         cc.Node.prototype.removeAllChildren.call(this, cleanup);
629         this._hasChildren = false;
630     },
631 
632     //
633     // cc.Node property overloads
634     //
635 
636 	/**
637 	 * Sets recursively the dirty flag.
638 	 * Used only when parent is cc.SpriteBatchNode
639 	 * @param {Boolean} value
640 	 */
641 	setDirtyRecursively:function (value) {
642 		this._recursiveDirty = value;
643 		this.dirty = value;
644 		// recursively set dirty
645 		var locChildren = this._children, child, l = locChildren ? locChildren.length : 0;
646 		for (var i = 0; i < l; i++) {
647 			child = locChildren[i];
648 			(child instanceof cc.Sprite) && child.setDirtyRecursively(true);
649 		}
650 	},
651 
652 	/**
653 	 * Make the node dirty
654 	 * @param {Boolean} norecursive When true children will not be set dirty recursively, by default, they will be.
655 	 * @override
656 	 */
657 	setNodeDirty: function(norecursive) {
658 		cc.Node.prototype.setNodeDirty.call(this);
659 		// Lazy set dirty
660 		if (!norecursive && this._batchNode && !this._recursiveDirty) {
661 			if (this._hasChildren)
662 				this.setDirtyRecursively(true);
663 			else {
664 				this._recursiveDirty = true;
665 				this.dirty = true;
666 			}
667 		}
668 	},
669 
670     /**
671      * Sets whether ignore anchor point for positioning
672      * @param {Boolean} relative
673      * @override
674      */
675     ignoreAnchorPointForPosition:function (relative) {
676         if(this._batchNode){
677             cc.log(cc._LogInfos.Sprite_ignoreAnchorPointForPosition);
678             return;
679         }
680         cc.Node.prototype.ignoreAnchorPointForPosition.call(this, relative);
681     },
682 
683     /**
684      * Sets whether the sprite should be flipped horizontally or not.
685      * @param {Boolean} flippedX true if the sprite should be flipped horizontally, false otherwise.
686      */
687     setFlippedX:function (flippedX) {
688         if (this._flippedX != flippedX) {
689             this._flippedX = flippedX;
690             this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
691             this.setNodeDirty(true);
692         }
693     },
694 
695     /**
696      * Sets whether the sprite should be flipped vertically or not.
697      * @param {Boolean} flippedY true if the sprite should be flipped vertically, false otherwise.
698      */
699     setFlippedY:function (flippedY) {
700         if (this._flippedY != flippedY) {
701             this._flippedY = flippedY;
702             this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
703             this.setNodeDirty(true);
704         }
705     },
706 
707     /**
708      * <p>
709      * Returns the flag which indicates whether the sprite is flipped horizontally or not.                      <br/>
710      *                                                                                                              <br/>
711      * It only flips the texture of the sprite, and not the texture of the sprite's children.                       <br/>
712      * Also, flipping the texture doesn't alter the anchorPoint.                                                    <br/>
713      * If you want to flip the anchorPoint too, and/or to flip the children too use:                                <br/>
714      *      sprite.setScaleX(sprite.getScaleX() * -1);  <p/>
715      * @return {Boolean} true if the sprite is flipped horizontally, false otherwise.
716      */
717     isFlippedX:function () {
718         return this._flippedX;
719     },
720 
721     /**
722      * <p>
723      *     Return the flag which indicates whether the sprite is flipped vertically or not.                         <br/>
724      *                                                                                                              <br/>
725      *      It only flips the texture of the sprite, and not the texture of the sprite's children.                  <br/>
726      *      Also, flipping the texture doesn't alter the anchorPoint.                                               <br/>
727      *      If you want to flip the anchorPoint too, and/or to flip the children too use:                           <br/>
728      *         sprite.setScaleY(sprite.getScaleY() * -1); <p/>
729      * @return {Boolean} true if the sprite is flipped vertically, false otherwise.
730      */
731     isFlippedY:function () {
732         return this._flippedY;
733     },
734 
735     //
736     // RGBA protocol
737     //
738     /**
739      * Sets whether opacity modify color or not.
740      * @function
741      * @param {Boolean} modify
742      */
743     setOpacityModifyRGB:null,
744 
745     /**
746      * Returns whether opacity modify color or not.
747      * @return {Boolean}
748      */
749     isOpacityModifyRGB:function () {
750         return this._opacityModifyRGB;
751     },
752 
753     /**
754      * Update the display opacity.
755      * @function
756      */
757     updateDisplayedOpacity: null,
758 
759     // Animation
760 
761     /**
762      * Changes the display frame with animation name and index.<br/>
763      * The animation name will be get from the CCAnimationCache
764      * @param {String} animationName
765      * @param {Number} frameIndex
766      */
767     setDisplayFrameWithAnimationName:function (animationName, frameIndex) {
768         cc.assert(animationName, cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_3);
769 
770         var cache = cc.animationCache.getAnimation(animationName);
771         if(!cache){
772             cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName);
773             return;
774         }
775         var animFrame = cache.getFrames()[frameIndex];
776         if(!animFrame){
777             cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_2);
778             return;
779         }
780         this.setSpriteFrame(animFrame.getSpriteFrame());
781     },
782 
783     /**
784      * Returns the batch node object if this sprite is rendered by cc.SpriteBatchNode
785      * @returns {cc.SpriteBatchNode|null} The cc.SpriteBatchNode object if this sprite is rendered by cc.SpriteBatchNode, null if the sprite isn't used batch node.
786      */
787     getBatchNode:function () {
788         return this._batchNode;
789     },
790 
791     _setReorderChildDirtyRecursively:function () {
792         //only set parents flag the first time
793         if (!this._reorderChildDirty) {
794             this._reorderChildDirty = true;
795             var pNode = this._parent;
796             while (pNode && pNode != this._batchNode) {
797                 pNode._setReorderChildDirtyRecursively();
798                 pNode = pNode.parent;
799             }
800         }
801     },
802 
803     // CCTextureProtocol
804     /**
805      * Returns the texture of the sprite node
806      * @returns {cc.Texture2D}
807      */
808     getTexture:function () {
809         return this._texture;
810     },
811 
812     _quad: null, // vertex coords, texture coords and color info
813     _quadWebBuffer: null,
814     _quadDirty: false,
815     _colorized: false,
816     _blendFuncStr: "source",
817     _originalTexture: null,
818     _textureRect_Canvas: null,
819     _drawSize_Canvas: null,
820 
821     ctor: null,
822 
823 	_softInit: function (fileName, rect, rotated) {
824 		if (fileName === undefined)
825 			cc.Sprite.prototype.init.call(this);
826 		else if (cc.isString(fileName)) {
827 			if (fileName[0] === "#") {
828 				// Init with a sprite frame name
829 				var frameName = fileName.substr(1, fileName.length - 1);
830 				var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName);
831 				this.initWithSpriteFrame(spriteFrame);
832 			} else {
833 				// Init  with filename and rect
834 				cc.Sprite.prototype.init.call(this, fileName, rect);
835 			}
836 		} else if (cc.isObject(fileName)) {
837 			if (fileName instanceof cc.Texture2D) {
838 				// Init  with texture and rect
839 				this.initWithTexture(fileName, rect, rotated);
840 			} else if (fileName instanceof cc.SpriteFrame) {
841 				// Init with a sprite frame
842 				this.initWithSpriteFrame(fileName);
843 			} else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) {
844 				// Init with a canvas or image element
845 				var texture2d = new cc.Texture2D();
846 				texture2d.initWithElement(fileName);
847 				texture2d.handleLoadedTexture();
848 				this.initWithTexture(texture2d);
849 			}
850 		}
851 	},
852 
853     /**
854      * Returns the quad (tex coords, vertex coords and color) information.
855      * @return {cc.V3F_C4B_T2F_Quad}
856      */
857     getQuad:function () {
858         return this._quad;
859     },
860 
861     /**
862      * conforms to cc.TextureProtocol protocol
863      * @function
864      * @param {Number|cc.BlendFunc} src
865      * @param {Number} dst
866      */
867     setBlendFunc: null,
868 
869     /**
870      * Initializes an empty sprite with nothing init.<br/>
871      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
872      * @function
873      * @return {Boolean}
874      */
875     init:null,
876 
877     /**
878      * <p>
879      *     Initializes a sprite with an image filename.<br/>
880      *
881      *     This method will find pszFilename from local file system, load its content to CCTexture2D,<br/>
882      *     then use CCTexture2D to create a sprite.<br/>
883      *     After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/>
884      *     Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
885      * </p>
886      * @param {String} filename The path to an image file in local file system
887      * @param {cc.Rect} rect The rectangle assigned the content area from texture.
888      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
889      */
890     initWithFile:function (filename, rect) {
891         cc.assert(filename, cc._LogInfos.Sprite_initWithFile);
892 
893         var tex = cc.textureCache.getTextureForKey(filename);
894         if (!tex) {
895             tex = cc.textureCache.addImage(filename);
896             return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height));
897         } else {
898             if (!rect) {
899                 var size = tex.getContentSize();
900                 rect = cc.rect(0, 0, size.width, size.height);
901             }
902             return this.initWithTexture(tex, rect);
903         }
904     },
905 
906     /**
907      * Initializes a sprite with a texture and a rect in points, optionally rotated.  <br/>
908      * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/>
909      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
910      * @function
911      * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites.
912      * @param {cc.Rect} rect Only the contents inside rect of this texture will be applied for this sprite.
913      * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
914      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
915      */
916     initWithTexture: null,
917 
918     _textureLoadedCallback: null,
919 
920     /**
921      * Updates the texture rect of the CCSprite in points.
922      * @function
923      * @param {cc.Rect} rect a rect of texture
924      * @param {Boolean} [rotated] Whether or not the texture is rotated
925      * @param {cc.Size} [untrimmedSize] The original pixels size of the texture
926      */
927     setTextureRect:null,
928 
929     // BatchNode methods
930     /**
931      * Updates the quad according the the rotation, position, scale values.
932      * @function
933      */
934     updateTransform: null,
935 
936     /**
937      * Add child to sprite (override cc.Node)
938      * @function
939      * @param {cc.Sprite} child
940      * @param {Number} localZOrder  child's zOrder
941      * @param {String} tag child's tag
942      * @override
943      */
944     addChild: null,
945 
946     /**
947      * Update sprite's color
948      */
949     updateColor:function () {
950         var locDisplayedColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity;
951         var color4 = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: locDisplayedOpacity};
952         // special opacity for premultiplied textures
953         if (this._opacityModifyRGB) {
954             color4.r *= locDisplayedOpacity / 255.0;
955             color4.g *= locDisplayedOpacity / 255.0;
956             color4.b *= locDisplayedOpacity / 255.0;
957         }
958         var locQuad = this._quad;
959         locQuad.bl.colors = color4;
960         locQuad.br.colors = color4;
961         locQuad.tl.colors = color4;
962         locQuad.tr.colors = color4;
963 
964         // renders using Sprite Manager
965         if (this._batchNode) {
966             if (this.atlasIndex != cc.Sprite.INDEX_NOT_INITIALIZED) {
967                 this.textureAtlas.updateQuad(locQuad, this.atlasIndex)
968             } else {
969                 // no need to set it recursively
970                 // update dirty_, don't update recursiveDirty_
971                 this.dirty = true;
972             }
973         }
974         // self render
975         // do nothing
976         this._quadDirty = true;
977     },
978 
979     /**
980      * Sets opacity of the sprite
981      * @function
982      * @param {Number} opacity
983      */
984     setOpacity:null,
985 
986     /**
987      * Sets color of the sprite
988      * @function
989      * @param {cc.Color} color3
990      */
991     setColor: null,
992 
993     /**
994      * Updates the display color
995      * @function
996      */
997     updateDisplayedColor: null,
998 
999     // Frames
1000     /**
1001      * Sets a new sprite frame to the sprite.
1002      * @function
1003      * @param {cc.SpriteFrame|String} newFrame
1004      */
1005     setSpriteFrame: null,
1006 
1007     /**
1008      * Sets a new display frame to the sprite.
1009      * @param {cc.SpriteFrame|String} newFrame
1010      * @deprecated
1011      */
1012     setDisplayFrame: function(newFrame){
1013         cc.log(cc._LogInfos.Sprite_setDisplayFrame);
1014         this.setSpriteFrame(newFrame);
1015     },
1016 
1017     /**
1018      * Returns whether or not a cc.SpriteFrame is being displayed
1019      * @function
1020      * @param {cc.SpriteFrame} frame
1021      * @return {Boolean}
1022      */
1023     isFrameDisplayed: null,
1024 
1025     /**
1026      * Returns the current displayed frame.
1027      * @return {cc.SpriteFrame}
1028      */
1029     displayFrame: function () {
1030         return cc.SpriteFrame.create(this._texture,
1031             cc.rectPointsToPixels(this._rect),
1032             this._rectRotated,
1033             cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter),
1034             cc.sizePointsToPixels(this._contentSize));
1035     },
1036 
1037     /**
1038      * Sets the batch node to sprite
1039      * @function
1040      * @param {cc.SpriteBatchNode|null} spriteBatchNode
1041      * @example
1042      *  var batch = cc.SpriteBatchNode.create("Images/grossini_dance_atlas.png", 15);
1043      *  var sprite = cc.Sprite.create(batch.texture, cc.rect(0, 0, 57, 57));
1044      *  batch.addChild(sprite);
1045      *  layer.addChild(batch);
1046      */
1047     setBatchNode:null,
1048 
1049     // CCTextureProtocol
1050     /**
1051      * Sets the texture of sprite
1052      * @function
1053      * @param {cc.Texture2D|String} texture
1054      */
1055     setTexture: null,
1056 
1057     // Texture protocol
1058     _updateBlendFunc:function () {
1059         if(this._batchNode){
1060             cc.log(cc._LogInfos.Sprite__updateBlendFunc);
1061             return;
1062         }
1063 
1064         // it's possible to have an untextured sprite
1065         if (!this._texture || !this._texture.hasPremultipliedAlpha()) {
1066             this._blendFunc.src = cc.SRC_ALPHA;
1067             this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
1068             this.opacityModifyRGB = false;
1069         } else {
1070             this._blendFunc.src = cc.BLEND_SRC;
1071             this._blendFunc.dst = cc.BLEND_DST;
1072             this.opacityModifyRGB = true;
1073         }
1074     },
1075 
1076     _changeTextureColor: function () {
1077         var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect();
1078         if (locTexture && locRect.validRect && this._originalTexture) {
1079             locElement = locTexture.getHtmlElementObj();
1080             if (!locElement)
1081                 return;
1082 
1083             this._colorized = true;
1084             if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor
1085                 && this._originalTexture._htmlElementObj != locElement)
1086                 cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect, locElement);
1087             else {
1088                 locElement = cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect);
1089                 locTexture = new cc.Texture2D();
1090                 locTexture.initWithElement(locElement);
1091                 locTexture.handleLoadedTexture();
1092                 this.texture = locTexture;
1093             }
1094         }
1095     },
1096 
1097     _setTextureCoords:function (rect) {
1098         rect = cc.rectPointsToPixels(rect);
1099 
1100         var tex = this._batchNode ? this.textureAtlas.texture : this._texture;
1101         if (!tex)
1102             return;
1103 
1104         var atlasWidth = tex.pixelsWidth;
1105         var atlasHeight = tex.pixelsHeight;
1106 
1107         var left, right, top, bottom, tempSwap, locQuad = this._quad;
1108         if (this._rectRotated) {
1109             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
1110                 left = (2 * rect.x + 1) / (2 * atlasWidth);
1111                 right = left + (rect.height * 2 - 2) / (2 * atlasWidth);
1112                 top = (2 * rect.y + 1) / (2 * atlasHeight);
1113                 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight);
1114             } else {
1115                 left = rect.x / atlasWidth;
1116                 right = (rect.x + rect.height) / atlasWidth;
1117                 top = rect.y / atlasHeight;
1118                 bottom = (rect.y + rect.width) / atlasHeight;
1119             }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
1120 
1121             if (this._flippedX) {
1122                 tempSwap = top;
1123                 top = bottom;
1124                 bottom = tempSwap;
1125             }
1126 
1127             if (this._flippedY) {
1128                 tempSwap = left;
1129                 left = right;
1130                 right = tempSwap;
1131             }
1132 
1133             locQuad.bl.texCoords.u = left;
1134             locQuad.bl.texCoords.v = top;
1135             locQuad.br.texCoords.u = left;
1136             locQuad.br.texCoords.v = bottom;
1137             locQuad.tl.texCoords.u = right;
1138             locQuad.tl.texCoords.v = top;
1139             locQuad.tr.texCoords.u = right;
1140             locQuad.tr.texCoords.v = bottom;
1141         } else {
1142             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
1143                 left = (2 * rect.x + 1) / (2 * atlasWidth);
1144                 right = left + (rect.width * 2 - 2) / (2 * atlasWidth);
1145                 top = (2 * rect.y + 1) / (2 * atlasHeight);
1146                 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight);
1147             } else {
1148                 left = rect.x / atlasWidth;
1149                 right = (rect.x + rect.width) / atlasWidth;
1150                 top = rect.y / atlasHeight;
1151                 bottom = (rect.y + rect.height) / atlasHeight;
1152             } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
1153 
1154             if (this._flippedX) {
1155                 tempSwap = left;
1156                 left = right;
1157                 right = tempSwap;
1158             }
1159 
1160             if (this._flippedY) {
1161                 tempSwap = top;
1162                 top = bottom;
1163                 bottom = tempSwap;
1164             }
1165 
1166             locQuad.bl.texCoords.u = left;
1167             locQuad.bl.texCoords.v = bottom;
1168             locQuad.br.texCoords.u = right;
1169             locQuad.br.texCoords.v = bottom;
1170             locQuad.tl.texCoords.u = left;
1171             locQuad.tl.texCoords.v = top;
1172             locQuad.tr.texCoords.u = right;
1173             locQuad.tr.texCoords.v = top;
1174         }
1175         this._quadDirty = true;
1176     },
1177     /**
1178      * draw sprite to canvas
1179      * @function
1180      */
1181     draw: null
1182 });
1183 
1184 /**
1185  * Create a sprite with image path or frame name or texture or spriteFrame.
1186  * @deprecated since v3.0, please use new construction instead
1187  * @see cc.Sprite
1188  * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName  The string which indicates a path to image file, e.g., "scene1/monster.png".
1189  * @param {cc.Rect} rect  Only the contents inside rect of pszFileName's texture will be applied for this sprite.
1190  * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
1191  * @return {cc.Sprite} A valid sprite object
1192  */
1193 cc.Sprite.create = function (fileName, rect, rotated) {
1194     return new cc.Sprite(fileName, rect, rotated);
1195 };
1196 
1197 /**
1198  * @deprecated since v3.0, please use new construction instead
1199  * @see cc.Sprite
1200  * @function
1201  */
1202 cc.Sprite.createWithTexture = cc.Sprite.create;
1203 
1204 /**
1205  * @deprecated since v3.0, please use new construction instead
1206  * @see cc.Sprite
1207  * @function
1208  */
1209 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create;
1210 
1211 /**
1212  * @deprecated since v3.0, please use new construction instead
1213  * @see cc.Sprite
1214  * @function
1215  */
1216 cc.Sprite.createWithSpriteFrame = cc.Sprite.create;
1217 /**
1218  * cc.Sprite invalid index on the cc.SpriteBatchNode
1219  * @constant
1220  * @type {Number}
1221  */
1222 cc.Sprite.INDEX_NOT_INITIALIZED = -1;
1223 
1224 
1225 if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1226     var _p = cc.Sprite.prototype;
1227 
1228     _p._spriteFrameLoadedCallback = function(spriteFrame){
1229         var _t = this;
1230         _t.setNodeDirty(true);
1231         _t.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize());
1232         var curColor = _t.color;
1233         if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255)
1234             _t._changeTextureColor();
1235 
1236         _t._callLoadedEventCallbacks();
1237     };
1238 
1239     _p.setOpacityModifyRGB = function (modify) {
1240         if (this._opacityModifyRGB !== modify) {
1241             this._opacityModifyRGB = modify;
1242             this.setNodeDirty(true);
1243         }
1244     };
1245 
1246     _p.updateDisplayedOpacity = function (parentOpacity) {
1247         cc.Node.prototype.updateDisplayedOpacity.call(this, parentOpacity);
1248         this._setNodeDirtyForCache();
1249     };
1250 
1251     _p.ctor = function (fileName, rect, rotated) {
1252         var self = this;
1253         cc.Node.prototype.ctor.call(self);
1254         self._shouldBeHidden = false;
1255         self._offsetPosition = cc.p(0, 0);
1256         self._unflippedOffsetPositionFromCenter = cc.p(0, 0);
1257         self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
1258         self._rect = cc.rect(0, 0, 0, 0);
1259 
1260         self._newTextureWhenChangeColor = false;
1261         self._textureLoaded = true;
1262         self._textureRect_Canvas = {x: 0, y: 0, width: 0, height:0, validRect: false};
1263         self._drawSize_Canvas = cc.size(0, 0);
1264 
1265         self._softInit(fileName, rect, rotated);
1266     };
1267 
1268     _p.setBlendFunc = function (src, dst) {
1269         var _t = this, locBlendFunc = this._blendFunc;
1270         if (dst === undefined) {
1271             locBlendFunc.src = src.src;
1272             locBlendFunc.dst = src.dst;
1273         } else {
1274             locBlendFunc.src = src;
1275             locBlendFunc.dst = dst;
1276         }
1277         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
1278             _t._blendFuncStr = cc._getCompositeOperationByBlendFunc(locBlendFunc);
1279     };
1280 
1281     _p.init = function () {
1282         var _t = this;
1283         if (arguments.length > 0)
1284             return _t.initWithFile(arguments[0], arguments[1]);
1285 
1286         cc.Node.prototype.init.call(_t);
1287         _t.dirty = _t._recursiveDirty = false;
1288         _t._opacityModifyRGB = true;
1289 
1290         _t._blendFunc.src = cc.BLEND_SRC;
1291         _t._blendFunc.dst = cc.BLEND_DST;
1292 
1293         // update texture (calls _updateBlendFunc)
1294         _t.texture = null;
1295         _t._textureLoaded = true;
1296         _t._flippedX = _t._flippedY = false;
1297 
1298         // default transform anchor: center
1299         _t.anchorX = 0.5;
1300         _t.anchorY = 0.5;
1301 
1302         // zwoptex default values
1303         _t._offsetPosition.x = 0;
1304         _t._offsetPosition.y = 0;
1305         _t._hasChildren = false;
1306 
1307         // updated in "useSelfRender"
1308         // Atlas: TexCoords
1309         _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0));
1310         return true;
1311     };
1312 
1313     _p.initWithTexture = function (texture, rect, rotated) {
1314         var _t = this;
1315         cc.assert(arguments.length != 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture);
1316 
1317         rotated = rotated || false;
1318 
1319         if (rotated && texture.isLoaded()) {
1320             var tempElement = texture.getHtmlElementObj();
1321             tempElement = cc.cutRotateImageToCanvas(tempElement, rect);
1322             var tempTexture = new cc.Texture2D();
1323             tempTexture.initWithElement(tempElement);
1324             tempTexture.handleLoadedTexture();
1325             texture = tempTexture;
1326 
1327             _t._rect = cc.rect(0, 0, rect.width, rect.height);
1328         }
1329 
1330         if (!cc.Node.prototype.init.call(_t))
1331             return false;
1332 
1333         _t._batchNode = null;
1334         _t._recursiveDirty = false;
1335         _t.dirty = false;
1336         _t._opacityModifyRGB = true;
1337 
1338         _t._blendFunc.src = cc.BLEND_SRC;
1339         _t._blendFunc.dst = cc.BLEND_DST;
1340 
1341         _t._flippedX = _t._flippedY = false;
1342 
1343         // default transform anchor: center
1344         _t.anchorX = 0.5;
1345         _t.anchorY = 0.5;
1346 
1347         // zwoptex default values
1348         _t._offsetPosition.x = 0;
1349         _t._offsetPosition.y = 0;
1350         _t._hasChildren = false;
1351 
1352         var locTextureLoaded = texture.isLoaded();
1353         _t._textureLoaded = locTextureLoaded;
1354 
1355         if (!locTextureLoaded) {
1356             _t._rectRotated = rotated;
1357             if (rect) {
1358                 _t._rect.x = rect.x;
1359                 _t._rect.y = rect.y;
1360                 _t._rect.width = rect.width;
1361                 _t._rect.height = rect.height;
1362             }
1363             if(_t.texture)
1364                 _t.texture.removeLoadedEventListener(_t);
1365             texture.addLoadedEventListener(_t._textureLoadedCallback, _t);
1366             _t.texture = texture;
1367             return true;
1368         }
1369 
1370         if (!rect) {
1371             rect = cc.rect(0, 0, texture.width, texture.height);
1372         }
1373 
1374         if(texture && texture.url) {
1375             var _x = rect.x + rect.width, _y = rect.y + rect.height;
1376             if(_x > texture.width){
1377                 cc.error(cc._LogInfos.RectWidth, texture.url);
1378             }
1379             if(_y > texture.height){
1380                 cc.error(cc._LogInfos.RectHeight, texture.url);
1381             }
1382         }
1383         _t._originalTexture = texture;
1384         _t.texture = texture;
1385         _t.setTextureRect(rect, rotated);
1386 
1387         // by default use "Self Render".
1388         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
1389         _t.batchNode = null;
1390         return true;
1391     };
1392 
1393     _p._textureLoadedCallback = function (sender) {
1394         var _t = this;
1395         if(_t._textureLoaded)
1396             return;
1397 
1398         _t._textureLoaded = true;
1399         var locRect = _t._rect;
1400         if (!locRect) {
1401             locRect = cc.rect(0, 0, sender.width, sender.height);
1402         } else if (cc._rectEqualToZero(locRect)) {
1403             locRect.width = sender.width;
1404             locRect.height = sender.height;
1405         }
1406         _t._originalTexture = sender;
1407 
1408         _t.texture = sender;
1409         _t.setTextureRect(locRect, _t._rectRotated);
1410 
1411         //set the texture's color after the it loaded
1412         var locColor = this._displayedColor;
1413         if(locColor.r != 255 || locColor.g != 255 || locColor.b != 255)
1414             _t._changeTextureColor();
1415 
1416         // by default use "Self Render".
1417         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
1418         _t.batchNode = _t._batchNode;
1419         _t._callLoadedEventCallbacks();
1420     };
1421 
1422     _p.setTextureRect = function (rect, rotated, untrimmedSize) {
1423         var _t = this;
1424         _t._rectRotated = rotated || false;
1425         _t.setContentSize(untrimmedSize || rect);
1426 
1427         _t.setVertexRect(rect);
1428 
1429         var locTextureRect = _t._textureRect_Canvas, scaleFactor = cc.contentScaleFactor();
1430         locTextureRect.x = 0 | (rect.x * scaleFactor);
1431         locTextureRect.y = 0 | (rect.y * scaleFactor);
1432         locTextureRect.width = 0 | (rect.width * scaleFactor);
1433         locTextureRect.height = 0 | (rect.height * scaleFactor);
1434         locTextureRect.validRect = !(locTextureRect.width === 0 || locTextureRect.height === 0 || locTextureRect.x < 0 || locTextureRect.y < 0);
1435 
1436         var relativeOffset = _t._unflippedOffsetPositionFromCenter;
1437         if (_t._flippedX)
1438             relativeOffset.x = -relativeOffset.x;
1439         if (_t._flippedY)
1440             relativeOffset.y = -relativeOffset.y;
1441         _t._offsetPosition.x = relativeOffset.x + (_t._contentSize.width - _t._rect.width) / 2;
1442         _t._offsetPosition.y = relativeOffset.y + (_t._contentSize.height - _t._rect.height) / 2;
1443 
1444         // rendering using batch node
1445         if (_t._batchNode) {
1446             // update dirty, don't update _recursiveDirty
1447             _t.dirty = true;
1448         }
1449     };
1450 
1451     _p.updateTransform = function () {
1452         var _t = this;
1453         //cc.assert(_t._batchNode, "updateTransform is only valid when cc.Sprite is being rendered using an cc.SpriteBatchNode");
1454 
1455         // recaculate matrix only if it is dirty
1456         if (_t.dirty) {
1457             // If it is not visible, or one of its ancestors is not visible, then do nothing:
1458             var locParent = _t._parent;
1459             if (!_t._visible || ( locParent && locParent != _t._batchNode && locParent._shouldBeHidden)) {
1460                 _t._shouldBeHidden = true;
1461             } else {
1462                 _t._shouldBeHidden = false;
1463 
1464                 if (!locParent || locParent == _t._batchNode) {
1465                     _t._transformToBatch = _t.nodeToParentTransform();
1466                 } else {
1467                     //cc.assert(_t._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite");
1468                     _t._transformToBatch = cc.affineTransformConcat(_t.nodeToParentTransform(), locParent._transformToBatch);
1469                 }
1470             }
1471             _t._recursiveDirty = false;
1472             _t.dirty = false;
1473         }
1474 
1475         // recursively iterate over children
1476         if (_t._hasChildren)
1477             _t._arrayMakeObjectsPerformSelector(_t._children, cc.Node._StateCallbackType.updateTransform);
1478     };
1479 
1480     _p.addChild = function (child, localZOrder, tag) {
1481 
1482         cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2);
1483 
1484         if (localZOrder == null)
1485             localZOrder = child._localZOrder;
1486         if (tag == null)
1487             tag = child.tag;
1488 
1489         //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check
1490         cc.Node.prototype.addChild.call(this, child, localZOrder, tag);
1491         this._hasChildren = true;
1492     };
1493 
1494     _p.setOpacity = function (opacity) {
1495         cc.Node.prototype.setOpacity.call(this, opacity);
1496         this._setNodeDirtyForCache();
1497     };
1498 
1499     _p.setColor = function (color3) {
1500         var _t = this;
1501         var curColor = _t.color;
1502         this._oldDisplayColor = curColor;
1503         if ((curColor.r === color3.r) && (curColor.g === color3.g) && (curColor.b === color3.b))
1504             return;
1505         cc.Node.prototype.setColor.call(_t, color3);
1506     };
1507 
1508     _p.updateDisplayedColor = function (parentColor) {
1509         var _t = this;
1510         cc.Node.prototype.updateDisplayedColor.call(_t, parentColor);
1511         var oColor = _t._oldDisplayColor;
1512         var nColor = _t._displayedColor;
1513         if (oColor.r === nColor.r && oColor.g === nColor.g && oColor.b === nColor.b)
1514             return;
1515 
1516         _t._changeTextureColor();
1517         _t._setNodeDirtyForCache();
1518     };
1519 
1520     _p.setSpriteFrame = function (newFrame) {
1521         var _t = this;
1522         if(cc.isString(newFrame)){
1523             newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame);
1524             cc.assert(newFrame, cc._LogInfos.CCSpriteBatchNode_setSpriteFrame)
1525         }
1526 
1527         _t.setNodeDirty(true);
1528 
1529         var frameOffset = newFrame.getOffset();
1530         _t._unflippedOffsetPositionFromCenter.x = frameOffset.x;
1531         _t._unflippedOffsetPositionFromCenter.y = frameOffset.y;
1532 
1533         // update rect
1534         _t._rectRotated = newFrame.isRotated();
1535 
1536         var pNewTexture = newFrame.getTexture();
1537         var locTextureLoaded = newFrame.textureLoaded();
1538         if (!locTextureLoaded) {
1539             _t._textureLoaded = false;
1540             newFrame.addLoadedEventListener(function (sender) {
1541                 _t._textureLoaded = true;
1542                 var locNewTexture = sender.getTexture();
1543                 if (locNewTexture != _t._texture)
1544                     _t.texture = locNewTexture;
1545                 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize());
1546                 _t._callLoadedEventCallbacks();
1547             }, _t);
1548         }
1549         // update texture before updating texture rect
1550         if (pNewTexture != _t._texture)
1551             _t.texture = pNewTexture;
1552 
1553         if (_t._rectRotated)
1554             _t._originalTexture = pNewTexture;
1555 
1556         _t.setTextureRect(newFrame.getRect(), _t._rectRotated, newFrame.getOriginalSize());
1557         _t._colorized = false;
1558         if (locTextureLoaded) {
1559             var curColor = _t.color;
1560             if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255)
1561                 _t._changeTextureColor();
1562         }
1563     };
1564 
1565     _p.isFrameDisplayed = function (frame) {
1566         if (frame.getTexture() != this._texture)
1567             return false;
1568         return cc.rectEqualToRect(frame.getRect(), this._rect);
1569     };
1570 
1571     _p.setBatchNode = function (spriteBatchNode) {
1572         var _t = this;
1573         _t._batchNode = spriteBatchNode; // weak reference
1574 
1575         // self render
1576         if (!_t._batchNode) {
1577             _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED;
1578             _t.textureAtlas = null;
1579             _t._recursiveDirty = false;
1580             _t.dirty = false;
1581         } else {
1582             // using batch
1583             _t._transformToBatch = cc.affineTransformIdentity();
1584             _t.textureAtlas = _t._batchNode.textureAtlas; // weak ref
1585         }
1586     };
1587 
1588     _p.setTexture = function (texture) {
1589         var _t = this;
1590         if(texture && (cc.isString(texture))){
1591             texture = cc.textureCache.addImage(texture);
1592             _t.setTexture(texture);
1593 
1594             //TODO
1595             var size = texture.getContentSize();
1596             _t.setTextureRect(cc.rect(0,0, size.width, size.height));
1597             return;
1598         }
1599 
1600         // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet
1601         cc.assert(!texture || texture instanceof cc.Texture2D, cc._LogInfos.CCSpriteBatchNode_setTexture);
1602 
1603         if (_t._texture != texture) {
1604             if (texture && texture.getHtmlElementObj() instanceof  HTMLImageElement) {
1605                 _t._originalTexture = texture;
1606             }
1607             _t._texture = texture;
1608         }
1609     };
1610 
1611     _p.draw = function (ctx) {
1612         var _t = this;
1613         if (!_t._textureLoaded)
1614             return;
1615 
1616         var context = ctx || cc._renderContext;
1617         if (_t._blendFuncStr != "source")
1618             context.globalCompositeOperation = _t._blendFuncStr;
1619 
1620         var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY();
1621 
1622         context.globalAlpha = _t._displayedOpacity / 255;
1623         var locRect = _t._rect, locContentSize = _t._contentSize, locOffsetPosition = _t._offsetPosition, locDrawSizeCanvas = _t._drawSize_Canvas;
1624         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = _t._textureRect_Canvas;
1625         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
1626         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
1627 
1628         if (_t._flippedX || _t._flippedY) {
1629             context.save();
1630             if (_t._flippedX) {
1631                 flipXOffset = -locOffsetPosition.x - locRect.width;
1632                 context.scale(-1, 1);
1633             }
1634             if (_t._flippedY) {
1635                 flipYOffset = locOffsetPosition.y;
1636                 context.scale(1, -1);
1637             }
1638         }
1639 
1640         flipXOffset *= locEGL_ScaleX;
1641         flipYOffset *= locEGL_ScaleY;
1642 
1643         if (_t._texture && locTextureCoord.validRect) {
1644             var image = _t._texture.getHtmlElementObj();
1645             if (_t._colorized) {
1646                 context.drawImage(image,
1647                     0, 0, locTextureCoord.width, locTextureCoord.height,
1648                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
1649             } else {
1650                 context.drawImage(image,
1651                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
1652                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
1653             }
1654         } else if (!_t._texture && locTextureCoord.validRect) {
1655             var curColor = _t.color;
1656             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
1657             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
1658         }
1659 
1660         if (cc.SPRITE_DEBUG_DRAW === 1 || _t._showNode) {
1661             // draw bounding box
1662             context.strokeStyle = "rgba(0,255,0,1)";
1663             flipXOffset /= locEGL_ScaleX;
1664             flipYOffset /= locEGL_ScaleY;
1665             flipYOffset = -flipYOffset;
1666             var vertices1 = [cc.p(flipXOffset, flipYOffset),
1667                 cc.p(flipXOffset + locRect.width, flipYOffset),
1668                 cc.p(flipXOffset + locRect.width, flipYOffset - locRect.height),
1669                 cc.p(flipXOffset, flipYOffset - locRect.height)];
1670             cc._drawingUtil.drawPoly(vertices1, 4, true);
1671         } else if (cc.SPRITE_DEBUG_DRAW === 2) {
1672             // draw texture box
1673             context.strokeStyle = "rgba(0,255,0,1)";
1674             var drawRect = _t._rect;
1675             flipYOffset = -flipYOffset;
1676             var vertices2 = [cc.p(flipXOffset, flipYOffset), cc.p(flipXOffset + drawRect.width, flipYOffset),
1677                 cc.p(flipXOffset + drawRect.width, flipYOffset - drawRect.height), cc.p(flipXOffset, flipYOffset - drawRect.height)];
1678             cc._drawingUtil.drawPoly(vertices2, 4, true);
1679         }
1680         if (_t._flippedX || _t._flippedY)
1681             context.restore();
1682         cc.g_NumberOfDraws++;
1683     };
1684 
1685     if(!cc.sys._supportCanvasNewBlendModes)
1686         _p._changeTextureColor =  function () {
1687             var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect();
1688             if (locTexture && locRect.validRect && this._originalTexture) {
1689                 locElement = locTexture.getHtmlElementObj();
1690                 if (!locElement)
1691                     return;
1692 
1693                 var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj());
1694                 if (cacheTextureForColor) {
1695                     this._colorized = true;
1696                     //generate color texture cache
1697                     if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor)
1698                         cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect, locElement);
1699                     else {
1700                         locElement = cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect);
1701                         locTexture = new cc.Texture2D();
1702                         locTexture.initWithElement(locElement);
1703                         locTexture.handleLoadedTexture();
1704                         this.texture = locTexture;
1705                     }
1706                 }
1707             }
1708         };
1709 
1710     delete _p;
1711 } else {
1712     cc.assert(cc.isFunction(cc._tmp.WebGLSprite), cc._LogInfos.MissingFile, "SpritesWebGL.js");
1713     cc._tmp.WebGLSprite();
1714     delete cc._tmp.WebGLSprite;
1715 }
1716 
1717 cc.assert(cc.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js");
1718 cc._tmp.PrototypeSprite();
1719 delete cc._tmp.PrototypeSprite;
1720 
1721