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 (typeof(fileName) === "string") { 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 } 837 else if (typeof(fileName) === "object") { 838 if (fileName instanceof cc.Texture2D) { 839 // Init with texture and rect 840 this.initWithTexture(fileName, rect, rotated); 841 } else if (fileName instanceof cc.SpriteFrame) { 842 // Init with a sprite frame 843 this.initWithSpriteFrame(fileName); 844 } else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) { 845 // Init with a canvas or image element 846 var texture2d = new cc.Texture2D(); 847 texture2d.initWithElement(fileName); 848 texture2d.handleLoadedTexture(); 849 this.initWithTexture(texture2d); 850 } 851 } 852 }, 853 854 /** 855 * Returns the quad (tex coords, vertex coords and color) information. 856 * @return {cc.V3F_C4B_T2F_Quad} 857 */ 858 getQuad:function () { 859 return this._quad; 860 }, 861 862 /** 863 * conforms to cc.TextureProtocol protocol 864 * @function 865 * @param {Number|cc.BlendFunc} src 866 * @param {Number} dst 867 */ 868 setBlendFunc: null, 869 870 /** 871 * Initializes an empty sprite with nothing init.<br/> 872 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 873 * @function 874 * @return {Boolean} 875 */ 876 init:null, 877 878 /** 879 * <p> 880 * Initializes a sprite with an image filename.<br/> 881 * 882 * This method will find pszFilename from local file system, load its content to CCTexture2D,<br/> 883 * then use CCTexture2D to create a sprite.<br/> 884 * After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/> 885 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 886 * </p> 887 * @param {String} filename The path to an image file in local file system 888 * @param {cc.Rect} rect The rectangle assigned the content area from texture. 889 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 890 */ 891 initWithFile:function (filename, rect) { 892 cc.assert(filename, cc._LogInfos.Sprite_initWithFile); 893 894 var tex = cc.textureCache.getTextureForKey(filename); 895 if (!tex) { 896 tex = cc.textureCache.addImage(filename); 897 return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height)); 898 } else { 899 if (!rect) { 900 var size = tex.getContentSize(); 901 rect = cc.rect(0, 0, size.width, size.height); 902 } 903 return this.initWithTexture(tex, rect); 904 } 905 }, 906 907 /** 908 * Initializes a sprite with a texture and a rect in points, optionally rotated. <br/> 909 * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/> 910 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 911 * @function 912 * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites. 913 * @param {cc.Rect} rect Only the contents inside rect of this texture will be applied for this sprite. 914 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 915 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 916 */ 917 initWithTexture: null, 918 919 _textureLoadedCallback: null, 920 921 /** 922 * Updates the texture rect of the CCSprite in points. 923 * @function 924 * @param {cc.Rect} rect a rect of texture 925 * @param {Boolean} [rotated] Whether or not the texture is rotated 926 * @param {cc.Size} [untrimmedSize] The original pixels size of the texture 927 */ 928 setTextureRect:null, 929 930 // BatchNode methods 931 /** 932 * Updates the quad according the the rotation, position, scale values. 933 * @function 934 */ 935 updateTransform: null, 936 937 /** 938 * Add child to sprite (override cc.Node) 939 * @function 940 * @param {cc.Sprite} child 941 * @param {Number} localZOrder child's zOrder 942 * @param {String} tag child's tag 943 * @override 944 */ 945 addChild: null, 946 947 /** 948 * Update sprite's color 949 */ 950 updateColor:function () { 951 var locDisplayedColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity; 952 var color4 = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: locDisplayedOpacity}; 953 // special opacity for premultiplied textures 954 if (this._opacityModifyRGB) { 955 color4.r *= locDisplayedOpacity / 255.0; 956 color4.g *= locDisplayedOpacity / 255.0; 957 color4.b *= locDisplayedOpacity / 255.0; 958 } 959 var locQuad = this._quad; 960 locQuad.bl.colors = color4; 961 locQuad.br.colors = color4; 962 locQuad.tl.colors = color4; 963 locQuad.tr.colors = color4; 964 965 // renders using Sprite Manager 966 if (this._batchNode) { 967 if (this.atlasIndex != cc.Sprite.INDEX_NOT_INITIALIZED) { 968 this.textureAtlas.updateQuad(locQuad, this.atlasIndex) 969 } else { 970 // no need to set it recursively 971 // update dirty_, don't update recursiveDirty_ 972 this.dirty = true; 973 } 974 } 975 // self render 976 // do nothing 977 this._quadDirty = true; 978 }, 979 980 /** 981 * Sets opacity of the sprite 982 * @function 983 * @param {Number} opacity 984 */ 985 setOpacity:null, 986 987 /** 988 * Sets color of the sprite 989 * @function 990 * @param {cc.Color} color3 991 */ 992 setColor: null, 993 994 /** 995 * Updates the display color 996 * @function 997 */ 998 updateDisplayedColor: null, 999 1000 // Frames 1001 /** 1002 * Sets a new sprite frame to the sprite. 1003 * @function 1004 * @param {cc.SpriteFrame|String} newFrame 1005 */ 1006 setSpriteFrame: null, 1007 1008 /** 1009 * Sets a new display frame to the sprite. 1010 * @param {cc.SpriteFrame|String} newFrame 1011 * @deprecated 1012 */ 1013 setDisplayFrame: function(newFrame){ 1014 cc.log(cc._LogInfos.Sprite_setDisplayFrame); 1015 this.setSpriteFrame(newFrame); 1016 }, 1017 1018 /** 1019 * Returns whether or not a cc.SpriteFrame is being displayed 1020 * @function 1021 * @param {cc.SpriteFrame} frame 1022 * @return {Boolean} 1023 */ 1024 isFrameDisplayed: null, 1025 1026 /** 1027 * Returns the current displayed frame. 1028 * @return {cc.SpriteFrame} 1029 */ 1030 displayFrame: function () { 1031 return cc.SpriteFrame.create(this._texture, 1032 cc.rectPointsToPixels(this._rect), 1033 this._rectRotated, 1034 cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter), 1035 cc.sizePointsToPixels(this._contentSize)); 1036 }, 1037 1038 /** 1039 * Sets the batch node to sprite 1040 * @function 1041 * @param {cc.SpriteBatchNode|null} spriteBatchNode 1042 * @example 1043 * var batch = cc.SpriteBatchNode.create("Images/grossini_dance_atlas.png", 15); 1044 * var sprite = cc.Sprite.create(batch.texture, cc.rect(0, 0, 57, 57)); 1045 * batch.addChild(sprite); 1046 * layer.addChild(batch); 1047 */ 1048 setBatchNode:null, 1049 1050 // CCTextureProtocol 1051 /** 1052 * Sets the texture of sprite 1053 * @function 1054 * @param {cc.Texture2D|String} texture 1055 */ 1056 setTexture: null, 1057 1058 // Texture protocol 1059 _updateBlendFunc:function () { 1060 if(this._batchNode){ 1061 cc.log(cc._LogInfos.Sprite__updateBlendFunc); 1062 return; 1063 } 1064 1065 // it's possible to have an untextured sprite 1066 if (!this._texture || !this._texture.hasPremultipliedAlpha()) { 1067 this._blendFunc.src = cc.SRC_ALPHA; 1068 this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; 1069 this.opacityModifyRGB = false; 1070 } else { 1071 this._blendFunc.src = cc.BLEND_SRC; 1072 this._blendFunc.dst = cc.BLEND_DST; 1073 this.opacityModifyRGB = true; 1074 } 1075 }, 1076 1077 _changeTextureColor: function () { 1078 var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect(); 1079 if (locTexture && locRect.validRect && this._originalTexture) { 1080 locElement = locTexture.getHtmlElementObj(); 1081 if (!locElement) 1082 return; 1083 1084 this._colorized = true; 1085 if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor 1086 && this._originalTexture._htmlElementObj != locElement) 1087 cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect, locElement); 1088 else { 1089 locElement = cc.generateTintImageWithMultiply(this._originalTexture._htmlElementObj, this._displayedColor, locRect); 1090 locTexture = new cc.Texture2D(); 1091 locTexture.initWithElement(locElement); 1092 locTexture.handleLoadedTexture(); 1093 this.texture = locTexture; 1094 } 1095 } 1096 }, 1097 1098 _setTextureCoords:function (rect) { 1099 rect = cc.rectPointsToPixels(rect); 1100 1101 var tex = this._batchNode ? this.textureAtlas.texture : this._texture; 1102 if (!tex) 1103 return; 1104 1105 var atlasWidth = tex.pixelsWidth; 1106 var atlasHeight = tex.pixelsHeight; 1107 1108 var left, right, top, bottom, tempSwap, locQuad = this._quad; 1109 if (this._rectRotated) { 1110 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 1111 left = (2 * rect.x + 1) / (2 * atlasWidth); 1112 right = left + (rect.height * 2 - 2) / (2 * atlasWidth); 1113 top = (2 * rect.y + 1) / (2 * atlasHeight); 1114 bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); 1115 } else { 1116 left = rect.x / atlasWidth; 1117 right = (rect.x + rect.height) / atlasWidth; 1118 top = rect.y / atlasHeight; 1119 bottom = (rect.y + rect.width) / atlasHeight; 1120 }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 1121 1122 if (this._flippedX) { 1123 tempSwap = top; 1124 top = bottom; 1125 bottom = tempSwap; 1126 } 1127 1128 if (this._flippedY) { 1129 tempSwap = left; 1130 left = right; 1131 right = tempSwap; 1132 } 1133 1134 locQuad.bl.texCoords.u = left; 1135 locQuad.bl.texCoords.v = top; 1136 locQuad.br.texCoords.u = left; 1137 locQuad.br.texCoords.v = bottom; 1138 locQuad.tl.texCoords.u = right; 1139 locQuad.tl.texCoords.v = top; 1140 locQuad.tr.texCoords.u = right; 1141 locQuad.tr.texCoords.v = bottom; 1142 } else { 1143 if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { 1144 left = (2 * rect.x + 1) / (2 * atlasWidth); 1145 right = left + (rect.width * 2 - 2) / (2 * atlasWidth); 1146 top = (2 * rect.y + 1) / (2 * atlasHeight); 1147 bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); 1148 } else { 1149 left = rect.x / atlasWidth; 1150 right = (rect.x + rect.width) / atlasWidth; 1151 top = rect.y / atlasHeight; 1152 bottom = (rect.y + rect.height) / atlasHeight; 1153 } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 1154 1155 if (this._flippedX) { 1156 tempSwap = left; 1157 left = right; 1158 right = tempSwap; 1159 } 1160 1161 if (this._flippedY) { 1162 tempSwap = top; 1163 top = bottom; 1164 bottom = tempSwap; 1165 } 1166 1167 locQuad.bl.texCoords.u = left; 1168 locQuad.bl.texCoords.v = bottom; 1169 locQuad.br.texCoords.u = right; 1170 locQuad.br.texCoords.v = bottom; 1171 locQuad.tl.texCoords.u = left; 1172 locQuad.tl.texCoords.v = top; 1173 locQuad.tr.texCoords.u = right; 1174 locQuad.tr.texCoords.v = top; 1175 } 1176 this._quadDirty = true; 1177 }, 1178 /** 1179 * draw sprite to canvas 1180 * @function 1181 */ 1182 draw: null 1183 }); 1184 1185 /** 1186 * Create a sprite with image path or frame name or texture or spriteFrame. 1187 * @deprecated since v3.0, please use new construction instead 1188 * @see cc.Sprite 1189 * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". 1190 * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. 1191 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 1192 * @return {cc.Sprite} A valid sprite object 1193 */ 1194 cc.Sprite.create = function (fileName, rect, rotated) { 1195 return new cc.Sprite(fileName, rect, rotated); 1196 }; 1197 1198 /** 1199 * @deprecated since v3.0, please use new construction instead 1200 * @see cc.Sprite 1201 * @function 1202 */ 1203 cc.Sprite.createWithTexture = cc.Sprite.create; 1204 1205 /** 1206 * @deprecated since v3.0, please use new construction instead 1207 * @see cc.Sprite 1208 * @function 1209 */ 1210 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create; 1211 1212 /** 1213 * @deprecated since v3.0, please use new construction instead 1214 * @see cc.Sprite 1215 * @function 1216 */ 1217 cc.Sprite.createWithSpriteFrame = cc.Sprite.create; 1218 /** 1219 * cc.Sprite invalid index on the cc.SpriteBatchNode 1220 * @constant 1221 * @type {Number} 1222 */ 1223 cc.Sprite.INDEX_NOT_INITIALIZED = -1; 1224 1225 1226 if (cc._renderType === cc._RENDER_TYPE_CANVAS) { 1227 var _p = cc.Sprite.prototype; 1228 1229 _p._spriteFrameLoadedCallback = function(spriteFrame){ 1230 var _t = this; 1231 _t.setNodeDirty(true); 1232 _t.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize()); 1233 var curColor = _t.color; 1234 if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255) 1235 _t._changeTextureColor(); 1236 1237 _t._callLoadedEventCallbacks(); 1238 }; 1239 1240 _p.setOpacityModifyRGB = function (modify) { 1241 if (this._opacityModifyRGB !== modify) { 1242 this._opacityModifyRGB = modify; 1243 this.setNodeDirty(true); 1244 } 1245 }; 1246 1247 _p.updateDisplayedOpacity = function (parentOpacity) { 1248 cc.Node.prototype.updateDisplayedOpacity.call(this, parentOpacity); 1249 this._setNodeDirtyForCache(); 1250 }; 1251 1252 _p.ctor = function (fileName, rect, rotated) { 1253 var self = this; 1254 cc.Node.prototype.ctor.call(self); 1255 self._shouldBeHidden = false; 1256 self._offsetPosition = cc.p(0, 0); 1257 self._unflippedOffsetPositionFromCenter = cc.p(0, 0); 1258 self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 1259 self._rect = cc.rect(0, 0, 0, 0); 1260 1261 self._newTextureWhenChangeColor = false; 1262 self._textureLoaded = true; 1263 self._textureRect_Canvas = {x: 0, y: 0, width: 0, height:0, validRect: false}; 1264 self._drawSize_Canvas = cc.size(0, 0); 1265 1266 self._softInit(fileName, rect, rotated); 1267 }; 1268 1269 _p.setBlendFunc = function (src, dst) { 1270 var _t = this, locBlendFunc = this._blendFunc; 1271 if (dst === undefined) { 1272 locBlendFunc.src = src.src; 1273 locBlendFunc.dst = src.dst; 1274 } else { 1275 locBlendFunc.src = src; 1276 locBlendFunc.dst = dst; 1277 } 1278 if (cc._renderType === cc._RENDER_TYPE_CANVAS) 1279 _t._blendFuncStr = cc._getCompositeOperationByBlendFunc(locBlendFunc); 1280 }; 1281 1282 _p.init = function () { 1283 var _t = this; 1284 if (arguments.length > 0) 1285 return _t.initWithFile(arguments[0], arguments[1]); 1286 1287 cc.Node.prototype.init.call(_t); 1288 _t.dirty = _t._recursiveDirty = false; 1289 _t._opacityModifyRGB = true; 1290 1291 _t._blendFunc.src = cc.BLEND_SRC; 1292 _t._blendFunc.dst = cc.BLEND_DST; 1293 1294 // update texture (calls _updateBlendFunc) 1295 _t.texture = null; 1296 _t._textureLoaded = true; 1297 _t._flippedX = _t._flippedY = false; 1298 1299 // default transform anchor: center 1300 _t.anchorX = 0.5; 1301 _t.anchorY = 0.5; 1302 1303 // zwoptex default values 1304 _t._offsetPosition.x = 0; 1305 _t._offsetPosition.y = 0; 1306 _t._hasChildren = false; 1307 1308 // updated in "useSelfRender" 1309 // Atlas: TexCoords 1310 _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0)); 1311 return true; 1312 }; 1313 1314 _p.initWithTexture = function (texture, rect, rotated) { 1315 var _t = this; 1316 cc.assert(arguments.length != 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture); 1317 1318 rotated = rotated || false; 1319 1320 if (rotated && texture.isLoaded()) { 1321 var tempElement = texture.getHtmlElementObj(); 1322 tempElement = cc.cutRotateImageToCanvas(tempElement, rect); 1323 var tempTexture = new cc.Texture2D(); 1324 tempTexture.initWithElement(tempElement); 1325 tempTexture.handleLoadedTexture(); 1326 texture = tempTexture; 1327 1328 _t._rect = cc.rect(0, 0, rect.width, rect.height); 1329 } 1330 1331 if (!cc.Node.prototype.init.call(_t)) 1332 return false; 1333 1334 _t._batchNode = null; 1335 _t._recursiveDirty = false; 1336 _t.dirty = false; 1337 _t._opacityModifyRGB = true; 1338 1339 _t._blendFunc.src = cc.BLEND_SRC; 1340 _t._blendFunc.dst = cc.BLEND_DST; 1341 1342 _t._flippedX = _t._flippedY = false; 1343 1344 // default transform anchor: center 1345 _t.anchorX = 0.5; 1346 _t.anchorY = 0.5; 1347 1348 // zwoptex default values 1349 _t._offsetPosition.x = 0; 1350 _t._offsetPosition.y = 0; 1351 _t._hasChildren = false; 1352 1353 var locTextureLoaded = texture.isLoaded(); 1354 _t._textureLoaded = locTextureLoaded; 1355 1356 if (!locTextureLoaded) { 1357 _t._rectRotated = rotated; 1358 if (rect) { 1359 _t._rect.x = rect.x; 1360 _t._rect.y = rect.y; 1361 _t._rect.width = rect.width; 1362 _t._rect.height = rect.height; 1363 } 1364 if(_t.texture) 1365 _t.texture.removeLoadedEventListener(_t); 1366 texture.addLoadedEventListener(_t._textureLoadedCallback, _t); 1367 _t.texture = texture; 1368 return true; 1369 } 1370 1371 if (!rect) { 1372 rect = cc.rect(0, 0, texture.width, texture.height); 1373 } 1374 1375 if(texture && texture.url) { 1376 var _x = rect.x + rect.width, _y = rect.y + rect.height; 1377 if(_x > texture.width){ 1378 cc.error(cc._LogInfos.RectWidth, texture.url); 1379 } 1380 if(_y > texture.height){ 1381 cc.error(cc._LogInfos.RectHeight, texture.url); 1382 } 1383 } 1384 _t._originalTexture = texture; 1385 _t.texture = texture; 1386 _t.setTextureRect(rect, rotated); 1387 1388 // by default use "Self Render". 1389 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" 1390 _t.batchNode = null; 1391 return true; 1392 }; 1393 1394 _p._textureLoadedCallback = function (sender) { 1395 var _t = this; 1396 if(_t._textureLoaded) 1397 return; 1398 1399 _t._textureLoaded = true; 1400 var locRect = _t._rect; 1401 if (!locRect) { 1402 locRect = cc.rect(0, 0, sender.width, sender.height); 1403 } else if (cc._rectEqualToZero(locRect)) { 1404 locRect.width = sender.width; 1405 locRect.height = sender.height; 1406 } 1407 _t._originalTexture = sender; 1408 1409 _t.texture = sender; 1410 _t.setTextureRect(locRect, _t._rectRotated); 1411 1412 //set the texture's color after the it loaded 1413 var locColor = this._displayedColor; 1414 if(locColor.r != 255 || locColor.g != 255 || locColor.b != 255) 1415 _t._changeTextureColor(); 1416 1417 // by default use "Self Render". 1418 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" 1419 _t.batchNode = _t._batchNode; 1420 _t._callLoadedEventCallbacks(); 1421 }; 1422 1423 _p.setTextureRect = function (rect, rotated, untrimmedSize) { 1424 var _t = this; 1425 _t._rectRotated = rotated || false; 1426 _t.setContentSize(untrimmedSize || rect); 1427 1428 _t.setVertexRect(rect); 1429 1430 var locTextureRect = _t._textureRect_Canvas, scaleFactor = cc.contentScaleFactor(); 1431 locTextureRect.x = 0 | (rect.x * scaleFactor); 1432 locTextureRect.y = 0 | (rect.y * scaleFactor); 1433 locTextureRect.width = 0 | (rect.width * scaleFactor); 1434 locTextureRect.height = 0 | (rect.height * scaleFactor); 1435 locTextureRect.validRect = !(locTextureRect.width === 0 || locTextureRect.height === 0 || locTextureRect.x < 0 || locTextureRect.y < 0); 1436 1437 var relativeOffset = _t._unflippedOffsetPositionFromCenter; 1438 if (_t._flippedX) 1439 relativeOffset.x = -relativeOffset.x; 1440 if (_t._flippedY) 1441 relativeOffset.y = -relativeOffset.y; 1442 _t._offsetPosition.x = relativeOffset.x + (_t._contentSize.width - _t._rect.width) / 2; 1443 _t._offsetPosition.y = relativeOffset.y + (_t._contentSize.height - _t._rect.height) / 2; 1444 1445 // rendering using batch node 1446 if (_t._batchNode) { 1447 // update dirty, don't update _recursiveDirty 1448 _t.dirty = true; 1449 } 1450 }; 1451 1452 _p.updateTransform = function () { 1453 var _t = this; 1454 //cc.assert(_t._batchNode, "updateTransform is only valid when cc.Sprite is being rendered using an cc.SpriteBatchNode"); 1455 1456 // recaculate matrix only if it is dirty 1457 if (_t.dirty) { 1458 // If it is not visible, or one of its ancestors is not visible, then do nothing: 1459 var locParent = _t._parent; 1460 if (!_t._visible || ( locParent && locParent != _t._batchNode && locParent._shouldBeHidden)) { 1461 _t._shouldBeHidden = true; 1462 } else { 1463 _t._shouldBeHidden = false; 1464 1465 if (!locParent || locParent == _t._batchNode) { 1466 _t._transformToBatch = _t.nodeToParentTransform(); 1467 } else { 1468 //cc.assert(_t._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite"); 1469 _t._transformToBatch = cc.affineTransformConcat(_t.nodeToParentTransform(), locParent._transformToBatch); 1470 } 1471 } 1472 _t._recursiveDirty = false; 1473 _t.dirty = false; 1474 } 1475 1476 // recursively iterate over children 1477 if (_t._hasChildren) 1478 _t._arrayMakeObjectsPerformSelector(_t._children, cc.Node._StateCallbackType.updateTransform); 1479 }; 1480 1481 _p.addChild = function (child, localZOrder, tag) { 1482 1483 cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2); 1484 1485 if (localZOrder == null) 1486 localZOrder = child._localZOrder; 1487 if (tag == null) 1488 tag = child.tag; 1489 1490 //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check 1491 cc.Node.prototype.addChild.call(this, child, localZOrder, tag); 1492 this._hasChildren = true; 1493 }; 1494 1495 _p.setOpacity = function (opacity) { 1496 cc.Node.prototype.setOpacity.call(this, opacity); 1497 this._setNodeDirtyForCache(); 1498 }; 1499 1500 _p.setColor = function (color3) { 1501 var _t = this; 1502 var curColor = _t.color; 1503 this._oldDisplayColor = curColor; 1504 if ((curColor.r === color3.r) && (curColor.g === color3.g) && (curColor.b === color3.b)) 1505 return; 1506 cc.Node.prototype.setColor.call(_t, color3); 1507 }; 1508 1509 _p.updateDisplayedColor = function (parentColor) { 1510 var _t = this; 1511 cc.Node.prototype.updateDisplayedColor.call(_t, parentColor); 1512 var oColor = _t._oldDisplayColor; 1513 var nColor = _t._displayedColor; 1514 if (oColor.r === nColor.r && oColor.g === nColor.g && oColor.b === nColor.b) 1515 return; 1516 1517 _t._changeTextureColor(); 1518 _t._setNodeDirtyForCache(); 1519 }; 1520 1521 _p.setSpriteFrame = function (newFrame) { 1522 var _t = this; 1523 if(typeof(newFrame) == "string"){ 1524 newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame); 1525 cc.assert(newFrame, cc._LogInfos.CCSpriteBatchNode_setSpriteFrame) 1526 } 1527 1528 _t.setNodeDirty(true); 1529 1530 var frameOffset = newFrame.getOffset(); 1531 _t._unflippedOffsetPositionFromCenter.x = frameOffset.x; 1532 _t._unflippedOffsetPositionFromCenter.y = frameOffset.y; 1533 1534 // update rect 1535 _t._rectRotated = newFrame.isRotated(); 1536 1537 var pNewTexture = newFrame.getTexture(); 1538 var locTextureLoaded = newFrame.textureLoaded(); 1539 if (!locTextureLoaded) { 1540 _t._textureLoaded = false; 1541 newFrame.addLoadedEventListener(function (sender) { 1542 _t._textureLoaded = true; 1543 var locNewTexture = sender.getTexture(); 1544 if (locNewTexture != _t._texture) 1545 _t.texture = locNewTexture; 1546 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize()); 1547 _t._callLoadedEventCallbacks(); 1548 }, _t); 1549 } 1550 // update texture before updating texture rect 1551 if (pNewTexture != _t._texture) 1552 _t.texture = pNewTexture; 1553 1554 if (_t._rectRotated) 1555 _t._originalTexture = pNewTexture; 1556 1557 _t.setTextureRect(newFrame.getRect(), _t._rectRotated, newFrame.getOriginalSize()); 1558 _t._colorized = false; 1559 if (locTextureLoaded) { 1560 var curColor = _t.color; 1561 if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255) 1562 _t._changeTextureColor(); 1563 } 1564 }; 1565 1566 _p.isFrameDisplayed = function (frame) { 1567 if (frame.getTexture() != this._texture) 1568 return false; 1569 return cc.rectEqualToRect(frame.getRect(), this._rect); 1570 }; 1571 1572 _p.setBatchNode = function (spriteBatchNode) { 1573 var _t = this; 1574 _t._batchNode = spriteBatchNode; // weak reference 1575 1576 // self render 1577 if (!_t._batchNode) { 1578 _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED; 1579 _t.textureAtlas = null; 1580 _t._recursiveDirty = false; 1581 _t.dirty = false; 1582 } else { 1583 // using batch 1584 _t._transformToBatch = cc.affineTransformIdentity(); 1585 _t.textureAtlas = _t._batchNode.textureAtlas; // weak ref 1586 } 1587 }; 1588 1589 _p.setTexture = function (texture) { 1590 var _t = this; 1591 if(texture && (typeof(texture) === "string")){ 1592 texture = cc.textureCache.addImage(texture); 1593 _t.setTexture(texture); 1594 1595 //TODO 1596 var size = texture.getContentSize(); 1597 _t.setTextureRect(cc.rect(0,0, size.width, size.height)); 1598 return; 1599 } 1600 1601 // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet 1602 cc.assert(!texture || texture instanceof cc.Texture2D, cc._LogInfos.CCSpriteBatchNode_setTexture); 1603 1604 if (_t._texture != texture) { 1605 if (texture && texture.getHtmlElementObj() instanceof HTMLImageElement) { 1606 _t._originalTexture = texture; 1607 } 1608 _t._texture = texture; 1609 } 1610 }; 1611 1612 _p.draw = function (ctx) { 1613 var _t = this; 1614 if (!_t._textureLoaded) 1615 return; 1616 1617 var context = ctx || cc._renderContext; 1618 if (_t._blendFuncStr != "source") 1619 context.globalCompositeOperation = _t._blendFuncStr; 1620 1621 var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY(); 1622 1623 context.globalAlpha = _t._displayedOpacity / 255; 1624 var locRect = _t._rect, locContentSize = _t._contentSize, locOffsetPosition = _t._offsetPosition, locDrawSizeCanvas = _t._drawSize_Canvas; 1625 var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = _t._textureRect_Canvas; 1626 locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX; 1627 locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY; 1628 1629 if (_t._flippedX || _t._flippedY) { 1630 context.save(); 1631 if (_t._flippedX) { 1632 flipXOffset = -locOffsetPosition.x - locRect.width; 1633 context.scale(-1, 1); 1634 } 1635 if (_t._flippedY) { 1636 flipYOffset = locOffsetPosition.y; 1637 context.scale(1, -1); 1638 } 1639 } 1640 1641 flipXOffset *= locEGL_ScaleX; 1642 flipYOffset *= locEGL_ScaleY; 1643 1644 if (_t._texture && locTextureCoord.validRect) { 1645 var image = _t._texture.getHtmlElementObj(); 1646 if (_t._colorized) { 1647 context.drawImage(image, 1648 0, 0, locTextureCoord.width, locTextureCoord.height, 1649 flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height); 1650 } else { 1651 context.drawImage(image, 1652 locTextureCoord.x, locTextureCoord.y, locTextureCoord.width, locTextureCoord.height, 1653 flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height); 1654 } 1655 } else if (!_t._texture && locTextureCoord.validRect) { 1656 var curColor = _t.color; 1657 context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)"; 1658 context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY); 1659 } 1660 1661 if (cc.SPRITE_DEBUG_DRAW === 1 || _t._showNode) { 1662 // draw bounding box 1663 context.strokeStyle = "rgba(0,255,0,1)"; 1664 flipXOffset /= locEGL_ScaleX; 1665 flipYOffset /= locEGL_ScaleY; 1666 flipYOffset = -flipYOffset; 1667 var vertices1 = [cc.p(flipXOffset, flipYOffset), 1668 cc.p(flipXOffset + locRect.width, flipYOffset), 1669 cc.p(flipXOffset + locRect.width, flipYOffset - locRect.height), 1670 cc.p(flipXOffset, flipYOffset - locRect.height)]; 1671 cc._drawingUtil.drawPoly(vertices1, 4, true); 1672 } else if (cc.SPRITE_DEBUG_DRAW === 2) { 1673 // draw texture box 1674 context.strokeStyle = "rgba(0,255,0,1)"; 1675 var drawRect = _t._rect; 1676 flipYOffset = -flipYOffset; 1677 var vertices2 = [cc.p(flipXOffset, flipYOffset), cc.p(flipXOffset + drawRect.width, flipYOffset), 1678 cc.p(flipXOffset + drawRect.width, flipYOffset - drawRect.height), cc.p(flipXOffset, flipYOffset - drawRect.height)]; 1679 cc._drawingUtil.drawPoly(vertices2, 4, true); 1680 } 1681 if (_t._flippedX || _t._flippedY) 1682 context.restore(); 1683 cc.g_NumberOfDraws++; 1684 }; 1685 1686 if(!cc.sys._supportCanvasNewBlendModes) 1687 _p._changeTextureColor = function () { 1688 var locElement, locTexture = this._texture, locRect = this._textureRect_Canvas; //this.getTextureRect(); 1689 if (locTexture && locRect.validRect && this._originalTexture) { 1690 locElement = locTexture.getHtmlElementObj(); 1691 if (!locElement) 1692 return; 1693 1694 var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj()); 1695 if (cacheTextureForColor) { 1696 this._colorized = true; 1697 //generate color texture cache 1698 if (locElement instanceof HTMLCanvasElement && !this._rectRotated && !this._newTextureWhenChangeColor) 1699 cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect, locElement); 1700 else { 1701 locElement = cc.generateTintImage(locElement, cacheTextureForColor, this._displayedColor, locRect); 1702 locTexture = new cc.Texture2D(); 1703 locTexture.initWithElement(locElement); 1704 locTexture.handleLoadedTexture(); 1705 this.texture = locTexture; 1706 } 1707 } 1708 } 1709 }; 1710 1711 delete _p; 1712 } else { 1713 cc.assert(typeof cc._tmp.WebGLSprite === "function", cc._LogInfos.MissingFile, "SpritesWebGL.js"); 1714 cc._tmp.WebGLSprite(); 1715 delete cc._tmp.WebGLSprite; 1716 } 1717 1718 cc.assert(typeof cc._tmp.PrototypeSprite === "function", cc._LogInfos.MissingFile, "SpritesPropertyDefine.js"); 1719 cc._tmp.PrototypeSprite(); 1720 delete cc._tmp.PrototypeSprite; 1721 1722