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  * <p>cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) )  <br/>
 29  *
 30  * cc.Sprite can be created with an image, or with a sub-rectangle of an image.  <br/>
 31  *
 32  * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid   <br/>
 33  *    - Features when the parent is a cc.BatchNode: <br/>
 34  *        - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch.  <br/>
 35  *
 36  *    - Limitations   <br/>
 37  *        - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)  <br/>
 38  *        - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) <br/>
 39  *        - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.  <br/>
 40  *        - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. <br/>
 41  *        - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.        <br/>
 42  *
 43  *  If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node:      <br/>
 44  *    - It supports blending functions    <br/>
 45  *    - It supports aliasing / antialiasing    <br/>
 46  *    - But the rendering will be slower: 1 draw per children.   <br/>
 47  *
 48  * The default anchorPoint in cc.Sprite is (0.5, 0.5). </p>
 49  * @class
 50  * @extends cc.Node
 51  *
 52  * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName  The string which indicates a path to image file, e.g., "scene1/monster.png".
 53  * @param {cc.Rect} rect  Only the contents inside rect of pszFileName's texture will be applied for this sprite.
 54  * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
 55  * @example
 56  *
 57  * 1.Create a sprite with image path and rect
 58  * var sprite1 = new cc.Sprite("res/HelloHTML5World.png");
 59  * var sprite2 = new cc.Sprite("res/HelloHTML5World.png",cc.rect(0,0,480,320));
 60  *
 61  * 2.Create a sprite with a sprite frame name. Must add "#" before frame name.
 62  * var sprite = new cc.Sprite('#grossini_dance_01.png');
 63  *
 64  * 3.Create a sprite with a sprite frame
 65  * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png");
 66  * var sprite = new cc.Sprite(spriteFrame);
 67  *
 68  * 4.Create a sprite with an existing texture contained in a CCTexture2D object
 69  *      After creation, the rect will be the size of the texture, and the offset will be (0,0).
 70  * var texture = cc.textureCache.addImage("HelloHTML5World.png");
 71  * var sprite1 = new cc.Sprite(texture);
 72  * var sprite2 = new cc.Sprite(texture, cc.rect(0,0,480,320));
 73  *
 74  * @property {Boolean}              dirty               - Indicates whether the sprite needs to be updated.
 75  * @property {Boolean}              flippedX            - Indicates whether or not the sprite is flipped on x axis.
 76  * @property {Boolean}              flippedY            - Indicates whether or not the sprite is flipped on y axis.
 77  * @property {Number}               offsetX             - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex.
 78  * @property {Number}               offsetY             - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex.
 79  * @property {Number}               atlasIndex          - The index used on the TextureAtlas.
 80  * @property {cc.Texture2D}         texture             - Texture used to render the sprite.
 81  * @property {Boolean}              textureRectRotated  - <@readonly> Indicate whether the texture rectangle is rotated.
 82  * @property {cc.TextureAtlas}      textureAtlas        - The weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode.
 83  * @property {cc.SpriteBatchNode}   batchNode           - The batch node object if this sprite is rendered by cc.SpriteBatchNode.
 84  * @property {cc.V3F_C4B_T2F_Quad}  quad                - <@readonly> The quad (tex coords, vertex coords and color) information.
 85  */
 86 cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{
 87 	dirty:false,
 88 	atlasIndex:0,
 89     textureAtlas:null,
 90 
 91     _batchNode:null,
 92     _recursiveDirty:null, //Whether all of the sprite's children needs to be updated
 93     _hasChildren:null, //Whether the sprite contains children
 94     _shouldBeHidden:false, //should not be drawn because one of the ancestors is not visible
 95     _transformToBatch:null,
 96 
 97     //
 98     // Data used when the sprite is self-rendered
 99     //
100     _blendFunc:null, //It's required for CCTextureProtocol inheritance
101     _texture:null, //cc.Texture2D object that is used to render the sprite
102 
103     //
104     // Shared data
105     //
106     // texture
107     _rect:null, //Rectangle of cc.Texture2D
108     _rectRotated:false, //Whether the texture is rotated
109 
110     // Offset Position (used by Zwoptex)
111     _offsetPosition:null, // absolute
112     _unflippedOffsetPositionFromCenter:null,
113 
114     _opacityModifyRGB:false,
115 
116     // image is flipped
117     _flippedX:false, //Whether the sprite is flipped horizontally or not.
118     _flippedY:false, //Whether the sprite is flipped vertically or not.
119 
120     _textureLoaded:false,
121     _className:"Sprite",
122 
123     ctor: function (fileName, rect, rotated) {
124         var self = this;
125         cc.Node.prototype.ctor.call(self);
126         self._shouldBeHidden = false;
127         self._offsetPosition = cc.p(0, 0);
128         self._unflippedOffsetPositionFromCenter = cc.p(0, 0);
129         self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
130         self._rect = cc.rect(0, 0, 0, 0);
131 
132         self._softInit(fileName, rect, rotated);
133     },
134 
135     /**
136      * Returns whether the texture have been loaded
137      * @returns {boolean}
138      */
139     textureLoaded:function(){
140         return this._textureLoaded;
141     },
142 
143     /**
144      * Add a event listener for texture loaded event.
145      * @param {Function} callback
146      * @param {Object} target
147      * @deprecated since 3.1, please use addEventListener instead
148      */
149     addLoadedEventListener:function(callback, target){
150         this.addEventListener("load", callback, target);
151     },
152 
153     /**
154      * Returns whether or not the Sprite needs to be updated in the Atlas
155      * @return {Boolean} True if the sprite needs to be updated in the Atlas, false otherwise.
156      */
157     isDirty:function () {
158         return this.dirty;
159     },
160 
161     /**
162      * Makes the sprite to be updated in the Atlas.
163      * @param {Boolean} bDirty
164      */
165     setDirty:function (bDirty) {
166         this.dirty = bDirty;
167     },
168 
169     /**
170      * Returns whether or not the texture rectangle is rotated.
171      * @return {Boolean}
172      */
173     isTextureRectRotated:function () {
174         return this._rectRotated;
175     },
176 
177     /**
178      * Returns the index used on the TextureAtlas.
179      * @return {Number}
180      */
181     getAtlasIndex:function () {
182         return this.atlasIndex;
183     },
184 
185     /**
186      * Sets the index used on the TextureAtlas.
187      * @warning Don't modify this value unless you know what you are doing
188      * @param {Number} atlasIndex
189      */
190     setAtlasIndex:function (atlasIndex) {
191         this.atlasIndex = atlasIndex;
192     },
193 
194     /**
195      * Returns the rect of the cc.Sprite in points
196      * @return {cc.Rect}
197      */
198     getTextureRect:function () {
199         return cc.rect(this._rect);
200     },
201 
202     /**
203      * Returns the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode
204      * @return {cc.TextureAtlas}
205      */
206     getTextureAtlas:function () {
207         return this.textureAtlas;
208     },
209 
210     /**
211      * Sets the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode
212      * @param {cc.TextureAtlas} textureAtlas
213      */
214     setTextureAtlas:function (textureAtlas) {
215         this.textureAtlas = textureAtlas;
216     },
217 
218     /**
219      * Returns the offset position of the sprite. Calculated automatically by editors like Zwoptex.
220      * @return {cc.Point}
221      */
222     getOffsetPosition:function () {
223         return cc.p(this._offsetPosition);
224     },
225 
226 	_getOffsetX: function () {
227 		return this._offsetPosition.x;
228 	},
229 	_getOffsetY: function () {
230 		return this._offsetPosition.y;
231 	},
232 
233     /**
234      * Returns the blend function
235      * @return {cc.BlendFunc}
236      */
237     getBlendFunc:function () {
238         return this._blendFunc;
239     },
240 
241     /**
242      * Initializes a sprite with a SpriteFrame. The texture and rect in SpriteFrame will be applied on this sprite.<br/>
243      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself,
244      * @param {cc.SpriteFrame} spriteFrame A CCSpriteFrame object. It should includes a valid texture and a rect
245      * @return {Boolean}  true if the sprite is initialized properly, false otherwise.
246      */
247     initWithSpriteFrame:function (spriteFrame) {
248         cc.assert(spriteFrame, cc._LogInfos.Sprite_initWithSpriteFrame);
249 
250         if(!spriteFrame.textureLoaded()){
251             //add event listener
252             this._textureLoaded = false;
253             spriteFrame.addEventListener("load", this._renderCmd._spriteFrameLoadedCallback, this);
254         }
255 
256         //TODO
257         var rotated = cc._renderType === cc.game.RENDER_TYPE_CANVAS ? false : spriteFrame._rotated;
258         var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect(), rotated);
259         this.setSpriteFrame(spriteFrame);
260 
261         return ret;
262     },
263 
264     /**
265      * Initializes a sprite with a sprite frame name. <br/>
266      * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name.  <br/>
267      * If the cc.SpriteFrame doesn't exist it will raise an exception. <br/>
268      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
269      * @param {String} spriteFrameName A key string that can fected a valid cc.SpriteFrame from cc.SpriteFrameCache
270      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
271      * @example
272      * var sprite = new cc.Sprite();
273      * sprite.initWithSpriteFrameName("grossini_dance_01.png");
274      */
275     initWithSpriteFrameName:function (spriteFrameName) {
276         cc.assert(spriteFrameName, cc._LogInfos.Sprite_initWithSpriteFrameName);
277         var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName);
278         cc.assert(frame, spriteFrameName + cc._LogInfos.Sprite_initWithSpriteFrameName1);
279         return this.initWithSpriteFrame(frame);
280     },
281 
282     /**
283      * Tell the sprite to use batch node render.
284      * @param {cc.SpriteBatchNode} batchNode
285      */
286     useBatchNode:function (batchNode) {
287         this.textureAtlas = batchNode.getTextureAtlas(); // weak ref
288         this._batchNode = batchNode;
289     },
290 
291     /**
292      * <p>
293      *    set the vertex rect.<br/>
294      *    It will be called internally by setTextureRect.                           <br/>
295      *    Useful if you want to create 2x images from SD images in Retina Display.  <br/>
296      *    Do not call it manually. Use setTextureRect instead.  <br/>
297      *    (override this method to generate "double scale" sprites)
298      * </p>
299      * @param {cc.Rect} rect
300      */
301     setVertexRect:function (rect) {
302         var locRect = this._rect;
303         locRect.x = rect.x;
304         locRect.y = rect.y;
305         locRect.width = rect.width;
306         locRect.height = rect.height;
307     },
308 
309     /**
310      * Sort all children of this sprite node.
311      * @override
312      */
313     sortAllChildren:function () {
314         if (this._reorderChildDirty) {
315             var _children = this._children;
316 
317             // insertion sort
318             var len = _children.length, i, j, tmp;
319             for(i=1; i<len; i++){
320                 tmp = _children[i];
321                 j = i - 1;
322 
323                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
324                 while(j >= 0){
325                     if(tmp._localZOrder < _children[j]._localZOrder){
326                         _children[j+1] = _children[j];
327                     }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){
328                         _children[j+1] = _children[j];
329                     }else{
330                         break;
331                     }
332                     j--;
333                 }
334                 _children[j+1] = tmp;
335             }
336 
337             if (this._batchNode) {
338                 this._arrayMakeObjectsPerformSelector(_children, cc.Node._stateCallbackType.sortAllChildren);
339             }
340 
341             //don't need to check children recursively, that's done in visit of each child
342             this._reorderChildDirty = false;
343         }
344 
345     },
346 
347     /**
348      * Reorders a child according to a new z value.  (override cc.Node )
349      * @param {cc.Node} child
350      * @param {Number} zOrder
351      * @override
352      */
353     reorderChild:function (child, zOrder) {
354         cc.assert(child, cc._LogInfos.Sprite_reorderChild_2);
355         if(this._children.indexOf(child) === -1){
356             cc.log(cc._LogInfos.Sprite_reorderChild);
357             return;
358         }
359 
360         if (zOrder === child.zIndex)
361             return;
362 
363         if (this._batchNode && !this._reorderChildDirty) {
364             this._setReorderChildDirtyRecursively();
365             this._batchNode.reorderBatch(true);
366         }
367         cc.Node.prototype.reorderChild.call(this, child, zOrder);
368     },
369 
370     /**
371      * Removes a child from the sprite.
372      * @param child
373      * @param cleanup  whether or not cleanup all running actions
374      * @override
375      */
376     removeChild:function (child, cleanup) {
377         if (this._batchNode)
378             this._batchNode.removeSpriteFromAtlas(child);
379         cc.Node.prototype.removeChild.call(this, child, cleanup);
380     },
381 
382     /**
383      * Sets whether the sprite is visible or not.
384      * @param {Boolean} visible
385      * @override
386      */
387     setVisible:function (visible) {
388         cc.Node.prototype.setVisible.call(this, visible);
389         this._renderCmd.setDirtyRecursively(true);
390     },
391 
392     /**
393      * Removes all children from the container.
394      * @param cleanup whether or not cleanup all running actions
395      * @override
396      */
397     removeAllChildren:function (cleanup) {
398         var locChildren = this._children, locBatchNode = this._batchNode;
399         if (locBatchNode && locChildren != null) {
400             for (var i = 0, len = locChildren.length; i < len; i++)
401                 locBatchNode.removeSpriteFromAtlas(locChildren[i]);
402         }
403 
404         cc.Node.prototype.removeAllChildren.call(this, cleanup);
405         this._hasChildren = false;
406     },
407 
408     //
409     // cc.Node property overloads
410     //
411 
412     /**
413      * Sets whether ignore anchor point for positioning
414      * @param {Boolean} relative
415      * @override
416      */
417     ignoreAnchorPointForPosition:function (relative) {
418         if(this._batchNode){
419             cc.log(cc._LogInfos.Sprite_ignoreAnchorPointForPosition);
420             return;
421         }
422         cc.Node.prototype.ignoreAnchorPointForPosition.call(this, relative);
423     },
424 
425     /**
426      * Sets whether the sprite should be flipped horizontally or not.
427      * @param {Boolean} flippedX true if the sprite should be flipped horizontally, false otherwise.
428      */
429     setFlippedX:function (flippedX) {
430         if (this._flippedX !== flippedX) {
431             this._flippedX = flippedX;
432             this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
433             this.setNodeDirty(true);
434         }
435     },
436 
437     /**
438      * Sets whether the sprite should be flipped vertically or not.
439      * @param {Boolean} flippedY true if the sprite should be flipped vertically, false otherwise.
440      */
441     setFlippedY:function (flippedY) {
442         if (this._flippedY !== flippedY) {
443             this._flippedY = flippedY;
444             this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
445             this.setNodeDirty(true);
446         }
447     },
448 
449     /**
450      * <p>
451      * Returns the flag which indicates whether the sprite is flipped horizontally or not.                      <br/>
452      *                                                                                                              <br/>
453      * It only flips the texture of the sprite, and not the texture of the sprite's children.                       <br/>
454      * Also, flipping the texture doesn't alter the anchorPoint.                                                    <br/>
455      * If you want to flip the anchorPoint too, and/or to flip the children too use:                                <br/>
456      *      sprite.setScaleX(sprite.getScaleX() * -1);  <p/>
457      * @return {Boolean} true if the sprite is flipped horizontally, false otherwise.
458      */
459     isFlippedX:function () {
460         return this._flippedX;
461     },
462 
463     /**
464      * <p>
465      *     Return the flag which indicates whether the sprite is flipped vertically or not.                         <br/>
466      *                                                                                                              <br/>
467      *      It only flips the texture of the sprite, and not the texture of the sprite's children.                  <br/>
468      *      Also, flipping the texture doesn't alter the anchorPoint.                                               <br/>
469      *      If you want to flip the anchorPoint too, and/or to flip the children too use:                           <br/>
470      *         sprite.setScaleY(sprite.getScaleY() * -1); <p/>
471      * @return {Boolean} true if the sprite is flipped vertically, false otherwise.
472      */
473     isFlippedY:function () {
474         return this._flippedY;
475     },
476 
477     //
478     // RGBA protocol
479     //
480     /**
481      * Sets whether opacity modify color or not.
482      * @function
483      * @param {Boolean} modify
484      */
485     setOpacityModifyRGB: function (modify) {
486         if (this._opacityModifyRGB !== modify) {
487             this._opacityModifyRGB = modify;
488             this._renderCmd._setColorDirty();
489         }
490     },
491 
492     /**
493      * Returns whether opacity modify color or not.
494      * @return {Boolean}
495      */
496     isOpacityModifyRGB:function () {
497         return this._opacityModifyRGB;
498     },
499 
500     // Animation
501 
502     /**
503      * Changes the display frame with animation name and index.<br/>
504      * The animation name will be get from the CCAnimationCache
505      * @param {String} animationName
506      * @param {Number} frameIndex
507      */
508     setDisplayFrameWithAnimationName:function (animationName, frameIndex) {
509         cc.assert(animationName, cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_3);
510 
511         var cache = cc.animationCache.getAnimation(animationName);
512         if(!cache){
513             cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName);
514             return;
515         }
516         var animFrame = cache.getFrames()[frameIndex];
517         if(!animFrame){
518             cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_2);
519             return;
520         }
521         this.setSpriteFrame(animFrame.getSpriteFrame());
522     },
523 
524     /**
525      * Returns the batch node object if this sprite is rendered by cc.SpriteBatchNode
526      * @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.
527      */
528     getBatchNode:function () {
529         return this._batchNode;
530     },
531 
532     _setReorderChildDirtyRecursively:function () {
533         //only set parents flag the first time
534         if (!this._reorderChildDirty) {
535             this._reorderChildDirty = true;
536             var pNode = this._parent;
537             while (pNode && pNode !== this._batchNode) {
538                 pNode._setReorderChildDirtyRecursively();
539                 pNode = pNode.parent;
540             }
541         }
542     },
543 
544     // CCTextureProtocol
545     /**
546      * Returns the texture of the sprite node
547      * @returns {cc.Texture2D}
548      */
549     getTexture:function () {
550         return this._texture;
551     },
552 
553 	_softInit: function (fileName, rect, rotated) {
554 		if (fileName === undefined)
555 			cc.Sprite.prototype.init.call(this);
556 		else if (cc.isString(fileName)) {
557 			if (fileName[0] === "#") {
558 				// Init with a sprite frame name
559 				var frameName = fileName.substr(1, fileName.length - 1);
560 				var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName);
561 				if (spriteFrame)
562 					this.initWithSpriteFrame(spriteFrame);
563 				else
564 					cc.log("%s does not exist", fileName);
565 			} else {
566 				// Init  with filename and rect
567 				cc.Sprite.prototype.init.call(this, fileName, rect);
568 			}
569 		} else if (typeof fileName === "object") {
570 			if (fileName instanceof cc.Texture2D) {
571 				// Init  with texture and rect
572 				this.initWithTexture(fileName, rect, rotated);
573 			} else if (fileName instanceof cc.SpriteFrame) {
574 				// Init with a sprite frame
575 				this.initWithSpriteFrame(fileName);
576 			} else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) {
577 				// Init with a canvas or image element
578 				var texture2d = new cc.Texture2D();
579 				texture2d.initWithElement(fileName);
580 				texture2d.handleLoadedTexture();
581 				this.initWithTexture(texture2d);
582 			}
583 		}
584 	},
585 
586     /**
587      * Returns the quad (tex coords, vertex coords and color) information.
588      * @return {cc.V3F_C4B_T2F_Quad|null} Returns a cc.V3F_C4B_T2F_Quad object when render mode is WebGL, returns null when render mode is Canvas.
589      */
590     getQuad:function () {
591         return this._renderCmd.getQuad();
592     },
593 
594     /**
595      * conforms to cc.TextureProtocol protocol
596      * @function
597      * @param {Number|cc.BlendFunc} src
598      * @param {Number} dst
599      */
600     setBlendFunc: function (src, dst) {
601         var locBlendFunc = this._blendFunc;
602         if (dst === undefined) {
603             locBlendFunc.src = src.src;
604             locBlendFunc.dst = src.dst;
605         } else {
606             locBlendFunc.src = src;
607             locBlendFunc.dst = dst;
608         }
609         this._renderCmd.updateBlendFunc(locBlendFunc);
610     },
611 
612     /**
613      * Initializes an empty sprite with nothing init.<br/>
614      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
615      * @function
616      * @return {Boolean}
617      */
618     init: function () {
619         var _t = this;
620         if (arguments.length > 0)
621             return _t.initWithFile(arguments[0], arguments[1]);
622 
623         cc.Node.prototype.init.call(_t);
624         _t.dirty = _t._recursiveDirty = false;
625 
626         _t._blendFunc.src = cc.BLEND_SRC;
627         _t._blendFunc.dst = cc.BLEND_DST;
628 
629         _t.texture = null;
630         _t._flippedX = _t._flippedY = false;
631 
632         // default transform anchor: center
633         _t.anchorX = 0.5;
634         _t.anchorY = 0.5;
635 
636         // zwoptex default values
637         _t._offsetPosition.x = 0;
638         _t._offsetPosition.y = 0;
639         _t._hasChildren = false;
640 
641         this._renderCmd._init();
642         // updated in "useSelfRender"
643         // Atlas: TexCoords
644         _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0));
645         return true;
646     },
647 
648     /**
649      * <p>
650      *     Initializes a sprite with an image filename.<br/>
651      *
652      *     This method will find pszFilename from local file system, load its content to CCTexture2D,<br/>
653      *     then use CCTexture2D to create a sprite.<br/>
654      *     After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/>
655      *     Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
656      * </p>
657      * @param {String} filename The path to an image file in local file system
658      * @param {cc.Rect} rect The rectangle assigned the content area from texture.
659      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
660      */
661     initWithFile:function (filename, rect) {
662         cc.assert(filename, cc._LogInfos.Sprite_initWithFile);
663 
664         var tex = cc.textureCache.getTextureForKey(filename);
665         if (!tex) {
666             tex = cc.textureCache.addImage(filename);
667             return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height));
668         } else {
669             if (!rect) {
670                 var size = tex.getContentSize();
671                 rect = cc.rect(0, 0, size.width, size.height);
672             }
673             return this.initWithTexture(tex, rect);
674         }
675     },
676 
677     /**
678      * Initializes a sprite with a texture and a rect in points, optionally rotated.  <br/>
679      * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/>
680      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
681      * @function
682      * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites.
683      * @param {cc.Rect} [rect] Only the contents inside rect of this texture will be applied for this sprite.
684      * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
685      * @param {Boolean} [counterclockwise=true] Whether or not the texture rectangle rotation is counterclockwise (texture package is counterclockwise, spine is clockwise).
686      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
687      */
688     initWithTexture: function (texture, rect, rotated, counterclockwise) {
689         var _t = this;
690         cc.assert(arguments.length !== 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture);
691 
692         rotated = rotated || false;
693         texture = this._renderCmd._handleTextureForRotatedTexture(texture, rect, rotated, counterclockwise);
694 
695         if (!cc.Node.prototype.init.call(_t))
696             return false;
697 
698         _t._batchNode = null;
699         _t._recursiveDirty = false;
700         _t.dirty = false;
701         _t._opacityModifyRGB = true;
702 
703         _t._blendFunc.src = cc.BLEND_SRC;
704         _t._blendFunc.dst = cc.BLEND_DST;
705 
706         _t._flippedX = _t._flippedY = false;
707 
708         // default transform anchor: center
709         _t.setAnchorPoint(0.5, 0.5);
710 
711         // zwoptex default values
712         _t._offsetPosition.x = 0;
713         _t._offsetPosition.y = 0;
714         _t._hasChildren = false;
715 
716         this._renderCmd._init();
717 
718         var locTextureLoaded = texture.isLoaded();
719         _t._textureLoaded = locTextureLoaded;
720 
721         if (!locTextureLoaded) {
722             _t._rectRotated = rotated;
723             if (rect) {
724                 _t._rect.x = rect.x;
725                 _t._rect.y = rect.y;
726                 _t._rect.width = rect.width;
727                 _t._rect.height = rect.height;
728             }
729             if(_t.texture)
730                 _t.texture.removeEventListener("load", _t);
731             texture.addEventListener("load", _t._renderCmd._textureLoadedCallback, _t);
732             _t.texture = texture;
733             return true;
734         }
735 
736         if (!rect)
737             rect = cc.rect(0, 0, texture.width, texture.height);
738 
739         this._renderCmd._checkTextureBoundary(texture, rect, rotated);
740 
741         _t.setTexture(texture);
742         _t.setTextureRect(rect, rotated);
743 
744         // by default use "Self Render".
745         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
746         _t.setBatchNode(null);
747         return true;
748     },
749 
750     /**
751      * Updates the texture rect of the CCSprite in points.
752      * @function
753      * @param {cc.Rect} rect a rect of texture
754      * @param {Boolean} [rotated] Whether or not the texture is rotated
755      * @param {cc.Size} [untrimmedSize] The original pixels size of the texture
756      * @param {Boolean} [needConvert] contentScaleFactor switch
757      */
758     setTextureRect: function (rect, rotated, untrimmedSize, needConvert) {
759         var _t = this;
760         _t._rectRotated = rotated || false;
761         _t.setContentSize(untrimmedSize || rect);
762 
763         _t.setVertexRect(rect);
764         _t._renderCmd._setTextureCoords(rect, needConvert);
765 
766         var relativeOffsetX = _t._unflippedOffsetPositionFromCenter.x, relativeOffsetY = _t._unflippedOffsetPositionFromCenter.y;
767         if (_t._flippedX)
768             relativeOffsetX = -relativeOffsetX;
769         if (_t._flippedY)
770             relativeOffsetY = -relativeOffsetY;
771         var locRect = _t._rect;
772         _t._offsetPosition.x = relativeOffsetX + (_t._contentSize.width - locRect.width) / 2;
773         _t._offsetPosition.y = relativeOffsetY + (_t._contentSize.height - locRect.height) / 2;
774 
775         // rendering using batch node
776         if (_t._batchNode) {
777             // update dirty, don't update _recursiveDirty
778             _t.dirty = true;
779         } else {
780             // self rendering
781             // Atlas: Vertex
782             this._renderCmd._resetForBatchNode();
783         }
784     },
785 
786     // BatchNode methods
787     /**
788      * Updates the quad according the the rotation, position, scale values.
789      * @function
790      */
791     updateTransform: function(){
792         this._renderCmd.updateTransform();
793     },
794 
795     /**
796      * Add child to sprite (override cc.Node)
797      * @function
798      * @param {cc.Sprite} child
799      * @param {Number} localZOrder  child's zOrder
800      * @param {String} [tag] child's tag
801      * @override
802      */
803     addChild: function (child, localZOrder, tag) {
804         cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2);
805 
806         if (localZOrder == null)
807             localZOrder = child._localZOrder;
808         if (tag == null)
809             tag = child.tag;
810 
811         if(this._renderCmd._setBatchNodeForAddChild(child)){
812             //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check
813             cc.Node.prototype.addChild.call(this, child, localZOrder, tag);
814             this._hasChildren = true;
815         }
816     },
817 
818     // Frames
819     /**
820      * Sets a new sprite frame to the sprite.
821      * @function
822      * @param {cc.SpriteFrame|String} newFrame
823      */
824     setSpriteFrame: function (newFrame) {
825         var _t = this;
826         if(cc.isString(newFrame)){
827             newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame);
828             cc.assert(newFrame, cc._LogInfos.Sprite_setSpriteFrame)
829         }
830 
831         this.setNodeDirty(true);
832 
833         var frameOffset = newFrame.getOffset();
834         _t._unflippedOffsetPositionFromCenter.x = frameOffset.x;
835         _t._unflippedOffsetPositionFromCenter.y = frameOffset.y;
836 
837         // update rect
838         var pNewTexture = newFrame.getTexture();
839         var locTextureLoaded = newFrame.textureLoaded();
840         if (!locTextureLoaded) {
841             _t._textureLoaded = false;
842             newFrame.addEventListener("load", function (sender) {
843                 _t._textureLoaded = true;
844                 var locNewTexture = sender.getTexture();
845                 if (locNewTexture !== _t._texture)
846                     _t._setTexture(locNewTexture);
847                 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize());
848                 _t.dispatchEvent("load");
849                 _t.setColor(_t._realColor);
850             }, _t);
851         } else {
852             _t._textureLoaded = true;
853             // update texture before updating texture rect
854             if (pNewTexture !== _t._texture) {
855                 _t._setTexture(pNewTexture);
856                 _t.setColor(_t._realColor);
857             }
858             _t.setTextureRect(newFrame.getRect(), newFrame.isRotated(), newFrame.getOriginalSize());
859         }
860         this._renderCmd._updateForSetSpriteFrame(pNewTexture);
861     },
862 
863     /**
864      * Sets a new display frame to the sprite.
865      * @param {cc.SpriteFrame|String} newFrame
866      * @deprecated
867      */
868     setDisplayFrame: function(newFrame){
869         cc.log(cc._LogInfos.Sprite_setDisplayFrame);
870         this.setSpriteFrame(newFrame);
871     },
872 
873     /**
874      * Returns whether or not a cc.SpriteFrame is being displayed
875      * @function
876      * @param {cc.SpriteFrame} frame
877      * @return {Boolean}
878      */
879     isFrameDisplayed: function(frame){
880         return this._renderCmd.isFrameDisplayed(frame);
881     },
882 
883     /**
884      * Returns the current displayed frame.
885      * @deprecated since 3.4, please use getSpriteFrame instead
886      * @return {cc.SpriteFrame}
887      */
888     displayFrame: function () {
889         return this.getSpriteFrame();
890     },
891 
892     /**
893      * Returns the current displayed frame.
894      * @return {cc.SpriteFrame}
895      */
896     getSpriteFrame: function () {
897         return new cc.SpriteFrame(this._texture,
898             cc.rectPointsToPixels(this._rect),
899             this._rectRotated,
900             cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter),
901             cc.sizePointsToPixels(this._contentSize));
902     },
903 
904     /**
905      * Sets the batch node to sprite
906      * @function
907      * @param {cc.SpriteBatchNode|null} spriteBatchNode
908      * @example
909      *  var batch = new cc.SpriteBatchNode("Images/grossini_dance_atlas.png", 15);
910      *  var sprite = new cc.Sprite(batch.texture, cc.rect(0, 0, 57, 57));
911      *  batch.addChild(sprite);
912      *  layer.addChild(batch);
913      */
914     setBatchNode:function (spriteBatchNode) {
915         var _t = this;
916         _t._batchNode = spriteBatchNode; // weak reference
917 
918         // self render
919         if (!_t._batchNode) {
920             _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED;
921             _t.textureAtlas = null;
922             _t._recursiveDirty = false;
923             _t.dirty = false;
924 
925             this._renderCmd._resetForBatchNode();
926         } else {
927             // using batch
928             _t._transformToBatch = cc.affineTransformIdentity();
929             _t.textureAtlas = _t._batchNode.getTextureAtlas(); // weak ref
930         }
931     },
932 
933     // CCTextureProtocol
934     /**
935      * Sets the texture of sprite
936      * @function
937      * @param {cc.Texture2D|String} texture
938      */
939     setTexture: function (texture) {
940         if(!texture)
941             return this._renderCmd._setTexture(null);
942 
943         //CCSprite.cpp 327 and 338
944         var isFileName = cc.isString(texture);
945 
946         if(isFileName)
947             texture = cc.textureCache.addImage(texture);
948 
949         if(texture._textureLoaded){
950             this._setTexture(texture, isFileName);
951             this.setColor(this._realColor);
952             this._textureLoaded = true;
953         }else{
954             this._renderCmd._setTexture(null);
955             texture.addEventListener("load", function(){
956                 this._setTexture(texture, isFileName);
957                 this.setColor(this._realColor);
958                 this._textureLoaded = true;
959             }, this);
960         }
961     },
962 
963     _setTexture: function(texture, change){
964         this._renderCmd._setTexture(texture);
965         if(change)
966             this._changeRectWithTexture(texture);
967     },
968 
969     _changeRectWithTexture: function(texture){
970         var contentSize = texture._contentSize;
971         var rect = cc.rect(
972                 0, 0,
973                 contentSize.width, contentSize.height
974             );
975         this.setTextureRect(rect);
976     },
977 
978     _createRenderCmd: function(){
979         if(cc._renderType === cc.game.RENDER_TYPE_CANVAS)
980             return new cc.Sprite.CanvasRenderCmd(this);
981         else
982             return new cc.Sprite.WebGLRenderCmd(this);
983     }
984 });
985 
986 /**
987  * Create a sprite with image path or frame name or texture or spriteFrame.
988  * @deprecated since v3.0, please use new construction instead
989  * @see cc.Sprite
990  * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName  The string which indicates a path to image file, e.g., "scene1/monster.png".
991  * @param {cc.Rect} rect  Only the contents inside rect of pszFileName's texture will be applied for this sprite.
992  * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
993  * @return {cc.Sprite} A valid sprite object
994  */
995 cc.Sprite.create = function (fileName, rect, rotated) {
996     return new cc.Sprite(fileName, rect, rotated);
997 };
998 
999 /**
1000  * @deprecated since v3.0, please use new construction instead
1001  * @see cc.Sprite
1002  * @function
1003  */
1004 cc.Sprite.createWithTexture = cc.Sprite.create;
1005 
1006 /**
1007  * @deprecated since v3.0, please use new construction instead
1008  * @see cc.Sprite
1009  * @function
1010  */
1011 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create;
1012 
1013 /**
1014  * @deprecated since v3.0, please use new construction instead
1015  * @see cc.Sprite
1016  * @function
1017  */
1018 cc.Sprite.createWithSpriteFrame = cc.Sprite.create;
1019 /**
1020  * cc.Sprite invalid index on the cc.SpriteBatchNode
1021  * @constant
1022  * @type {Number}
1023  */
1024 cc.Sprite.INDEX_NOT_INITIALIZED = -1;
1025 
1026 cc.EventHelper.prototype.apply(cc.Sprite.prototype);
1027 
1028 cc.assert(cc.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js");
1029 cc._tmp.PrototypeSprite();
1030 delete cc._tmp.PrototypeSprite;