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