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.NodeRGBA 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.NodeRGBA.extend(/** @lends cc.Scale9Sprite# */{ 50 RGBAProtocol: true, 51 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 _colorUnmodified: null, 68 _opacityModifyRGB: false, 69 70 _originalSize: null, 71 _preferredSize: null, 72 _opacity: 0, 73 _color: null, 74 _capInsets: null, 75 _insetLeft: 0, 76 _insetTop: 0, 77 _insetRight: 0, 78 _insetBottom: 0, 79 80 _spritesGenerated: false, 81 _spriteFrameRotated: false, 82 _textureLoaded:false, 83 _loadedEventListeners: null, 84 _className:"Scale9Sprite", 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.rect(0, 0, 0, 0); 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._renderType == cc._RENDER_TYPE_WEBGL){ 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.rect(0, 0, 0, 0); 204 this._capInsetsInternal = cc.rect(0, 0, 0, 0); 205 206 this._colorUnmodified = cc.color(255, 255, 255, 255); 207 this._originalSize = cc.size(0, 0); 208 this._preferredSize = cc.size(0, 0); 209 this._color = cc.color(255, 255, 255, 255); 210 this._opacity = 255; 211 this._capInsets = cc.rect(0, 0, 0, 0); 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 _getPreferredWidth: function () { 225 return this._preferredSize.width; 226 }, 227 _getPreferredHeight: function () { 228 return this._preferredSize.height; 229 }, 230 setPreferredSize: function (preferredSize) { 231 this.setContentSize(preferredSize); 232 this._preferredSize = preferredSize; 233 }, 234 _setPreferredWidth: function (value) { 235 this._setWidth(value); 236 this._preferredSize.width = value; 237 }, 238 _setPreferredHeight: function (value) { 239 this._setHeight(value); 240 this._preferredSize.height = value; 241 }, 242 243 /** Opacity: conforms to CCRGBAProtocol protocol */ 244 getOpacity: function () { 245 return this._opacity; 246 }, 247 setOpacity: function (opacity) { 248 if(!this._scale9Image){ 249 return; 250 } 251 this._opacity = opacity; 252 var scaleChildren = this._scale9Image.getChildren(); 253 for (var i = 0; i < scaleChildren.length; i++) { 254 var selChild = scaleChildren[i]; 255 if (selChild && selChild.RGBAProtocol) 256 selChild.setOpacity(opacity); 257 } 258 this._color.a = opacity; 259 }, 260 261 updateDisplayedOpacity: function(parentOpacity){ 262 cc.NodeRGBA.prototype.updateDisplayedOpacity.call(this, parentOpacity); 263 this.setOpacity(this._displayedOpacity); 264 }, 265 266 /** Color: conforms to CCRGBAProtocol protocol */ 267 getColor: function () { 268 var locColor = this._color; 269 return cc.color(locColor.r, locColor.g, locColor.b, locColor.a); 270 }, 271 setColor: function (color) { 272 if(!this._scale9Image){ 273 return; 274 } 275 var locColor = this._color; 276 locColor.r = color.r; 277 locColor.g = color.g; 278 locColor.b = color.b; 279 280 var scaleChildren = this._scale9Image.getChildren(); 281 for (var i = 0; i < scaleChildren.length; i++) { 282 var selChild = scaleChildren[i]; 283 if (selChild && selChild.RGBAProtocol) 284 selChild.setColor(color); 285 } 286 287 if (color.a !== undefined && !color.a_undefined) { 288 this.setOpacity(color.a); 289 } 290 }, 291 292 updateDisplayedColor: function(parentColor){ 293 cc.NodeRGBA.prototype.updateDisplayedColor.call(this, parentColor); 294 this.setColor(this._displayedColor); 295 }, 296 297 getCapInsets: function () { 298 return this._capInsets; 299 }, 300 301 setCapInsets: function (capInsets) { 302 if(!this._scale9Image){ 303 return; 304 } 305 //backup the contentSize 306 var contentSize = this._contentSize; 307 var tempWidth = contentSize.width, tempHeight = contentSize.height; 308 309 this.updateWithBatchNode(this._scale9Image, this._spriteRect, this._spriteFrameRotated, capInsets); 310 //restore the contentSize 311 this.setContentSize(tempWidth, tempHeight); 312 }, 313 314 /** 315 * Gets the left side inset 316 * @returns {number} 317 */ 318 getInsetLeft: function () { 319 return this._insetLeft; 320 }, 321 322 /** 323 * Sets the left side inset 324 * @param {Number} insetLeft 325 */ 326 setInsetLeft: function (insetLeft) { 327 this._insetLeft = insetLeft; 328 this._updateCapInset(); 329 }, 330 331 /** 332 * Gets the top side inset 333 * @returns {number} 334 */ 335 getInsetTop: function () { 336 return this._insetTop; 337 }, 338 339 /** 340 * Sets the top side inset 341 * @param {Number} insetTop 342 */ 343 setInsetTop: function (insetTop) { 344 this._insetTop = insetTop; 345 this._updateCapInset(); 346 }, 347 348 /** 349 * Gets the right side inset 350 * @returns {number} 351 */ 352 getInsetRight: function () { 353 return this._insetRight; 354 }, 355 /** 356 * Sets the right side inset 357 * @param {Number} insetRight 358 */ 359 setInsetRight: function (insetRight) { 360 this._insetRight = insetRight; 361 this._updateCapInset(); 362 }, 363 364 /** 365 * Gets the bottom side inset 366 * @returns {number} 367 */ 368 getInsetBottom: function () { 369 return this._insetBottom; 370 }, 371 /** 372 * Sets the bottom side inset 373 * @param {number} insetBottom 374 */ 375 setInsetBottom: function (insetBottom) { 376 this._insetBottom = insetBottom; 377 this._updateCapInset(); 378 }, 379 380 /** 381 * Sets the untransformed size of the Scale9Sprite. 382 * @override 383 * @param {cc.Size|Number} size The untransformed size of the Scale9Sprite or The untransformed size's width of the Scale9Sprite. 384 * @param {Number} [height] The untransformed size's height of the Scale9Sprite. 385 */ 386 setContentSize: function (size, height) { 387 cc.Node.prototype.setContentSize.call(this, size, height); 388 this._positionsAreDirty = true; 389 }, 390 _setWidth: function (value) { 391 cc.Node.prototype._setWidth.call(this, value); 392 this._positionsAreDirty = true; 393 }, 394 _setHeight: function (value) { 395 cc.Node.prototype._setHeight.call(this, value); 396 this._positionsAreDirty = true; 397 }, 398 399 visit: function (ctx) { 400 if (this._positionsAreDirty) { 401 this._updatePositions(); 402 this._positionsAreDirty = false; 403 } 404 cc.NodeRGBA.prototype.visit.call(this, ctx); 405 }, 406 407 init: function () { 408 return this.initWithBatchNode(null, cc.rect(0, 0, 0, 0), false, cc.rect(0, 0, 0, 0)); 409 }, 410 411 initWithBatchNode: function (batchNode, rect, rotated, capInsets) { 412 if (capInsets === undefined) { 413 capInsets = rotated; 414 rotated = false; 415 } 416 417 if (batchNode) { 418 this.updateWithBatchNode(batchNode, rect, rotated, capInsets); 419 } 420 this.setAnchorPoint(0.5, 0.5); 421 this._positionsAreDirty = true; 422 return true; 423 }, 424 425 /** 426 * Initializes a 9-slice sprite with a texture file, a delimitation zone and 427 * with the specified cap insets. 428 * Once the sprite is created, you can then call its "setContentSize:" method 429 * to resize the sprite will all it's 9-slice goodness intact. 430 * It respects the anchorPoint too. 431 * 432 * @param file The name of the texture file. 433 * @param rect The rectangle that describes the sub-part of the texture that 434 * is the whole image. If the shape is the whole texture, set this to the 435 * texture's full rect. 436 * @param capInsets The values to use for the cap insets. 437 */ 438 initWithFile: function (file, rect, capInsets) { 439 if (file instanceof cc.Rect) { 440 file = arguments[1]; 441 capInsets = arguments[0]; 442 rect = cc.rect(0, 0, 0, 0); 443 } else { 444 rect = rect || cc.rect(0, 0, 0, 0); 445 capInsets = capInsets || cc.rect(0, 0, 0, 0); 446 } 447 448 if(!file) 449 throw "cc.Scale9Sprite.initWithFile(): file should be non-null"; 450 451 var texture = cc.textureCache.textureForKey(file); 452 if (!texture) { 453 texture = cc.textureCache.addImage(file); 454 var locLoaded = texture.isLoaded(); 455 this._textureLoaded = locLoaded; 456 if(!locLoaded){ 457 texture.addLoadedEventListener(function(sender){ 458 // the texture is rotated on Canvas render mode, so isRotated always is false. 459 var preferredSize = this._preferredSize; 460 preferredSize = cc.size(preferredSize.width, preferredSize.height); 461 var size = sender.getContentSize(); 462 this.updateWithBatchNode(this._scale9Image, cc.rect(0,0,size.width,size.height), false, this._capInsets); 463 this.setPreferredSize(preferredSize); 464 this._positionsAreDirty = true; 465 this._callLoadedEventCallbacks(); 466 }, this); 467 } 468 } 469 return this.initWithBatchNode(cc.SpriteBatchNode.create(file, 9), rect, false, capInsets); 470 }, 471 472 /** 473 * Initializes a 9-slice sprite with an sprite frame and with the specified 474 * cap insets. 475 * Once the sprite is created, you can then call its "setContentSize:" method 476 * to resize the sprite will all it's 9-slice goodness intract. 477 * It respects the anchorPoint too. 478 * 479 * @param spriteFrame The sprite frame object. 480 * @param capInsets The values to use for the cap insets. 481 */ 482 initWithSpriteFrame: function (spriteFrame, capInsets) { 483 if(!spriteFrame || !spriteFrame.getTexture()) 484 throw "cc.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null"; 485 486 capInsets = capInsets || cc.rect(0, 0, 0, 0); 487 var locLoaded = spriteFrame.textureLoaded(); 488 this._textureLoaded = locLoaded; 489 if(!locLoaded){ 490 spriteFrame.addLoadedEventListener(function(sender){ 491 // the texture is rotated on Canvas render mode, so isRotated always is false. 492 var preferredSize = this._preferredSize; 493 preferredSize = cc.size(preferredSize.width, preferredSize.height); 494 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets); 495 this.setPreferredSize(preferredSize); 496 this._positionsAreDirty = true; 497 this._callLoadedEventCallbacks(); 498 },this); 499 } 500 var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9); 501 // the texture is rotated on Canvas render mode, so isRotated always is false. 502 return this.initWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), capInsets); 503 }, 504 505 /** 506 * Initializes a 9-slice sprite with an sprite frame name and with the specified 507 * cap insets. 508 * Once the sprite is created, you can then call its "setContentSize:" method 509 * to resize the sprite will all it's 9-slice goodness intract. 510 * It respects the anchorPoint too. 511 * 512 * @param spriteFrameName The sprite frame name. 513 * @param capInsets The values to use for the cap insets. 514 */ 515 initWithSpriteFrameName: function (spriteFrameName, capInsets) { 516 if(!spriteFrameName) 517 throw "cc.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null"; 518 capInsets = capInsets || cc.rect(0, 0, 0, 0); 519 520 var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); 521 if (frame == null) { 522 cc.log("cc.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName"); 523 return false; 524 } 525 526 return this.initWithSpriteFrame(frame, capInsets); 527 }, 528 529 /** 530 * Creates and returns a new sprite object with the specified cap insets. 531 * You use this method to add cap insets to a sprite or to change the existing 532 * cap insets of a sprite. In both cases, you get back a new image and the 533 * original sprite remains untouched. 534 * 535 * @param capInsets The values to use for the cap insets. 536 */ 537 resizableSpriteWithCapInsets: function (capInsets) { 538 var pReturn = new cc.Scale9Sprite(); 539 if (pReturn && pReturn.initWithBatchNode(this._scale9Image, this._spriteRect, false, capInsets)) { 540 return pReturn; 541 } 542 return null; 543 }, 544 545 /** sets the premultipliedAlphaOpacity property. 546 If set to NO then opacity will be applied as: glColor(R,G,B,opacity); 547 If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity ); 548 Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO 549 @since v0.8 550 */ 551 setOpacityModifyRGB: function (value) { 552 if(!this._scale9Image){ 553 return; 554 } 555 this._opacityModifyRGB = value; 556 var scaleChildren = this._scale9Image.getChildren(); 557 if (scaleChildren) { 558 for (var i = 0, len = scaleChildren.length; i < len; i++) 559 scaleChildren[i].setOpacityModifyRGB(value); 560 } 561 }, 562 563 /** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity); 564 @since v0.8 565 */ 566 isOpacityModifyRGB: function () { 567 return this._opacityModifyRGB; 568 }, 569 570 updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) { 571 var opacity = this.getOpacity(); 572 var color = this.getColor(); 573 var rect = cc.rect(originalRect.x, originalRect.y, originalRect.width, originalRect.height); 574 575 // Release old sprites 576 this.removeAllChildren(true); 577 578 if (this._scale9Image != batchNode){ 579 this._scale9Image = batchNode; 580 } 581 var tmpTexture = batchNode.getTexture(); 582 var locLoaded = tmpTexture.isLoaded(); 583 this._textureLoaded = locLoaded; 584 if(!locLoaded){ 585 tmpTexture.addLoadedEventListener(function(sender){ 586 this._positionsAreDirty = true; 587 this._callLoadedEventCallbacks(); 588 },this); 589 return; 590 } 591 var locScale9Image = this._scale9Image; 592 locScale9Image.removeAllChildren(true); 593 594 //this._capInsets = capInsets; 595 var locCapInsets = this._capInsets; 596 locCapInsets.x = capInsets.x; 597 locCapInsets.y = capInsets.y; 598 locCapInsets.width = capInsets.width; 599 locCapInsets.height = capInsets.height; 600 this._spriteFrameRotated = rotated; 601 602 var selTexture = locScale9Image.getTexture(); 603 604 // If there is no given rect 605 if (cc._rectEqualToZero(rect)) { 606 // Get the texture size as original 607 var textureSize = selTexture.getContentSize(); 608 rect = cc.rect(0, 0, textureSize.width, textureSize.height); 609 } 610 611 // Set the given rect's size as original size 612 this._spriteRect = rect; 613 var locSpriteRect = this._spriteRect; 614 locSpriteRect.x = rect.x; 615 locSpriteRect.y = rect.y; 616 locSpriteRect.width = rect.width; 617 locSpriteRect.height = rect.height; 618 619 this._originalSize.width = rect.width; 620 this._originalSize.height = rect.height; 621 622 var locPreferredSize = this._preferredSize; 623 if(locPreferredSize.width === 0 && locPreferredSize.height === 0){ 624 locPreferredSize.width = rect.width; 625 locPreferredSize.height = rect.height; 626 } 627 628 var locCapInsetsInternal = this._capInsetsInternal; 629 if(capInsets){ 630 locCapInsetsInternal.x = capInsets.x; 631 locCapInsetsInternal.y = capInsets.y; 632 locCapInsetsInternal.width = capInsets.width; 633 locCapInsetsInternal.height = capInsets.height; 634 } 635 var w = rect.width; 636 var h = rect.height; 637 638 // If there is no specified center region 639 if (cc._rectEqualToZero(locCapInsetsInternal)) { 640 // CCLog("... cap insets not specified : using default cap insets ..."); 641 locCapInsetsInternal.x = w / 3; 642 locCapInsetsInternal.y = h / 3; 643 locCapInsetsInternal.width = w / 3; 644 locCapInsetsInternal.height = h / 3; 645 } 646 647 var left_w = locCapInsetsInternal.x; 648 var center_w = locCapInsetsInternal.width; 649 var right_w = w - (left_w + center_w); 650 651 var top_h = locCapInsetsInternal.y; 652 var center_h = locCapInsetsInternal.height; 653 var bottom_h = h - (top_h + center_h); 654 655 // calculate rects 656 // ... top row 657 var x = 0.0; 658 var y = 0.0; 659 660 // top left 661 var lefttopbounds = cc.rect(x, y, left_w, top_h); 662 663 // top center 664 x += left_w; 665 var centertopbounds = cc.rect(x, y, center_w, top_h); 666 667 // top right 668 x += center_w; 669 var righttopbounds = cc.rect(x, y, right_w, top_h); 670 671 // ... center row 672 x = 0.0; 673 y = 0.0; 674 675 y += top_h; 676 // center left 677 var leftcenterbounds = cc.rect(x, y, left_w, center_h); 678 679 // center center 680 x += left_w; 681 var centerbounds = cc.rect(x, y, center_w, center_h); 682 683 // center right 684 x += center_w; 685 var rightcenterbounds = cc.rect(x, y, right_w, center_h); 686 687 // ... bottom row 688 x = 0.0; 689 y = 0.0; 690 y += top_h; 691 y += center_h; 692 693 // bottom left 694 var leftbottombounds = cc.rect(x, y, left_w, bottom_h); 695 696 // bottom center 697 x += left_w; 698 var centerbottombounds = cc.rect(x, y, center_w, bottom_h); 699 700 // bottom right 701 x += center_w; 702 var rightbottombounds = cc.rect(x, y, right_w, bottom_h); 703 704 var t = cc.AffineTransformMakeIdentity(); 705 if (!rotated) { 706 // CCLog("!rotated"); 707 t = cc.AffineTransformTranslate(t, rect.x, rect.y); 708 709 cc._RectApplyAffineTransformIn(centerbounds, t); 710 cc._RectApplyAffineTransformIn(rightbottombounds, t); 711 cc._RectApplyAffineTransformIn(leftbottombounds, t); 712 cc._RectApplyAffineTransformIn(righttopbounds, t); 713 cc._RectApplyAffineTransformIn(lefttopbounds, t); 714 cc._RectApplyAffineTransformIn(rightcenterbounds, t); 715 cc._RectApplyAffineTransformIn(leftcenterbounds, t); 716 cc._RectApplyAffineTransformIn(centerbottombounds, t); 717 cc._RectApplyAffineTransformIn(centertopbounds, t); 718 719 // Centre 720 this._centre = new cc.Sprite(); 721 this._centre.initWithTexture(selTexture, centerbounds); 722 locScale9Image.addChild(this._centre, 0, cc.Scale9Sprite.POSITIONS_CENTRE); 723 724 // Top 725 this._top = new cc.Sprite(); 726 this._top.initWithTexture(selTexture, centertopbounds); 727 locScale9Image.addChild(this._top, 1, cc.Scale9Sprite.POSITIONS_TOP); 728 729 // Bottom 730 this._bottom = new cc.Sprite(); 731 this._bottom.initWithTexture(selTexture, centerbottombounds); 732 locScale9Image.addChild(this._bottom, 1, cc.Scale9Sprite.POSITIONS_BOTTOM); 733 734 // Left 735 this._left = new cc.Sprite(); 736 this._left.initWithTexture(selTexture, leftcenterbounds); 737 locScale9Image.addChild(this._left, 1, cc.Scale9Sprite.POSITIONS_LEFT); 738 739 // Right 740 this._right = new cc.Sprite(); 741 this._right.initWithTexture(selTexture, rightcenterbounds); 742 locScale9Image.addChild(this._right, 1, cc.Scale9Sprite.POSITIONS_RIGHT); 743 744 // Top left 745 this._topLeft = new cc.Sprite(); 746 this._topLeft.initWithTexture(selTexture, lefttopbounds); 747 locScale9Image.addChild(this._topLeft, 2, cc.Scale9Sprite.POSITIONS_TOPLEFT); 748 749 // Top right 750 this._topRight = new cc.Sprite(); 751 this._topRight.initWithTexture(selTexture, righttopbounds); 752 locScale9Image.addChild(this._topRight, 2, cc.Scale9Sprite.POSITIONS_TOPRIGHT); 753 754 // Bottom left 755 this._bottomLeft = new cc.Sprite(); 756 this._bottomLeft.initWithTexture(selTexture, leftbottombounds); 757 locScale9Image.addChild(this._bottomLeft, 2, cc.Scale9Sprite.POSITIONS_BOTTOMLEFT); 758 759 // Bottom right 760 this._bottomRight = new cc.Sprite(); 761 this._bottomRight.initWithTexture(selTexture, rightbottombounds); 762 locScale9Image.addChild(this._bottomRight, 2, cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT); 763 } else { 764 // set up transformation of coordinates 765 // to handle the case where the sprite is stored rotated 766 // in the spritesheet 767 // CCLog("rotated"); 768 var rotatedcenterbounds = centerbounds; 769 var rotatedrightbottombounds = rightbottombounds; 770 var rotatedleftbottombounds = leftbottombounds; 771 var rotatedrighttopbounds = righttopbounds; 772 var rotatedlefttopbounds = lefttopbounds; 773 var rotatedrightcenterbounds = rightcenterbounds; 774 var rotatedleftcenterbounds = leftcenterbounds; 775 var rotatedcenterbottombounds = centerbottombounds; 776 var rotatedcentertopbounds = centertopbounds; 777 778 t = cc.AffineTransformTranslate(t, rect.height + rect.x, rect.y); 779 t = cc.AffineTransformRotate(t, 1.57079633); 780 781 centerbounds = cc.RectApplyAffineTransform(centerbounds, t); 782 rightbottombounds = cc.RectApplyAffineTransform(rightbottombounds, t); 783 leftbottombounds = cc.RectApplyAffineTransform(leftbottombounds, t); 784 righttopbounds = cc.RectApplyAffineTransform(righttopbounds, t); 785 lefttopbounds = cc.RectApplyAffineTransform(lefttopbounds, t); 786 rightcenterbounds = cc.RectApplyAffineTransform(rightcenterbounds, t); 787 leftcenterbounds = cc.RectApplyAffineTransform(leftcenterbounds, t); 788 centerbottombounds = cc.RectApplyAffineTransform(centerbottombounds, t); 789 centertopbounds = cc.RectApplyAffineTransform(centertopbounds, t); 790 791 rotatedcenterbounds.x = centerbounds.x; 792 rotatedcenterbounds.y = centerbounds.y; 793 794 rotatedrightbottombounds.x = rightbottombounds.x; 795 rotatedrightbottombounds.y = rightbottombounds.y; 796 797 rotatedleftbottombounds.x = leftbottombounds.x; 798 rotatedleftbottombounds.y = leftbottombounds.y; 799 800 rotatedrighttopbounds.x = righttopbounds.x; 801 rotatedrighttopbounds.y = righttopbounds.y; 802 803 rotatedlefttopbounds.x = lefttopbounds.x; 804 rotatedlefttopbounds.y = lefttopbounds.y; 805 806 rotatedrightcenterbounds.x = rightcenterbounds.x; 807 rotatedrightcenterbounds.y = rightcenterbounds.y; 808 809 rotatedleftcenterbounds.x = leftcenterbounds.x; 810 rotatedleftcenterbounds.y = leftcenterbounds.y; 811 812 rotatedcenterbottombounds.x = centerbottombounds.x; 813 rotatedcenterbottombounds.y = centerbottombounds.y; 814 815 rotatedcentertopbounds.x = centertopbounds.x; 816 rotatedcentertopbounds.y = centertopbounds.y; 817 818 // Centre 819 this._centre = new cc.Sprite(); 820 this._centre.initWithTexture(selTexture, rotatedcenterbounds, true); 821 locScale9Image.addChild(this._centre, 0, cc.Scale9Sprite.POSITIONS_CENTRE); 822 823 // Top 824 this._top = new cc.Sprite(); 825 this._top.initWithTexture(selTexture, rotatedcentertopbounds, true); 826 locScale9Image.addChild(this._top, 1, cc.Scale9Sprite.POSITIONS_TOP); 827 828 // Bottom 829 this._bottom = new cc.Sprite(); 830 this._bottom.initWithTexture(selTexture, rotatedcenterbottombounds, true); 831 locScale9Image.addChild(this._bottom, 1, cc.Scale9Sprite.POSITIONS_BOTTOM); 832 833 // Left 834 this._left = new cc.Sprite(); 835 this._left.initWithTexture(selTexture, rotatedleftcenterbounds, true); 836 locScale9Image.addChild(this._left, 1, cc.Scale9Sprite.POSITIONS_LEFT); 837 838 // Right 839 this._right = new cc.Sprite(); 840 this._right.initWithTexture(selTexture, rotatedrightcenterbounds, true); 841 locScale9Image.addChild(this._right, 1, cc.Scale9Sprite.POSITIONS_RIGHT); 842 843 // Top left 844 this._topLeft = new cc.Sprite(); 845 this._topLeft.initWithTexture(selTexture, rotatedlefttopbounds, true); 846 locScale9Image.addChild(this._topLeft, 2, cc.Scale9Sprite.POSITIONS_TOPLEFT); 847 848 // Top right 849 this._topRight = new cc.Sprite(); 850 this._topRight.initWithTexture(selTexture, rotatedrighttopbounds, true); 851 locScale9Image.addChild(this._topRight, 2, cc.Scale9Sprite.POSITIONS_TOPRIGHT); 852 853 // Bottom left 854 this._bottomLeft = new cc.Sprite(); 855 this._bottomLeft.initWithTexture(selTexture, rotatedleftbottombounds, true); 856 locScale9Image.addChild(this._bottomLeft, 2, cc.Scale9Sprite.POSITIONS_BOTTOMLEFT); 857 858 // Bottom right 859 this._bottomRight = new cc.Sprite(); 860 this._bottomRight.initWithTexture(selTexture, rotatedrightbottombounds, true); 861 locScale9Image.addChild(this._bottomRight, 2, cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT); 862 } 863 864 this.setContentSize(rect); 865 this.addChild(locScale9Image); 866 867 if (this._spritesGenerated) { 868 // Restore color and opacity 869 this.setOpacity(opacity); 870 if(color.r !== 255 || color.g !== 255 || color.b !== 255){ 871 this.setColor(color); 872 } 873 } 874 this._spritesGenerated = true; 875 return true; 876 }, 877 878 setSpriteFrame: function (spriteFrame) { 879 var batchNode = cc.SpriteBatchNode.create(spriteFrame.getTexture(), 9); 880 // the texture is rotated on Canvas render mode, so isRotated always is false. 881 var locLoaded = spriteFrame.textureLoaded(); 882 this._textureLoaded = locLoaded; 883 if(!locLoaded){ 884 spriteFrame.addLoadedEventListener(function(sender){ 885 // the texture is rotated on Canvas render mode, so isRotated always is false. 886 var preferredSize = this._preferredSize; 887 preferredSize = cc.size(preferredSize.width, preferredSize.height); 888 this.updateWithBatchNode(this._scale9Image, sender.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && sender.isRotated(), this._capInsets); 889 this.setPreferredSize(preferredSize); 890 this._positionsAreDirty = true; 891 this._callLoadedEventCallbacks(); 892 },this); 893 } 894 this.updateWithBatchNode(batchNode, spriteFrame.getRect(), cc._renderType == cc._RENDER_TYPE_WEBGL && spriteFrame.isRotated(), cc.rect(0, 0, 0, 0)); 895 896 // Reset insets 897 this._insetLeft = 0; 898 this._insetTop = 0; 899 this._insetRight = 0; 900 this._insetBottom = 0; 901 } 902 }); 903 904 var _p = cc.Scale9Sprite.prototype; 905 906 // Extended properties 907 /** @expose */ 908 _p.preferredSize; 909 cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize); 910 /** @expose */ 911 _p.capInsets; 912 cc.defineGetterSetter(_p, "capInsets", _p.getCapInsets, _p.setCapInsets); 913 /** @expose */ 914 _p.insetLeft; 915 cc.defineGetterSetter(_p, "insetLeft", _p.getInsetLeft, _p.setInsetLeft); 916 /** @expose */ 917 _p.insetTop; 918 cc.defineGetterSetter(_p, "insetTop", _p.getInsetTop, _p.setInsetTop); 919 /** @expose */ 920 _p.insetRight; 921 cc.defineGetterSetter(_p, "insetRight", _p.getInsetRight, _p.setInsetRight); 922 /** @expose */ 923 _p.insetBottom; 924 cc.defineGetterSetter(_p, "insetBottom", _p.getInsetBottom, _p.setInsetBottom); 925 926 _p = null; 927 928 /** 929 * Creates a 9-slice sprite with a texture file, a delimitation zone and 930 * with the specified cap insets. 931 * 932 * @see initWithFile:rect:centerRegion: 933 */ 934 cc.Scale9Sprite.create = function (file, rect, capInsets) { 935 var pReturn; 936 if (arguments.length === 2) { 937 if (typeof(file) == "string") { 938 pReturn = new cc.Scale9Sprite(); 939 if (pReturn && pReturn.initWithFile(file, rect)) { 940 return pReturn; 941 } 942 } else if (file instanceof cc.Rect) { 943 pReturn = new cc.Scale9Sprite(); 944 if (pReturn && pReturn.initWithFile(file, capInsets)) { 945 return pReturn; 946 } 947 } 948 } else if (arguments.length === 3) { 949 pReturn = new cc.Scale9Sprite(); 950 if (pReturn && pReturn.initWithFile(file, rect, capInsets)) { 951 return pReturn; 952 } 953 } else if (arguments.length === 1) { 954 pReturn = new cc.Scale9Sprite(); 955 if (pReturn && pReturn.initWithFile(file)) { 956 return pReturn; 957 } 958 } else if (arguments.length === 0) { 959 pReturn = new cc.Scale9Sprite(); 960 if (pReturn && pReturn.init()) { 961 return pReturn; 962 } 963 } 964 return null; 965 }; 966 967 cc.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) { 968 var pReturn = new cc.Scale9Sprite(); 969 if (pReturn && pReturn.initWithSpriteFrame(spriteFrame, capInsets)) { 970 return pReturn; 971 } 972 return null; 973 }; 974 975 cc.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) { 976 if(!spriteFrameName) 977 throw "cc.Scale9Sprite.createWithSpriteFrameName(): spriteFrameName should be non-null"; 978 var pReturn = new cc.Scale9Sprite(); 979 if (pReturn && pReturn.initWithSpriteFrameName(spriteFrameName, capInsets)) 980 return pReturn; 981 return null; 982 }; 983 984 985 /** 986 * @ignore 987 */ 988 cc.Scale9Sprite.POSITIONS_CENTRE = 0; 989 cc.Scale9Sprite.POSITIONS_TOP = 1; 990 cc.Scale9Sprite.POSITIONS_LEFT = 2; 991 cc.Scale9Sprite.POSITIONS_RIGHT = 3; 992 cc.Scale9Sprite.POSITIONS_BOTTOM = 4; 993 cc.Scale9Sprite.POSITIONS_TOPRIGHT = 5; 994 cc.Scale9Sprite.POSITIONS_TOPLEFT = 6; 995 cc.Scale9Sprite.POSITIONS_BOTTOMRIGHT = 7; 996