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