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