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