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