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 	    cc.Node.prototype.setContentSize.call(this, size, height);
354         this._positionsAreDirty = true;
355     },
356 
357     visit: function (ctx) {
358         if (this._positionsAreDirty) {
359             this._updatePositions();
360             this._positionsAreDirty = false;
361         }
362         cc.NodeRGBA.prototype.visit.call(this, ctx);
363     },
364 
365     init: function () {
366         return this.initWithBatchNode(null, cc.RectZero(), false, cc.RectZero());
367     },
368 
369     initWithBatchNode: function (batchNode, rect, rotated, capInsets) {
370         if (capInsets === undefined) {
371             capInsets = rotated;
372             rotated = false;
373         }
374 
375         if (batchNode) {
376             this.updateWithBatchNode(batchNode, rect, rotated, capInsets);
377         }
378         this.setAnchorPoint(0.5, 0.5);
379         this._positionsAreDirty = true;
380         return true;
381     },
382 
383     /**
384      * Initializes a 9-slice sprite with a texture file, a delimitation zone and
385      * with the specified cap insets.
386      * Once the sprite is created, you can then call its "setContentSize:" method
387      * to resize the sprite will all it's 9-slice goodness intact.
388      * It respects the anchorPoint too.
389      *
390      * @param file The name of the texture file.
391      * @param rect The rectangle that describes the sub-part of the texture that
392      * is the whole image. If the shape is the whole texture, set this to the
393      * texture's full rect.
394      * @param capInsets The values to use for the cap insets.
395      */
396     initWithFile: function (file, rect, capInsets) {
397         if (file instanceof cc.Rect) {
398             file = arguments[1];
399             capInsets = arguments[0];
400             rect = cc.RectZero();
401         } else {
402             rect = rect || cc.RectZero();
403             capInsets = capInsets || cc.RectZero();
404         }
405 
406         if(!file)
407             throw "cc.Scale9Sprite.initWithFile(): file should be non-null";
408 
409         var texture = cc.TextureCache.getInstance().textureForKey(file);
410         if (!texture) {
411             texture = cc.TextureCache.getInstance().addImage(file);
412             var locLoaded = texture.isLoaded();
413             this._textureLoaded = locLoaded;
414             if(!locLoaded){
415                 texture.addLoadedEventListener(function(sender){
416                     // the texture is rotated on Canvas render mode, so isRotated always is false.
417                     var preferredSize = this._preferredSize;
418                     preferredSize = cc.size(preferredSize.width, preferredSize.height);
419                     var size  = sender.getContentSize();
420                     this.updateWithBatchNode(this._scale9Image, cc.rect(0,0,size.width,size.height), false, this._capInsets);
421                     this.setPreferredSize(preferredSize);
422                     this._positionsAreDirty = true;
423                     this._callLoadedEventCallbacks();
424                 }, this);
425             }
426         }
427         var batchnode = cc.SpriteBatchNode.create(file, 9);
428         return this.initWithBatchNode(batchnode, rect, false, capInsets);
429     },
430 
431     /**
432      * Initializes a 9-slice sprite with an sprite frame and with the specified
433      * cap insets.
434      * Once the sprite is created, you can then call its "setContentSize:" method
435      * to resize the sprite will all it's 9-slice goodness intract.
436      * It respects the anchorPoint too.
437      *
438      * @param spriteFrame The sprite frame object.
439      * @param capInsets The values to use for the cap insets.
440      */
441     initWithSpriteFrame: function (spriteFrame, capInsets) {
442         if(!spriteFrame || !spriteFrame.getTexture())
443             throw "cc.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null";
444 
445         capInsets = capInsets || cc.RectZero();
446         var locLoaded = spriteFrame.textureLoaded();
447         this._textureLoaded = locLoaded;
448         if(!locLoaded){
449             spriteFrame.addLoadedEventListener(function(sender){
450                 // the texture is rotated on Canvas render mode, so isRotated always is false.
451                 var preferredSize = this._preferredSize;
452                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
453                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc.Browser.supportWebGL ? sender.isRotated() : false, this._capInsets);
454                 this.setPreferredSize(preferredSize);
455                 this._positionsAreDirty = true;
456                 this._callLoadedEventCallbacks();
457             },this);
458         }
459         var batchNode = cc.SpriteBatchNode.createWithTexture(spriteFrame.getTexture(), 9);
460         // the texture is rotated on Canvas render mode, so isRotated always is false.
461         return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc.Browser.supportWebGL ? spriteFrame.isRotated() : false, capInsets);
462     },
463 
464     /**
465      * Initializes a 9-slice sprite with an sprite frame name and with the specified
466      * cap insets.
467      * Once the sprite is created, you can then call its "setContentSize:" method
468      * to resize the sprite will all it's 9-slice goodness intract.
469      * It respects the anchorPoint too.
470      *
471      * @param spriteFrameName The sprite frame name.
472      * @param capInsets The values to use for the cap insets.
473      */
474     initWithSpriteFrameName: function (spriteFrameName, capInsets) {
475         if(!spriteFrameName)
476             throw "cc.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null";
477         capInsets = capInsets || cc.RectZero();
478 
479         var frame = cc.SpriteFrameCache.getInstance().getSpriteFrame(spriteFrameName);
480         if (frame == null) {
481             cc.log("cc.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName");
482             return false;
483         }
484 
485         return this.initWithSpriteFrame(frame, capInsets);
486     },
487 
488     /**
489      * Creates and returns a new sprite object with the specified cap insets.
490      * You use this method to add cap insets to a sprite or to change the existing
491      * cap insets of a sprite. In both cases, you get back a new image and the
492      * original sprite remains untouched.
493      *
494      * @param capInsets The values to use for the cap insets.
495      */
496     resizableSpriteWithCapInsets: function (capInsets) {
497         var pReturn = new cc.Scale9Sprite();
498         if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) {
499             return pReturn;
500         }
501         return null;
502     },
503 
504     /** sets the premultipliedAlphaOpacity property.
505      If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
506      If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
507      Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
508      @since v0.8
509      */
510     setOpacityModifyRGB: function (value) {
511         if(!this._scale9Image){
512             return;
513         }
514         this._opacityModifyRGB = value;
515         var scaleChildren = this._scale9Image.getChildren();
516         if (scaleChildren) {
517             for (var i = 0, len = scaleChildren.length; i < len; i++)
518                 scaleChildren[i].setOpacityModifyRGB(value);
519         }
520     },
521 
522     /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
523      @since v0.8
524      */
525     isOpacityModifyRGB: function () {
526         return this._opacityModifyRGB;
527     },
528 
529     updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) {
530         var opacity = this.getOpacity();
531         var color = this.getColor();
532         var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height);
533 
534         // Release old sprites
535         this.removeAllChildren(true);
536 
537         if (this._scale9Image != batchNode){
538             this._scale9Image = batchNode;
539         }
540         var tmpTexture = batchNode.getTexture();
541         var locLoaded = tmpTexture.isLoaded();
542         this._textureLoaded = locLoaded;
543         if(!locLoaded){
544             tmpTexture.addLoadedEventListener(function(sender){
545                 this._positionsAreDirty = true;
546                 this._callLoadedEventCallbacks();
547             },this);
548             return;
549         }
550         var locScale9Image = this._scale9Image;
551         locScale9Image.removeAllChildren(true);
552 
553         //this._capInsets = capInsets;
554         var locCapInsets = this._capInsets;
555         locCapInsets.x = capInsets.x;
556         locCapInsets.y = capInsets.y;
557         locCapInsets.width = capInsets.width;
558         locCapInsets.height = capInsets.height;
559         this._spriteFrameRotated = rotated;
560 
561         var selTexture = locScale9Image.getTexture();
562 
563         // If there is no given rect
564         if (cc._rectEqualToZero(rect)) {
565             // Get the texture size as original
566             var textureSize = selTexture.getContentSize();
567             rect = cc.rect(0, 0, textureSize.width, textureSize.height);
568         }
569 
570         // Set the given rect's size as original size
571         this._spriteRect = rect;
572         var locSpriteRect = this._spriteRect;
573         locSpriteRect.x = rect.x;
574         locSpriteRect.y = rect.y;
575         locSpriteRect.width = rect.width;
576         locSpriteRect.height = rect.height;
577 
578         var rectSize = rect._size;
579         this._originalSize.width = rectSize.width;
580         this._originalSize.height = rectSize.height;
581 
582         var locPreferredSize = this._preferredSize;
583         if(locPreferredSize.width === 0 && locPreferredSize.height === 0){
584             locPreferredSize.width = rectSize.width;
585             locPreferredSize.height = rectSize.height;
586         }
587 
588         var locCapInsetsInternal = this._capInsetsInternal;
589         if(capInsets){
590             locCapInsetsInternal.x = capInsets.x;
591             locCapInsetsInternal.y = capInsets.y;
592             locCapInsetsInternal.width = capInsets.width;
593             locCapInsetsInternal.height = capInsets.height;
594         }
595         var w = rectSize.width;
596         var h = rectSize.height;
597 
598         // If there is no specified center region
599         if (cc._rectEqualToZero(locCapInsetsInternal)) {
600             // CCLog("... cap insets not specified : using default cap insets ...");
601             locCapInsetsInternal.x = w / 3;
602             locCapInsetsInternal.y = h / 3;
603             locCapInsetsInternal.width = w / 3;
604             locCapInsetsInternal.height = h / 3;
605         }
606 
607         var left_w = locCapInsetsInternal.x;
608         var center_w = locCapInsetsInternal.width;
609         var right_w = w - (left_w + center_w);
610 
611         var top_h = locCapInsetsInternal.y;
612         var center_h = locCapInsetsInternal.height;
613         var bottom_h = h - (top_h + center_h);
614 
615         // calculate rects
616         // ... top row
617         var x = 0.0;
618         var y = 0.0;
619 
620         // top left
621         var lefttopbounds = cc.rect(x, y, left_w, top_h);
622 
623         // top center
624         x += left_w;
625         var centertopbounds = cc.rect(x, y, center_w, top_h);
626 
627         // top right
628         x += center_w;
629         var righttopbounds = cc.rect(x, y, right_w, top_h);
630 
631         // ... center row
632         x = 0.0;
633         y = 0.0;
634 
635         y += top_h;
636         // center left
637         var leftcenterbounds = cc.rect(x, y, left_w, center_h);
638 
639         // center center
640         x += left_w;
641         var centerbounds = cc.rect(x, y, center_w, center_h);
642 
643         // center right
644         x += center_w;
645         var rightcenterbounds = cc.rect(x, y, right_w, center_h);
646 
647         // ... bottom row
648         x = 0.0;
649         y = 0.0;
650         y += top_h;
651         y += center_h;
652 
653         // bottom left
654         var leftbottombounds = cc.rect(x, y, left_w, bottom_h);
655 
656         // bottom center
657         x += left_w;
658         var centerbottombounds = cc.rect(x, y, center_w, bottom_h);
659 
660         // bottom right
661         x += center_w;
662         var rightbottombounds = cc.rect(x, y, right_w, bottom_h);
663 
664         var t = cc.AffineTransformMakeIdentity();
665         if (!rotated) {
666             // CCLog("!rotated");
667             t = cc.AffineTransformTranslate(t, rect.x, rect.y);
668 
669             cc._RectApplyAffineTransformIn(centerbounds, t);
670             cc._RectApplyAffineTransformIn(rightbottombounds, t);
671             cc._RectApplyAffineTransformIn(leftbottombounds, t);
672             cc._RectApplyAffineTransformIn(righttopbounds, t);
673             cc._RectApplyAffineTransformIn(lefttopbounds, t);
674             cc._RectApplyAffineTransformIn(rightcenterbounds, t);
675             cc._RectApplyAffineTransformIn(leftcenterbounds, t);
676             cc._RectApplyAffineTransformIn(centerbottombounds, t);
677             cc._RectApplyAffineTransformIn(centertopbounds, t);
678 
679             // Centre
680             this._centre = new cc.Sprite();
681             this._centre.initWithTexture(selTexture, centerbounds);
682             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
683 
684             // Top
685             this._top = new cc.Sprite();
686             this._top.initWithTexture(selTexture, centertopbounds);
687             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
688 
689             // Bottom
690             this._bottom = new cc.Sprite();
691             this._bottom.initWithTexture(selTexture, centerbottombounds);
692             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
693 
694             // Left
695             this._left = new cc.Sprite();
696             this._left.initWithTexture(selTexture, leftcenterbounds);
697             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
698 
699             // Right
700             this._right = new cc.Sprite();
701             this._right.initWithTexture(selTexture, rightcenterbounds);
702             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
703 
704             // Top left
705             this._topLeft = new cc.Sprite();
706             this._topLeft.initWithTexture(selTexture, lefttopbounds);
707             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
708 
709             // Top right
710             this._topRight = new cc.Sprite();
711             this._topRight.initWithTexture(selTexture, righttopbounds);
712             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
713 
714             // Bottom left
715             this._bottomLeft = new cc.Sprite();
716             this._bottomLeft.initWithTexture(selTexture, leftbottombounds);
717             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
718 
719             // Bottom right
720             this._bottomRight = new cc.Sprite();
721             this._bottomRight.initWithTexture(selTexture, rightbottombounds);
722             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
723         } else {
724             // set up transformation of coordinates
725             // to handle the case where the sprite is stored rotated
726             // in the spritesheet
727             // CCLog("rotated");
728             var rotatedcenterbounds = centerbounds;
729             var rotatedrightbottombounds = rightbottombounds;
730             var rotatedleftbottombounds = leftbottombounds;
731             var rotatedrighttopbounds = righttopbounds;
732             var rotatedlefttopbounds = lefttopbounds;
733             var rotatedrightcenterbounds = rightcenterbounds;
734             var rotatedleftcenterbounds = leftcenterbounds;
735             var rotatedcenterbottombounds = centerbottombounds;
736             var rotatedcentertopbounds = centertopbounds;
737 
738             t = cc.AffineTransformTranslate(t, rect.height + rect.x, rect.y);
739             t = cc.AffineTransformRotate(t, 1.57079633);
740 
741             centerbounds = cc.RectApplyAffineTransform(centerbounds, t);
742             rightbottombounds = cc.RectApplyAffineTransform(rightbottombounds, t);
743             leftbottombounds = cc.RectApplyAffineTransform(leftbottombounds, t);
744             righttopbounds = cc.RectApplyAffineTransform(righttopbounds, t);
745             lefttopbounds = cc.RectApplyAffineTransform(lefttopbounds, t);
746             rightcenterbounds = cc.RectApplyAffineTransform(rightcenterbounds, t);
747             leftcenterbounds = cc.RectApplyAffineTransform(leftcenterbounds, t);
748             centerbottombounds = cc.RectApplyAffineTransform(centerbottombounds, t);
749             centertopbounds = cc.RectApplyAffineTransform(centertopbounds, t);
750 
751             rotatedcenterbounds.x = centerbounds.x;
752             rotatedcenterbounds.y = centerbounds.y;
753 
754             rotatedrightbottombounds.x = rightbottombounds.x;
755             rotatedrightbottombounds.y = rightbottombounds.y;
756 
757             rotatedleftbottombounds.x = leftbottombounds.x;
758             rotatedleftbottombounds.y = leftbottombounds.y;
759 
760             rotatedrighttopbounds.x = righttopbounds.x;
761             rotatedrighttopbounds.y = righttopbounds.y;
762 
763             rotatedlefttopbounds.x = lefttopbounds.x;
764             rotatedlefttopbounds.y = lefttopbounds.y;
765 
766             rotatedrightcenterbounds.x = rightcenterbounds.x;
767             rotatedrightcenterbounds.y = rightcenterbounds.y;
768 
769             rotatedleftcenterbounds.x = leftcenterbounds.x;
770             rotatedleftcenterbounds.y = leftcenterbounds.y;
771 
772             rotatedcenterbottombounds.x = centerbottombounds.x;
773             rotatedcenterbottombounds.y = centerbottombounds.y;
774 
775             rotatedcentertopbounds.x = centertopbounds.x;
776             rotatedcentertopbounds.y = centertopbounds.y;
777 
778             // Centre
779             this._centre = new cc.Sprite();
780             this._centre.initWithTexture(selTexture, rotatedcenterbounds, true);
781             locScale9Image.addChild(this._centre, 0, cc.POSITIONS_CENTRE);
782 
783             // Top
784             this._top = new cc.Sprite();
785             this._top.initWithTexture(selTexture, rotatedcentertopbounds, true);
786             locScale9Image.addChild(this._top, 1, cc.POSITIONS_TOP);
787 
788             // Bottom
789             this._bottom = new cc.Sprite();
790             this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true);
791             locScale9Image.addChild(this._bottom, 1, cc.POSITIONS_BOTTOM);
792 
793             // Left
794             this._left = new cc.Sprite();
795             this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true);
796             locScale9Image.addChild(this._left, 1, cc.POSITIONS_LEFT);
797 
798             // Right
799             this._right = new cc.Sprite();
800             this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true);
801             locScale9Image.addChild(this._right, 1, cc.POSITIONS_RIGHT);
802 
803             // Top left
804             this._topLeft = new cc.Sprite();
805             this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true);
806             locScale9Image.addChild(this._topLeft, 2, cc.POSITIONS_TOPLEFT);
807 
808             // Top right
809             this._topRight = new cc.Sprite();
810             this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true);
811             locScale9Image.addChild(this._topRight, 2, cc.POSITIONS_TOPRIGHT);
812 
813             // Bottom left
814             this._bottomLeft = new cc.Sprite();
815             this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true);
816             locScale9Image.addChild(this._bottomLeft, 2, cc.POSITIONS_BOTTOMLEFT);
817 
818             // Bottom right
819             this._bottomRight = new cc.Sprite();
820             this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true);
821             locScale9Image.addChild(this._bottomRight, 2, cc.POSITIONS_BOTTOMRIGHT);
822         }
823 
824         this.setContentSize(rect._size);
825         this.addChild(locScale9Image);
826 
827         if (this._spritesGenerated) {
828             // Restore color and opacity
829             this.setOpacity(opacity);
830             if(color.r !== 255 || color.g !== 255 || color.b !== 255){
831                 this.setColor(color);
832             }
833         }
834         this._spritesGenerated = true;
835         return true;
836     },
837 
838     setSpriteFrame: function (spriteFrame) {
839         var batchNode = cc.SpriteBatchNode.createWithTexture(spriteFrame.getTexture(), 9);
840         // the texture is rotated on Canvas render mode, so isRotated always is false.
841         var locLoaded = spriteFrame.textureLoaded();
842         this._textureLoaded = locLoaded;
843         if(!locLoaded){
844             spriteFrame.addLoadedEventListener(function(sender){
845                 // the texture is rotated on Canvas render mode, so isRotated always is false.
846                 var preferredSize = this._preferredSize;
847                 preferredSize = cc.size(preferredSize.width, preferredSize.height);
848                 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc.Browser.supportWebGL ? sender.isRotated() : false, this._capInsets);
849                 this.setPreferredSize(preferredSize);
850                 this._positionsAreDirty = true;
851                 this._callLoadedEventCallbacks();
852             },this);
853         }
854         this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc.Browser.supportWebGL ? spriteFrame.isRotated() : false, cc.RectZero());
855 
856         // Reset insets
857         this._insetLeft = 0;
858         this._insetTop = 0;
859         this._insetRight = 0;
860         this._insetBottom = 0;
861     }
862 });
863 
864 /**
865  * Creates a 9-slice sprite with a texture file, a delimitation zone and
866  * with the specified cap insets.
867  *
868  * @see initWithFile:rect:centerRegion:
869  */
870 cc.Scale9Sprite.create = function (file, rect, capInsets) {
871     var pReturn;
872     if (arguments.length === 2) {
873         if (typeof(file) == "string") {
874             pReturn = new cc.Scale9Sprite();
875             if (pReturn && pReturn.initWithFile(file, rect)) {
876                 return pReturn;
877             }
878         } else if (file instanceof cc.Rect) {
879             pReturn = new cc.Scale9Sprite();
880             if (pReturn && pReturn.initWithFile(file, capInsets)) {
881                 return pReturn;
882             }
883         }
884     } else if (arguments.length === 3) {
885         pReturn = new cc.Scale9Sprite();
886         if (pReturn && pReturn.initWithFile(file, rect, capInsets)) {
887             return pReturn;
888         }
889     } else if (arguments.length === 1) {
890         pReturn = new cc.Scale9Sprite();
891         if (pReturn && pReturn.initWithFile(file)) {
892             return pReturn;
893         }
894     } else if (arguments.length === 0) {
895         pReturn = new cc.Scale9Sprite();
896         if (pReturn && pReturn.init()) {
897             return pReturn;
898         }
899     }
900     return null;
901 };
902 
903 cc.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) {
904     var pReturn = new cc.Scale9Sprite();
905     if (pReturn && pReturn.initWithSpriteFrame(spriteFrame, capInsets)) {
906         return pReturn;
907     }
908     return null;
909 };
910 
911 cc.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) {
912     if(!spriteFrameName)
913         throw "cc.Scale9Sprite.createWithSpriteFrameName(): spriteFrameName should be non-null";
914     var pReturn = new cc.Scale9Sprite();
915     if (pReturn && pReturn.initWithSpriteFrameName(spriteFrameName, capInsets))
916         return pReturn;
917     return null;
918 };
919