1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * cc.Sprite invalid index on the cc.SpriteBatchode
 29  * @constant
 30  * @type Number
 31  */
 32 cc.SPRITE_INDEX_NOT_INITIALIZED = "0xffffffff";
 33 
 34 /**
 35  * generate texture's cache for texture tint
 36  * @function
 37  * @param {HTMLImageElement} texture
 38  * @return {Array}
 39  */
 40 cc.generateTextureCacheForColor = function (texture) {
 41     var w = texture.width;
 42     var h = texture.height;
 43     var textureCache = [];
 44 
 45     var canvas = document.createElement("canvas");
 46     canvas.width = w;
 47     canvas.height = h;
 48 
 49     var ctx = canvas.getContext("2d");
 50     ctx.drawImage(texture, 0, 0);
 51 
 52     var tempCanvas = document.createElement("canvas");
 53     tempCanvas.width = w;
 54     tempCanvas.height = h;
 55     var tempCtx = tempCanvas.getContext('2d');
 56 
 57     var pixels = ctx.getImageData(0, 0, w, h).data;
 58 
 59     for (var rgbI = 0; rgbI < 3; rgbI++) {
 60         var cacheCanvas = document.createElement("canvas");
 61         cacheCanvas.width = w;
 62         cacheCanvas.height = h;
 63         var cacheCtx = cacheCanvas.getContext('2d');
 64 
 65         tempCtx.drawImage(texture, 0, 0);
 66         var to = tempCtx.getImageData(0, 0, w, h);
 67         var toData = to.data;
 68 
 69         for (var i = 0; i < pixels.length; i += 4) {
 70             toData[i  ] = (rgbI === 0) ? pixels[i  ] : 0;
 71             toData[i + 1] = (rgbI === 1) ? pixels[i + 1] : 0;
 72             toData[i + 2] = (rgbI === 2) ? pixels[i + 2] : 0;
 73             toData[i + 3] = pixels[i + 3];
 74         }
 75         cacheCtx.putImageData(to, 0, 0);
 76         textureCache.push(cacheCanvas);
 77     }
 78     return textureCache;
 79 };
 80 
 81 cc.generateTintImage2 = function (texture, color, rect) {
 82     if (!rect) {
 83         rect = cc.rect(0, 0, texture.width, texture.height);
 84     }
 85     var selColor;
 86     if (color instanceof cc.Color4F) {
 87         selColor = cc.c4b(color.r * 255, color.g * 255, color.b * 255, color.a * 255);
 88     } else {
 89         selColor = cc.c4b(color.r, color.g, color.b, 50);//color;
 90     }
 91 
 92     var buff = document.createElement("canvas");
 93     var ctx = buff.getContext("2d");
 94 
 95     if (buff.width != rect.size.width) buff.width = rect.size.width;
 96     if (buff.height != rect.size.height) buff.height = rect.size.height;
 97     ctx.save();
 98 
 99     ctx.drawImage(texture, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, 0, 0, rect.size.width, rect.size.height);
100 
101     ctx.globalCompositeOperation = "source-in";
102     ctx.globalAlpha = selColor.a / 255.0;
103     ctx.fillStyle = "rgb(" + selColor.r + "," + selColor.g + "," + selColor.b + ")";
104     ctx.fillRect(0, 0, rect.size.width, rect.size.height);
105     ctx.restore();
106 
107     return buff;
108 };
109 
110 /**
111  * generate tinted texture
112  * @function
113  * @param {HTMLImageElement} texture
114  * @param {Array} tintedImgCache
115  * @param {cc.Color3B|cc.Color4F} color
116  * @param {cc.Rect} rect
117  * @return {HTMLCanvasElement}
118  */
119 cc.generateTintImage = function (texture, tintedImgCache, color, rect, renderCanvas) {
120     if (!rect) {
121         rect = cc.rect(0, 0, texture.width, texture.height);
122     }
123     var selColor;
124     if (color instanceof cc.Color4F) {
125         selColor = cc.c3b(color.r * 255, color.g * 255, color.b * 255);
126     } else {
127         selColor = color;
128     }
129     var buff = renderCanvas || document.createElement("canvas");
130     buff.width = rect.size.width;
131     buff.height = rect.size.height;
132     var ctx = buff.getContext("2d");
133 
134     ctx.globalCompositeOperation = 'lighter';
135     if (selColor.r > 0) {
136         ctx.globalAlpha = selColor.r / 255.0;
137         ctx.drawImage(tintedImgCache[0], rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, 0, 0, rect.size.width, rect.size.height);
138     }
139     if (selColor.g > 0) {
140         ctx.globalAlpha = selColor.g / 255.0;
141         ctx.drawImage(tintedImgCache[1], rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, 0, 0, rect.size.width, rect.size.height);
142     }
143     if (selColor.b > 0) {
144         ctx.globalAlpha = selColor.b / 255.0;
145         ctx.drawImage(tintedImgCache[2], rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, 0, 0, rect.size.width, rect.size.height);
146     }
147     return buff;
148 };
149 
150 cc.cutRotateImageToCanvas = function (texture, rect) {
151     if (!texture)
152         return null;
153 
154     if (!rect)
155         return texture;
156 
157     var nCanvas = document.createElement("canvas");
158     nCanvas.width = rect.size.width;
159     nCanvas.height = rect.size.height;
160 
161     var ctx = nCanvas.getContext("2d");
162     ctx.translate(nCanvas.width / 2, nCanvas.height / 2);
163     ctx.rotate(-1.5707963267948966);
164     ctx.drawImage(texture, rect.origin.x, rect.origin.y, rect.size.height, rect.size.width, -rect.size.height / 2, -rect.size.width / 2, rect.size.height, rect.size.width);
165     return nCanvas;
166 };
167 
168 
169 /**
170  * a Values object for transform
171  * @Class
172  * @Construct
173  * @param {cc.Point} pos position x and y
174  * @param {cc.Point} scale scale x and y
175  * @param {Number} rotation
176  * @param {cc.Point} skew skew x and y
177  * @param {cc.Point} ap anchor point in pixels
178  * @param {Boolean} visible
179  */
180 cc.TransformValues = function (pos, scale, rotation, skew, ap, visible) {
181     this.pos = pos;		// position x and y
182     this.scale = scale;		// scale x and y
183     this.rotation = rotation;
184     this.skew = skew;		// skew x and y
185     this.ap = ap;			// anchor point in pixels
186     this.visible = visible;
187 };
188 
189 cc.RENDER_IN_SUBPIXEL = function (A) {
190     if (cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) {
191         return A;
192     } else {
193         return parseInt(A);
194     }
195 };
196 
197 /**
198  * <p>cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) <br/>
199  *
200  * cc.Sprite can be created with an image, or with a sub-rectangle of an image.  <br/>
201  *
202  * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid   <br/>
203  *    - Features when the parent is a cc.BatchNode: <br/>
204  *        - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch.  <br/>
205  *
206  *    - Limitations   <br/>
207  *        - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)  <br/>
208  *        - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) <br/>
209  *        - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.  <br/>
210  *        - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. <br/>
211  *        - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.        <br/>
212  *
213  *  If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node:      <br/>
214  *    - It supports blending functions    <br/>
215  *    - It supports aliasing / antialiasing    <br/>
216  *    - But the rendering will be slower: 1 draw per children.   <br/>
217  *
218  * The default anchorPoint in cc.Sprite is (0.5, 0.5). </p>
219  * @class
220  * @extends cc.Node
221  *
222  * @example
223  * var aSprite = new cc.Sprite();
224  * aSprite.initWithFile("HelloHTML5World.png",cc.rect(0,0,480,320));
225  */
226 cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{
227     RGBAProtocol:true,
228     //
229     // Data used when the sprite is rendered using a CCSpriteSheet
230     //
231     _textureAtlas:null,
232     _atlasIndex:0,
233     _batchNode:null,
234     _dirty:null, // Sprite needs to be updated
235     _recursiveDirty:null,
236     _hasChildren:null,
237     _shouldBeHidden:false, //should not be drawn because one of the ancestors is not visible
238     _transformToBatch:null,
239 
240     //
241     // Data used when the sprite is self-rendered
242     //
243     _blendFunc:{src:cc.BLEND_SRC, dst:cc.BLEND_DST},
244     _texture:null,
245     _originalTexture:null,
246     _color:null,
247     //
248     // Shared data
249     //
250     // texture
251     _rect:cc.rect(0, 0, 0, 0),
252     _rectRotated:null,
253 
254     // Offset Position (used by Zwoptex)
255     _offsetPosition:cc.p(0, 0), // absolute
256     _unflippedOffsetPositionFromCenter:cc.PointZero(),
257 
258     // vertex coords, texture coords and color info
259     _quad:cc.V3F_C4B_T2F_QuadZero(),
260 
261     // opacity and RGB protocol
262     colorUnmodified:null,
263     _opacityModifyRGB:null,
264 
265     // image is flipped
266     _flipX:null,
267     _flipY:null,
268 
269     _opacity:255,
270 
271     /**
272      * Constructor
273      * @param {String|cc.SpriteFrame|cc.SpriteBatchNode|HTMLImageElement|cc.Texture2D} fileName sprite construct parameter
274      */
275     ctor:function (fileName) {
276         this._super();
277         this._shouldBeHidden = false;
278         this._offsetPosition = cc.p(0, 0);
279         this._unflippedOffsetPositionFromCenter = cc.p(0, 0);
280         this._color = cc.white();
281 
282         if (fileName) {
283             if (typeof(fileName) == "string") {
284                 var frame = cc.SpriteFrameCache.getInstance().getSpriteFrame(fileName);
285                 this.initWithSpriteFrame(frame);
286             } else if (typeof(fileName) == "object") {
287                 if (fileName instanceof cc.SpriteFrame) {
288                     this.initWithSpriteFrame(fileName);
289                 } else if (fileName instanceof cc.SpriteBatchNode) {
290                     if (arguments.length > 1) {
291                         var rect = arguments[1];
292                         if (rect instanceof cc.Rect) {
293                             this.initWithBatchNode(fileName, rect);
294                         }
295                     }
296                 } else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) {
297                     this.initWithTexture(fileName)
298                 } else if (fileName instanceof cc.Texture2D) {
299                     this.initWithTexture(fileName)
300                 }
301             }
302         }
303     },
304 
305     /**
306      * whether or not the Sprite needs to be updated in the Atlas
307      * @return {Boolean}
308      */
309     isDirty:function () {
310         return this._dirty;
311     },
312 
313     /**
314      * make the Sprite to be updated in the Atlas.
315      * @param {Boolean} bDirty
316      */
317     setDirty:function (bDirty) {
318         this._dirty = bDirty;
319     },
320 
321     /**
322      * get the quad (tex coords, vertex coords and color) information
323      * @return {cc.V3F_C4B_T2F_Quad}
324      */
325     getQuad:function () {
326         return this._quad;
327     },
328 
329     /**
330      * returns whether or not the texture rectangle is rotated
331      * @return {Boolean}
332      */
333     isTextureRectRotated:function () {
334         return this._rectRotated;
335     },
336 
337     /**
338      * Set the index used on the TextureAtlas.
339      * @return {Number}
340      */
341     getAtlasIndex:function () {
342         return this._atlasIndex;
343     },
344 
345     /**
346      * Set the index used on the TextureAtlas.
347      * @warning Don't modify this value unless you know what you are doing
348      * @param {Number} atlasIndex
349      */
350     setAtlasIndex:function (atlasIndex) {
351         this._atlasIndex = atlasIndex;
352     },
353 
354     /**
355      * returns the rect of the cc.Sprite in points
356      * @return {cc.Rect}
357      */
358     getTextureRect:function () {
359         return cc.rect(this._rect.origin.x, this._rect.origin.y, this._rect.size.width, this._rect.size.height);
360     },
361 
362     /**
363      * return the TextureAtlas of the cc.Sprite
364      * @param {Boolean} pobTextureAtlas
365      * @return {cc.TextureAtlas}
366      */
367     getTextureAtlas:function (pobTextureAtlas) {
368         return this._textureAtlas;
369     },
370 
371     /**
372      * set the TextureAtlas of the cc.Sprite
373      * @param {cc.TextureAtlas} textureAtlas
374      */
375     setTextureAtlas:function (textureAtlas) {
376         this._textureAtlas = textureAtlas;
377     },
378 
379     /**
380      * return the SpriteBatchNode of the cc.Sprite
381      * @return {cc.SpriteBatchNode}
382      */
383     getSpriteBatchNode:function () {
384         return this._batchNode;
385     },
386 
387     /**
388      * set the SpriteBatchNode of the cc.Sprite
389      * @param {cc.SpriteBatchNode} spriteBatchNode
390      */
391     setSpriteBatchNode:function (spriteBatchNode) {
392         this._batchNode = spriteBatchNode;
393     },
394 
395     /**
396      * Get offset position of the sprite. Calculated automatically by editors like Zwoptex.
397      * @return {cc.Point}
398      */
399     getOffsetPosition:function () {
400         return cc.p(this._offsetPosition.x, this._offsetPosition.y);
401     },
402 
403     /**
404      * conforms to cc.TextureProtocol protocol
405      * @return {cc.BlendFunc}
406      */
407     getBlendFunc:function () {
408         return this._blendFunc;
409     },
410 
411     _isLighterMode:false,
412     /**
413      * conforms to cc.TextureProtocol protocol
414      * @param {Number} src
415      * @param {Number} dst
416      */
417     setBlendFunc:function (src, dst) {
418         if (arguments.length == 1)
419             this._blendFunc = src;
420         else
421             this._blendFunc = {src:src, dst:dst};
422 
423         this._isLighterMode = (this._blendFunc && (this._blendFunc.src == gl.SRC_ALPHA) && (this._blendFunc.dst == gl.ONE));
424     },
425 
426     /**
427      * Initializes a sprite
428      * @return {Boolean}
429      */
430     init:function () {
431         this._super();
432 
433         this._dirty = this._recursiveDirty = false;
434 
435         this._opacityModifyRGB = true;
436         this._opacity = 255;
437         this._color = cc.white();
438         this._colorUnmodified = cc.white();
439 
440         this._blendFunc.src = cc.BLEND_SRC;
441         this._blendFunc.dst = cc.BLEND_DST;
442 
443         // update texture (calls _updateBlendFunc)
444         this.setTexture(null);
445 
446         this._flipX = this._flipY = false;
447 
448         // default transform anchor: center
449         this.setAnchorPoint(cc.p(0.5, 0.5));
450 
451         // zwoptex default values
452         this._offsetPosition = cc.PointZero();
453         this._hasChildren = false;
454 
455         // Atlas: Color
456         var tmpColor = new cc.Color4B(255, 255, 255, 255);
457         this._quad.bl.colors = tmpColor;
458         this._quad.br.colors = tmpColor;
459         this._quad.tl.colors = tmpColor;
460         this._quad.tr.colors = tmpColor;
461 
462 
463         // updated in "useSelfRender"
464         // Atlas: TexCoords
465         this.setTextureRect(cc.RectZero(), false, cc.SizeZero());
466 
467         return true;
468     },
469 
470     /**
471      * Initializes a sprite with a texture and a rect in texture
472      * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture
473      * @param {cc.Rect} rect
474      * @return {Boolean}
475      * @example
476      * var img =cc.TextureCache.getInstance().addImage("HelloHTML5World.png");
477      * var mySprite = new cc.Sprite();
478      * mySprite.initWithTexture(img,cc.rect(0,0,480,320));
479      */
480     initWithTexture:function (texture, rect, rotated) {
481         var argnum = arguments.length;
482         if (argnum == 0)
483             throw "Sprite.initWithTexture(): Argument must be non-nil ";
484 
485         rotated = rotated || false;
486 
487         this._batchNode = null;
488         //this.setShaderProgram(CCShaderCache::getInstance()->programForKey(kCCShader_PositionTextureColor));
489 
490         this._recursiveDirty = false;
491         this.setDirty(false);
492         this._opacityModifyRGB = true;
493         this._opacity = 255;
494         this._color = cc.white();
495         this._colorUnmodified = cc.white();
496 
497         this._blendFunc.src = cc.BLEND_SRC;
498         this._blendFunc.dst = cc.BLEND_DST;
499 
500         this._flipX = this._flipY = false;
501 
502         // default transform anchor: center
503         this.setAnchorPoint(cc.p(0.5, 0.5));
504 
505         // zwoptex default values
506         this._offsetPosition = cc.p(0, 0);
507         this._hasChildren = false;
508 
509         // Atlas: Color
510         var tmpColor = new cc.Color4B(255, 255, 255, 255);
511         this._quad.bl.colors = tmpColor;
512         this._quad.br.colors = tmpColor;
513         this._quad.tl.colors = tmpColor;
514         this._quad.tr.colors = tmpColor;
515 
516         if (!rect) {
517             rect = cc.rect(0, 0, 0, 0);
518             if (texture instanceof cc.Texture2D) {
519                 rect.size = texture.getContentSize();
520             } else if ((texture instanceof HTMLImageElement) || (texture instanceof HTMLCanvasElement))
521                 rect.size = cc.size(texture.width, texture.height);
522         }
523 
524         if (cc.renderContextType == cc.CANVAS) {
525             this._originalTexture = texture;
526         }
527 
528         this.setTexture(texture);
529         this.setTextureRect(rect, rotated, rect.size);
530 
531         // by default use "Self Render".
532         // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
533         this.setBatchNode(null);
534         return true;
535     },
536 
537     /**
538      * Initializes a sprite with a texture's filename and a rect in texture
539      * @param {String} filename
540      * @param {cc.Rect} rect
541      * @return {Boolean}
542      * @example
543      * var mySprite = new cc.Sprite();
544      * mySprite.initWithFile("HelloHTML5World.png",cc.rect(0,0,480,320));
545      */
546     initWithFile:function (filename, rect) {
547         cc.Assert(filename != null, "Sprite#initWithFile():Invalid filename for sprite");
548         var selfPointer = this;
549 
550         var texture = cc.TextureCache.getInstance().textureForKey(filename);
551         if (!texture) {
552             //texture = cc.TextureCache.getInstance().addImage(filename);
553             this._visible = false;
554             var loadImg = new Image();
555             loadImg.addEventListener("load", function () {
556                 if (!rect) {
557                     rect = cc.rect(0, 0, loadImg.width, loadImg.height);
558                 }
559                 selfPointer.initWithTexture(loadImg, rect);
560                 cc.TextureCache.getInstance().cacheImage(filename, loadImg);
561                 selfPointer._visible = true;
562             });
563             loadImg.addEventListener("error", function () {
564                 cc.log("load failure:" + filename);
565             });
566             loadImg.src = filename;
567             return true;
568         } else {
569             if (texture) {
570                 if (!rect) {
571                     rect = cc.rect(0, 0, 0, 0);
572                     if (texture instanceof cc.Texture2D)
573                         rect.size = texture.getContentSize();
574                     else if ((texture instanceof HTMLImageElement) || (texture instanceof HTMLCanvasElement))
575                         rect.size = cc.size(texture.width, texture.height);
576                 }
577                 return this.initWithTexture(texture, rect);
578             }
579         }
580         return false;
581     },
582 
583     /**
584      * Initializes a sprite with a sprite frame.
585      * @param {cc.SpriteFrame} spriteFrame
586      * @return {Boolean}
587      * @example
588      * var spriteFrame = cc.SpriteFrameCache.getInstance().getSpriteFrame("grossini_dance_01.png");
589      * var sprite = new cc.Sprite();
590      * sprite.initWithSpriteFrame(spriteFrame);
591      */
592     initWithSpriteFrame:function (spriteFrame) {
593         cc.Assert(spriteFrame != null, "");
594         var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect());
595         this.setDisplayFrame(spriteFrame);
596 
597         return ret;
598     },
599 
600     /**
601      * Initializes a sprite with a sprite frame name. <br/>
602      * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name.  <br/>
603      * If the cc.SpriteFrame doesn't exist it will raise an exception. <br/>
604      * @param {String} spriteFrameName
605      * @return {Boolean}
606      * @example
607      * var sprite = new cc.Sprite();
608      * sprite.initWithSpriteFrameName("grossini_dance_01.png");
609      */
610     initWithSpriteFrameName:function (spriteFrameName) {
611         cc.Assert(spriteFrameName != null, "");
612         var frame = cc.SpriteFrameCache.getInstance().getSpriteFrame(spriteFrameName);
613         return this.initWithSpriteFrame(frame);
614     },
615 
616     /**
617      * tell the sprite to use batch node render.
618      * @param {cc.SpriteBatchNode} batchNode
619      */
620     useBatchNode:function (batchNode) {
621         this._textureAtlas = batchNode.getTextureAtlas(); // weak ref
622         this._batchNode = batchNode;
623     },
624 
625     /**
626      * updates the texture rect of the CCSprite in points.
627      * @param {cc.Rect} rect a rect of texture
628      * @param {Boolean} rotated
629      * @param {cc.Size} untrimmedSize
630      */
631     setTextureRect:function (rect, rotated, untrimmedSize) {
632         this._rectRotated = rotated || false;
633         untrimmedSize = untrimmedSize || rect.size;
634 
635         this.setContentSize(untrimmedSize);
636         this.setVertexRect(rect);
637         this._setTextureCoords(rect);
638 
639         var relativeOffset = this._unflippedOffsetPositionFromCenter;
640 
641         /* WEBGL Code
642          if (this._flipX) {
643          //relativeOffset.x = -relativeOffset.x;
644          }
645          if (this._flipY) {
646          //relativeOffset.y = -relativeOffset.y;
647          }
648          */
649 
650         this._offsetPosition.x = relativeOffset.x + (this._contentSize.width - this._rect.size.width) / 2;
651         this._offsetPosition.y = relativeOffset.y + (this._contentSize.height - this._rect.size.height) / 2;
652 
653         // rendering using batch node
654         if (this._batchNode) {
655             // update dirty_, don't update recursiveDirty_
656             //this.setDirty(true);
657             this._dirty = true;
658         } else {
659             // self rendering
660 
661             // Atlas: Vertex
662             var x1 = 0 + this._offsetPosition.x;
663             var y1 = 0 + this._offsetPosition.y;
664             var x2 = x1 + this._rect.size.width;
665             var y2 = y1 + this._rect.size.height;
666 
667             // Don't update Z.
668             this._quad.bl.vertices = cc.vertex3(x1, y1, 0);
669             this._quad.br.vertices = cc.vertex3(x2, y1, 0);
670             this._quad.tl.vertices = cc.vertex3(x1, y2, 0);
671             this._quad.tr.vertices = cc.vertex3(x2, y2, 0);
672         }
673     },
674 
675     /**
676      * <p>
677      *    set the vertex rect.<br/>
678      *    It will be called internally by setTextureRect. Useful if you want to create 2x images from SD images in Retina Display.  <br/>
679      *    Do not call it manually. Use setTextureRect instead.  <br/>
680      *    (override this method to generate "double scale" sprites)
681      * </p>
682      * @param rect
683      */
684     setVertexRect:function (rect) {
685         this._rect = rect;
686     },
687 
688     _setTextureCoords:function (rect) {
689         if (cc.renderContextType == cc.WEBGL) {
690             rect = cc.RECT_POINTS_TO_PIXELS(rect);
691 
692             var tex = this._batchNode ? this._textureAtlas.getTexture() : this._texture;
693             if (!tex) {
694                 return;
695             }
696 
697             var atlasWidth = tex.getPixelsWide();
698             var atlasHeight = tex.getPixelsHigh();
699 
700             var left, right, top, bottom;
701 
702             if (this._rectRotated) {
703                 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
704                     left = (2 * rect.origin.x + 1) / (2 * atlasWidth);
705                     right = left + (rect.size.height * 2 - 2) / (2 * atlasWidth);
706                     top = (2 * rect.origin.y + 1) / (2 * atlasHeight);
707                     bottom = top + (rect.size.width * 2 - 2) / (2 * atlasHeight);
708                 } else {
709                     left = rect.origin.x / atlasWidth;
710                     right = (rect.origin.x + rect.size.height) / atlasWidth;
711                     top = rect.origin.y / atlasHeight;
712                     bottom = (rect.origin.y + rect.size.width) / atlasHeight;
713                 }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
714 
715 
716                 if (this._flipX) {
717                     cc.SWAP(top, bottom);
718                 }
719 
720                 if (this._flipY) {
721                     cc.SWAP(left, right);
722                 }
723 
724                 this._quad.bl.texCoords.u = left;
725                 this._quad.bl.texCoords.v = top;
726                 this._quad.br.texCoords.u = left;
727                 this._quad.br.texCoords.v = bottom;
728                 this._quad.tl.texCoords.u = right;
729                 this._quad.tl.texCoords.v = top;
730                 this._quad.tr.texCoords.u = right;
731                 this._quad.tr.texCoords.v = bottom;
732             } else {
733                 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
734                     left = (2 * rect.origin.x + 1) / (2 * atlasWidth);
735                     right = left + (rect.size.width * 2 - 2) / (2 * atlasWidth);
736                     top = (2 * rect.origin.y + 1) / (2 * atlasHeight);
737                     bottom = top + (rect.size.height * 2 - 2) / (2 * atlasHeight);
738                 }
739                 else {
740                     left = rect.origin.x / atlasWidth;
741                     right = (rect.origin.x + rect.size.width) / atlasWidth;
742                     top = rect.origin.y / atlasHeight;
743                     bottom = (rect.origin.y + rect.size.height) / atlasHeight;
744                 } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
745 
746                 if (this._flipX) {
747                     cc.SWAP(left, right);
748                 }
749 
750                 if (this._flipY) {
751                     cc.SWAP(top, bottom);
752                 }
753 
754                 this._quad.bl.texCoords.u = left;
755                 this._quad.bl.texCoords.v = bottom;
756                 this._quad.br.texCoords.u = right;
757                 this._quad.br.texCoords.v = bottom;
758                 this._quad.tl.texCoords.u = left;
759                 this._quad.tl.texCoords.v = top;
760                 this._quad.tr.texCoords.u = right;
761                 this._quad.tr.texCoords.v = top;
762             }
763         }
764     },
765 
766     // BatchNode methods
767     /**
768      * updates the quad according the the rotation, position, scale values.
769      */
770     updateTransform:function () {
771         cc.Assert(this._batchNode, "updateTransform is only valid when cc.Sprite is being rendered using an cc.SpriteBatchNode");
772 
773         // recaculate matrix only if it is dirty
774         if (this.isDirty()) {
775 
776             // If it is not visible, or one of its ancestors is not visible, then do nothing:
777             if (!this._visible || ( this._parent && this._parent != this._batchNode && this._parent._shouldBeHidden)) {
778                 this._quad.br.vertices = this._quad.tl.vertices = this._quad.tr.vertices = this._quad.bl.vertices = cc.vertex3(0, 0, 0);
779                 this._shouldBeHidden = true;
780             } else {
781                 this._shouldBeHidden = false;
782 
783                 if (!this._parent || this._parent == this._batchNode) {
784                     this._transformToBatch = this.nodeToParentTransform();
785                 } else {
786                     cc.Assert(this._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite");
787                     this._transformToBatch = cc.AffineTransformConcat(this.nodeToParentTransform(), this._parent._transformToBatch);
788                 }
789 
790                 //
791                 // calculate the Quad based on the Affine Matrix
792                 //
793                 var size = this._rect.size;
794 
795                 var x1 = this._offsetPosition.x;
796                 var y1 = this._offsetPosition.y;
797 
798                 var x2 = x1 + size.width;
799                 var y2 = y1 + size.height;
800                 var x = this._transformToBatch.tx;
801                 var y = this._transformToBatch.ty;
802 
803                 var cr = this._transformToBatch.a;
804                 var sr = this._transformToBatch.b;
805                 var cr2 = this._transformToBatch.d;
806                 var sr2 = -this._transformToBatch.c;
807                 var ax = x1 * cr - y1 * sr2 + x;
808                 var ay = x1 * sr + y1 * cr2 + y;
809 
810                 var bx = x2 * cr - y1 * sr2 + x;
811                 var by = x2 * sr + y1 * cr2 + y;
812 
813                 var cx = x2 * cr - y2 * sr2 + x;
814                 var cy = x2 * sr + y2 * cr2 + y;
815 
816                 var dx = x1 * cr - y2 * sr2 + x;
817                 var dy = x1 * sr + y2 * cr2 + y;
818 
819                 this._quad.bl.vertices = cc.vertex3(cc.RENDER_IN_SUBPIXEL(ax), cc.RENDER_IN_SUBPIXEL(ay), this._vertexZ);
820                 this._quad.br.vertices = cc.vertex3(cc.RENDER_IN_SUBPIXEL(bx), cc.RENDER_IN_SUBPIXEL(by), this._vertexZ);
821                 this._quad.tl.vertices = cc.vertex3(cc.RENDER_IN_SUBPIXEL(dx), cc.RENDER_IN_SUBPIXEL(dy), this._vertexZ);
822                 this._quad.tr.vertices = cc.vertex3(cc.RENDER_IN_SUBPIXEL(cx), cc.RENDER_IN_SUBPIXEL(cy), this._vertexZ);
823             }
824 
825             this._textureAtlas.updateQuad(this._quad, this._atlasIndex);
826             this._recursiveDirty = false;
827             this.setDirty(false);
828         }
829 
830         // recursively iterate over children
831         if (this._hasChildren) {
832             this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.updateTransform);
833         }
834 
835         if (cc.SPRITE_DEBUG_DRAW) {
836             // draw bounding box
837             var vertices = [
838                 cc.p(this._quad.bl.vertices.x, this._quad.bl.vertices.y),
839                 cc.p(this._quad.br.vertices.x, this._quad.br.vertices.y),
840                 cc.p(this._quad.tr.vertices.x, this._quad.tr.vertices.y),
841                 cc.p(this._quad.tl.vertices.x, this._quad.tl.vertices.y)
842             ];
843             cc.drawingUtil.drawPoly(vertices, 4, true);
844         }
845     },
846 
847     /**
848      * <p>Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation,<br/>
849      * this fuction return the 5 values in 1 single call <p/>
850      * @param {cc.TransformValues} tv
851      * @return {cc.TransformValues}
852      * @private
853      */
854     //TODO
855     _getTransformValues:function (tv) {
856         tv.pos = this._position;
857         tv.scale.x = this._scaleX;
858         tv.scale.y = this._scaleY;
859         tv.rotation = this._rotation;
860         tv.skew.x = this._skewX;
861         tv.skew.y = this._skewY;
862         tv.ap = this._anchorPointInPoints;
863         tv.visible = this._visible;
864         return tv;
865     },
866 
867     /**
868      * draw sprite to canvas
869      * @param {CanvasContext} ctx 2d context of canvas
870      */
871     draw:function (ctx) {
872         //draw for canvas
873         //cc.PROFILER_START_CATEGORY(kCCProfilerCategorySprite, "cc.Sprite - draw");
874         var context = ctx || cc.renderContext;
875 
876         if (this._isLighterMode)
877             context.globalCompositeOperation = 'lighter';
878 
879         context.globalAlpha = this._opacity / 255;
880         var mpX = 0, mpY = 0;
881         if (this._flipX) {
882             mpX = 0 | (this._contentSize.width / 2 - this._anchorPointInPoints.x);
883             context.translate(mpX, 0);
884             context.scale(-1, 1);
885         }
886         if (this._flipY) {
887             mpY = -(0 | (this._contentSize.height / 2 - this._anchorPointInPoints.y));
888             context.translate(0, mpY);
889             context.scale(1, -1);
890         }
891 
892         var posX = 0 | ( -this._anchorPointInPoints.x - mpX + this._offsetPosition.x);
893         var posY = 0 | ( -this._anchorPointInPoints.y + mpY + this._offsetPosition.y);
894 
895         if (this._texture) {
896             if (this._texture instanceof HTMLImageElement) {
897                 if ((this._contentSize.width == 0) && (this._contentSize.height == 0)) {
898                     this.setContentSize(cc.size(this._texture.width, this._texture.height));
899                     this._rect.size.width = this._texture.width;
900                     this._rect.size.height = this._texture.height;
901                     context.drawImage(this._texture, posX, -(posY + this._texture.height));
902                 } else {
903                     context.drawImage(this._texture,
904                         this._rect.origin.x, this._rect.origin.y,
905                         this._rect.size.width, this._rect.size.height,
906                         posX, -(posY + this._rect.size.height),
907                         this._rect.size.width, this._rect.size.height);
908                 }
909             } else {
910                 if ((this._contentSize.width == 0) && (this._contentSize.height == 0)) {
911                     this.setContentSize(cc.size(this._texture.width, this._texture.height));
912                     this._rect.size.width = this._texture.width;
913                     this._rect.size.height = this._texture.height;
914                     context.drawImage(this._texture, posX, -(posY + this._texture.height));
915                 } else {
916                     context.drawImage(this._texture,
917                         0, 0,
918                         this._rect.size.width, this._rect.size.height,
919                         posX, -(posY + this._rect.size.height),
920                         this._rect.size.width, this._rect.size.height);
921                 }
922             }
923         } else {
924             context.fillStyle = "rgba(" + this._color.r + "," + this._color.g + "," + this._color.b + ",1)";
925             context.fillRect(posX, posY, this._contentSize.width, this._contentSize.height);
926         }
927 
928         if (cc.SPRITE_DEBUG_DRAW == 1) {
929             // draw bounding box
930             context.strokeStyle = "rgba(0,255,0,1)";
931             var vertices1 = [cc.p(posX, posY), cc.p(posX + this._rect.size.width, posY), cc.p(posX + this._rect.size.width, posY + this._rect.size.height),
932                 cc.p(posX, posY + this._rect.size.height)];
933             cc.drawingUtil.drawPoly(vertices1, 4, true);
934         } else if (cc.SPRITE_DEBUG_DRAW == 2) {
935             // draw texture box
936             context.strokeStyle = "rgba(0,255,0,1)";
937             var drawSize = this._rect.size;
938             var offsetPix = this.getOffsetPosition();
939             var vertices2 = [cc.p(offsetPix.x, offsetPix.y), cc.p(offsetPix.x + drawSize.width, offsetPix.y),
940                 cc.p(offsetPix.x + drawSize.width, offsetPix.y + drawSize.height), cc.p(offsetPix.x, offsetPix.y + drawSize.height)];
941             cc.drawingUtil.drawPoly(vertices2, 4, true);
942         }
943 
944         //cc.INCREMENT_GL_DRAWS(1);
945         cc.g_NumberOfDraws++;
946 
947         //CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");
948     },
949 
950     _drawForWebGL:function (ctx) {
951         var context = ctx;
952         //cc.PROFILER_START_CATEGORY(kCCProfilerCategorySprite, "cc.Sprite - draw");
953         //TODO  WebGL Draw of sprite
954         cc.Assert(!this._batchNode, "If cc.Sprite is being rendered by cc.SpriteBatchNode, cc.Sprite#draw SHOULD NOT be called");
955 
956         cc.NODE_DRAW_SETUP(this);
957 
958         //ccGLBlendFunc( m_sBlendFunc.src, m_sBlendFunc.dst );
959 
960         if (this._texture) {
961             //ccGLBindTexture2D(this._texture.getName());
962         } else {
963             //ccGLBindTexture2D(0);
964         }
965 
966         //
967         // Attributes
968         //
969         //ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );
970 
971         //#define kQuadSize  sizeof(this._quad.bl)
972         var offset = this._quad;
973 
974         // vertex
975         //int diff = offsetof( ccV3F_C4B_T2F, vertices);
976         //glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
977 
978         // texCoods
979         //diff = offsetof( ccV3F_C4B_T2F, texCoords);
980         //glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
981 
982         // color
983         //diff = offsetof( ccV3F_C4B_T2F, colors);
984         //glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
985 
986         //glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
987 
988         //CHECK_GL_ERROR_DEBUG();
989 
990         if (cc.SPRITE_DEBUG_DRAW == 1) {
991             // draw bounding box
992             var verticesG1 = [
993                 cc.p(this._quad.tl.vertices.x, this._quad.tl.vertices.y),
994                 cc.p(this._quad.bl.vertices.x, this._quad.bl.vertices.y),
995                 cc.p(this._quad.br.vertices.x, this._quad.br.vertices.y),
996                 cc.p(this._quad.tr.vertices.x, this._quad.tr.vertices.y)
997             ];
998             cc.drawingUtil.drawPoly(verticesG1, 4, true);
999         }
1000         else if (cc.SPRITE_DEBUG_DRAW == 2) {
1001             // draw texture box
1002             var drawSizeG2 = this.getTextureRect().size;
1003             var offsetPixG2 = this.getOffsetPosition();
1004             var verticesG2 = [cc.p(offsetPixG2.x, offsetPixG2.y), cc.p(offsetPixG2.x + drawSizeG2.width, offsetPixG2.y),
1005                 cc.p(offsetPixG2.x + drawSizeG2.width, offsetPixG2.y + drawSizeG2.height), cc.p(offsetPixG2.x, offsetPixG2.y + drawSizeG2.height)];
1006             cc.drawingUtil.drawPoly(verticesG2, 4, true);
1007         } // CC_SPRITE_DEBUG_DRAW
1008 
1009         cc.g_NumberOfDraws++;
1010         //CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");
1011     },
1012 
1013     /**
1014      * Add child to sprite (override cc.Node )
1015      * @param {cc.Sprite} child
1016      * @param {Number} zOrder  child's zOrder
1017      * @param {String} tag child's tag
1018      * @override
1019      */
1020     addChild:function (child, zOrder, tag) {
1021         var argnum = arguments.length;
1022         switch (argnum) {
1023             case 1:
1024                 this._super(child);
1025                 break;
1026             case 2:
1027                 this._super(child, zOrder);
1028                 break;
1029             case 3:
1030                 cc.Assert(child != null, "Argument must be non-NULL");
1031                 if (cc.renderContextType == cc.WEBGL) {
1032                     //TODO
1033                     if (this._batchNode) {
1034                         cc.Assert((child instanceof cc.Sprite), "cc.Sprite only supports cc.Sprites as children when using cc.SpriteBatchNode");
1035                         cc.Assert(child.getTexture().getName() == this._textureAtlas.getTexture().getName(), "");
1036 
1037                         //put it in descendants array of batch node
1038                         this._batchNode.appendChild(child);
1039                         if (!this._reorderChildDirty) {
1040                             this._setReorderChildDirtyRecursively();
1041                         }
1042                     }
1043                 }
1044 
1045                 //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check
1046                 this._super(child, zOrder, tag);
1047                 this._hasChildren = true;
1048                 break;
1049             default:
1050                 throw "Sprite.addChild():Argument must be non-nil ";
1051                 break;
1052         }
1053     },
1054 
1055     sortAllChildren:function () {
1056         if (this._reorderChildDirty) {
1057             var j;
1058             var tempItem = null;
1059             for (var i = 1; i < this._children.length; i++) {
1060                 tempItem = this._children[i];
1061                 j = i - 1;
1062 
1063                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but orderOfArrival is smaller
1064                 while (j >= 0 && ( tempItem.getZOrder() < this._children[j].getZOrder() || ( tempItem.getZOrder() == this._children[j].getZOrder()
1065                     && tempItem.getOrderOfArrival() < this._children[j].getOrderOfArrival() ) )) {
1066                     this._children[j + 1] = this._children[j];
1067                     j = j - 1;
1068                 }
1069 
1070                 this._children[j + 1] = tempItem;
1071             }
1072 
1073             if (this._batchNode) {
1074                 this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.sortAllChildren);
1075             }
1076             this._reorderChildDirty = false;
1077         }
1078     },
1079 
1080     /**
1081      * Reorders a child according to a new z value.  (override cc.Node )
1082      * @param {cc.Node} child
1083      * @param {Number} zOrder
1084      * @override
1085      */
1086     reorderChild:function (child, zOrder) {
1087         cc.Assert(child != null, "child is null");
1088         cc.Assert(this._children.indexOf(child) > -1, "this child is not in children list");
1089 
1090         if (zOrder == child.getZOrder()) {
1091             return;
1092         }
1093 
1094         if (this._batchNode && this._reorderChildDirty) {
1095             this._setReorderChildDirtyRecursively();
1096             this._batchNode.reorderBatch(true);
1097         }
1098 
1099         this._super(child, zOrder);
1100     },
1101 
1102     /**
1103      * Removes a child from the sprite. (override cc.Node )
1104      * @param child
1105      * @param cleanup  whether or not cleanup all running actions
1106      * @override
1107      */
1108     removeChild:function (child, cleanup) {
1109         if (this._batchNode) {
1110             this._batchNode.removeSpriteFromAtlas(child);
1111         }
1112         this._super(child, cleanup);
1113     },
1114 
1115     /**
1116      * Removes all children from the container  (override cc.Node )
1117      * @param cleanup whether or not cleanup all running actions
1118      * @override
1119      */
1120     removeAllChildren:function (cleanup) {
1121         if (this._batchNode) {
1122             if (this._children != null) {
1123                 for (var i = 0; i < this._children.length; i++) {
1124                     if (this._children[i] instanceof cc.Sprite) {
1125                         this._batchNode.removeSpriteFromAtlas(this._children[i]);
1126                     }
1127                 }
1128             }
1129         }
1130 
1131         this._super(cleanup);
1132         this._hasChildren = false;
1133     },
1134 //
1135 // CCNode property overloads
1136 //
1137 
1138     /**
1139      * set Recursively is or isn't Dirty
1140      * used only when parent is CCSpriteBatchNode
1141      * @param {Boolean} value
1142      */
1143     setDirtyRecursively:function (value) {
1144         this._recursiveDirty = value;
1145         this.setDirty(value);
1146         // recursively set dirty
1147         if (this._children != null) {
1148             for (var i = 0; i < this._children.length; i++) {
1149                 if (this._children[i] instanceof cc.Sprite) {
1150                     this._children[i].setDirtyRecursively(true);
1151                 }
1152             }
1153         }
1154     },
1155 
1156     /**
1157      * HACK: optimization
1158      */
1159     SET_DIRTY_RECURSIVELY:function () {
1160         if (this._batchNode && !this._recursiveDirty) {
1161             this._recursiveDirty = true;
1162             //this.setDirty(true);
1163             this._dirty = true;
1164             if (this._hasChildren)
1165                 this.setDirtyRecursively(true);
1166         }
1167     },
1168 
1169     /**
1170      * position setter (override cc.Node )
1171      * @param {cc.Point} pos
1172      * @override
1173      */
1174     setPosition:function (pos) {
1175         if (arguments.length >= 2)
1176             cc.Node.prototype.setPosition.call(this, pos, arguments[1]);
1177         else
1178             cc.Node.prototype.setPosition.call(this, pos);
1179         this.SET_DIRTY_RECURSIVELY();
1180     },
1181 
1182     /**
1183      * Rotation setter (override cc.Node )
1184      * @param {Number} rotation
1185      * @override
1186      */
1187     setRotation:function (rotation) {
1188         cc.Node.prototype.setRotation.call(this, rotation);
1189         this.SET_DIRTY_RECURSIVELY();
1190     },
1191 
1192     /**
1193      * SkewX setter (override cc.Node )
1194      * @param {Number} sx SkewX value
1195      * @override
1196      */
1197     setSkewX:function (sx) {
1198         cc.Node.prototype.setSkewX.call(this, sx);
1199         this.SET_DIRTY_RECURSIVELY();
1200     },
1201 
1202     /**
1203      * SkewY setter (override cc.Node )
1204      * @param {Number} sy SkewY value
1205      * @override
1206      */
1207     setSkewY:function (sy) {
1208         cc.Node.prototype.setSkewY.call(this, sy);
1209         this.SET_DIRTY_RECURSIVELY();
1210     },
1211 
1212     /**
1213      * ScaleX setter (override cc.Node )
1214      * @param {Number} scaleX
1215      * @override
1216      */
1217     setScaleX:function (scaleX) {
1218         cc.Node.prototype.setScaleX.call(this, scaleX);
1219         this.SET_DIRTY_RECURSIVELY();
1220     },
1221 
1222     /**
1223      * ScaleY setter (override cc.Node )
1224      * @param {Number} scaleY
1225      * @override
1226      */
1227     setScaleY:function (scaleY) {
1228         cc.Node.prototype.setScaleY.call(this, scaleY);
1229         this.SET_DIRTY_RECURSIVELY();
1230     },
1231 
1232     /**
1233      * <p>The scale factor of the node. 1.0 is the default scale factor. <br/>
1234      * It modifies the X and Y scale at the same time. (override cc.Node ) <p/>
1235      * @param {Number} scale
1236      * @override
1237      */
1238     setScale:function (scale, scaleY) {
1239         cc.Node.prototype.setScale.call(this, scale, scaleY);
1240         this.SET_DIRTY_RECURSIVELY();
1241     },
1242 
1243     /**
1244      * VertexZ setter (override cc.Node )
1245      * @param {Number} vertexZ
1246      * @override
1247      */
1248     setVertexZ:function (vertexZ) {
1249         cc.Node.prototype.setVertexZ.call(this, vertexZ);
1250         this.SET_DIRTY_RECURSIVELY();
1251     },
1252 
1253     /**
1254      * AnchorPoint setter  (override cc.Node )
1255      * @param {cc.Point} anchor
1256      * @override
1257      */
1258     setAnchorPoint:function (anchor) {
1259         cc.Node.prototype.setAnchorPoint.call(this, anchor);
1260         this.SET_DIRTY_RECURSIVELY();
1261     },
1262 
1263     /**
1264      * visible setter  (override cc.Node )
1265      * @param {Boolean} visible
1266      * @override
1267      */
1268     setVisible:function (visible) {
1269         cc.Node.prototype.setVisible.call(this, visible);
1270         this.SET_DIRTY_RECURSIVELY();
1271     },
1272 
1273     /**
1274      * IsRelativeAnchorPoint setter  (override cc.Node )
1275      * @param {Boolean} relative
1276      * @override
1277      */
1278     ignoreAnchorPointForPosition:function (relative) {
1279         cc.Assert(!this._batchNode, "ignoreAnchorPointForPosition is invalid in cc.Sprite");
1280         this._super(relative);
1281     },
1282 
1283     /**
1284      * FlipX value setter  (override cc.Node )
1285      * @param {Boolean} flipX
1286      */
1287     setFlipX:function (flipX) {
1288         if (this._flipX != flipX) {
1289             //save dirty region when before change
1290             //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1291 
1292             this._flipX = flipX;
1293             this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
1294 
1295             //save dirty region when after changed
1296             //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1297             this.setNodeDirty();
1298         }
1299     },
1300 
1301     /**
1302      * FlipY value setter  (override cc.Node )
1303      * @param {Boolean} flipY
1304      */
1305     setFlipY:function (flipY) {
1306         if (this._flipY != flipY) {
1307             //save dirty region when before change
1308             //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1309 
1310             this._flipY = flipY;
1311             //this.setTextureRect(this._rect, this._rectRotated, this._contentSize);
1312 
1313             //save dirty region when after changed
1314             //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1315             this.setNodeDirty();
1316         }
1317     },
1318 
1319     /**
1320      * <p>whether or not the sprite is flipped horizontally.<br/>
1321      * It only flips the texture of the sprite, and not the texture of the sprite's children. <br/>
1322      * Also, flipping the texture doesn't alter the anchorPoint.<br/>
1323      * If you want to flip the anchorPoint too, and/or to flip the children too use:<br/>
1324      *      sprite->setScaleX(sprite->getScaleX() * -1);  <p/>
1325      * @return {Boolean}
1326      */
1327     isFlippedX:function () {
1328         return this._flipX;
1329     },
1330 
1331     /**
1332      * <p>whether or not the sprite is flipped vertically.<br/>
1333      * It only flips the texture of the sprite, and not the texture of the sprite's children.<br/>
1334      * Also, flipping the texture doesn't alter the anchorPoint.<br/>
1335      * If you want to flip the anchorPoint too, and/or to flip the children too use:<br/>
1336      *         sprite->setScaleY(sprite->getScaleY() * -1); <p/>
1337      * @return {Boolean}
1338      */
1339     isFlippedY:function () {
1340         return this._flipY;
1341     },
1342 
1343     //
1344     // RGBA protocol
1345     //
1346 
1347     /**
1348      * Update sprite's color
1349      */
1350     updateColor:function () {
1351         var color4 = new cc.Color4B(this._color.r, this._color.g, this._color.b, this._opacity);
1352 
1353         this._quad.bl.colors = color4;
1354         this._quad.br.colors = color4;
1355         this._quad.tl.colors = color4;
1356         this._quad.tr.colors = color4;
1357 
1358         // renders using Sprite Manager
1359         //TODO
1360         if (this._batchNode) {
1361             if (this._atlasIndex != cc.SPRITE_INDEX_NOT_INITIALIZED) {
1362                 this._textureAtlas.updateQuad(this._quad, this._atlasIndex)
1363             } else {
1364                 // no need to set it recursively
1365                 // update dirty_, don't update recursiveDirty_
1366                 //this.setDirty(true);
1367                 this._dirty = true;
1368             }
1369         }
1370         // self render
1371         // do nothing
1372     },
1373 
1374     /**
1375      * Return opacity of sprite
1376      * @return {Number}
1377      */
1378     getOpacity:function () {
1379         return this._opacity;
1380     },
1381 
1382     /**
1383      * opacity setter
1384      * @param {Number} opacity
1385      */
1386     setOpacity:function (opacity) {
1387         this._opacity = opacity;
1388 
1389         //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1390         this.setNodeDirty();
1391         //TODO in canvas
1392         return;
1393         // special opacity for premultiplied textures
1394         if (this._opacityModifyRGB) {
1395             this.setColor(this._colorUnmodified);
1396         }
1397 
1398         this.updateColor();
1399     },
1400 
1401     /**
1402      * Return color of sprite
1403      * @return {cc.Color3B}
1404      */
1405     getColor:function () {
1406         if (this._opacityModifyRGB) {
1407             return new cc.Color3B(this._colorUnmodified);
1408         }
1409         return new cc.Color3B(this._color);
1410     },
1411 
1412     /**
1413      * color setter
1414      * @param {cc.Color3B} color3
1415      */
1416     setColor:function (color3) {
1417         if ((this._color.r == color3.r) && (this._color.g == color3.g) && (this._color.b == color3.b)) {
1418             return;
1419         }
1420 
1421         this._color = this._colorUnmodified = new cc.Color3B(color3.r, color3.g, color3.b);
1422         this._changeTextureColor();
1423 
1424         /*
1425          if (this._opacityModifyRGB) {
1426          this._color.r = Math.round(color3.r * this._opacity / 255);
1427          this._color.g = Math.round(color3.g * this._opacity / 255);
1428          this._color.b = Math.round(color3.b * this._opacity / 255);
1429          }
1430          */
1431         this.updateColor();
1432         //save dirty region when after changed
1433         //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld());
1434 
1435         this.setNodeDirty();
1436     },
1437 
1438     _changeTextureColor:function(){
1439         if (this.getTexture()) {
1440             if (cc.renderContextType === cc.CANVAS) {
1441                 var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(this._originalTexture);
1442                 if (cacheTextureForColor) {
1443                     //generate color texture cache
1444                     if (this._texture instanceof HTMLCanvasElement && !this._rectRotated) {
1445                         cc.generateTintImage(this.getTexture(), cacheTextureForColor, this._color, this.getTextureRect(), this._texture);
1446                     } else {
1447                         var colorTexture = cc.generateTintImage(this.getTexture(), cacheTextureForColor, this._color, this.getTextureRect());
1448                         this.setTexture(colorTexture);
1449                     }
1450                 }
1451             }
1452         }
1453     },
1454 
1455     // RGBAProtocol
1456 
1457     /**
1458      * opacity: conforms to CCRGBAProtocol protocol
1459      * @param {Boolean} value
1460      */
1461     setOpacityModifyRGB:function (value) {
1462         var oldColor = this._color;
1463         this._opacityModifyRGB = value;
1464         this._color = oldColor;
1465     },
1466 
1467     /**
1468      * return IsOpacityModifyRGB value
1469      * @return {Boolean}
1470      */
1471     isOpacityModifyRGB:function () {
1472         return this._opacityModifyRGB;
1473     },
1474 
1475     // Frames
1476     /**
1477      * Sets a new display frame to the cc.Sprite.
1478      * @param {cc.SpriteFrame} newFrame
1479      */
1480     setDisplayFrame:function (newFrame) {
1481         this.setNodeDirty();
1482         this._unflippedOffsetPositionFromCenter = newFrame.getOffset();
1483         var pNewTexture = newFrame.getTexture();
1484         // update texture before updating texture rect
1485         if (pNewTexture != this._texture) {
1486             this.setTexture(pNewTexture);
1487         }
1488         // update rect
1489         this._rectRotated = newFrame.isRotated();
1490         if(this._rectRotated)
1491             this._originalTexture = pNewTexture;
1492 
1493         this.setTextureRect(newFrame.getRect(), this._rectRotated, newFrame.getOriginalSize());
1494 
1495         if(this._color.r !== 255 || this._color.g !== 255 || this._color.b !== 255)
1496             this._changeTextureColor();
1497     },
1498 
1499     // Animation
1500 
1501     /**
1502      * changes the display frame with animation name and index.<br/>
1503      * The animation name will be get from the CCAnimationCache
1504      * @param animationName
1505      * @param frameIndex
1506      */
1507     setDisplayFrameWithAnimationName:function (animationName, frameIndex) {
1508         cc.Assert(animationName, "cc.Sprite#setDisplayFrameWithAnimationName. animationName must not be null");
1509         var cache = cc.AnimationCache.getInstance().getAnimation(animationName);
1510         cc.Assert(cache, "cc.Sprite#setDisplayFrameWithAnimationName: Frame not found");
1511         var animFrame = cache.getFrames()[frameIndex];
1512         cc.Assert(animFrame, "cc.Sprite#setDisplayFrame. Invalid frame");
1513         this.setDisplayFrame(animFrame.getSpriteFrame());
1514     },
1515 
1516     /**
1517      * Returns whether or not a cc.SpriteFrame is being displayed
1518      * @param {cc.SpriteFrame} frame
1519      * @return {Boolean}
1520      */
1521     isFrameDisplayed:function (frame) {
1522         if (cc.renderContextType == cc.CANVAS) {
1523             if (frame.getTexture() != this._texture)
1524                 return false;
1525             return cc.Rect.CCRectEqualToRect(frame.getRect(), this._rect);
1526         } else {
1527             return (cc.Rect.CCRectEqualToRect(frame.getRect(), this._rect) && frame.getTexture().getName() == this._texture.getName()
1528                 && cc.Point.CCPointEqualToPoint(frame.getOffset(), this._unflippedOffsetPositionFromCenter));
1529         }
1530     },
1531 
1532     /**
1533      * Returns the current displayed frame.
1534      * @return {cc.SpriteFrame}
1535      */
1536     displayFrame:function () {
1537         if (cc.renderContextType == cc.CANVAS) {
1538             return cc.SpriteFrame._frameWithTextureForCanvas(this._texture,
1539                 cc.RECT_POINTS_TO_PIXELS(this._rect),
1540                 this._rectRotated,
1541                 this._unflippedOffsetPositionFromCenter,
1542                 cc.SIZE_POINTS_TO_PIXELS(this._contentSize));
1543         } else {
1544             return cc.SpriteFrame.createWithTexture(this._texture,
1545                 cc.RECT_POINTS_TO_PIXELS(this._rect),
1546                 this._rectRotated,
1547                 this._unflippedOffsetPositionFromCenter,
1548                 cc.SIZE_POINTS_TO_PIXELS(this._contentSize));
1549         }
1550     },
1551 
1552     getBatchNode:function () {
1553         return this._batchNode;
1554     },
1555 
1556     setBatchNode:function (spriteBatchNode) {
1557         this._batchNode = spriteBatchNode; // weak reference
1558 
1559         // self render
1560         if (!this._batchNode) {
1561             this._atlasIndex = cc.SPRITE_INDEX_NOT_INITIALIZED;
1562             this.setTextureAtlas(null);
1563             this._recursiveDirty = false;
1564             this.setDirty(false);
1565 
1566             var x1 = this._offsetPosition.x;
1567             var y1 = this._offsetPosition.y;
1568             var x2 = x1 + this._rect.size.width;
1569             var y2 = y1 + this._rect.size.height;
1570             this._quad.bl.vertices = cc.vertex3(x1, y1, 0);
1571             this._quad.br.vertices = cc.vertex3(x2, y1, 0);
1572             this._quad.tl.vertices = cc.vertex3(x1, y2, 0);
1573             this._quad.tr.vertices = cc.vertex3(x2, y2, 0);
1574 
1575         } else {
1576             // using batch
1577             this._transformToBatch = cc.AffineTransformIdentity();
1578             this.setTextureAtlas(this._batchNode.getTextureAtlas()); // weak ref
1579         }
1580     },
1581 
1582     // Texture protocol
1583     _updateBlendFunc:function () {
1584         if (cc.renderContextType == cc.WEBGL) {
1585             //TODO
1586             cc.Assert(!this._batchNode, "cc.Sprite: _updateBlendFunc doesn't work when the sprite is rendered using a cc.SpriteSheet");
1587             // it's possible to have an untextured sprite
1588             if (!this._texture || !this._texture.hasPremultipliedAlpha()) {
1589                 this._blendFunc.src = gl.SRC_ALPHA;
1590                 this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA;
1591                 this.setOpacityModifyRGB(false);
1592             } else {
1593                 this._blendFunc.src = cc.BLEND_SRC;
1594                 this._blendFunc.dst = cc.BLEND_DST;
1595                 this.setOpacityModifyRGB(true);
1596             }
1597         }
1598     },
1599 
1600     _setReorderChildDirtyRecursively:function () {
1601         //only set parents flag the first time
1602         if (!this._reorderChildDirty) {
1603             this._reorderChildDirty = true;
1604             var pNode = this._parent;
1605             while (pNode && pNode != this._batchNode) {
1606                 pNode._setReorderChildDirtyRecursively();
1607                 pNode = pNode.getParent();
1608             }
1609         }
1610     },
1611 
1612     // CCTextureProtocol
1613     /**
1614      * Texture of sprite setter
1615      * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture
1616      */
1617     setTexture:function (texture) {
1618         // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet
1619         cc.Assert(!texture || texture instanceof cc.Texture2D || texture instanceof HTMLImageElement
1620             || texture instanceof HTMLCanvasElement, "setTexture expects a CCTexture2D. Invalid argument");
1621 
1622         if (cc.renderContextType != cc.CANVAS) {
1623             //TODO
1624             cc.Assert(!this._batchNode, "cc.Sprite: Batched sprites should use the same texture as the batchnode");
1625 
1626             if (!this._batchNode && this._texture != texture) {
1627                 this._texture = texture;
1628                 this._updateBlendFunc();
1629             }
1630         } else {
1631             if (this._texture != texture) {
1632                 if (texture instanceof  HTMLImageElement) {
1633                     if(!this._rect || cc.rectEqualToRect(this._rect,cc.RectZero()))
1634                         this._rect = cc.rect(0, 0, texture.width, texture.height);
1635                     this._texture = texture;
1636                     this._originalTexture = texture;
1637                 } else {
1638                     this._texture = texture;
1639                     this._updateBlendFunc();
1640                 }
1641             }
1642         }
1643     },
1644 
1645     getTexture:function () {
1646         return this._texture;
1647     }
1648 });
1649 
1650 /**
1651  * Create a sprite with texture
1652  * @constructs
1653  * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture
1654  * @param {cc.Rect} rect rect of the texture
1655  * @param {cc.Point} offset offset of the texture
1656  * @return {cc.Sprite}
1657  * @example
1658  * //get an image
1659  * var img = cc.TextureCache.getInstance().addImage("HelloHTML5World.png");
1660  *
1661  * //create a sprite with texture
1662  * var sprite1 = cc.Sprite.createWithTexture(img);
1663  *
1664  * //create a sprite with texture and rect
1665  * var sprite2 = cc.Sprite.createWithTexture(img, cc.rect(0,0,480,320));
1666  *
1667  * //create a sprite with texture and rect and offset
1668  * var sprite3 = cc.Sprite.createWithTexture(img, cc.rect(0,0,480,320),cc.p(0,0));
1669  */
1670 cc.Sprite.createWithTexture = function (texture, rect, offset) {
1671     var argnum = arguments.length;
1672     var sprite = new cc.Sprite();
1673     switch (argnum) {
1674         case 1:
1675             /** Creates an sprite with a texture.
1676              The rect used will be the size of the texture.
1677              The offset will be (0,0).
1678              */
1679             if (sprite && sprite.initWithTexture(texture)) {
1680                 return sprite;
1681             }
1682             return null;
1683             break;
1684 
1685         case 2:
1686             /** Creates an sprite with a texture and a rect.
1687              The offset will be (0,0).
1688              */
1689             if (sprite && sprite.initWithTexture(texture, rect)) {
1690                 return sprite;
1691             }
1692             return null;
1693             break;
1694 
1695         case 3:
1696             /** Creates an sprite with a texture, a rect and offset. */
1697                 // not implement
1698             cc.Assert(0, "");
1699             return null;
1700             break;
1701 
1702         default:
1703             throw "Sprite.createWithTexture(): Argument must be non-nil ";
1704             break;
1705     }
1706 };
1707 
1708 /**
1709  * Create a sprite with filename and rect
1710  * @constructs
1711  * @param {String} fileName
1712  * @param {cc.Rect} rect
1713  * @return {cc.Sprite}
1714  * @example
1715  * //create a sprite with filename
1716  * var sprite1 = cc.Sprite.create("HelloHTML5World.png");
1717  *
1718  * //create a sprite with filename and rect
1719  * var sprite2 = cc.Sprite.create("HelloHTML5World.png",cc.rect(0,0,480,320));
1720  */
1721 cc.Sprite.create = function (fileName, rect) {
1722     var argnum = arguments.length;
1723     var sprite = new cc.Sprite();
1724     if (argnum === 0) {
1725         if (sprite.init())
1726             return sprite;
1727         return null;
1728     } else if (argnum < 2) {
1729         /** Creates an sprite with an image filename.
1730          The rect used will be the size of the image.
1731          The offset will be (0,0).
1732          */
1733         if (sprite && sprite.initWithFile(fileName)) {
1734             return sprite;
1735         }
1736         return null;
1737     } else {
1738         /** Creates an sprite with an CCBatchNode and a rect
1739          */
1740         if (sprite && sprite.initWithFile(fileName, rect)) {
1741             return sprite;
1742         }
1743         return null;
1744     }
1745 };
1746 
1747 /**
1748  * Creates a sprite with a sprite frame name
1749  * @param {String} spriteFrame name
1750  * @return {cc.Sprite}
1751  * @example
1752  *
1753  * //create a sprite with a sprite frame
1754  * var sprite = cc.Sprite.createWithSpriteFrameName('grossini_dance_01.png');
1755  */
1756 cc.Sprite.createWithSpriteFrameName = function (spriteFrameName) {
1757     var spriteFrame = null;
1758     if (typeof(spriteFrameName) == 'string') {
1759         spriteFrame = cc.SpriteFrameCache.getInstance().getSpriteFrame(spriteFrameName);
1760         if (!spriteFrame) {
1761             cc.log("Invalid spriteFrameName: " + spriteFrameName);
1762             return null;
1763         }
1764     } else {
1765         cc.log("Invalid argument. Expecting string.");
1766         return null;
1767     }
1768     var sprite = new cc.Sprite();
1769     if (sprite && sprite.initWithSpriteFrame(spriteFrame)) {
1770         return sprite;
1771     }
1772     return null;
1773 };
1774 
1775 /**
1776  * Creates a sprite with a sprite frame.
1777  * @param {cc.SpriteFrame} spriteFrame
1778  * @return {cc.Sprite}
1779  * @example
1780  * //get a sprite frame
1781  * var spriteFrame = cc.SpriteFrameCache.getInstance().getSpriteFrame("grossini_dance_01.png");
1782  *
1783  * //create a sprite with a sprite frame
1784  * var sprite = cc.Sprite.createWithSpriteFrameName(spriteFrame);
1785  */
1786 cc.Sprite.createWithSpriteFrame = function (spriteFrame) {
1787     var sprite = new cc.Sprite();
1788     if (sprite && sprite.initWithSpriteFrame(spriteFrame)) {
1789         return sprite;
1790     }
1791     return null;
1792 };
1793