UIScale9Sprite.js 42 KB

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