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 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * Tint a texture using the "multiply" operation 29 * @param {HTMLImageElement} image 30 * @param {cc.Color} color 31 * @param {cc.Rect} [rect] 32 * @param {HTMLCanvasElement} renderCanvas 33 * @returns {HTMLCanvasElement} 34 */ 35 cc.generateTintImageWithMultiply = function(image, color, rect, renderCanvas){ 36 renderCanvas = renderCanvas || cc.newElement("canvas"); 37 38 rect = rect || cc.rect(0,0, image.width, image.height); 39 40 var context = renderCanvas.getContext( "2d" ); 41 if(renderCanvas.width != rect.width || renderCanvas.height != rect.height){ 42 renderCanvas.width = rect.width; 43 renderCanvas.height = rect.height; 44 }else{ 45 context.globalCompositeOperation = "source-over"; 46 } 47 48 context.fillStyle = "rgb(" + (0|color.r) + "," + (0|color.g) + "," + (0|color.b) + ")"; 49 context.fillRect(0, 0, rect.width, rect.height); 50 context.globalCompositeOperation = "multiply"; 51 context.drawImage(image, 52 rect.x, 53 rect.y, 54 rect.width, 55 rect.height, 56 0, 57 0, 58 rect.width, 59 rect.height); 60 context.globalCompositeOperation = "destination-atop"; 61 context.drawImage(image, 62 rect.x, 63 rect.y, 64 rect.width, 65 rect.height, 66 0, 67 0, 68 rect.width, 69 rect.height); 70 return renderCanvas; 71 }; 72 73 /** 74 * Generate tinted texture with lighter. 75 * lighter: The source and destination colors are added to each other, resulting in brighter colors, 76 * moving towards color values of 1 (maximum brightness for that color). 77 * @function 78 * @param {HTMLImageElement} texture 79 * @param {Array} tintedImgCache 80 * @param {cc.Color} color 81 * @param {cc.Rect} rect 82 * @param {HTMLCanvasElement} [renderCanvas] 83 * @return {HTMLCanvasElement} 84 */ 85 cc.generateTintImage = function (texture, tintedImgCache, color, rect, renderCanvas) { 86 if (!rect) 87 rect = cc.rect(0, 0, texture.width, texture.height); 88 89 var r = color.r / 255; 90 var g = color.g / 255; 91 var b = color.b / 255; 92 93 var w = Math.min(rect.width, tintedImgCache[0].width); 94 var h = Math.min(rect.height, tintedImgCache[0].height); 95 var buff = renderCanvas; 96 var ctx; 97 98 // Create a new buffer if required 99 if (!buff) { 100 buff = cc.newElement("canvas"); 101 buff.width = w; 102 buff.height = h; 103 ctx = buff.getContext("2d"); 104 } else { 105 ctx = buff.getContext("2d"); 106 ctx.clearRect(0, 0, w, h); 107 } 108 109 ctx.save(); 110 ctx.globalCompositeOperation = 'lighter'; 111 112 // Make sure to keep the renderCanvas alpha in mind in case of overdraw 113 var a = ctx.globalAlpha; 114 if (r > 0) { 115 ctx.globalAlpha = r * a; 116 ctx.drawImage(tintedImgCache[0], rect.x, rect.y, w, h, 0, 0, w, h); 117 } 118 if (g > 0) { 119 ctx.globalAlpha = g * a; 120 ctx.drawImage(tintedImgCache[1], rect.x, rect.y, w, h, 0, 0, w, h); 121 } 122 if (b > 0) { 123 ctx.globalAlpha = b * a; 124 ctx.drawImage(tintedImgCache[2], rect.x, rect.y, w, h, 0, 0, w, h); 125 } 126 127 if (r + g + b < 1) { 128 ctx.globalAlpha = a; 129 ctx.drawImage(tintedImgCache[3], rect.x, rect.y, w, h, 0, 0, w, h); 130 } 131 132 ctx.restore(); 133 return buff; 134 }; 135 136 /** 137 * Generates texture's cache for texture tint 138 * @function 139 * @param {HTMLImageElement} texture 140 * @return {Array} 141 */ 142 cc.generateTextureCacheForColor = function (texture) { 143 if (texture.channelCache) { 144 return texture.channelCache; 145 } 146 147 var textureCache = [ 148 cc.newElement("canvas"), 149 cc.newElement("canvas"), 150 cc.newElement("canvas"), 151 cc.newElement("canvas") 152 ]; 153 154 function renderToCache() { 155 var ref = cc.generateTextureCacheForColor; 156 157 var w = texture.width; 158 var h = texture.height; 159 160 textureCache[0].width = w; 161 textureCache[0].height = h; 162 textureCache[1].width = w; 163 textureCache[1].height = h; 164 textureCache[2].width = w; 165 textureCache[2].height = h; 166 textureCache[3].width = w; 167 textureCache[3].height = h; 168 169 ref.canvas.width = w; 170 ref.canvas.height = h; 171 172 var ctx = ref.canvas.getContext("2d"); 173 ctx.drawImage(texture, 0, 0); 174 175 ref.tempCanvas.width = w; 176 ref.tempCanvas.height = h; 177 178 var pixels = ctx.getImageData(0, 0, w, h).data; 179 180 for (var rgbI = 0; rgbI < 4; rgbI++) { 181 var cacheCtx = textureCache[rgbI].getContext('2d'); 182 cacheCtx.getImageData(0, 0, w, h).data; 183 ref.tempCtx.drawImage(texture, 0, 0); 184 185 var to = ref.tempCtx.getImageData(0, 0, w, h); 186 var toData = to.data; 187 188 for (var i = 0; i < pixels.length; i += 4) { 189 toData[i ] = (rgbI === 0) ? pixels[i ] : 0; 190 toData[i + 1] = (rgbI === 1) ? pixels[i + 1] : 0; 191 toData[i + 2] = (rgbI === 2) ? pixels[i + 2] : 0; 192 toData[i + 3] = pixels[i + 3]; 193 } 194 cacheCtx.putImageData(to, 0, 0); 195 } 196 texture.onload = null; 197 } 198 199 try { 200 renderToCache(); 201 } catch (e) { 202 texture.onload = renderToCache; 203 } 204 205 texture.channelCache = textureCache; 206 return textureCache; 207 }; 208 209 cc.generateTextureCacheForColor.canvas = cc.newElement('canvas'); 210 cc.generateTextureCacheForColor.tempCanvas = cc.newElement('canvas'); 211 cc.generateTextureCacheForColor.tempCtx = cc.generateTextureCacheForColor.tempCanvas.getContext('2d'); 212 213 cc.cutRotateImageToCanvas = function (texture, rect) { 214 if (!texture) 215 return null; 216 217 if (!rect) 218 return texture; 219 220 var nCanvas = cc.newElement("canvas"); 221 nCanvas.width = rect.width; 222 nCanvas.height = rect.height; 223 var ctx = nCanvas.getContext("2d"); 224 ctx.translate(nCanvas.width / 2, nCanvas.height / 2); 225 ctx.rotate(-1.5707963267948966); 226 ctx.drawImage(texture, rect.x, rect.y, rect.height, rect.width, -rect.height / 2, -rect.width / 2, rect.height, rect.width); 227 return nCanvas; 228 }; 229 230 cc._getCompositeOperationByBlendFunc = function(blendFunc){ 231 if(!blendFunc) 232 return "source"; 233 else{ 234 if(( blendFunc.src == cc.SRC_ALPHA && blendFunc.dst == cc.ONE) || (blendFunc.src == cc.ONE && blendFunc.dst == cc.ONE)) 235 return "lighter"; 236 else if(blendFunc.src == cc.ZERO && blendFunc.dst == cc.SRC_ALPHA) 237 return "destination-in"; 238 else if(blendFunc.src == cc.ZERO && blendFunc.dst == cc.ONE_MINUS_SRC_ALPHA) 239 return "destination-out"; 240 else 241 return "source"; 242 } 243 }; 244 245 /** 246 * <p>cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) <br/> 247 * 248 * cc.Sprite can be created with an image, or with a sub-rectangle of an image. <br/> 249 * 250 * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid <br/> 251 * - Features when the parent is a cc.BatchNode: <br/> 252 * - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch. <br/> 253 * 254 * - Limitations <br/> 255 * - Camera is not supported yet (eg: CCOrbitCamera action doesn't work) <br/> 256 * - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) <br/> 257 * - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property. <br/> 258 * - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. <br/> 259 * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite. <br/> 260 * 261 * If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node: <br/> 262 * - It supports blending functions <br/> 263 * - It supports aliasing / antialiasing <br/> 264 * - But the rendering will be slower: 1 draw per children. <br/> 265 * 266 * The default anchorPoint in cc.Sprite is (0.5, 0.5). </p> 267 * @class 268 * @extends cc.Node 269 * 270 * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". 271 * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. 272 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 273 * @example 274 * 275 * 1.Create a sprite with image path and rect 276 * var sprite1 = new cc.Sprite("res/HelloHTML5World.png"); 277 * var sprite2 = new cc.Sprite("res/HelloHTML5World.png",cc.rect(0,0,480,320)); 278 * 279 * 2.Create a sprite with a sprite frame name. Must add "#" before frame name. 280 * var sprite = new cc.Sprite('#grossini_dance_01.png'); 281 * 282 * 3.Create a sprite with a sprite frame 283 * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); 284 * var sprite = new cc.Sprite(spriteFrame); 285 * 286 * 4.Create a sprite with an existing texture contained in a CCTexture2D object 287 * After creation, the rect will be the size of the texture, and the offset will be (0,0). 288 * var texture = cc.textureCache.addImage("HelloHTML5World.png"); 289 * var sprite1 = new cc.Sprite(texture); 290 * var sprite2 = new cc.Sprite(texture, cc.rect(0,0,480,320)); 291 * 292 * @property {Boolean} dirty - Indicates whether the sprite needs to be updated. 293 * @property {Boolean} flippedX - Indicates whether or not the spirte is flipped on x axis. 294 * @property {Boolean} flippedY - Indicates whether or not the spirte is flipped on y axis. 295 * @property {Number} offsetX - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. 296 * @property {Number} offsetY - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. 297 * @property {Number} atlasIndex - The index used on the TextureAtlas. 298 * @property {cc.Texture2D} texture - Texture used to render the sprite. 299 * @property {Boolean} textureRectRotated - <@readonly> Indicate whether the texture rectangle is rotated. 300 * @property {cc.TextureAtlas} textureAtlas - The weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode. 301 * @property {cc.SpriteBatchNode} batchNode - The batch node object if this sprite is rendered by cc.SpriteBatchNode. 302 * @property {cc.V3F_C4B_T2F_Quad} quad - <@readonly> The quad (tex coords, vertex coords and color) information. 303 */ 304 cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ 305 dirty:false, 306 atlasIndex:0, 307 textureAtlas:null, 308 309 _batchNode:null, 310 _recursiveDirty:null, //Whether all of the sprite's children needs to be updated 311 _hasChildren:null, //Whether the sprite contains children 312 _shouldBeHidden:false, //should not be drawn because one of the ancestors is not visible 313 _transformToBatch:null, 314 315 // 316 // Data used when the sprite is self-rendered 317 // 318 _blendFunc:null, //It's required for CCTextureProtocol inheritance 319 _texture:null, //cc.Texture2D object that is used to render the sprite 320 321 // 322 // Shared data 323 // 324 // texture 325 _rect:null, //Retangle of cc.Texture2D 326 _rectRotated:false, //Whether the texture is rotated 327 328 // Offset Position (used by Zwoptex) 329 _offsetPosition:null, // absolute 330 _unflippedOffsetPositionFromCenter:null, 331 332 _opacityModifyRGB:false, 333 334 // image is flipped 335 _flippedX:false, //Whether the sprite is flipped horizontally or not. 336 _flippedY:false, //Whether the sprite is flipped vertically or not. 337 338 _textureLoaded:false, 339 _loadedEventListeners: null, 340 _newTextureWhenChangeColor: null, //hack property for LabelBMFont 341 _className:"Sprite", 342 343 //Only for texture update judgment 344 _oldDisplayColor: cc.color.WHITE, 345 346 /** 347 * Returns whether the texture have been loaded 348 * @returns {boolean} 349 */ 350 textureLoaded:function(){ 351 return this._textureLoaded; 352 }, 353 354 /** 355 * Add a event listener for texture loaded event. 356 * @param {Function} callback 357 * @param {Object} target 358 */ 359 addLoadedEventListener:function(callback, target){ 360 if(!this._loadedEventListeners) 361 this._loadedEventListeners = []; 362 this._loadedEventListeners.push({eventCallback:callback, eventTarget:target}); 363 }, 364 365 _callLoadedEventCallbacks:function(){ 366 if(!this._loadedEventListeners) 367 return; 368 var locListeners = this._loadedEventListeners; 369 for(var i = 0, len = locListeners.length; i < len; i++){ 370 var selCallback = locListeners[i]; 371 selCallback.eventCallback.call(selCallback.eventTarget, this); 372 } 373 locListeners.length = 0; 374 }, 375 376 /** 377 * Returns whether or not the Sprite needs to be updated in the Atlas 378 * @return {Boolean} True if the sprite needs to be updated in the Atlas, false otherwise. 379 */ 380 isDirty:function () { 381 return this.dirty; 382 }, 383 384 /** 385 * Makes the sprite to be updated in the Atlas. 386 * @param {Boolean} bDirty 387 */ 388 setDirty:function (bDirty) { 389 this.dirty = bDirty; 390 }, 391 392 /** 393 * Returns whether or not the texture rectangle is rotated. 394 * @return {Boolean} 395 */ 396 isTextureRectRotated:function () { 397 return this._rectRotated; 398 }, 399 400 /** 401 * Returns the index used on the TextureAtlas. 402 * @return {Number} 403 */ 404 getAtlasIndex:function () { 405 return this.atlasIndex; 406 }, 407 408 /** 409 * Sets the index used on the TextureAtlas. 410 * @warning Don't modify this value unless you know what you are doing 411 * @param {Number} atlasIndex 412 */ 413 setAtlasIndex:function (atlasIndex) { 414 this.atlasIndex = atlasIndex; 415 }, 416 417 /** 418 * Returns the rect of the cc.Sprite in points 419 * @return {cc.Rect} 420 */ 421 getTextureRect:function () { 422 return cc.rect(this._rect.x, this._rect.y, this._rect.width, this._rect.height); 423 }, 424 425 /** 426 * Returns the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode 427 * @return {cc.TextureAtlas} 428 */ 429 getTextureAtlas:function () { 430 return this.textureAtlas; 431 }, 432 433 /** 434 * Sets the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode 435 * @param {cc.TextureAtlas} textureAtlas 436 */ 437 setTextureAtlas:function (textureAtlas) { 438 this.textureAtlas = textureAtlas; 439 }, 440 441 /** 442 * Returns the offset position of the sprite. Calculated automatically by editors like Zwoptex. 443 * @return {cc.Point} 444 */ 445 getOffsetPosition:function () { 446 return cc.p(this._offsetPosition); 447 }, 448 449 _getOffsetX: function () { 450 return this._offsetPosition.x; 451 }, 452 _getOffsetY: function () { 453 return this._offsetPosition.y; 454 }, 455 456 /** 457 * Returns the blend function 458 * @return {cc.BlendFunc} 459 */ 460 getBlendFunc:function () { 461 return this._blendFunc; 462 }, 463 464 /** 465 * Initializes a sprite with an SpriteFrame. The texture and rect in SpriteFrame will be applied on this sprite.<br/> 466 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself, 467 * @param {cc.SpriteFrame} spriteFrame A CCSpriteFrame object. It should includes a valid texture and a rect 468 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 469 */ 470 initWithSpriteFrame:function (spriteFrame) { 471 472 cc.assert(spriteFrame, cc._LogInfos.Sprite_initWithSpriteFrame); 473 474 if(!spriteFrame.textureLoaded()){ 475 //add event listener 476 this._textureLoaded = false; 477 spriteFrame.addLoadedEventListener(this._spriteFrameLoadedCallback, this); 478 } 479 480 var rotated = cc._renderType === cc._RENDER_TYPE_CANVAS ? false : spriteFrame._rotated; 481 var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect(), rotated); 482 this.setSpriteFrame(spriteFrame); 483 484 return ret; 485 }, 486 487 _spriteFrameLoadedCallback:null, 488 489 /** 490 * Initializes a sprite with a sprite frame name. <br/> 491 * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name. <br/> 492 * If the cc.SpriteFrame doesn't exist it will raise an exception. <br/> 493 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 494 * @param {String} spriteFrameName A key string that can fected a volid cc.SpriteFrame from cc.SpriteFrameCache 495 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 496 * @example 497 * var sprite = new cc.Sprite(); 498 * sprite.initWithSpriteFrameName("grossini_dance_01.png"); 499 */ 500 initWithSpriteFrameName:function (spriteFrameName) { 501 cc.assert(spriteFrameName, cc._LogInfos.Sprite_initWithSpriteFrameName); 502 var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); 503 cc.assert(frame, spriteFrameName + cc._LogInfos.Sprite_initWithSpriteFrameName1); 504 return this.initWithSpriteFrame(frame); 505 }, 506 507 /** 508 * Tell the sprite to use batch node render. 509 * @param {cc.SpriteBatchNode} batchNode 510 */ 511 useBatchNode:function (batchNode) { 512 this.textureAtlas = batchNode.textureAtlas; // weak ref 513 this._batchNode = batchNode; 514 }, 515 516 /** 517 * <p> 518 * set the vertex rect.<br/> 519 * It will be called internally by setTextureRect. <br/> 520 * Useful if you want to create 2x images from SD images in Retina Display. <br/> 521 * Do not call it manually. Use setTextureRect instead. <br/> 522 * (override this method to generate "double scale" sprites) 523 * </p> 524 * @param {cc.Rect} rect 525 */ 526 setVertexRect:function (rect) { 527 this._rect.x = rect.x; 528 this._rect.y = rect.y; 529 this._rect.width = rect.width; 530 this._rect.height = rect.height; 531 }, 532 533 /** 534 * Sort all children of this sprite node. 535 * @override 536 */ 537 sortAllChildren:function () { 538 if (this._reorderChildDirty) { 539 var _children = this._children; 540 541 // insertion sort 542 var len = _children.length, i, j, tmp; 543 for(i=1; i<len; i++){ 544 tmp = _children[i]; 545 j = i - 1; 546 547 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller 548 while(j >= 0){ 549 if(tmp._localZOrder < _children[j]._localZOrder){ 550 _children[j+1] = _children[j]; 551 }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){ 552 _children[j+1] = _children[j]; 553 }else{ 554 break; 555 } 556 j--; 557 } 558 _children[j+1] = tmp; 559 } 560 561 if (this._batchNode) { 562 this._arrayMakeObjectsPerformSelector(_children, cc.Node._StateCallbackType.sortAllChildren); 563 } 564 565 //don't need to check children recursively, that's done in visit of each child 566 this._reorderChildDirty = false; 567 } 568 569 }, 570 571 /** 572 * Reorders a child according to a new z value. (override cc.Node ) 573 * @param {cc.Node} child 574 * @param {Number} zOrder 575 * @override 576 */ 577 reorderChild:function (child, zOrder) { 578 cc.assert(child, cc._LogInfos.Sprite_reorderChild_2); 579 if(this._children.indexOf(child) === -1){ 580 cc.log(cc._LogInfos.Sprite_reorderChild); 581 return; 582 } 583 584 if (zOrder === child.zIndex) 585 return; 586 587 if (this._batchNode && !this._reorderChildDirty) { 588 this._setReorderChildDirtyRecursively(); 589 this._batchNode.reorderBatch(true); 590 } 591 cc.Node.prototype.reorderChild.call(this, child, zOrder); 592 }, 593 594 /** 595 * Removes a child from the sprite. 596 * @param child 597 * @param cleanup whether or not cleanup all running actions 598 * @override 599 */ 600 removeChild:function (child, cleanup) { 601 if (this._batchNode) 602 this._batchNode.removeSpriteFromAtlas(child); 603 cc.Node.prototype.removeChild.call(this, child, cleanup); 604 }, 605 606 /** 607 * Sets whether the sprite is visible or not. 608 * @param {Boolean} visible 609 * @override 610 */ 611 setVisible:function (visible) { 612 cc.Node.prototype.setVisible.call(this, visible); 613 this.setDirtyRecursively(true); 614 }, 615 616 /** 617 * Removes all children from the container. 618 * @param cleanup whether or not cleanup all running actions 619 * @override 620 */ 621 removeAllChildren:function (cleanup) { 622 var locChildren = this._children, locBatchNode = this._batchNode; 623 if (locBatchNode && locChildren != null) { 624 for (var i = 0, len = locChildren.length; i < len; i++) 625 locBatchNode.removeSpriteFromAtlas(locChildren[i]); 626 } 627 628 cc.Node.prototype.removeAllChildren.call(this, cleanup); 629 this._hasChildren = false; 630 }, 631 632 // 633 // cc.Node property overloads 634 // 635 636 /** 637 * Sets recursively the dirty flag. 638 * Used only when parent is cc.SpriteBatchNode 639 * @param {Boolean} value 640 */ 641 setDirtyRecursively:function (value) { 642 this._recursiveDirty = value; 643 this.dirty = value; 644 // recursively set dirty 645 var locChildren = this._children, child, l = locChildren ? locChildren.length : 0; 646 for (var i = 0; i < l; i++) { 647 child = locChildren[i]; 648 (child instanceof cc.Sprite) && child.setDirtyRecursively(true); 649 } 650 }, 651 652 /** 653 * Make the node dirty 654 * @param {Boolean} norecursive When true children will not be set dirty recursively, by default, they will be. 655 * @override 656 */ 657 setNodeDirty: function(norecursive) { 658 cc.Node.prototype.setNodeDirty.call(this); 659 // Lazy set dirty 660 if (!norecursive && this._batchNode && !this._recursiveDirty) { 661 if (this._hasChildren) 662 this.setDirtyRecursively(true); 663 else { 664 this._recursiveDirty = true; 665 this.dirty = true; 666 } 667 } 668 }, 669 670 /** 671 * Sets whether ignore anchor point for positioning 672 * @param {Boolean} relative 673 * @override 674 */ 675 ignoreAnchorPointForPosition:function (relative) { 676 if(this._batchNode){ 677 cc.log(cc._LogInfos.Sprite_ignoreAnchorPointForPosition); 678 return; 679 } 680 cc.Node.prototype.ignoreAnchorPointForPosition.call(this, relative); 681 }, 682 683 /** 684 * Sets whether the sprite should be flipped horizontally or not. 685 * @param {Boolean} flippedX true if the sprite should be flipped horizontally, false otherwise. 686 */ 687 setFlippedX:function (flippedX) { 688 if (this._flippedX != flippedX) { 689 this._flippedX = flippedX; 690 this.setTextureRect(this._rect, this._rectRotated, this._contentSize); 691 this.setNodeDirty(true); 692 } 693 }, 694 695 /** 696 * Sets whether the sprite should be flipped vertically or not. 697 * @param {Boolean} flippedY true if the sprite should be flipped vertically, false otherwise. 698 */ 699 setFlippedY:function (flippedY) { 700 if (this._flippedY != flippedY) { 701 this._flippedY = flippedY; 702 this.setTextureRect(this._rect, this._rectRotated, this._contentSize); 703 this.setNodeDirty(true); 704 } 705 }, 706 707 /** 708 * <p> 709 * Returns the flag which indicates whether the sprite is flipped horizontally or not. <br/> 710 * <br/> 711 * It only flips the texture of the sprite, and not the texture of the sprite's children. <br/> 712 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 713 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 714 * sprite.setScaleX(sprite.getScaleX() * -1); <p/> 715 * @return {Boolean} true if the sprite is flipped horizontally, false otherwise. 716 */ 717 isFlippedX:function () { 718 return this._flippedX; 719 }, 720 721 /** 722 * <p> 723 * Return the flag which indicates whether the sprite is flipped vertically or not. <br/> 724 * <br/> 725 * It only flips the texture of the sprite, and not the texture of the sprite's children. <br/> 726 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 727 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 728 * sprite.setScaleY(sprite.getScaleY() * -1); <p/> 729 * @return {Boolean} true if the sprite is flipped vertically, false otherwise. 730 */ 731 isFlippedY:function () { 732 return this._flippedY; 733 }, 734 735 // 736 // RGBA protocol 737 // 738 /** 739 * Sets whether opacity modify color or not. 740 * @function 741 * @param {Boolean} modify 742 */ 743 setOpacityModifyRGB:null, 744 745 /** 746 * Returns whether opacity modify color or not. 747 * @return {Boolean} 748 */ 749 isOpacityModifyRGB:function () { 750 return this._opacityModifyRGB; 751 }, 752 753 /** 754 * Update the display opacity. 755 * @function 756 */ 757 updateDisplayedOpacity: null, 758 759 // Animation 760 761 /** 762 * Changes the display frame with animation name and index.<br/> 763 * The animation name will be get from the CCAnimationCache 764 * @param {String} animationName 765 * @param {Number} frameIndex 766 */ 767 setDisplayFrameWithAnimationName:function (animationName, frameIndex) { 768 cc.assert(animationName, cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_3); 769 770 var cache = cc.animationCache.getAnimation(animationName); 771 if(!cache){ 772 cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName); 773 return; 774 } 775 var animFrame = cache.getFrames()[frameIndex]; 776 if(!animFrame){ 777 cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_2); 778 return; 779 } 780 this.setSpriteFrame(animFrame.getSpriteFrame()); 781 }, 782 783 /** 784 * Returns the batch node object if this sprite is rendered by cc.SpriteBatchNode 785 * @returns {cc.SpriteBatchNode|null} The cc.SpriteBatchNode object if this sprite is rendered by cc.SpriteBatchNode, null if the sprite isn't used batch node. 786 */ 787 getBatchNode:function () { 788 return this._batchNode; 789 }, 790 791 _setReorderChildDirtyRecursively:function () { 792 //only set parents flag the first time 793 if (!this._reorderChildDirty) { 794 this._reorderChildDirty = true; 795 var pNode = this._parent; 796 while (pNode && pNode != this._batchNode) { 797 pNode._setReorderChildDirtyRecursively(); 798 pNode = pNode.parent; 799 } 800 } 801 }, 802 803 // CCTextureProtocol 804 /** 805 * Returns the texture of the sprite node 806 * @returns {cc.Texture2D} 807 */ 808 getTexture:function () { 809 return this._texture; 810 }, 811 812 _quad: null, // vertex coords, texture coords and color info 813 _quadWebBuffer: null, 814 _quadDirty: false, 815 _colorized: false, 816 _blendFuncStr: "source", 817 _originalTexture: null, 818 _textureRect_Canvas: null, 819 _drawSize_Canvas: null, 820 821 ctor: null, 822 823 _softInit: function (fileName, rect, rotated) { 824 if (fileName === undefined) 825 cc.Sprite.prototype.init.call(this); 826 else if (cc.isString(fileName)) { 827 if (fileName[0] === "#") { 828 // Init with a sprite frame name 829 var frameName = fileName.substr(1, fileName.length - 1); 830 var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName); 831 this.initWithSpriteFrame(spriteFrame); 832 } else { 833 // Init with filename and rect 834 cc.Sprite.prototype.init.call(this, fileName, rect); 835 } 836 } else if (cc.isObject(fileName)) { 837 if (fileName instanceof cc.Texture2D) { 838 // Init with texture and rect 839 this.initWithTexture(fileName, rect, rotated); 840 } else if (fileName instanceof cc.SpriteFrame) { 841 // Init with a sprite frame 842 this.initWithSpriteFrame(fileName); 843 } else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) { 844 // Init with a canvas or image element 845 var texture2d = new cc.Texture2D(); 846 texture2d.initWithElement(fileName); 847 texture2d.handleLoadedTexture(); 848 this.initWithTexture(texture2d); 849 } 850 } 851 }, 852 853 /** 854 * Returns the quad (tex coords, vertex coords and color) information. 855 * @return {cc.V3F_C4B_T2F_Quad} 856 */ 857 getQuad:function () { 858 return this._quad; 859 }, 860 861 /** 862 * conforms to cc.TextureProtocol protocol 863 * @function 864 * @param {Number|cc.BlendFunc} src 865 * @param {Number} dst 866 */ 867 setBlendFunc: null, 868 869 /** 870 * Initializes an empty sprite with nothing init.<br/> 871 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 872 * @function 873 * @return {Boolean} 874 */ 875 init:null, 876 877 /** 878 * <p> 879 * Initializes a sprite with an image filename.<br/> 880 * 881 * This method will find pszFilename from local file system, load its content to CCTexture2D,<br/> 882 * then use CCTexture2D to create a sprite.<br/> 883 * After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/> 884 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 885 * </p> 886 * @param {String} filename The path to an image file in local file system 887 * @param {cc.Rect} rect The rectangle assigned the content area from texture. 888 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 889 */ 890 initWithFile:function (filename, rect) { 891 cc.assert(filename, cc._LogInfos.Sprite_initWithFile); 892 893 var tex = cc.textureCache.getTextureForKey(filename); 894 if (!tex) { 895 tex = cc.textureCache.addImage(filename); 896 return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height)); 897 } else { 898 if (!rect) { 899 var size = tex.getContentSize(); 900 rect = cc.rect(0, 0, size.width, size.height); 901 } 902 return this.initWithTexture(tex, rect); 903 } 904 }, 905 906 /** 907 * Initializes a sprite with a texture and a rect in points, optionally rotated. <br/> 908 * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/> 909 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 910 * @function 911 * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites. 912 * @param {cc.Rect} rect Only the contents inside rect of this texture will be applied for this sprite. 913 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 914 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 915 */ 916 initWithTexture: null, 917 918 _textureLoadedCallback: null, 919 920 /** 921 * Updates the texture rect of the CCSprite in points. 922 * @function 923 * @param {cc.Rect} rect a rect of texture 924 * @param {Boolean} [rotated] Whether or not the texture is rotated 925 * @param {cc.Size} [untrimmedSize] The original pixels size of the texture 926 */ 927 setTextureRect:null, 928 929 // BatchNode methods 930 /** 931 * Updates the quad according the the rotation, position, scale values. 932 * @function 933 */ 934 updateTransform: null, 935 936 /** 937 * Add child to sprite (override cc.Node) 938 * @function 939 * @param {cc.Sprite} child 940 * @param {Number} localZOrder child's zOrder 941 * @param {String} tag child's tag 942 * @override 943 */ 944 addChild: null, 945 946 /** 947 * Update sprite's color 948 */ 949 updateColor:function () { 950 var locDisplayedColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity; 951 var color4 = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: locDisplayedOpacity}; 952 // special opacity for premultiplied textures 953 if (this._opacityModifyRGB) { 954 color4.r *= locDisplayedOpacity / 255.0; 955 color4.g *= locDisplayedOpacity / 255.0; 956 color4.b *= locDisplayedOpacity / 255.0; 957 } 958 var locQuad = this._quad; 959 locQuad.bl.colors = color4; 960 locQuad.br.colors = color4; 961 locQuad.tl.colors = color4; 962 locQuad.tr.colors = color4; 963 964 // renders using Sprite Manager 965 if (this._batchNode) { 966 if (this.atlasIndex != cc.Sprite.INDEX_NOT_INITIALIZED) { 967 this.textureAtlas.updateQuad(locQuad, this.atlasIndex) 968 } else { 969 // no need to set it recursively 970 // update dirty_, don't update recursiveDirty_ 971 this.dirty = true; 972 } 973 } 974 // self render 975 // do nothing 976 this._quadDirty = true; 977 }, 978 979 /** 980 * Sets opacity of the sprite 981 * @function 982 * @param {Number} opacity 983 */ 984 setOpacity:null, 985 986 /** 987 * Sets color of the sprite 988 * @function 989 * @param {cc.Color} color3 990 */ 991 setColor: null, 992 993 /** 994 * Updates the display color 995 * @function 996 */ 997 updateDisplayedColor: null, 998 999 // Frames 1000 /** 1001 * Sets a new sprite frame to the sprite. 1002 * @function 1003 * @param {cc.SpriteFrame|String} newFrame 1004 */ 1005 setSpriteFrame: null, 1006 1007 /** 1008 * Sets a new display frame to the sprite. 1009 * @param {cc.SpriteFrame|String} newFrame 1010 * @deprecated 1011 */ 1012 setDisplayFrame: function(newFrame){ 1013 cc.log(cc._LogInfos.Sprite_setDisplayFrame); 1014 this.setSpriteFrame(newFrame); 1015 }, 1016 1017 /** 1018 * Returns whether or not a cc.SpriteFrame is being displayed 1019 * @function 1020 * @param {cc.SpriteFrame} frame 1021 * @return {Boolean} 1022 */ 1023 isFrameDisplayed: null, 1024 1025 /** 1026 * Returns the current displayed frame. 1027 * @return {cc.SpriteFrame} 1028 */ 1029 displayFrame: function () { 1030 return cc.SpriteFrame.create(this._texture, 1031 cc.rectPointsToPixels(this._rect), 1032 this._rectRotated, 1033 cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter), 1034 cc.sizePointsToPixels(this._contentSize)); 1035 }, 1036 1037 /** 1038 * Sets the batch node to sprite 1039 * @function 1040 * @param {cc.SpriteBatchNode|null} spriteBatchNode 1041 * @example 1042 * var batch = cc.SpriteBatchNode.create("Images/grossini_dance_atlas.png", 15); 1043 * var sprite = cc.Sprite.create(batch.texture, cc.rect(0, 0, 57, 57)); 1044 * batch.addChild(sprite); 1045 * layer.addChild(batch); 1046 */ 1047 setBatchNode:null, 1048 1049 // CCTextureProtocol 1050 /** 1051 * Sets the texture of sprite 1052 * @function 1053 * @param {cc.Texture2D|String} texture 1054 */ 1055 setTexture: null, 1056 1057 // Texture protocol 1058 _updateBlendFunc:function () { 1059 if(this._batchNode){ 1060 cc.log(cc._LogInfos.Sprite__updateBlendFunc); 1061 return; 1062 } 1063 1064 // it's possible to have an untextured sprite 1065 if (!this._texture || !this._texture.hasPremultipliedAlpha()) { 1066 this._blendFunc.src = cc.SRC_ALPHA; 1067 this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; 1068 this.opacityModifyRGB = false; 1069 } else { 1070 this._blendFunc.src = cc.BLEND_SRC; 1071 this._blendFunc.dst = cc.BLEND_DST; 1072 this.opacityModifyRGB = true; 1073 } 1074 }, 1075 1076 _changeTextureColor: function () { 1077 var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect(); 1078 if (locTexture && locRect.validRect && this._originalTexture) { 1079 locElement = locTexture.getHtmlElementObj(); 1080 if (!locElement) 1081 return; 1082 1083 this._colorized = true; 1084 if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor 1085 && this._originalTexture._htmlElementObj != locElement) 1086 cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect, locElement); 1087 else { 1088 locElement = cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect); 1089 locTexture = new cc.Texture2D(); 1090 locTexture.initWithElement(locElement); 1091 locTexture.handleLoadedTexture(); 1092 this.texture = locTexture; 1093 } 1094 } 1095 }, 1096 1097 _setTextureCoords:function (rect) { 1098 rect = cc.rectPointsToPixels(rect); 1099 1100 var tex = this._batchNode ? this.textureAtlas.texture : this._texture; 1101 if (!tex) 1102 return; 1103 1104 var atlasWidth = tex.pixelsWidth; 1105 var atlasHeight = tex.pixelsHeight; 1106 1107 var left, right, top, bottom, tempSwap, locQuad = this._quad; 1108 if (this._rectRotated) { 1109 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 1110 left = (2 * rect.x + 1) / (2 * atlasWidth); 1111 right = left + (rect.height * 2 - 2) / (2 * atlasWidth); 1112 top = (2 * rect.y + 1) / (2 * atlasHeight); 1113 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); 1114 } else { 1115 left = rect.x / atlasWidth; 1116 right = (rect.x + rect.height) / atlasWidth; 1117 top = rect.y / atlasHeight; 1118 bottom = (rect.y + rect.width) / atlasHeight; 1119 }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 1120 1121 if (this._flippedX) { 1122 tempSwap = top; 1123 top = bottom; 1124 bottom = tempSwap; 1125 } 1126 1127 if (this._flippedY) { 1128 tempSwap = left; 1129 left = right; 1130 right = tempSwap; 1131 } 1132 1133 locQuad.bl.texCoords.u = left; 1134 locQuad.bl.texCoords.v = top; 1135 locQuad.br.texCoords.u = left; 1136 locQuad.br.texCoords.v = bottom; 1137 locQuad.tl.texCoords.u = right; 1138 locQuad.tl.texCoords.v = top; 1139 locQuad.tr.texCoords.u = right; 1140 locQuad.tr.texCoords.v = bottom; 1141 } else { 1142 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 1143 left = (2 * rect.x + 1) / (2 * atlasWidth); 1144 right = left + (rect.width * 2 - 2) / (2 * atlasWidth); 1145 top = (2 * rect.y + 1) / (2 * atlasHeight); 1146 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); 1147 } else { 1148 left = rect.x / atlasWidth; 1149 right = (rect.x + rect.width) / atlasWidth; 1150 top = rect.y / atlasHeight; 1151 bottom = (rect.y + rect.height) / atlasHeight; 1152 } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 1153 1154 if (this._flippedX) { 1155 tempSwap = left; 1156 left = right; 1157 right = tempSwap; 1158 } 1159 1160 if (this._flippedY) { 1161 tempSwap = top; 1162 top = bottom; 1163 bottom = tempSwap; 1164 } 1165 1166 locQuad.bl.texCoords.u = left; 1167 locQuad.bl.texCoords.v = bottom; 1168 locQuad.br.texCoords.u = right; 1169 locQuad.br.texCoords.v = bottom; 1170 locQuad.tl.texCoords.u = left; 1171 locQuad.tl.texCoords.v = top; 1172 locQuad.tr.texCoords.u = right; 1173 locQuad.tr.texCoords.v = top; 1174 } 1175 this._quadDirty = true; 1176 }, 1177 /** 1178 * draw sprite to canvas 1179 * @function 1180 */ 1181 draw: null 1182 }); 1183 1184 /** 1185 * Create a sprite with image path or frame name or texture or spriteFrame. 1186 * @deprecated since v3.0, please use new construction instead 1187 * @see cc.Sprite 1188 * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". 1189 * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. 1190 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 1191 * @return {cc.Sprite} A valid sprite object 1192 */ 1193 cc.Sprite.create = function (fileName, rect, rotated) { 1194 return new cc.Sprite(fileName, rect, rotated); 1195 }; 1196 1197 /** 1198 * @deprecated since v3.0, please use new construction instead 1199 * @see cc.Sprite 1200 * @function 1201 */ 1202 cc.Sprite.createWithTexture = cc.Sprite.create; 1203 1204 /** 1205 * @deprecated since v3.0, please use new construction instead 1206 * @see cc.Sprite 1207 * @function 1208 */ 1209 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create; 1210 1211 /** 1212 * @deprecated since v3.0, please use new construction instead 1213 * @see cc.Sprite 1214 * @function 1215 */ 1216 cc.Sprite.createWithSpriteFrame = cc.Sprite.create; 1217 /** 1218 * cc.Sprite invalid index on the cc.SpriteBatchNode 1219 * @constant 1220 * @type {Number} 1221 */ 1222 cc.Sprite.INDEX_NOT_INITIALIZED = -1; 1223 1224 1225 if (cc._renderType === cc._RENDER_TYPE_CANVAS) { 1226 var _p = cc.Sprite.prototype; 1227 1228 _p._spriteFrameLoadedCallback = function(spriteFrame){ 1229 var _t = this; 1230 _t.setNodeDirty(true); 1231 _t.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize()); 1232 var curColor = _t.color; 1233 if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255) 1234 _t._changeTextureColor(); 1235 1236 _t._callLoadedEventCallbacks(); 1237 }; 1238 1239 _p.setOpacityModifyRGB = function (modify) { 1240 if (this._opacityModifyRGB !== modify) { 1241 this._opacityModifyRGB = modify; 1242 this.setNodeDirty(true); 1243 } 1244 }; 1245 1246 _p.updateDisplayedOpacity = function (parentOpacity) { 1247 cc.Node.prototype.updateDisplayedOpacity.call(this, parentOpacity); 1248 this._setNodeDirtyForCache(); 1249 }; 1250 1251 _p.ctor = function (fileName, rect, rotated) { 1252 var self = this; 1253 cc.Node.prototype.ctor.call(self); 1254 self._shouldBeHidden = false; 1255 self._offsetPosition = cc.p(0, 0); 1256 self._unflippedOffsetPositionFromCenter = cc.p(0, 0); 1257 self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 1258 self._rect = cc.rect(0, 0, 0, 0); 1259 1260 self._newTextureWhenChangeColor = false; 1261 self._textureLoaded = true; 1262 self._textureRect_Canvas = {x: 0, y: 0, width: 0, height:0, validRect: false}; 1263 self._drawSize_Canvas = cc.size(0, 0); 1264 1265 self._softInit(fileName, rect, rotated); 1266 }; 1267 1268 _p.setBlendFunc = function (src, dst) { 1269 var _t = this, locBlendFunc = this._blendFunc; 1270 if (dst === undefined) { 1271 locBlendFunc.src = src.src; 1272 locBlendFunc.dst = src.dst; 1273 } else { 1274 locBlendFunc.src = src; 1275 locBlendFunc.dst = dst; 1276 } 1277 if (cc._renderType === cc._RENDER_TYPE_CANVAS) 1278 _t._blendFuncStr = cc._getCompositeOperationByBlendFunc(locBlendFunc); 1279 }; 1280 1281 _p.init = function () { 1282 var _t = this; 1283 if (arguments.length > 0) 1284 return _t.initWithFile(arguments[0], arguments[1]); 1285 1286 cc.Node.prototype.init.call(_t); 1287 _t.dirty = _t._recursiveDirty = false; 1288 _t._opacityModifyRGB = true; 1289 1290 _t._blendFunc.src = cc.BLEND_SRC; 1291 _t._blendFunc.dst = cc.BLEND_DST; 1292 1293 // update texture (calls _updateBlendFunc) 1294 _t.texture = null; 1295 _t._textureLoaded = true; 1296 _t._flippedX = _t._flippedY = false; 1297 1298 // default transform anchor: center 1299 _t.anchorX = 0.5; 1300 _t.anchorY = 0.5; 1301 1302 // zwoptex default values 1303 _t._offsetPosition.x = 0; 1304 _t._offsetPosition.y = 0; 1305 _t._hasChildren = false; 1306 1307 // updated in "useSelfRender" 1308 // Atlas: TexCoords 1309 _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0)); 1310 return true; 1311 }; 1312 1313 _p.initWithTexture = function (texture, rect, rotated) { 1314 var _t = this; 1315 cc.assert(arguments.length != 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture); 1316 1317 rotated = rotated || false; 1318 1319 if (rotated && texture.isLoaded()) { 1320 var tempElement = texture.getHtmlElementObj(); 1321 tempElement = cc.cutRotateImageToCanvas(tempElement, rect); 1322 var tempTexture = new cc.Texture2D(); 1323 tempTexture.initWithElement(tempElement); 1324 tempTexture.handleLoadedTexture(); 1325 texture = tempTexture; 1326 1327 _t._rect = cc.rect(0, 0, rect.width, rect.height); 1328 } 1329 1330 if (!cc.Node.prototype.init.call(_t)) 1331 return false; 1332 1333 _t._batchNode = null; 1334 _t._recursiveDirty = false; 1335 _t.dirty = false; 1336 _t._opacityModifyRGB = true; 1337 1338 _t._blendFunc.src = cc.BLEND_SRC; 1339 _t._blendFunc.dst = cc.BLEND_DST; 1340 1341 _t._flippedX = _t._flippedY = false; 1342 1343 // default transform anchor: center 1344 _t.anchorX = 0.5; 1345 _t.anchorY = 0.5; 1346 1347 // zwoptex default values 1348 _t._offsetPosition.x = 0; 1349 _t._offsetPosition.y = 0; 1350 _t._hasChildren = false; 1351 1352 var locTextureLoaded = texture.isLoaded(); 1353 _t._textureLoaded = locTextureLoaded; 1354 1355 if (!locTextureLoaded) { 1356 _t._rectRotated = rotated; 1357 if (rect) { 1358 _t._rect.x = rect.x; 1359 _t._rect.y = rect.y; 1360 _t._rect.width = rect.width; 1361 _t._rect.height = rect.height; 1362 } 1363 if(_t.texture) 1364 _t.texture.removeLoadedEventListener(_t); 1365 texture.addLoadedEventListener(_t._textureLoadedCallback, _t); 1366 _t.texture = texture; 1367 return true; 1368 } 1369 1370 if (!rect) { 1371 rect = cc.rect(0, 0, texture.width, texture.height); 1372 } 1373 1374 if(texture && texture.url) { 1375 var _x = rect.x + rect.width, _y = rect.y + rect.height; 1376 if(_x > texture.width){ 1377 cc.error(cc._LogInfos.RectWidth, texture.url); 1378 } 1379 if(_y > texture.height){ 1380 cc.error(cc._LogInfos.RectHeight, texture.url); 1381 } 1382 } 1383 _t._originalTexture = texture; 1384 _t.texture = texture; 1385 _t.setTextureRect(rect, rotated); 1386 1387 // by default use "Self Render". 1388 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" 1389 _t.batchNode = null; 1390 return true; 1391 }; 1392 1393 _p._textureLoadedCallback = function (sender) { 1394 var _t = this; 1395 if(_t._textureLoaded) 1396 return; 1397 1398 _t._textureLoaded = true; 1399 var locRect = _t._rect; 1400 if (!locRect) { 1401 locRect = cc.rect(0, 0, sender.width, sender.height); 1402 } else if (cc._rectEqualToZero(locRect)) { 1403 locRect.width = sender.width; 1404 locRect.height = sender.height; 1405 } 1406 _t._originalTexture = sender; 1407 1408 _t.texture = sender; 1409 _t.setTextureRect(locRect, _t._rectRotated); 1410 1411 //set the texture's color after the it loaded 1412 var locColor = this._displayedColor; 1413 if(locColor.r != 255 || locColor.g != 255 || locColor.b != 255) 1414 _t._changeTextureColor(); 1415 1416 // by default use "Self Render". 1417 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" 1418 _t.batchNode = _t._batchNode; 1419 _t._callLoadedEventCallbacks(); 1420 }; 1421 1422 _p.setTextureRect = function (rect, rotated, untrimmedSize) { 1423 var _t = this; 1424 _t._rectRotated = rotated || false; 1425 _t.setContentSize(untrimmedSize || rect); 1426 1427 _t.setVertexRect(rect); 1428 1429 var locTextureRect = _t._textureRect_Canvas, scaleFactor = cc.contentScaleFactor(); 1430 locTextureRect.x = 0 | (rect.x * scaleFactor); 1431 locTextureRect.y = 0 | (rect.y * scaleFactor); 1432 locTextureRect.width = 0 | (rect.width * scaleFactor); 1433 locTextureRect.height = 0 | (rect.height * scaleFactor); 1434 locTextureRect.validRect = !(locTextureRect.width === 0 || locTextureRect.height === 0 || locTextureRect.x < 0 || locTextureRect.y < 0); 1435 1436 var relativeOffset = _t._unflippedOffsetPositionFromCenter; 1437 if (_t._flippedX) 1438 relativeOffset.x = -relativeOffset.x; 1439 if (_t._flippedY) 1440 relativeOffset.y = -relativeOffset.y; 1441 _t._offsetPosition.x = relativeOffset.x + (_t._contentSize.width - _t._rect.width) / 2; 1442 _t._offsetPosition.y = relativeOffset.y + (_t._contentSize.height - _t._rect.height) / 2; 1443 1444 // rendering using batch node 1445 if (_t._batchNode) { 1446 // update dirty, don't update _recursiveDirty 1447 _t.dirty = true; 1448 } 1449 }; 1450 1451 _p.updateTransform = function () { 1452 var _t = this; 1453 //cc.assert(_t._batchNode, "updateTransform is only valid when cc.Sprite is being rendered using an cc.SpriteBatchNode"); 1454 1455 // recaculate matrix only if it is dirty 1456 if (_t.dirty) { 1457 // If it is not visible, or one of its ancestors is not visible, then do nothing: 1458 var locParent = _t._parent; 1459 if (!_t._visible || ( locParent && locParent != _t._batchNode && locParent._shouldBeHidden)) { 1460 _t._shouldBeHidden = true; 1461 } else { 1462 _t._shouldBeHidden = false; 1463 1464 if (!locParent || locParent == _t._batchNode) { 1465 _t._transformToBatch = _t.nodeToParentTransform(); 1466 } else { 1467 //cc.assert(_t._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite"); 1468 _t._transformToBatch = cc.affineTransformConcat(_t.nodeToParentTransform(), locParent._transformToBatch); 1469 } 1470 } 1471 _t._recursiveDirty = false; 1472 _t.dirty = false; 1473 } 1474 1475 // recursively iterate over children 1476 if (_t._hasChildren) 1477 _t._arrayMakeObjectsPerformSelector(_t._children, cc.Node._StateCallbackType.updateTransform); 1478 }; 1479 1480 _p.addChild = function (child, localZOrder, tag) { 1481 1482 cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2); 1483 1484 if (localZOrder == null) 1485 localZOrder = child._localZOrder; 1486 if (tag == null) 1487 tag = child.tag; 1488 1489 //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check 1490 cc.Node.prototype.addChild.call(this, child, localZOrder, tag); 1491 this._hasChildren = true; 1492 }; 1493 1494 _p.setOpacity = function (opacity) { 1495 cc.Node.prototype.setOpacity.call(this, opacity); 1496 this._setNodeDirtyForCache(); 1497 }; 1498 1499 _p.setColor = function (color3) { 1500 var _t = this; 1501 var curColor = _t.color; 1502 this._oldDisplayColor = curColor; 1503 if ((curColor.r === color3.r) && (curColor.g === color3.g) && (curColor.b === color3.b)) 1504 return; 1505 cc.Node.prototype.setColor.call(_t, color3); 1506 }; 1507 1508 _p.updateDisplayedColor = function (parentColor) { 1509 var _t = this; 1510 cc.Node.prototype.updateDisplayedColor.call(_t, parentColor); 1511 var oColor = _t._oldDisplayColor; 1512 var nColor = _t._displayedColor; 1513 if (oColor.r === nColor.r && oColor.g === nColor.g && oColor.b === nColor.b) 1514 return; 1515 1516 _t._changeTextureColor(); 1517 _t._setNodeDirtyForCache(); 1518 }; 1519 1520 _p.setSpriteFrame = function (newFrame) { 1521 var _t = this; 1522 if(cc.isString(newFrame)){ 1523 newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame); 1524 cc.assert(newFrame, cc._LogInfos.CCSpriteBatchNode_setSpriteFrame) 1525 } 1526 1527 _t.setNodeDirty(true); 1528 1529 var frameOffset = newFrame.getOffset(); 1530 _t._unflippedOffsetPositionFromCenter.x = frameOffset.x; 1531 _t._unflippedOffsetPositionFromCenter.y = frameOffset.y; 1532 1533 // update rect 1534 _t._rectRotated = newFrame.isRotated(); 1535 1536 var pNewTexture = newFrame.getTexture(); 1537 var locTextureLoaded = newFrame.textureLoaded(); 1538 if (!locTextureLoaded) { 1539 _t._textureLoaded = false; 1540 newFrame.addLoadedEventListener(function (sender) { 1541 _t._textureLoaded = true; 1542 var locNewTexture = sender.getTexture(); 1543 if (locNewTexture != _t._texture) 1544 _t.texture = locNewTexture; 1545 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize()); 1546 _t._callLoadedEventCallbacks(); 1547 }, _t); 1548 } 1549 // update texture before updating texture rect 1550 if (pNewTexture != _t._texture) 1551 _t.texture = pNewTexture; 1552 1553 if (_t._rectRotated) 1554 _t._originalTexture = pNewTexture; 1555 1556 _t.setTextureRect(newFrame.getRect(), _t._rectRotated, newFrame.getOriginalSize()); 1557 _t._colorized = false; 1558 if (locTextureLoaded) { 1559 var curColor = _t.color; 1560 if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255) 1561 _t._changeTextureColor(); 1562 } 1563 }; 1564 1565 _p.isFrameDisplayed = function (frame) { 1566 if (frame.getTexture() != this._texture) 1567 return false; 1568 return cc.rectEqualToRect(frame.getRect(), this._rect); 1569 }; 1570 1571 _p.setBatchNode = function (spriteBatchNode) { 1572 var _t = this; 1573 _t._batchNode = spriteBatchNode; // weak reference 1574 1575 // self render 1576 if (!_t._batchNode) { 1577 _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED; 1578 _t.textureAtlas = null; 1579 _t._recursiveDirty = false; 1580 _t.dirty = false; 1581 } else { 1582 // using batch 1583 _t._transformToBatch = cc.affineTransformIdentity(); 1584 _t.textureAtlas = _t._batchNode.textureAtlas; // weak ref 1585 } 1586 }; 1587 1588 _p.setTexture = function (texture) { 1589 var _t = this; 1590 if(texture && (cc.isString(texture))){ 1591 texture = cc.textureCache.addImage(texture); 1592 _t.setTexture(texture); 1593 1594 //TODO 1595 var size = texture.getContentSize(); 1596 _t.setTextureRect(cc.rect(0,0, size.width, size.height)); 1597 return; 1598 } 1599 1600 // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet 1601 cc.assert(!texture || texture instanceof cc.Texture2D, cc._LogInfos.CCSpriteBatchNode_setTexture); 1602 1603 if (_t._texture != texture) { 1604 if (texture && texture.getHtmlElementObj() instanceof HTMLImageElement) { 1605 _t._originalTexture = texture; 1606 } 1607 _t._texture = texture; 1608 } 1609 }; 1610 1611 _p.draw = function (ctx) { 1612 var _t = this; 1613 if (!_t._textureLoaded) 1614 return; 1615 1616 var context = ctx || cc._renderContext; 1617 if (_t._blendFuncStr != "source") 1618 context.globalCompositeOperation = _t._blendFuncStr; 1619 1620 var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY(); 1621 1622 context.globalAlpha = _t._displayedOpacity / 255; 1623 var locRect = _t._rect, locContentSize = _t._contentSize, locOffsetPosition = _t._offsetPosition, locDrawSizeCanvas = _t._drawSize_Canvas; 1624 var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = _t._textureRect_Canvas; 1625 locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX; 1626 locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY; 1627 1628 if (_t._flippedX || _t._flippedY) { 1629 context.save(); 1630 if (_t._flippedX) { 1631 flipXOffset = -locOffsetPosition.x - locRect.width; 1632 context.scale(-1, 1); 1633 } 1634 if (_t._flippedY) { 1635 flipYOffset = locOffsetPosition.y; 1636 context.scale(1, -1); 1637 } 1638 } 1639 1640 flipXOffset *= locEGL_ScaleX; 1641 flipYOffset *= locEGL_ScaleY; 1642 1643 if (_t._texture && locTextureCoord.validRect) { 1644 var image = _t._texture.getHtmlElementObj(); 1645 if (_t._colorized) { 1646 context.drawImage(image, 1647 0, 0, locTextureCoord.width, locTextureCoord.height, 1648 flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height); 1649 } else { 1650 context.drawImage(image, 1651 locTextureCoord.x, locTextureCoord.y, locTextureCoord.width, locTextureCoord.height, 1652 flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height); 1653 } 1654 } else if (!_t._texture && locTextureCoord.validRect) { 1655 var curColor = _t.color; 1656 context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)"; 1657 context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY); 1658 } 1659 1660 if (cc.SPRITE_DEBUG_DRAW === 1 || _t._showNode) { 1661 // draw bounding box 1662 context.strokeStyle = "rgba(0,255,0,1)"; 1663 flipXOffset /= locEGL_ScaleX; 1664 flipYOffset /= locEGL_ScaleY; 1665 flipYOffset = -flipYOffset; 1666 var vertices1 = [cc.p(flipXOffset, flipYOffset), 1667 cc.p(flipXOffset + locRect.width, flipYOffset), 1668 cc.p(flipXOffset + locRect.width, flipYOffset - locRect.height), 1669 cc.p(flipXOffset, flipYOffset - locRect.height)]; 1670 cc._drawingUtil.drawPoly(vertices1, 4, true); 1671 } else if (cc.SPRITE_DEBUG_DRAW === 2) { 1672 // draw texture box 1673 context.strokeStyle = "rgba(0,255,0,1)"; 1674 var drawRect = _t._rect; 1675 flipYOffset = -flipYOffset; 1676 var vertices2 = [cc.p(flipXOffset, flipYOffset), cc.p(flipXOffset + drawRect.width, flipYOffset), 1677 cc.p(flipXOffset + drawRect.width, flipYOffset - drawRect.height), cc.p(flipXOffset, flipYOffset - drawRect.height)]; 1678 cc._drawingUtil.drawPoly(vertices2, 4, true); 1679 } 1680 if (_t._flippedX || _t._flippedY) 1681 context.restore(); 1682 cc.g_NumberOfDraws++; 1683 }; 1684 1685 if(!cc.sys._supportCanvasNewBlendModes) 1686 _p._changeTextureColor = function () { 1687 var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect(); 1688 if (locTexture && locRect.validRect && this._originalTexture) { 1689 locElement = locTexture.getHtmlElementObj(); 1690 if (!locElement) 1691 return; 1692 1693 var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj()); 1694 if (cacheTextureForColor) { 1695 this._colorized = true; 1696 //generate color texture cache 1697 if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor) 1698 cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect, locElement); 1699 else { 1700 locElement = cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect); 1701 locTexture = new cc.Texture2D(); 1702 locTexture.initWithElement(locElement); 1703 locTexture.handleLoadedTexture(); 1704 this.texture = locTexture; 1705 } 1706 } 1707 } 1708 }; 1709 1710 delete _p; 1711 } else { 1712 cc.assert(cc.isFunction(cc._tmp.WebGLSprite), cc._LogInfos.MissingFile, "SpritesWebGL.js"); 1713 cc._tmp.WebGLSprite(); 1714 delete cc._tmp.WebGLSprite; 1715 } 1716 1717 cc.assert(cc.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js"); 1718 cc._tmp.PrototypeSprite(); 1719 delete cc._tmp.PrototypeSprite; 1720 1721