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 (typeof(fileName) === "string") {
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 		}
837 		else if (typeof(fileName) === "object") {
838 			if (fileName instanceof cc.Texture2D) {
839 				// Init  with texture and rect
840 				this.initWithTexture(fileName, rect, rotated);
841 			} else if (fileName instanceof cc.SpriteFrame) {
842 				// Init with a sprite frame
843 				this.initWithSpriteFrame(fileName);
844 			} else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) {
845 				// Init with a canvas or image element
846 				var texture2d = new cc.Texture2D();
847 				texture2d.initWithElement(fileName);
848 				texture2d.handleLoadedTexture();
849 				this.initWithTexture(texture2d);
850 			}
851 		}
852 	},
853 
854     /**
855      * Returns the quad (tex coords, vertex coords and color) information.
856      * @return {cc.V3F_C4B_T2F_Quad}
857      */
858     getQuad:function () {
859         return this._quad;
860     },
861 
862     /**
863      * conforms to cc.TextureProtocol protocol
864      * @function
865      * @param {Number|cc.BlendFunc} src
866      * @param {Number} dst
867      */
868     setBlendFunc: null,
869 
870     /**
871      * Initializes an empty sprite with nothing init.<br/>
872      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
873      * @function
874      * @return {Boolean}
875      */
876     init:null,
877 
878     /**
879      * <p>
880      *     Initializes a sprite with an image filename.<br/>
881      *
882      *     This method will find pszFilename from local file system, load its content to CCTexture2D,<br/>
883      *     then use CCTexture2D to create a sprite.<br/>
884      *     After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/>
885      *     Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
886      * </p>
887      * @param {String} filename The path to an image file in local file system
888      * @param {cc.Rect} rect The rectangle assigned the content area from texture.
889      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
890      */
891     initWithFile:function (filename, rect) {
892         cc.assert(filename, cc._LogInfos.Sprite_initWithFile);
893 
894         var tex = cc.textureCache.getTextureForKey(filename);
895         if (!tex) {
896             tex = cc.textureCache.addImage(filename);
897             return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height));
898         } else {
899             if (!rect) {
900                 var size = tex.getContentSize();
901                 rect = cc.rect(0, 0, size.width, size.height);
902             }
903             return this.initWithTexture(tex, rect);
904         }
905     },
906 
907     /**
908      * Initializes a sprite with a texture and a rect in points, optionally rotated.  <br/>
909      * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/>
910      * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself.
911      * @function
912      * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites.
913      * @param {cc.Rect} rect Only the contents inside rect of this texture will be applied for this sprite.
914      * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
915      * @return {Boolean} true if the sprite is initialized properly, false otherwise.
916      */
917     initWithTexture: null,
918 
919     _textureLoadedCallback: null,
920 
921     /**
922      * Updates the texture rect of the CCSprite in points.
923      * @function
924      * @param {cc.Rect} rect a rect of texture
925      * @param {Boolean} [rotated] Whether or not the texture is rotated
926      * @param {cc.Size} [untrimmedSize] The original pixels size of the texture
927      */
928     setTextureRect:null,
929 
930     // BatchNode methods
931     /**
932      * Updates the quad according the the rotation, position, scale values.
933      * @function
934      */
935     updateTransform: null,
936 
937     /**
938      * Add child to sprite (override cc.Node)
939      * @function
940      * @param {cc.Sprite} child
941      * @param {Number} localZOrder  child's zOrder
942      * @param {String} tag child's tag
943      * @override
944      */
945     addChild: null,
946 
947     /**
948      * Update sprite's color
949      */
950     updateColor:function () {
951         var locDisplayedColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity;
952         var color4 = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: locDisplayedOpacity};
953         // special opacity for premultiplied textures
954         if (this._opacityModifyRGB) {
955             color4.r *= locDisplayedOpacity / 255.0;
956             color4.g *= locDisplayedOpacity / 255.0;
957             color4.b *= locDisplayedOpacity / 255.0;
958         }
959         var locQuad = this._quad;
960         locQuad.bl.colors = color4;
961         locQuad.br.colors = color4;
962         locQuad.tl.colors = color4;
963         locQuad.tr.colors = color4;
964 
965         // renders using Sprite Manager
966         if (this._batchNode) {
967             if (this.atlasIndex != cc.Sprite.INDEX_NOT_INITIALIZED) {
968                 this.textureAtlas.updateQuad(locQuad, this.atlasIndex)
969             } else {
970                 // no need to set it recursively
971                 // update dirty_, don't update recursiveDirty_
972                 this.dirty = true;
973             }
974         }
975         // self render
976         // do nothing
977         this._quadDirty = true;
978     },
979 
980     /**
981      * Sets opacity of the sprite
982      * @function
983      * @param {Number} opacity
984      */
985     setOpacity:null,
986 
987     /**
988      * Sets color of the sprite
989      * @function
990      * @param {cc.Color} color3
991      */
992     setColor: null,
993 
994     /**
995      * Updates the display color
996      * @function
997      */
998     updateDisplayedColor: null,
999 
1000     // Frames
1001     /**
1002      * Sets a new sprite frame to the sprite.
1003      * @function
1004      * @param {cc.SpriteFrame|String} newFrame
1005      */
1006     setSpriteFrame: null,
1007 
1008     /**
1009      * Sets a new display frame to the sprite.
1010      * @param {cc.SpriteFrame|String} newFrame
1011      * @deprecated
1012      */
1013     setDisplayFrame: function(newFrame){
1014         cc.log(cc._LogInfos.Sprite_setDisplayFrame);
1015         this.setSpriteFrame(newFrame);
1016     },
1017 
1018     /**
1019      * Returns whether or not a cc.SpriteFrame is being displayed
1020      * @function
1021      * @param {cc.SpriteFrame} frame
1022      * @return {Boolean}
1023      */
1024     isFrameDisplayed: null,
1025 
1026     /**
1027      * Returns the current displayed frame.
1028      * @return {cc.SpriteFrame}
1029      */
1030     displayFrame: function () {
1031         return cc.SpriteFrame.create(this._texture,
1032             cc.rectPointsToPixels(this._rect),
1033             this._rectRotated,
1034             cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter),
1035             cc.sizePointsToPixels(this._contentSize));
1036     },
1037 
1038     /**
1039      * Sets the batch node to sprite
1040      * @function
1041      * @param {cc.SpriteBatchNode|null} spriteBatchNode
1042      * @example
1043      *  var batch = cc.SpriteBatchNode.create("Images/grossini_dance_atlas.png", 15);
1044      *  var sprite = cc.Sprite.create(batch.texture, cc.rect(0, 0, 57, 57));
1045      *  batch.addChild(sprite);
1046      *  layer.addChild(batch);
1047      */
1048     setBatchNode:null,
1049 
1050     // CCTextureProtocol
1051     /**
1052      * Sets the texture of sprite
1053      * @function
1054      * @param {cc.Texture2D|String} texture
1055      */
1056     setTexture: null,
1057 
1058     // Texture protocol
1059     _updateBlendFunc:function () {
1060         if(this._batchNode){
1061             cc.log(cc._LogInfos.Sprite__updateBlendFunc);
1062             return;
1063         }
1064 
1065         // it's possible to have an untextured sprite
1066         if (!this._texture || !this._texture.hasPremultipliedAlpha()) {
1067             this._blendFunc.src = cc.SRC_ALPHA;
1068             this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
1069             this.opacityModifyRGB = false;
1070         } else {
1071             this._blendFunc.src = cc.BLEND_SRC;
1072             this._blendFunc.dst = cc.BLEND_DST;
1073             this.opacityModifyRGB = true;
1074         }
1075     },
1076 
1077     _changeTextureColor: function () {
1078         var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect();
1079         if (locTexture && locRect.validRect && this._originalTexture) {
1080             locElement = locTexture.getHtmlElementObj();
1081             if (!locElement)
1082                 return;
1083 
1084             this._colorized = true;
1085             if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor
1086                 && this._originalTexture._htmlElementObj != locElement)
1087                 cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect, locElement);
1088             else {
1089                 locElement = cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect);
1090                 locTexture = new cc.Texture2D();
1091                 locTexture.initWithElement(locElement);
1092                 locTexture.handleLoadedTexture();
1093                 this.texture = locTexture;
1094             }
1095         }
1096     },
1097 
1098     _setTextureCoords:function (rect) {
1099         rect = cc.rectPointsToPixels(rect);
1100 
1101         var tex = this._batchNode ? this.textureAtlas.texture : this._texture;
1102         if (!tex)
1103             return;
1104 
1105         var atlasWidth = tex.pixelsWidth;
1106         var atlasHeight = tex.pixelsHeight;
1107 
1108         var left, right, top, bottom, tempSwap, locQuad = this._quad;
1109         if (this._rectRotated) {
1110             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
1111                 left = (2 * rect.x + 1) / (2 * atlasWidth);
1112                 right = left + (rect.height * 2 - 2) / (2 * atlasWidth);
1113                 top = (2 * rect.y + 1) / (2 * atlasHeight);
1114                 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight);
1115             } else {
1116                 left = rect.x / atlasWidth;
1117                 right = (rect.x + rect.height) / atlasWidth;
1118                 top = rect.y / atlasHeight;
1119                 bottom = (rect.y + rect.width) / atlasHeight;
1120             }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
1121 
1122             if (this._flippedX) {
1123                 tempSwap = top;
1124                 top = bottom;
1125                 bottom = tempSwap;
1126             }
1127 
1128             if (this._flippedY) {
1129                 tempSwap = left;
1130                 left = right;
1131                 right = tempSwap;
1132             }
1133 
1134             locQuad.bl.texCoords.u = left;
1135             locQuad.bl.texCoords.v = top;
1136             locQuad.br.texCoords.u = left;
1137             locQuad.br.texCoords.v = bottom;
1138             locQuad.tl.texCoords.u = right;
1139             locQuad.tl.texCoords.v = top;
1140             locQuad.tr.texCoords.u = right;
1141             locQuad.tr.texCoords.v = bottom;
1142         } else {
1143             if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
1144                 left = (2 * rect.x + 1) / (2 * atlasWidth);
1145                 right = left + (rect.width * 2 - 2) / (2 * atlasWidth);
1146                 top = (2 * rect.y + 1) / (2 * atlasHeight);
1147                 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight);
1148             } else {
1149                 left = rect.x / atlasWidth;
1150                 right = (rect.x + rect.width) / atlasWidth;
1151                 top = rect.y / atlasHeight;
1152                 bottom = (rect.y + rect.height) / atlasHeight;
1153             } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
1154 
1155             if (this._flippedX) {
1156                 tempSwap = left;
1157                 left = right;
1158                 right = tempSwap;
1159             }
1160 
1161             if (this._flippedY) {
1162                 tempSwap = top;
1163                 top = bottom;
1164                 bottom = tempSwap;
1165             }
1166 
1167             locQuad.bl.texCoords.u = left;
1168             locQuad.bl.texCoords.v = bottom;
1169             locQuad.br.texCoords.u = right;
1170             locQuad.br.texCoords.v = bottom;
1171             locQuad.tl.texCoords.u = left;
1172             locQuad.tl.texCoords.v = top;
1173             locQuad.tr.texCoords.u = right;
1174             locQuad.tr.texCoords.v = top;
1175         }
1176         this._quadDirty = true;
1177     },
1178     /**
1179      * draw sprite to canvas
1180      * @function
1181      */
1182     draw: null
1183 });
1184 
1185 /**
1186  * Create a sprite with image path or frame name or texture or spriteFrame.
1187  * @deprecated since v3.0, please use new construction instead
1188  * @see cc.Sprite
1189  * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName  The string which indicates a path to image file, e.g., "scene1/monster.png".
1190  * @param {cc.Rect} rect  Only the contents inside rect of pszFileName's texture will be applied for this sprite.
1191  * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated.
1192  * @return {cc.Sprite} A valid sprite object
1193  */
1194 cc.Sprite.create = function (fileName, rect, rotated) {
1195     return new cc.Sprite(fileName, rect, rotated);
1196 };
1197 
1198 /**
1199  * @deprecated since v3.0, please use new construction instead
1200  * @see cc.Sprite
1201  * @function
1202  */
1203 cc.Sprite.createWithTexture = cc.Sprite.create;
1204 
1205 /**
1206  * @deprecated since v3.0, please use new construction instead
1207  * @see cc.Sprite
1208  * @function
1209  */
1210 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create;
1211 
1212 /**
1213  * @deprecated since v3.0, please use new construction instead
1214  * @see cc.Sprite
1215  * @function
1216  */
1217 cc.Sprite.createWithSpriteFrame = cc.Sprite.create;
1218 /**
1219  * cc.Sprite invalid index on the cc.SpriteBatchNode
1220  * @constant
1221  * @type {Number}
1222  */
1223 cc.Sprite.INDEX_NOT_INITIALIZED = -1;
1224 
1225 
1226 if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1227     var _p = cc.Sprite.prototype;
1228 
1229     _p._spriteFrameLoadedCallback = function(spriteFrame){
1230         var _t = this;
1231         _t.setNodeDirty(true);
1232         _t.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize());
1233         var curColor = _t.color;
1234         if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255)
1235             _t._changeTextureColor();
1236 
1237         _t._callLoadedEventCallbacks();
1238     };
1239 
1240     _p.setOpacityModifyRGB = function (modify) {
1241         if (this._opacityModifyRGB !== modify) {
1242             this._opacityModifyRGB = modify;
1243             this.setNodeDirty(true);
1244         }
1245     };
1246 
1247     _p.updateDisplayedOpacity = function (parentOpacity) {
1248         cc.Node.prototype.updateDisplayedOpacity.call(this, parentOpacity);
1249         this._setNodeDirtyForCache();
1250     };
1251 
1252     _p.ctor = function (fileName, rect, rotated) {
1253         var self = this;
1254         cc.Node.prototype.ctor.call(self);
1255         self._shouldBeHidden = false;
1256         self._offsetPosition = cc.p(0, 0);
1257         self._unflippedOffsetPositionFromCenter = cc.p(0, 0);
1258         self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST};
1259         self._rect = cc.rect(0, 0, 0, 0);
1260 
1261         self._newTextureWhenChangeColor = false;
1262         self._textureLoaded = true;
1263         self._textureRect_Canvas = {x: 0, y: 0, width: 0, height:0, validRect: false};
1264         self._drawSize_Canvas = cc.size(0, 0);
1265 
1266         self._softInit(fileName, rect, rotated);
1267     };
1268 
1269     _p.setBlendFunc = function (src, dst) {
1270         var _t = this, locBlendFunc = this._blendFunc;
1271         if (dst === undefined) {
1272             locBlendFunc.src = src.src;
1273             locBlendFunc.dst = src.dst;
1274         } else {
1275             locBlendFunc.src = src;
1276             locBlendFunc.dst = dst;
1277         }
1278         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
1279             _t._blendFuncStr = cc._getCompositeOperationByBlendFunc(locBlendFunc);
1280     };
1281 
1282     _p.init = function () {
1283         var _t = this;
1284         if (arguments.length > 0)
1285             return _t.initWithFile(arguments[0], arguments[1]);
1286 
1287         cc.Node.prototype.init.call(_t);
1288         _t.dirty = _t._recursiveDirty = false;
1289         _t._opacityModifyRGB = true;
1290 
1291         _t._blendFunc.src = cc.BLEND_SRC;
1292         _t._blendFunc.dst = cc.BLEND_DST;
1293 
1294         // update texture (calls _updateBlendFunc)
1295         _t.texture = null;
1296         _t._textureLoaded = true;
1297         _t._flippedX = _t._flippedY = false;
1298 
1299         // default transform anchor: center
1300         _t.anchorX = 0.5;
1301         _t.anchorY = 0.5;
1302 
1303         // zwoptex default values
1304         _t._offsetPosition.x = 0;
1305         _t._offsetPosition.y = 0;
1306         _t._hasChildren = false;
1307 
1308         // updated in "useSelfRender"
1309         // Atlas: TexCoords
1310         _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0));
1311         return true;
1312     };
1313 
1314     _p.initWithTexture = function (texture, rect, rotated) {
1315         var _t = this;
1316         cc.assert(arguments.length != 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture);
1317 
1318         rotated = rotated || false;
1319 
1320         if (rotated && texture.isLoaded()) {
1321             var tempElement = texture.getHtmlElementObj();
1322             tempElement = cc.cutRotateImageToCanvas(tempElement, rect);
1323             var tempTexture = new cc.Texture2D();
1324             tempTexture.initWithElement(tempElement);
1325             tempTexture.handleLoadedTexture();
1326             texture = tempTexture;
1327 
1328             _t._rect = cc.rect(0, 0, rect.width, rect.height);
1329         }
1330 
1331         if (!cc.Node.prototype.init.call(_t))
1332             return false;
1333 
1334         _t._batchNode = null;
1335         _t._recursiveDirty = false;
1336         _t.dirty = false;
1337         _t._opacityModifyRGB = true;
1338 
1339         _t._blendFunc.src = cc.BLEND_SRC;
1340         _t._blendFunc.dst = cc.BLEND_DST;
1341 
1342         _t._flippedX = _t._flippedY = false;
1343 
1344         // default transform anchor: center
1345         _t.anchorX = 0.5;
1346         _t.anchorY = 0.5;
1347 
1348         // zwoptex default values
1349         _t._offsetPosition.x = 0;
1350         _t._offsetPosition.y = 0;
1351         _t._hasChildren = false;
1352 
1353         var locTextureLoaded = texture.isLoaded();
1354         _t._textureLoaded = locTextureLoaded;
1355 
1356         if (!locTextureLoaded) {
1357             _t._rectRotated = rotated;
1358             if (rect) {
1359                 _t._rect.x = rect.x;
1360                 _t._rect.y = rect.y;
1361                 _t._rect.width = rect.width;
1362                 _t._rect.height = rect.height;
1363             }
1364             if(_t.texture)
1365                 _t.texture.removeLoadedEventListener(_t);
1366             texture.addLoadedEventListener(_t._textureLoadedCallback, _t);
1367             _t.texture = texture;
1368             return true;
1369         }
1370 
1371         if (!rect) {
1372             rect = cc.rect(0, 0, texture.width, texture.height);
1373         }
1374 
1375         if(texture && texture.url) {
1376             var _x = rect.x + rect.width, _y = rect.y + rect.height;
1377             if(_x > texture.width){
1378                 cc.error(cc._LogInfos.RectWidth, texture.url);
1379             }
1380             if(_y > texture.height){
1381                 cc.error(cc._LogInfos.RectHeight, texture.url);
1382             }
1383         }
1384         _t._originalTexture = texture;
1385         _t.texture = texture;
1386         _t.setTextureRect(rect, rotated);
1387 
1388         // by default use "Self Render".
1389         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
1390         _t.batchNode = null;
1391         return true;
1392     };
1393 
1394     _p._textureLoadedCallback = function (sender) {
1395         var _t = this;
1396         if(_t._textureLoaded)
1397             return;
1398 
1399         _t._textureLoaded = true;
1400         var locRect = _t._rect;
1401         if (!locRect) {
1402             locRect = cc.rect(0, 0, sender.width, sender.height);
1403         } else if (cc._rectEqualToZero(locRect)) {
1404             locRect.width = sender.width;
1405             locRect.height = sender.height;
1406         }
1407         _t._originalTexture = sender;
1408 
1409         _t.texture = sender;
1410         _t.setTextureRect(locRect, _t._rectRotated);
1411 
1412         //set the texture's color after the it loaded
1413         var locColor = this._displayedColor;
1414         if(locColor.r != 255 || locColor.g != 255 || locColor.b != 255)
1415             _t._changeTextureColor();
1416 
1417         // by default use "Self Render".
1418         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
1419         _t.batchNode = _t._batchNode;
1420         _t._callLoadedEventCallbacks();
1421     };
1422 
1423     _p.setTextureRect = function (rect, rotated, untrimmedSize) {
1424         var _t = this;
1425         _t._rectRotated = rotated || false;
1426         _t.setContentSize(untrimmedSize || rect);
1427 
1428         _t.setVertexRect(rect);
1429 
1430         var locTextureRect = _t._textureRect_Canvas, scaleFactor = cc.contentScaleFactor();
1431         locTextureRect.x = 0 | (rect.x * scaleFactor);
1432         locTextureRect.y = 0 | (rect.y * scaleFactor);
1433         locTextureRect.width = 0 | (rect.width * scaleFactor);
1434         locTextureRect.height = 0 | (rect.height * scaleFactor);
1435         locTextureRect.validRect = !(locTextureRect.width === 0 || locTextureRect.height === 0 || locTextureRect.x < 0 || locTextureRect.y < 0);
1436 
1437         var relativeOffset = _t._unflippedOffsetPositionFromCenter;
1438         if (_t._flippedX)
1439             relativeOffset.x = -relativeOffset.x;
1440         if (_t._flippedY)
1441             relativeOffset.y = -relativeOffset.y;
1442         _t._offsetPosition.x = relativeOffset.x + (_t._contentSize.width - _t._rect.width) / 2;
1443         _t._offsetPosition.y = relativeOffset.y + (_t._contentSize.height - _t._rect.height) / 2;
1444 
1445         // rendering using batch node
1446         if (_t._batchNode) {
1447             // update dirty, don't update _recursiveDirty
1448             _t.dirty = true;
1449         }
1450     };
1451 
1452     _p.updateTransform = function () {
1453         var _t = this;
1454         //cc.assert(_t._batchNode, "updateTransform is only valid when cc.Sprite is being rendered using an cc.SpriteBatchNode");
1455 
1456         // recaculate matrix only if it is dirty
1457         if (_t.dirty) {
1458             // If it is not visible, or one of its ancestors is not visible, then do nothing:
1459             var locParent = _t._parent;
1460             if (!_t._visible || ( locParent && locParent != _t._batchNode && locParent._shouldBeHidden)) {
1461                 _t._shouldBeHidden = true;
1462             } else {
1463                 _t._shouldBeHidden = false;
1464 
1465                 if (!locParent || locParent == _t._batchNode) {
1466                     _t._transformToBatch = _t.nodeToParentTransform();
1467                 } else {
1468                     //cc.assert(_t._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite");
1469                     _t._transformToBatch = cc.affineTransformConcat(_t.nodeToParentTransform(), locParent._transformToBatch);
1470                 }
1471             }
1472             _t._recursiveDirty = false;
1473             _t.dirty = false;
1474         }
1475 
1476         // recursively iterate over children
1477         if (_t._hasChildren)
1478             _t._arrayMakeObjectsPerformSelector(_t._children, cc.Node._StateCallbackType.updateTransform);
1479     };
1480 
1481     _p.addChild = function (child, localZOrder, tag) {
1482 
1483         cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2);
1484 
1485         if (localZOrder == null)
1486             localZOrder = child._localZOrder;
1487         if (tag == null)
1488             tag = child.tag;
1489 
1490         //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check
1491         cc.Node.prototype.addChild.call(this, child, localZOrder, tag);
1492         this._hasChildren = true;
1493     };
1494 
1495     _p.setOpacity = function (opacity) {
1496         cc.Node.prototype.setOpacity.call(this, opacity);
1497         this._setNodeDirtyForCache();
1498     };
1499 
1500     _p.setColor = function (color3) {
1501         var _t = this;
1502         var curColor = _t.color;
1503         this._oldDisplayColor = curColor;
1504         if ((curColor.r === color3.r) && (curColor.g === color3.g) && (curColor.b === color3.b))
1505             return;
1506         cc.Node.prototype.setColor.call(_t, color3);
1507     };
1508 
1509     _p.updateDisplayedColor = function (parentColor) {
1510         var _t = this;
1511         cc.Node.prototype.updateDisplayedColor.call(_t, parentColor);
1512         var oColor = _t._oldDisplayColor;
1513         var nColor = _t._displayedColor;
1514         if (oColor.r === nColor.r && oColor.g === nColor.g && oColor.b === nColor.b)
1515             return;
1516 
1517         _t._changeTextureColor();
1518         _t._setNodeDirtyForCache();
1519     };
1520 
1521     _p.setSpriteFrame = function (newFrame) {
1522         var _t = this;
1523         if(typeof(newFrame) == "string"){
1524             newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame);
1525             cc.assert(newFrame, cc._LogInfos.CCSpriteBatchNode_setSpriteFrame)
1526         }
1527 
1528         _t.setNodeDirty(true);
1529 
1530         var frameOffset = newFrame.getOffset();
1531         _t._unflippedOffsetPositionFromCenter.x = frameOffset.x;
1532         _t._unflippedOffsetPositionFromCenter.y = frameOffset.y;
1533 
1534         // update rect
1535         _t._rectRotated = newFrame.isRotated();
1536 
1537         var pNewTexture = newFrame.getTexture();
1538         var locTextureLoaded = newFrame.textureLoaded();
1539         if (!locTextureLoaded) {
1540             _t._textureLoaded = false;
1541             newFrame.addLoadedEventListener(function (sender) {
1542                 _t._textureLoaded = true;
1543                 var locNewTexture = sender.getTexture();
1544                 if (locNewTexture != _t._texture)
1545                     _t.texture = locNewTexture;
1546                 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize());
1547                 _t._callLoadedEventCallbacks();
1548             }, _t);
1549         }
1550         // update texture before updating texture rect
1551         if (pNewTexture != _t._texture)
1552             _t.texture = pNewTexture;
1553 
1554         if (_t._rectRotated)
1555             _t._originalTexture = pNewTexture;
1556 
1557         _t.setTextureRect(newFrame.getRect(), _t._rectRotated, newFrame.getOriginalSize());
1558         _t._colorized = false;
1559         if (locTextureLoaded) {
1560             var curColor = _t.color;
1561             if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255)
1562                 _t._changeTextureColor();
1563         }
1564     };
1565 
1566     _p.isFrameDisplayed = function (frame) {
1567         if (frame.getTexture() != this._texture)
1568             return false;
1569         return cc.rectEqualToRect(frame.getRect(), this._rect);
1570     };
1571 
1572     _p.setBatchNode = function (spriteBatchNode) {
1573         var _t = this;
1574         _t._batchNode = spriteBatchNode; // weak reference
1575 
1576         // self render
1577         if (!_t._batchNode) {
1578             _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED;
1579             _t.textureAtlas = null;
1580             _t._recursiveDirty = false;
1581             _t.dirty = false;
1582         } else {
1583             // using batch
1584             _t._transformToBatch = cc.affineTransformIdentity();
1585             _t.textureAtlas = _t._batchNode.textureAtlas; // weak ref
1586         }
1587     };
1588 
1589     _p.setTexture = function (texture) {
1590         var _t = this;
1591         if(texture && (typeof(texture) === "string")){
1592             texture = cc.textureCache.addImage(texture);
1593             _t.setTexture(texture);
1594 
1595             //TODO
1596             var size = texture.getContentSize();
1597             _t.setTextureRect(cc.rect(0,0, size.width, size.height));
1598             return;
1599         }
1600 
1601         // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet
1602         cc.assert(!texture || texture instanceof cc.Texture2D, cc._LogInfos.CCSpriteBatchNode_setTexture);
1603 
1604         if (_t._texture != texture) {
1605             if (texture && texture.getHtmlElementObj() instanceof  HTMLImageElement) {
1606                 _t._originalTexture = texture;
1607             }
1608             _t._texture = texture;
1609         }
1610     };
1611 
1612     _p.draw = function (ctx) {
1613         var _t = this;
1614         if (!_t._textureLoaded)
1615             return;
1616 
1617         var context = ctx || cc._renderContext;
1618         if (_t._blendFuncStr != "source")
1619             context.globalCompositeOperation = _t._blendFuncStr;
1620 
1621         var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY();
1622 
1623         context.globalAlpha = _t._displayedOpacity / 255;
1624         var locRect = _t._rect, locContentSize = _t._contentSize, locOffsetPosition = _t._offsetPosition, locDrawSizeCanvas = _t._drawSize_Canvas;
1625         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = _t._textureRect_Canvas;
1626         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
1627         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
1628 
1629         if (_t._flippedX || _t._flippedY) {
1630             context.save();
1631             if (_t._flippedX) {
1632                 flipXOffset = -locOffsetPosition.x - locRect.width;
1633                 context.scale(-1, 1);
1634             }
1635             if (_t._flippedY) {
1636                 flipYOffset = locOffsetPosition.y;
1637                 context.scale(1, -1);
1638             }
1639         }
1640 
1641         flipXOffset *= locEGL_ScaleX;
1642         flipYOffset *= locEGL_ScaleY;
1643 
1644         if (_t._texture && locTextureCoord.validRect) {
1645             var image = _t._texture.getHtmlElementObj();
1646             if (_t._colorized) {
1647                 context.drawImage(image,
1648                     0, 0, locTextureCoord.width, locTextureCoord.height,
1649                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
1650             } else {
1651                 context.drawImage(image,
1652                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
1653                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
1654             }
1655         } else if (!_t._texture && locTextureCoord.validRect) {
1656             var curColor = _t.color;
1657             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
1658             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
1659         }
1660 
1661         if (cc.SPRITE_DEBUG_DRAW === 1 || _t._showNode) {
1662             // draw bounding box
1663             context.strokeStyle = "rgba(0,255,0,1)";
1664             flipXOffset /= locEGL_ScaleX;
1665             flipYOffset /= locEGL_ScaleY;
1666             flipYOffset = -flipYOffset;
1667             var vertices1 = [cc.p(flipXOffset, flipYOffset),
1668                 cc.p(flipXOffset + locRect.width, flipYOffset),
1669                 cc.p(flipXOffset + locRect.width, flipYOffset - locRect.height),
1670                 cc.p(flipXOffset, flipYOffset - locRect.height)];
1671             cc._drawingUtil.drawPoly(vertices1, 4, true);
1672         } else if (cc.SPRITE_DEBUG_DRAW === 2) {
1673             // draw texture box
1674             context.strokeStyle = "rgba(0,255,0,1)";
1675             var drawRect = _t._rect;
1676             flipYOffset = -flipYOffset;
1677             var vertices2 = [cc.p(flipXOffset, flipYOffset), cc.p(flipXOffset + drawRect.width, flipYOffset),
1678                 cc.p(flipXOffset + drawRect.width, flipYOffset - drawRect.height), cc.p(flipXOffset, flipYOffset - drawRect.height)];
1679             cc._drawingUtil.drawPoly(vertices2, 4, true);
1680         }
1681         if (_t._flippedX || _t._flippedY)
1682             context.restore();
1683         cc.g_NumberOfDraws++;
1684     };
1685 
1686     if(!cc.sys._supportCanvasNewBlendModes)
1687         _p._changeTextureColor =  function () {
1688             var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect();
1689             if (locTexture && locRect.validRect && this._originalTexture) {
1690                 locElement = locTexture.getHtmlElementObj();
1691                 if (!locElement)
1692                     return;
1693 
1694                 var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj());
1695                 if (cacheTextureForColor) {
1696                     this._colorized = true;
1697                     //generate color texture cache
1698                     if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor)
1699                         cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect, locElement);
1700                     else {
1701                         locElement = cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect);
1702                         locTexture = new cc.Texture2D();
1703                         locTexture.initWithElement(locElement);
1704                         locTexture.handleLoadedTexture();
1705                         this.texture = locTexture;
1706                     }
1707                 }
1708             }
1709         };
1710 
1711     delete _p;
1712 } else {
1713     cc.assert(typeof cc._tmp.WebGLSprite === "function", cc._LogInfos.MissingFile, "SpritesWebGL.js");
1714     cc._tmp.WebGLSprite();
1715     delete cc._tmp.WebGLSprite;
1716 }
1717 
1718 cc.assert(typeof cc._tmp.PrototypeSprite === "function", cc._LogInfos.MissingFile, "SpritesPropertyDefine.js");
1719 cc._tmp.PrototypeSprite();
1720 delete cc._tmp.PrototypeSprite;
1721 
1722