1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2012 Neofect. All rights reserved.
  4 
  5  http://www.cocos2d-x.org
  6 
  7  Permission is hereby granted, free of charge, to any person obtaining a copy
  8  of this software and associated documentation files (the "Software"), to deal
  9  in the Software without restriction, including without limitation the rights
 10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11  copies of the Software, and to permit persons to whom the Software is
 12  furnished to do so, subject to the following conditions:
 13 
 14  The above copyright notice and this permission notice shall be included in
 15  all copies or substantial portions of the Software.
 16 
 17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23  THE SOFTWARE.
 24 
 25  Created by Jung Sang-Taik on 2012-03-16
 26  ****************************************************************************/
 27 
 28 cc.POSITIONS_CENTRE = 0;
 29 cc.POSITIONS_TOP = 1;
 30 cc.POSITIONS_LEFT = 2;
 31 cc.POSITIONS_RIGHT = 3;
 32 cc.POSITIONS_BOTTOM = 4;
 33 cc.POSITIONS_TOPRIGHT = 5;
 34 cc.POSITIONS_TOPLEFT = 6;
 35 cc.POSITIONS_BOTTOMRIGHT = 7;
 36 cc.POSITIONS_BOTTOMLEFT = 8;
 37 
 38 /**
 39  * A 9-slice sprite for cocos2d.
 40  *
 41  * 9-slice scaling allows you to specify how scaling is applied
 42  * to specific areas of a sprite. With 9-slice scaling (3x3 grid),
 43  * you can ensure that the sprite does not become distorted when
 44  * scaled.
 45  *
 46  * @see http://yannickloriot.com/library/ios/cccontrolextension/Classes/CCScale9Sprite.html
 47  * @class
 48  * @extends cc.Sprite
 49  */
 50 cc.Scale9Sprite = cc.NodeRGBA.extend(/** @lends cc.Scale9Sprite# */{
 51     RGBAProtocol: true,
 52 
 53     _spriteRect: null,
 54     _capInsetsInternal: null,
 55     _positionsAreDirty: false,
 56 
 57     _scale9Image: null,
 58     _topLeft: null,
 59     _top: null,
 60     _topRight: null,
 61     _left: null,
 62     _centre: null,
 63     _right: null,
 64     _bottomLeft: null,
 65     _bottom: null,
 66     _bottomRight: null,
 67 
 68     _colorUnmodified: null,
 69     _opacityModifyRGB: false,
 70 
 71     _originalSize: null,
 72     _preferredSize: null,
 73     _opacity: 0,
 74     _color: null,
 75     _capInsets: null,
 76     _insetLeft: 0,
 77     _insetTop: 0,
 78     _insetRight: 0,
 79     _insetBottom: 0,
 80 
 81     _spritesGenerated: false,
 82     _spriteFrameRotated: false,
 83     _textureLoaded:false,
 84     _loadedEventListeners: null,
 85 
 86     /**
 87      * return  texture is loaded
 88      * @returns {boolean}
 89      */
 90     textureLoaded:function(){
 91         return this._textureLoaded;
 92     },
 93 
 94     /**
 95      * add texture loaded event listener
 96      * @param {Function} callback
 97      * @param {Object} target
 98      */
 99     addLoadedEventListener:function(callback, target){
100         this._loadedEventListeners.push({eventCallback:callback, eventTarget:target});
101     },
102 
103     _callLoadedEventCallbacks:function(){
104         this._textureLoaded = true;
105         var locListeners = this._loadedEventListeners;
106         for(var i = 0, len = locListeners.length;  i < len; i++){
107             var selCallback = locListeners[i];
108             selCallback.eventCallback.call(selCallback.eventTarget, this);
109         }
110         locListeners.length = 0;
111     },
112 
113     _updateCapInset: function () {
114         var insets, locInsetLeft = this._insetLeft, locInsetTop = this._insetTop, locInsetRight = this._insetRight;
115         var locSpriteRect = this._spriteRect, locInsetBottom = this._insetBottom;
116         if (locInsetLeft === 0 && locInsetTop === 0 && locInsetRight === 0 && locInsetBottom === 0) {
117             insets = cc.RectZero();
118         } else {
119             insets = this._spriteFrameRotated ? cc.rect(locInsetBottom, locInsetLeft,
120                 locSpriteRect.width - locInsetRight - locInsetLeft,
121                 locSpriteRect.height - locInsetTop - locInsetBottom) :
122                 cc.rect(locInsetLeft, locInsetTop,
123                     locSpriteRect.width - locInsetLeft - locInsetRight,
124                     locSpriteRect.height - locInsetTop - locInsetBottom);
125         }
126         this.setCapInsets(insets);
127     },
128 
129     _updatePositions: function () {
130         // Check that instances are non-NULL
131         if (!((this._topLeft) && (this._topRight) && (this._bottomRight) &&
132             (this._bottomLeft) && (this._centre))) {
133             // if any of the above sprites are NULL, return
134             return;
135         }
136 
137         var size = this._contentSize;
138         var locTopLeft = this._topLeft, locTopRight = this._topRight, locBottomRight = this._bottomRight, locBottomLeft = this._bottomLeft;
139         var locCenter = this._centre, locCenterContentSize = this._centre.getContentSize();
140         var locTopLeftContentSize = locTopLeft.getContentSize();
141         var locBottomLeftContentSize = locBottomLeft.getContentSize();
142 
143         var sizableWidth = size._width - locTopLeftContentSize.width - locTopRight.getContentSize().width;
144         var sizableHeight = size._height - locTopLeftContentSize.height - locBottomRight.getContentSize().height;
145         var horizontalScale = sizableWidth / locCenterContentSize.width;
146         var verticalScale = sizableHeight / locCenterContentSize.height;
147         var rescaledWidth = locCenterContentSize.width * horizontalScale;
148         var rescaledHeight = locCenterContentSize.height * verticalScale;
149 
150         var leftWidth = locBottomLeftContentSize.width;
151         var bottomHeight = locBottomLeftContentSize.height;
152 
153         if(!cc.Browser.supportWebGL) {
154             //browser is in canvas mode, need to manually control rounding to prevent overlapping pixels
155             var roundedRescaledWidth = Math.round(rescaledWidth);
156             if(rescaledWidth != roundedRescaledWidth) {
157                 rescaledWidth = roundedRescaledWidth;
158                 horizontalScale = rescaledWidth/locCenterContentSize.width;
159             }
160             var roundedRescaledHeight = Math.round(rescaledHeight);
161             if(rescaledHeight != roundedRescaledHeight) {
162                 rescaledHeight = roundedRescaledHeight;
163                 verticalScale = rescaledHeight/locCenterContentSize.height;
164             }
165         }
166         locCenter.setScaleX(horizontalScale);
167         locCenter.setScaleY(verticalScale);
168 
169         var locLeft = this._left, locRight = this._right, locTop = this._top, locBottom = this._bottom;
170         var tempAP = cc.p(0, 0);
171         locBottomLeft.setAnchorPoint(tempAP);
172         locBottomRight.setAnchorPoint(tempAP);
173         locTopLeft.setAnchorPoint(tempAP);
174         locTopRight.setAnchorPoint(tempAP);
175         locLeft.setAnchorPoint(tempAP);
176         locRight.setAnchorPoint(tempAP);
177         locTop.setAnchorPoint(tempAP);
178         locBottom.setAnchorPoint(tempAP);
179         locCenter.setAnchorPoint(tempAP);
180 
181         // Position corners
182         locBottomLeft.setPosition(0, 0);
183         locBottomRight.setPosition(leftWidth + rescaledWidth, 0);
184         locTopLeft.setPosition(0, bottomHeight + rescaledHeight);
185         locTopRight.setPosition(leftWidth + rescaledWidth, bottomHeight + rescaledHeight);
186 
187         // Scale and position borders
188         locLeft.setPosition(0, bottomHeight);
189         locLeft.setScaleY(verticalScale);
190         locRight.setPosition(leftWidth + rescaledWidth, bottomHeight);
191         locRight.setScaleY(verticalScale);
192         locBottom.setPosition(leftWidth, 0);
193         locBottom.setScaleX(horizontalScale);
194         locTop.setPosition(leftWidth, bottomHeight + rescaledHeight);
195         locTop.setScaleX(horizontalScale);
196 
197         // Position centre
198         locCenter.setPosition(leftWidth, bottomHeight);
199     },
200 
201     ctor: function () {
202         cc.NodeRGBA.prototype.ctor.call(this);
203         this._spriteRect = cc.RectZero();
204         this._capInsetsInternal = cc.RectZero();
205 
206         this._colorUnmodified = cc.white();
207         this._originalSize = new cc.Size(0, 0);
208         this._preferredSize = new cc.Size(0, 0);
209         this._color = cc.white();
210         this._opacity = 255;
211         this._capInsets = cc.RectZero();
212         this._loadedEventListeners = [];
213     },
214 
215     /** Original sprite's size. */
216     getOriginalSize: function () {
217         return this._originalSize;
218     },
219 
220     //if the preferredSize component is given as -1, it is ignored
221     getPreferredSize: function () {
222         return this._preferredSize;
223     },
224     setPreferredSize: function (preferredSize) {
225         this.setContentSize(preferredSize);
226         this._preferredSize = preferredSize;
227     },
228 
229     /** Opacity: conforms to CCRGBAProtocol protocol */
230     getOpacity: function () {
231         return this._opacity;
232     },
233     setOpacity: function (opacity) {
234         if(!this._scale9Image){
235             return;
236         }
237         this._opacity = opacity;
238         var scaleChildren = this._scale9Image.getChildren();
239         for (var i = 0; i < scaleChildren.length; i++) {
240             var selChild = scaleChildren[i];
241             if (selChild && selChild.RGBAProtocol)
242                 selChild.setOpacity(opacity);
243         }
244     },
245 
246     /** Color: conforms to CCRGBAProtocol protocol */
247     getColor: function () {
248         return this._color;
249     },
250     setColor: function (color) {
251         if(!this._scale9Image){
252             return;
253         }
254         this._color = color;
255         var scaleChildren = this._scale9Image.getChildren();
256         for (var i = 0; i < scaleChildren.length; i++) {
257             var selChild = scaleChildren[i];
258             if (selChild && selChild.RGBAProtocol)
259                 selChild.setColor(color);
260         }
261     },
262 
263     getCapInsets: function () {
264         return this._capInsets;
265     },
266 
267     setCapInsets: function (capInsets) {
268         if(!this._scale9Image){
269             return;
270         }
271         //backup the contentSize
272         var contentSize = this._contentSize;
273         var tempWidth = contentSize._width, tempHeight = contentSize._height;
274 
275         this.updateWithBatchNode(this._scale9Image, this._spriteRect, this._spriteFrameRotated, capInsets);
276         //restore the contentSize
277         this.setContentSize(tempWidth, tempHeight);
278     },
279 
280     /**
281      * Gets the left side inset
282      * @returns {number}
283      */
284     getInsetLeft: function () {
285         return this._insetLeft;
286     },
287 
288     /**
289      * Sets the left side inset
290      * @param {Number} insetLeft
291      */
292     setInsetLeft: function (insetLeft) {
293         this._insetLeft = insetLeft;
294         this._updateCapInset();
295     },
296 
297     /**
298      * Gets the top side inset
299      * @returns {number}
300      */
301     getInsetTop: function () {
302         return this._insetTop;
303     },
304 
305     /**
306      * Sets the top side inset
307      * @param {Number} insetTop
308      */
309     setInsetTop: function (insetTop) {
310         this._insetTop = insetTop;
311         this._updateCapInset();
312     },
313 
314     /**
315      * Gets the right side inset
316      * @returns {number}
317      */
318     getInsetRight: function () {
319         return this._insetRight;
320     },
321     /**
322      * Sets the right side inset
323      * @param {Number} insetRight
324      */
325     setInsetRight: function (insetRight) {
326         this._insetRight = insetRight;
327         this._updateCapInset();
328     },
329 
330     /**
331      * Gets the bottom side inset
332      * @returns {number}
333      */
334     getInsetBottom: function () {
335         return this._insetBottom;
336     },
337     /**
338      * Sets the bottom side inset
339      * @param {number} insetBottom
340      */
341     setInsetBottom: function (insetBottom) {
342         this._insetBottom = insetBottom;
343         this._updateCapInset();
344     },
345 
346     /**
347      * Sets the untransformed size of the Scale9Sprite.
348      * @override
349      * @param {cc.Size|Number} size The untransformed size of the Scale9Sprite or The untransformed size's width of the Scale9Sprite.
350      * @param {Number} [height] The untransformed size's height of the Scale9Sprite.
351      */
352     setContentSize: function (size, height) {
353         if(arguments.length === 2)
354             cc.Node.prototype.setContentSize.call(this, size, height);
355         else
356             cc.Node.prototype.setContentSize.call(this, size);
357         this._positionsAreDirty = true;
358     },
359 
360     visit: function (ctx) {
361         if (this._positionsAreDirty) {
362             this._updatePositions();
363             this._positionsAreDirty = false;
364         }
365         cc.NodeRGBA.prototype.visit.call(this, ctx);
366     },
367 
368     init: function () {
369         return this.initWithBatchNode(null, cc.RectZero(), false, cc.RectZero());
370     },
371 
372     initWithBatchNode: function (batchNode, rect, rotated, capInsets) {
373         if (arguments.length === 3) {
374             capInsets = rotated;
375             rotated = false;
376         }
377 
378         if (batchNode) {
379             this.updateWithBatchNode(batchNode, rect, rotated, capInsets);
380         }
381         this.setAnchorPoint(0.5, 0.5);
382         this._positionsAreDirty = true;
383         return true;
384     },
385 
386     /**
387      * Initializes a 9-slice sprite with a texture file, a delimitation zone and
388      * with the specified cap insets.
389      * Once the sprite is created, you can then call its "setContentSize:" method
390      * to resize the sprite will all it's 9-slice goodness intact.
391      * It respects the anchorPoint too.
392      *
393      * @param file The name of the texture file.
394      * @param rect The rectangle that describes the sub-part of the texture that
395      * is the whole image. If the shape is the whole texture, set this to the
396      * texture's full rect.
397      * @param capInsets The values to use for the cap insets.
398      */
399     initWithFile: function (file, rect, capInsets) {
400         if (file instanceof cc.Rect) {
401             file = arguments[1];
402             capInsets = arguments[0];
403             rect = cc.RectZero();
404         } else {
405             rect = rect || cc.RectZero();
406             capInsets = capInsets || cc.RectZero();
407         }
408 
409         if(!file)
410             throw "cc.Scale9Sprite.initWithFile(): file should be non-null";
411 
412         var texture = cc.TextureCache.getInstance().textureForKey(file);
413         if (!texture) {
414             texture = cc.TextureCache.getInstance().addImage(file);
415             var locLoaded = texture.isLoaded();
416             this._textureLoaded = locLoaded;
417             if(!locLoaded){
418                 texture.addLoadedEventListener(function(sender){
419                     // the texture is rotated on Canvas render mode, so isRotated always is false.
420                     var preferredSize = this._preferredSize;
421                     preferredSize = cc.size(preferredSize.width, preferredSize.height);
422                     var size  = sender.getContentSize();
423                     this.updateWithBatchNode(this._scale9Image, cc.rect(0,0,size.width,size.height), false, this._capInsets);
424                     this.setPreferredSize(preferredSize);
425                     this._positionsAreDirty = true;
426                     this._callLoadedEventCallbacks();
427                 }, this);
428             }
429         }
430         var batchnode = cc.SpriteBatchNode.create(file, 9);
431         return this.initWithBatchNode(batchnode, rect, false, capInsets);
432     },
433 
434     /**
435      * Initializes a 9-slice sprite with an sprite frame and with the specified
436      * cap insets.
437      * Once the sprite is created, you can then call its "setContentSize:" method
438      * to resize the sprite will all it's 9-slice goodness intract.
439      * It respects the anchorPoint too.
440      *
441      * @param spriteFrame The sprite frame object.
442      * @param capInsets The values to use for the cap insets.
443      */
444     initWithSpriteFrame: function (spriteFrame, capInsets) {
445         if(!spriteFrame || !spriteFrame.getTexture())
446             throw "cc.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null";
447 
448         capInsets = capInsets || cc.RectZero();
449         var locLoaded = spriteFrame.textureLoaded();
450         this._textureLoaded = locLoaded;
451         if(!locLoaded){
452             spriteFrame.addLoadedEventListener(function(sender){
453                 // the texture is rotated on Canvas render mode, so isRotated always is false.
454                 var preferredSize = this._preferredSize;
455                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
456                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc.Browser.supportWebGL ? sender.isRotated() : false, this._capInsets);
457                 this.setPreferredSize(preferredSize);
458                 this._positionsAreDirty = true;
459                 this._callLoadedEventCallbacks();
460             },this);
461         }
462         var batchNode = cc.SpriteBatchNode.createWithTexture(spriteFrame.getTexture(), 9);
463         // the texture is rotated on Canvas render mode, so isRotated always is false.
464         return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc.Browser.supportWebGL ? spriteFrame.isRotated() : false, capInsets);
465     },
466 
467     /**
468      * Initializes a 9-slice sprite with an sprite frame name and with the specified
469      * cap insets.
470      * Once the sprite is created, you can then call its "setContentSize:" method
471      * to resize the sprite will all it's 9-slice goodness intract.
472      * It respects the anchorPoint too.
473      *
474      * @param spriteFrameName The sprite frame name.
475      * @param capInsets The values to use for the cap insets.
476      */
477     initWithSpriteFrameName: function (spriteFrameName, capInsets) {
478         if(!spriteFrameName)
479             throw "cc.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null";
480         capInsets = capInsets || cc.RectZero();
481 
482         var frame = cc.SpriteFrameCache.getInstance().getSpriteFrame(spriteFrameName);
483         if (frame == null) {
484             cc.log("cc.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName");
485             return false;
486         }
487 
488         return this.initWithSpriteFrame(frame, capInsets);
489     },
490 
491     /**
492      * Creates and returns a new sprite object with the specified cap insets.
493      * You use this method to add cap insets to a sprite or to change the existing
494      * cap insets of a sprite. In both cases, you get back a new image and the
495      * original sprite remains untouched.
496      *
497      * @param capInsets The values to use for the cap insets.
498      */
499     resizableSpriteWithCapInsets: function (capInsets) {
500         var pReturn = new cc.Scale9Sprite();
501         if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) {
502             return pReturn;
503         }
504         return null;
505     },
506 
507     /** sets the premultipliedAlphaOpacity property.
508      If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
509      If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
510      Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
511      @since v0.8
512      */
513     setOpacityModifyRGB: function (value) {
514         if(!this._scale9Image){
515             return;
516         }
517         this._opacityModifyRGB = value;
518         var scaleChildren = this._scale9Image.getChildren();
519         if (scaleChildren) {
520             for (var i = 0, len = scaleChildren.length; i < len; i++)
521                 scaleChildren[i].setOpacityModifyRGB(value);
522         }
523     },
524 
525     /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
526      @since v0.8
527      */
528     isOpacityModifyRGB: function () {
529         return this._opacityModifyRGB;
530     },
531 
532     updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) {
533         var opacity = this.getOpacity();
534         var color = this.getColor();
535         var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height);
536 
537         // Release old sprites
538         this.removeAllChildren(true);
539 
540         if (this._scale9Image != batchNode){
541             this._scale9Image = batchNode;
542         }
543         var tmpTexture = batchNode.getTexture();
544         var locLoaded = tmpTexture.isLoaded();
545         this._textureLoaded = locLoaded;
546         if(!locLoaded){
547             tmpTexture.addLoadedEventListener(function(sender){
548                 this._positionsAreDirty = true;
549                 this._callLoadedEventCallbacks();
550             },this);
551             return;
552         }
553         var locScale9Image = this._scale9Image;
554         locScale9Image.removeAllChildren(true);
555 
556         //this._capInsets = capInsets;
557         var locCapInsets = this._capInsets;
558         locCapInsets.x = capInsets.x;
559         locCapInsets.y = capInsets.y;
560         locCapInsets.width = capInsets.width;
561         locCapInsets.height = capInsets.height;
562         this._spriteFrameRotated = rotated;
563 
564         var selTexture = locScale9Image.getTexture();
565 
566         // If there is no given rect
567         if (cc._rectEqualToZero(rect)) {
568             // Get the texture size as original
569             var textureSize = selTexture.getContentSize();
570             rect = cc.rect(0, 0, textureSize.width, textureSize.height);
571         }
572 
573         // Set the given rect's size as original size
574         this._spriteRect = rect;
575         var locSpriteRect = this._spriteRect;
576         locSpriteRect.x = rect.x;
577         locSpriteRect.y = rect.y;
578         locSpriteRect.width = rect.width;
579         locSpriteRect.height = rect.height;
580 
581         var rectSize = rect._size;
582         this._originalSize.width = rectSize.width;
583         this._originalSize.height = rectSize.height;
584 
585         var locPreferredSize = this._preferredSize;
586         if(locPreferredSize.width === 0 && locPreferredSize.height === 0){
587             locPreferredSize.width = rectSize.width;
588             locPreferredSize.height = rectSize.height;
589         }
590 
591         var locCapInsetsInternal = this._capInsetsInternal;
592         if(capInsets){
593             locCapInsetsInternal.x = capInsets.x;
594             locCapInsetsInternal.y = capInsets.y;
595             locCapInsetsInternal.width = capInsets.width;
596             locCapInsetsInternal.height = capInsets.height;
597         }
598         var w = rectSize.width;
599         var h = rectSize.height;
600 
601         // If there is no specified center region
602         if (cc._rectEqualToZero(locCapInsetsInternal)) {
603             // CCLog("... cap insets not specified : using default cap insets ...");
604             locCapInsetsInternal.x = w / 3;
605             locCapInsetsInternal.y = h / 3;
606             locCapInsetsInternal.width = w / 3;
607             locCapInsetsInternal.height = h / 3;
608         }
609 
610         var left_w = locCapInsetsInternal.x;
611         var center_w = locCapInsetsInternal.width;
612         var right_w = w - (left_w + center_w);
613 
614         var top_h = locCapInsetsInternal.y;
615         var center_h = locCapInsetsInternal.height;
616         var bottom_h = h - (top_h + center_h);
617 
618         // calculate rects
619         // ... top row
620         var x = 0.0;
621         var y = 0.0;
622 
623         // top left
624         var lefttopbounds = cc.rect(x, y, left_w, top_h);
625 
626         // top center
627         x += left_w;
628         var centertopbounds = cc.rect(x, y, center_w, top_h);
629 
630         // top right
631         x += center_w;
632         var righttopbounds = cc.rect(x, y, right_w, top_h);
633 
634         // ... center row
635         x = 0.0;
636         y = 0.0;
637 
638         y += top_h;
639         // center left
640         var leftcenterbounds = cc.rect(x, y, left_w, center_h);
641 
642         // center center
643         x += left_w;
644         var centerbounds = cc.rect(x, y, center_w, center_h);
645 
646         // center right
647         x += center_w;
648         var rightcenterbounds = cc.rect(x, y, right_w, center_h);
649 
650         // ... bottom row
651         x = 0.0;
652         y = 0.0;
653         y += top_h;
654         y += center_h;
655 
656         // bottom left
657         var leftbottombounds = cc.rect(x, y, left_w, bottom_h);
658 
659         // bottom center
660         x += left_w;
661         var centerbottombounds = cc.rect(x, y, center_w, bottom_h);
662 
663         // bottom right
664         x += center_w;
665         var rightbottombounds = cc.rect(x, y, right_w, bottom_h);
666 
667         var t = cc.AffineTransformMakeIdentity();
668         if (!rotated) {
669             // CCLog("!rotated");
670             t = cc.AffineTransformTranslate(t, rect.x, rect.y);
671 
672             cc._RectApplyAffineTransformIn(centerbounds, t);
673             cc._RectApplyAffineTransformIn(rightbottombounds, t);
674             cc._RectApplyAffineTransformIn(leftbottombounds, t);
675             cc._RectApplyAffineTransformIn(righttopbounds, t);
676             cc._RectApplyAffineTransformIn(lefttopbounds, t);
677             cc._RectApplyAffineTransformIn(rightcenterbounds, t);
678             cc._RectApplyAffineTransformIn(leftcenterbounds, t);
679             cc._RectApplyAffineTransformIn(centerbottombounds, t);
680             cc._RectApplyAffineTransformIn(centertopbounds, t);
681 
682             // Centre
683             this._centre = new cc.Sprite();
684             this._centre.initWithTexture(selTexture, centerbounds);
685             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
686 
687             // Top
688             this._top = new cc.Sprite();
689             this._top.initWithTexture(selTexture, centertopbounds);
690             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
691 
692             // Bottom
693             this._bottom = new cc.Sprite();
694             this._bottom.initWithTexture(selTexture, centerbottombounds);
695             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
696 
697             // Left
698             this._left = new cc.Sprite();
699             this._left.initWithTexture(selTexture, leftcenterbounds);
700             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
701 
702             // Right
703             this._right = new cc.Sprite();
704             this._right.initWithTexture(selTexture, rightcenterbounds);
705             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
706 
707             // Top left
708             this._topLeft = new cc.Sprite();
709             this._topLeft.initWithTexture(selTexture, lefttopbounds);
710             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
711 
712             // Top right
713             this._topRight = new cc.Sprite();
714             this._topRight.initWithTexture(selTexture, righttopbounds);
715             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
716 
717             // Bottom left
718             this._bottomLeft = new cc.Sprite();
719             this._bottomLeft.initWithTexture(selTexture, leftbottombounds);
720             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
721 
722             // Bottom right
723             this._bottomRight = new cc.Sprite();
724             this._bottomRight.initWithTexture(selTexture, rightbottombounds);
725             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
726         } else {
727             // set up transformation of coordinates
728             // to handle the case where the sprite is stored rotated
729             // in the spritesheet
730             // CCLog("rotated");
731             var rotatedcenterbounds = centerbounds;
732             var rotatedrightbottombounds = rightbottombounds;
733             var rotatedleftbottombounds = leftbottombounds;
734             var rotatedrighttopbounds = righttopbounds;
735             var rotatedlefttopbounds = lefttopbounds;
736             var rotatedrightcenterbounds = rightcenterbounds;
737             var rotatedleftcenterbounds = leftcenterbounds;
738             var rotatedcenterbottombounds = centerbottombounds;
739             var rotatedcentertopbounds = centertopbounds;
740 
741             t = cc.AffineTransformTranslate(t, rect.height + rect.x, rect.y);
742             t = cc.AffineTransformRotate(t, 1.57079633);
743 
744             centerbounds = cc.RectApplyAffineTransform(centerbounds, t);
745             rightbottombounds = cc.RectApplyAffineTransform(rightbottombounds, t);
746             leftbottombounds = cc.RectApplyAffineTransform(leftbottombounds, t);
747             righttopbounds = cc.RectApplyAffineTransform(righttopbounds, t);
748             lefttopbounds = cc.RectApplyAffineTransform(lefttopbounds, t);
749             rightcenterbounds = cc.RectApplyAffineTransform(rightcenterbounds, t);
750             leftcenterbounds = cc.RectApplyAffineTransform(leftcenterbounds, t);
751             centerbottombounds = cc.RectApplyAffineTransform(centerbottombounds, t);
752             centertopbounds = cc.RectApplyAffineTransform(centertopbounds, t);
753 
754             rotatedcenterbounds.x = centerbounds.x;
755             rotatedcenterbounds.y = centerbounds.y;
756 
757             rotatedrightbottombounds.x = rightbottombounds.x;
758             rotatedrightbottombounds.y = rightbottombounds.y;
759 
760             rotatedleftbottombounds.x = leftbottombounds.x;
761             rotatedleftbottombounds.y = leftbottombounds.y;
762 
763             rotatedrighttopbounds.x = righttopbounds.x;
764             rotatedrighttopbounds.y = righttopbounds.y;
765 
766             rotatedlefttopbounds.x = lefttopbounds.x;
767             rotatedlefttopbounds.y = lefttopbounds.y;
768 
769             rotatedrightcenterbounds.x = rightcenterbounds.x;
770             rotatedrightcenterbounds.y = rightcenterbounds.y;
771 
772             rotatedleftcenterbounds.x = leftcenterbounds.x;
773             rotatedleftcenterbounds.y = leftcenterbounds.y;
774 
775             rotatedcenterbottombounds.x = centerbottombounds.x;
776             rotatedcenterbottombounds.y = centerbottombounds.y;
777 
778             rotatedcentertopbounds.x = centertopbounds.x;
779             rotatedcentertopbounds.y = centertopbounds.y;
780 
781             // Centre
782             this._centre = new cc.Sprite();
783             this._centre.initWithTexture(selTexture, rotatedcenterbounds, true);
784             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
785 
786             // Top
787             this._top = new cc.Sprite();
788             this._top.initWithTexture(selTexture, rotatedcentertopbounds, true);
789             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
790 
791             // Bottom
792             this._bottom = new cc.Sprite();
793             this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true);
794             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
795 
796             // Left
797             this._left = new cc.Sprite();
798             this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true);
799             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
800 
801             // Right
802             this._right = new cc.Sprite();
803             this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true);
804             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
805 
806             // Top left
807             this._topLeft = new cc.Sprite();
808             this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true);
809             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
810 
811             // Top right
812             this._topRight = new cc.Sprite();
813             this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true);
814             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
815 
816             // Bottom left
817             this._bottomLeft = new cc.Sprite();
818             this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true);
819             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
820 
821             // Bottom right
822             this._bottomRight = new cc.Sprite();
823             this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true);
824             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
825         }
826 
827         this.setContentSize(rect._size);
828         this.addChild(locScale9Image);
829 
830         if (this._spritesGenerated) {
831             // Restore color and opacity
832             this.setOpacity(opacity);
833             if(color.r !== 255 || color.g !== 255 || color.b !== 255){
834                 this.setColor(color);
835             }
836         }
837         this._spritesGenerated = true;
838         return true;
839     },
840 
841     setSpriteFrame: function (spriteFrame) {
842         var batchNode = cc.SpriteBatchNode.createWithTexture(spriteFrame.getTexture(), 9);
843         // the texture is rotated on Canvas render mode, so isRotated always is false.
844         var locLoaded = spriteFrame.textureLoaded();
845         this._textureLoaded = locLoaded;
846         if(!locLoaded){
847             spriteFrame.addLoadedEventListener(function(sender){
848                 // the texture is rotated on Canvas render mode, so isRotated always is false.
849                 var preferredSize = this._preferredSize;
850                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
851                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc.Browser.supportWebGL ? sender.isRotated() : false, this._capInsets);
852                 this.setPreferredSize(preferredSize);
853                 this._positionsAreDirty = true;
854                 this._callLoadedEventCallbacks();
855             },this);
856         }
857         this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc.Browser.supportWebGL ? spriteFrame.isRotated() : false, cc.RectZero());
858 
859         // Reset insets
860         this._insetLeft = 0;
861         this._insetTop = 0;
862         this._insetRight = 0;
863         this._insetBottom = 0;
864     }
865 });
866 
867 /**
868  * Creates a 9-slice sprite with a texture file, a delimitation zone and
869  * with the specified cap insets.
870  *
871  * @see initWithFile:rect:centerRegion:
872  */
873 cc.Scale9Sprite.create = function (file, rect, capInsets) {
874     var pReturn;
875     if (arguments.length === 2) {
876         if (typeof(file) == "string") {
877             pReturn = new cc.Scale9Sprite();
878             if (pReturn && pReturn.initWithFile(file, rect)) {
879                 return pReturn;
880             }
881         } else if (file instanceof cc.Rect) {
882             pReturn = new cc.Scale9Sprite();
883             if (pReturn && pReturn.initWithFile(file, capInsets)) {
884                 return pReturn;
885             }
886         }
887     } else if (arguments.length === 3) {
888         pReturn = new cc.Scale9Sprite();
889         if (pReturn && pReturn.initWithFile(file, rect, capInsets)) {
890             return pReturn;
891         }
892     } else if (arguments.length === 1) {
893         pReturn = new cc.Scale9Sprite();
894         if (pReturn && pReturn.initWithFile(file)) {
895             return pReturn;
896         }
897     } else if (arguments.length === 0) {
898         pReturn = new cc.Scale9Sprite();
899         if (pReturn && pReturn.init()) {
900             return pReturn;
901         }
902     }
903     return null;
904 };
905 
906 cc.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) {
907     var pReturn = new cc.Scale9Sprite();
908     if (pReturn && pReturn.initWithSpriteFrame(spriteFrame, capInsets)) {
909         return pReturn;
910     }
911     return null;
912 };
913 
914 cc.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) {
915     if(!spriteFrameName)
916         throw "cc.Scale9Sprite.createWithSpriteFrameName(): spriteFrameName should be non-null";
917     var pReturn = new cc.Scale9Sprite();
918     if (pReturn && pReturn.initWithSpriteFrameName(spriteFrameName, capInsets))
919         return pReturn;
920     return null;
921 };
922