summary refs log tree commit diff stats
path: root/libs/cocos2d/CCSprite.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCSprite.m')
-rwxr-xr-xlibs/cocos2d/CCSprite.m1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/libs/cocos2d/CCSprite.m b/libs/cocos2d/CCSprite.m new file mode 100755 index 0000000..37f06d2 --- /dev/null +++ b/libs/cocos2d/CCSprite.m
@@ -0,0 +1,1029 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (c) 2008-2010 Ricardo Quesada
5 * Copyright (c) 2011 Zynga Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 */
26
27#import <Availability.h>
28
29#import "ccConfig.h"
30#import "CCSpriteBatchNode.h"
31#import "CCSprite.h"
32#import "CCSpriteFrame.h"
33#import "CCSpriteFrameCache.h"
34#import "CCAnimation.h"
35#import "CCAnimationCache.h"
36#import "CCTextureCache.h"
37#import "Support/CGPointExtension.h"
38#import "CCDrawingPrimitives.h"
39
40#pragma mark -
41#pragma mark CCSprite
42
43#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
44#define RENDER_IN_SUBPIXEL
45#else
46#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__))
47#endif
48
49// XXX: Optmization
50struct transformValues_ {
51 CGPoint pos; // position x and y
52 CGPoint scale; // scale x and y
53 float rotation;
54 CGPoint skew; // skew x and y
55 CGPoint ap; // anchor point in pixels
56 BOOL visible;
57};
58
59@interface CCSprite (Private)
60-(void)updateTextureCoords:(CGRect)rect;
61-(void)updateBlendFunc;
62-(void) initAnimationDictionary;
63-(void) getTransformValues:(struct transformValues_*)tv; // optimization
64@end
65
66@implementation CCSprite
67
68@synthesize dirty = dirty_;
69@synthesize quad = quad_;
70@synthesize atlasIndex = atlasIndex_;
71@synthesize textureRect = rect_;
72@synthesize textureRectRotated = rectRotated_;
73@synthesize blendFunc = blendFunc_;
74@synthesize usesBatchNode = usesBatchNode_;
75@synthesize textureAtlas = textureAtlas_;
76@synthesize batchNode = batchNode_;
77@synthesize honorParentTransform = honorParentTransform_;
78@synthesize offsetPositionInPixels = offsetPositionInPixels_;
79
80
81+(id)spriteWithTexture:(CCTexture2D*)texture
82{
83 return [[[self alloc] initWithTexture:texture] autorelease];
84}
85
86+(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
87{
88 return [[[self alloc] initWithTexture:texture rect:rect] autorelease];
89}
90
91+(id)spriteWithFile:(NSString*)filename
92{
93 return [[[self alloc] initWithFile:filename] autorelease];
94}
95
96+(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect
97{
98 return [[[self alloc] initWithFile:filename rect:rect] autorelease];
99}
100
101+(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame
102{
103 return [[[self alloc] initWithSpriteFrame:spriteFrame] autorelease];
104}
105
106+(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName
107{
108 CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
109
110 NSAssert1(frame!=nil, @"Invalid spriteFrameName: %@", spriteFrameName);
111 return [self spriteWithSpriteFrame:frame];
112}
113
114+(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key
115{
116 return [[[self alloc] initWithCGImage:image key:key] autorelease];
117}
118
119+(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
120{
121 return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease];
122}
123
124-(id) init
125{
126 if( (self=[super init]) ) {
127 dirty_ = recursiveDirty_ = NO;
128
129 // by default use "Self Render".
130 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
131 [self useSelfRender];
132
133 opacityModifyRGB_ = YES;
134 opacity_ = 255;
135 color_ = colorUnmodified_ = ccWHITE;
136
137 blendFunc_.src = CC_BLEND_SRC;
138 blendFunc_.dst = CC_BLEND_DST;
139
140 // update texture (calls updateBlendFunc)
141 [self setTexture:nil];
142
143 // clean the Quad
144 bzero(&quad_, sizeof(quad_));
145
146 flipY_ = flipX_ = NO;
147
148 // lazy alloc
149 animations_ = nil;
150
151 // default transform anchor: center
152 anchorPoint_ = ccp(0.5f, 0.5f);
153
154 // zwoptex default values
155 offsetPositionInPixels_ = CGPointZero;
156
157 honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL;
158 hasChildren_ = NO;
159
160 // Atlas: Color
161 ccColor4B tmpColor = {255,255,255,255};
162 quad_.bl.colors = tmpColor;
163 quad_.br.colors = tmpColor;
164 quad_.tl.colors = tmpColor;
165 quad_.tr.colors = tmpColor;
166
167 // Atlas: Vertex
168
169 // updated in "useSelfRender"
170
171 // Atlas: TexCoords
172 [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero];
173
174 // updateMethod selector
175 updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)];
176 }
177
178 return self;
179}
180
181-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
182{
183 NSAssert(texture!=nil, @"Invalid texture for sprite");
184 // IMPORTANT: [self init] and not [super init];
185 if( (self = [self init]) )
186 {
187 [self setTexture:texture];
188 [self setTextureRect:rect];
189 }
190 return self;
191}
192
193-(id) initWithTexture:(CCTexture2D*)texture
194{
195 NSAssert(texture!=nil, @"Invalid texture for sprite");
196
197 CGRect rect = CGRectZero;
198 rect.size = texture.contentSize;
199 return [self initWithTexture:texture rect:rect];
200}
201
202-(id) initWithFile:(NSString*)filename
203{
204 NSAssert(filename!=nil, @"Invalid filename for sprite");
205
206 CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
207 if( texture ) {
208 CGRect rect = CGRectZero;
209 rect.size = texture.contentSize;
210 return [self initWithTexture:texture rect:rect];
211 }
212
213 [self release];
214 return nil;
215}
216
217-(id) initWithFile:(NSString*)filename rect:(CGRect)rect
218{
219 NSAssert(filename!=nil, @"Invalid filename for sprite");
220
221 CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
222 if( texture )
223 return [self initWithTexture:texture rect:rect];
224
225 [self release];
226 return nil;
227}
228
229- (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame
230{
231 NSAssert(spriteFrame!=nil, @"Invalid spriteFrame for sprite");
232
233 id ret = [self initWithTexture:spriteFrame.texture rect:spriteFrame.rect];
234 [self setDisplayFrame:spriteFrame];
235 return ret;
236}
237
238-(id)initWithSpriteFrameName:(NSString*)spriteFrameName
239{
240 NSAssert(spriteFrameName!=nil, @"Invalid spriteFrameName for sprite");
241
242 CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
243 return [self initWithSpriteFrame:frame];
244}
245
246// XXX: deprecated
247- (id) initWithCGImage: (CGImageRef)image
248{
249 NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
250
251 // XXX: possible bug. See issue #349. New API should be added
252 NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image];
253 CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
254
255 CGRect rect = CGRectZero;
256 rect.size = texture.contentSize;
257
258 return [self initWithTexture:texture rect:rect];
259}
260
261- (id) initWithCGImage:(CGImageRef)image key:(NSString*)key
262{
263 NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
264
265 // XXX: possible bug. See issue #349. New API should be added
266 CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
267
268 CGRect rect = CGRectZero;
269 rect.size = texture.contentSize;
270
271 return [self initWithTexture:texture rect:rect];
272}
273
274-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
275{
276 id ret = [self initWithTexture:batchNode.texture rect:rect];
277 [self useBatchNode:batchNode];
278
279 return ret;
280}
281
282-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect
283{
284 id ret = [self initWithTexture:batchNode.texture];
285 [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
286 [self useBatchNode:batchNode];
287
288 return ret;
289}
290
291- (NSString*) description
292{
293 return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self,
294 rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height,
295 tag_,
296 atlasIndex_
297 ];
298}
299
300- (void) dealloc
301{
302 [texture_ release];
303 [animations_ release];
304 [super dealloc];
305}
306
307-(void) useSelfRender
308{
309 atlasIndex_ = CCSpriteIndexNotInitialized;
310 usesBatchNode_ = NO;
311 textureAtlas_ = nil;
312 batchNode_ = nil;
313 dirty_ = recursiveDirty_ = NO;
314
315 float x1 = 0 + offsetPositionInPixels_.x;
316 float y1 = 0 + offsetPositionInPixels_.y;
317 float x2 = x1 + rectInPixels_.size.width;
318 float y2 = y1 + rectInPixels_.size.height;
319 quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
320 quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
321 quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
322 quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 };
323}
324
325-(void) useBatchNode:(CCSpriteBatchNode*)batchNode
326{
327 usesBatchNode_ = YES;
328 textureAtlas_ = [batchNode textureAtlas]; // weak ref
329 batchNode_ = batchNode; // weak ref
330}
331
332-(void) initAnimationDictionary
333{
334 animations_ = [[NSMutableDictionary alloc] initWithCapacity:2];
335}
336
337-(void)setTextureRect:(CGRect)rect
338{
339 CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
340 [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size];
341}
342
343-(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize
344{
345 rectInPixels_ = rect;
346 rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
347 rectRotated_ = rotated;
348
349 [self setContentSizeInPixels:untrimmedSize];
350 [self updateTextureCoords:rectInPixels_];
351
352 CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_;
353
354 // issue #732
355 if( flipX_ )
356 relativeOffsetInPixels.x = -relativeOffsetInPixels.x;
357 if( flipY_ )
358 relativeOffsetInPixels.y = -relativeOffsetInPixels.y;
359
360 offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2;
361 offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2;
362
363
364 // rendering using batch node
365 if( usesBatchNode_ ) {
366 // update dirty_, don't update recursiveDirty_
367 dirty_ = YES;
368 }
369
370 // self rendering
371 else
372 {
373 // Atlas: Vertex
374 float x1 = 0 + offsetPositionInPixels_.x;
375 float y1 = 0 + offsetPositionInPixels_.y;
376 float x2 = x1 + rectInPixels_.size.width;
377 float y2 = y1 + rectInPixels_.size.height;
378
379 // Don't update Z.
380 quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
381 quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
382 quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
383 quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 };
384 }
385}
386
387-(void)updateTextureCoords:(CGRect)rect
388{
389 CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_;
390 if(!tex)
391 return;
392
393 float atlasWidth = (float)tex.pixelsWide;
394 float atlasHeight = (float)tex.pixelsHigh;
395
396 float left,right,top,bottom;
397
398 if(rectRotated_){
399#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
400 left = (2*rect.origin.x+1)/(2*atlasWidth);
401 right = left+(rect.size.height*2-2)/(2*atlasWidth);
402 top = (2*rect.origin.y+1)/(2*atlasHeight);
403 bottom = top+(rect.size.width*2-2)/(2*atlasHeight);
404#else
405 left = rect.origin.x/atlasWidth;
406 right = left+(rect.size.height/atlasWidth);
407 top = rect.origin.y/atlasHeight;
408 bottom = top+(rect.size.width/atlasHeight);
409#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
410
411 if( flipX_)
412 CC_SWAP(top,bottom);
413 if( flipY_)
414 CC_SWAP(left,right);
415
416 quad_.bl.texCoords.u = left;
417 quad_.bl.texCoords.v = top;
418 quad_.br.texCoords.u = left;
419 quad_.br.texCoords.v = bottom;
420 quad_.tl.texCoords.u = right;
421 quad_.tl.texCoords.v = top;
422 quad_.tr.texCoords.u = right;
423 quad_.tr.texCoords.v = bottom;
424 } else {
425#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
426 left = (2*rect.origin.x+1)/(2*atlasWidth);
427 right = left + (rect.size.width*2-2)/(2*atlasWidth);
428 top = (2*rect.origin.y+1)/(2*atlasHeight);
429 bottom = top + (rect.size.height*2-2)/(2*atlasHeight);
430#else
431 left = rect.origin.x/atlasWidth;
432 right = left + rect.size.width/atlasWidth;
433 top = rect.origin.y/atlasHeight;
434 bottom = top + rect.size.height/atlasHeight;
435#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
436
437 if( flipX_)
438 CC_SWAP(left,right);
439 if( flipY_)
440 CC_SWAP(top,bottom);
441
442 quad_.bl.texCoords.u = left;
443 quad_.bl.texCoords.v = bottom;
444 quad_.br.texCoords.u = right;
445 quad_.br.texCoords.v = bottom;
446 quad_.tl.texCoords.u = left;
447 quad_.tl.texCoords.v = top;
448 quad_.tr.texCoords.u = right;
449 quad_.tr.texCoords.v = top;
450 }
451}
452
453-(void)updateTransform
454{
455 NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode");
456
457 // optimization. Quick return if not dirty
458 if( ! dirty_ )
459 return;
460
461 CGAffineTransform matrix;
462
463 // Optimization: if it is not visible, then do nothing
464 if( ! visible_ ) {
465 quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
466 [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
467 dirty_ = recursiveDirty_ = NO;
468 return ;
469 }
470
471
472 // Optimization: If parent is batchnode, or parent is nil
473 // build Affine transform manually
474 if( ! parent_ || parent_ == batchNode_ ) {
475
476 float radians = -CC_DEGREES_TO_RADIANS(rotation_);
477 float c = cosf(radians);
478 float s = sinf(radians);
479
480 matrix = CGAffineTransformMake( c * scaleX_, s * scaleX_,
481 -s * scaleY_, c * scaleY_,
482 positionInPixels_.x, positionInPixels_.y);
483 if( skewX_ || skewY_ ) {
484 CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)),
485 tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f,
486 0.0f, 0.0f);
487 matrix = CGAffineTransformConcat(skewMatrix, matrix);
488 }
489 matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y);
490
491
492 } else { // parent_ != batchNode_
493
494 // else do affine transformation according to the HonorParentTransform
495
496 matrix = CGAffineTransformIdentity;
497 ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL;
498
499 for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) {
500
501 // Might happen. Issue #1053
502 NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." );
503
504 struct transformValues_ tv;
505 [(CCSprite*)p getTransformValues: &tv];
506
507 // If any of the parents are not visible, then don't draw this node
508 if( ! tv.visible ) {
509 quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
510 [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
511 dirty_ = recursiveDirty_ = NO;
512 return;
513 }
514 CGAffineTransform newMatrix = CGAffineTransformIdentity;
515
516 // 2nd: Translate, Skew, Rotate, Scale
517 if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE )
518 newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y);
519 if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE )
520 newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation));
521 if ( prevHonor & CC_HONOR_PARENT_TRANSFORM_SKEW ) {
522 CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(tv.skew.y)), tanf(CC_DEGREES_TO_RADIANS(tv.skew.x)), 1.0f, 0.0f, 0.0f);
523 // apply the skew to the transform
524 newMatrix = CGAffineTransformConcat(skew, newMatrix);
525 }
526 if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) {
527 newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y);
528 }
529
530 // 3rd: Translate anchor point
531 newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y);
532
533 // 4th: Matrix multiplication
534 matrix = CGAffineTransformConcat( matrix, newMatrix);
535
536 prevHonor = [(CCSprite*)p honorParentTransform];
537 }
538 }
539
540
541 //
542 // calculate the Quad based on the Affine Matrix
543 //
544
545 CGSize size = rectInPixels_.size;
546
547 float x1 = offsetPositionInPixels_.x;
548 float y1 = offsetPositionInPixels_.y;
549
550 float x2 = x1 + size.width;
551 float y2 = y1 + size.height;
552 float x = matrix.tx;
553 float y = matrix.ty;
554
555 float cr = matrix.a;
556 float sr = matrix.b;
557 float cr2 = matrix.d;
558 float sr2 = -matrix.c;
559 float ax = x1 * cr - y1 * sr2 + x;
560 float ay = x1 * sr + y1 * cr2 + y;
561
562 float bx = x2 * cr - y1 * sr2 + x;
563 float by = x2 * sr + y1 * cr2 + y;
564
565 float cx = x2 * cr - y2 * sr2 + x;
566 float cy = x2 * sr + y2 * cr2 + y;
567
568 float dx = x1 * cr - y2 * sr2 + x;
569 float dy = x1 * sr + y2 * cr2 + y;
570
571 quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ };
572 quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ };
573 quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ };
574 quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ };
575
576 [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
577 dirty_ = recursiveDirty_ = NO;
578}
579
580// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation,
581// this fuction return the 5 values in 1 single call
582-(void) getTransformValues:(struct transformValues_*) tv
583{
584 tv->pos = positionInPixels_;
585 tv->scale.x = scaleX_;
586 tv->scale.y = scaleY_;
587 tv->rotation = rotation_;
588 tv->skew.x = skewX_;
589 tv->skew.y = skewY_;
590 tv->ap = anchorPointInPixels_;
591 tv->visible = visible_;
592}
593
594#pragma mark CCSprite - draw
595
596-(void) draw
597{
598 [super draw];
599
600 NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");
601
602 // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
603 // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
604 // Unneeded states: -
605
606 BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
607 if( newBlend )
608 glBlendFunc( blendFunc_.src, blendFunc_.dst );
609
610#define kQuadSize sizeof(quad_.bl)
611 glBindTexture(GL_TEXTURE_2D, [texture_ name]);
612
613 long offset = (long)&quad_;
614
615 // vertex
616 NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
617 glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) );
618
619 // color
620 diff = offsetof( ccV3F_C4B_T2F, colors);
621 glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
622
623 // tex coords
624 diff = offsetof( ccV3F_C4B_T2F, texCoords);
625 glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
626
627 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
628
629 if( newBlend )
630 glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
631
632#if CC_SPRITE_DEBUG_DRAW == 1
633 // draw bounding box
634 CGSize s = self.contentSize;
635 CGPoint vertices[4] = {
636 ccp(0,0), ccp(s.width,0),
637 ccp(s.width,s.height), ccp(0,s.height)
638 };
639 ccDrawPoly(vertices, 4, YES);
640#elif CC_SPRITE_DEBUG_DRAW == 2
641 // draw texture box
642 CGSize s = self.textureRect.size;
643 CGPoint offsetPix = self.offsetPositionInPixels;
644 CGPoint vertices[4] = {
645 ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y),
646 ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height)
647 };
648 ccDrawPoly(vertices, 4, YES);
649#endif // CC_SPRITE_DEBUG_DRAW
650
651}
652
653#pragma mark CCSprite - CCNode overrides
654
655-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
656{
657 NSAssert( child != nil, @"Argument must be non-nil");
658
659 [super addChild:child z:z tag:aTag];
660
661 if( usesBatchNode_ ) {
662 NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode");
663 NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
664
665 NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z];
666 [batchNode_ insertChild:child inAtlasAtIndex:index];
667 }
668
669 hasChildren_ = YES;
670}
671
672-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
673{
674 NSAssert( child != nil, @"Child must be non-nil");
675 NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
676
677 if( z == child.zOrder )
678 return;
679
680 if( usesBatchNode_ ) {
681 // XXX: Instead of removing/adding, it is more efficient to reorder manually
682 [child retain];
683 [self removeChild:child cleanup:NO];
684 [self addChild:child z:z];
685 [child release];
686 }
687
688 else
689 [super reorderChild:child z:z];
690}
691
692-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
693{
694 if( usesBatchNode_ )
695 [batchNode_ removeSpriteFromAtlas:sprite];
696
697 [super removeChild:sprite cleanup:doCleanup];
698
699 hasChildren_ = ( [children_ count] > 0 );
700}
701
702-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
703{
704 if( usesBatchNode_ ) {
705 CCSprite *child;
706 CCARRAY_FOREACH(children_, child)
707 [batchNode_ removeSpriteFromAtlas:child];
708 }
709
710 [super removeAllChildrenWithCleanup:doCleanup];
711
712 hasChildren_ = NO;
713}
714
715//
716// CCNode property overloads
717// used only when parent is CCSpriteBatchNode
718//
719#pragma mark CCSprite - property overloads
720
721
722-(void) setDirtyRecursively:(BOOL)b
723{
724 dirty_ = recursiveDirty_ = b;
725 // recursively set dirty
726 if( hasChildren_ ) {
727 CCSprite *child;
728 CCARRAY_FOREACH(children_, child)
729 [child setDirtyRecursively:YES];
730 }
731}
732
733// XXX HACK: optimization
734#define SET_DIRTY_RECURSIVELY() { \
735 if( usesBatchNode_ && ! recursiveDirty_ ) { \
736 dirty_ = recursiveDirty_ = YES; \
737 if( hasChildren_) \
738 [self setDirtyRecursively:YES]; \
739 } \
740 }
741
742-(void)setPosition:(CGPoint)pos
743{
744 [super setPosition:pos];
745 SET_DIRTY_RECURSIVELY();
746}
747
748-(void)setPositionInPixels:(CGPoint)pos
749{
750 [super setPositionInPixels:pos];
751 SET_DIRTY_RECURSIVELY();
752}
753
754-(void)setRotation:(float)rot
755{
756 [super setRotation:rot];
757 SET_DIRTY_RECURSIVELY();
758}
759
760-(void)setSkewX:(float)sx
761{
762 [super setSkewX:sx];
763 SET_DIRTY_RECURSIVELY();
764}
765
766-(void)setSkewY:(float)sy
767{
768 [super setSkewY:sy];
769 SET_DIRTY_RECURSIVELY();
770}
771
772-(void)setScaleX:(float) sx
773{
774 [super setScaleX:sx];
775 SET_DIRTY_RECURSIVELY();
776}
777
778-(void)setScaleY:(float) sy
779{
780 [super setScaleY:sy];
781 SET_DIRTY_RECURSIVELY();
782}
783
784-(void)setScale:(float) s
785{
786 [super setScale:s];
787 SET_DIRTY_RECURSIVELY();
788}
789
790-(void) setVertexZ:(float)z
791{
792 [super setVertexZ:z];
793 SET_DIRTY_RECURSIVELY();
794}
795
796-(void)setAnchorPoint:(CGPoint)anchor
797{
798 [super setAnchorPoint:anchor];
799 SET_DIRTY_RECURSIVELY();
800}
801
802-(void)setIsRelativeAnchorPoint:(BOOL)relative
803{
804 NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite");
805 [super setIsRelativeAnchorPoint:relative];
806}
807
808-(void)setVisible:(BOOL)v
809{
810 [super setVisible:v];
811 SET_DIRTY_RECURSIVELY();
812}
813
814-(void)setFlipX:(BOOL)b
815{
816 if( flipX_ != b ) {
817 flipX_ = b;
818 [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
819 }
820}
821-(BOOL) flipX
822{
823 return flipX_;
824}
825
826-(void) setFlipY:(BOOL)b
827{
828 if( flipY_ != b ) {
829 flipY_ = b;
830 [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
831 }
832}
833-(BOOL) flipY
834{
835 return flipY_;
836}
837
838//
839// RGBA protocol
840//
841#pragma mark CCSprite - RGBA protocol
842-(void) updateColor
843{
844 ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ };
845
846 quad_.bl.colors = color4;
847 quad_.br.colors = color4;
848 quad_.tl.colors = color4;
849 quad_.tr.colors = color4;
850
851 // renders using Sprite Manager
852 if( usesBatchNode_ ) {
853 if( atlasIndex_ != CCSpriteIndexNotInitialized)
854 [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
855 else
856 // no need to set it recursively
857 // update dirty_, don't update recursiveDirty_
858 dirty_ = YES;
859 }
860 // self render
861 // do nothing
862}
863
864-(GLubyte) opacity
865{
866 return opacity_;
867}
868
869-(void) setOpacity:(GLubyte) anOpacity
870{
871 opacity_ = anOpacity;
872
873 // special opacity for premultiplied textures
874 if( opacityModifyRGB_ )
875 [self setColor: colorUnmodified_];
876
877 [self updateColor];
878}
879
880- (ccColor3B) color
881{
882 if(opacityModifyRGB_)
883 return colorUnmodified_;
884
885 return color_;
886}
887
888-(void) setColor:(ccColor3B)color3
889{
890 color_ = colorUnmodified_ = color3;
891
892 if( opacityModifyRGB_ ){
893 color_.r = color3.r * opacity_/255;
894 color_.g = color3.g * opacity_/255;
895 color_.b = color3.b * opacity_/255;
896 }
897
898 [self updateColor];
899}
900
901-(void) setOpacityModifyRGB:(BOOL)modify
902{
903 ccColor3B oldColor = self.color;
904 opacityModifyRGB_ = modify;
905 self.color = oldColor;
906}
907
908-(BOOL) doesOpacityModifyRGB
909{
910 return opacityModifyRGB_;
911}
912
913//
914// Frames
915//
916#pragma mark CCSprite - Frames
917
918-(void) setDisplayFrame:(CCSpriteFrame*)frame
919{
920 unflippedOffsetPositionFromCenter_ = frame.offsetInPixels;
921
922 CCTexture2D *newTexture = [frame texture];
923 // update texture before updating texture rect
924 if ( newTexture.name != texture_.name )
925 [self setTexture: newTexture];
926
927 // update rect
928 rectRotated_ = frame.rotated;
929 [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels];
930}
931
932// XXX deprecated
933-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex
934{
935 if( ! animations_ )
936 [self initAnimationDictionary];
937
938 CCAnimation *a = [animations_ objectForKey: animationName];
939 CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
940
941 NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
942
943 [self setDisplayFrame:frame];
944}
945
946-(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex
947{
948 NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil");
949
950 CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
951
952 NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found");
953
954 CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
955
956 NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
957
958 [self setDisplayFrame:frame];
959}
960
961
962-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame
963{
964 CGRect r = [frame rect];
965 return ( CGRectEqualToRect(r, rect_) &&
966 frame.texture.name == self.texture.name );
967}
968
969-(CCSpriteFrame*) displayedFrame
970{
971 return [CCSpriteFrame frameWithTexture:texture_
972 rectInPixels:rectInPixels_
973 rotated:rectRotated_
974 offset:unflippedOffsetPositionFromCenter_
975 originalSize:contentSizeInPixels_];
976}
977
978-(void) addAnimation: (CCAnimation*) anim
979{
980 // lazy alloc
981 if( ! animations_ )
982 [self initAnimationDictionary];
983
984 [animations_ setObject:anim forKey:[anim name]];
985}
986
987-(CCAnimation*)animationByName: (NSString*) animationName
988{
989 NSAssert( animationName != nil, @"animationName parameter must be non nil");
990 return [animations_ objectForKey:animationName];
991}
992
993#pragma mark CCSprite - CocosNodeTexture protocol
994
995-(void) updateBlendFunc
996{
997 NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode");
998
999 // it's possible to have an untextured sprite
1000 if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) {
1001 blendFunc_.src = GL_SRC_ALPHA;
1002 blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
1003 [self setOpacityModifyRGB:NO];
1004 } else {
1005 blendFunc_.src = CC_BLEND_SRC;
1006 blendFunc_.dst = CC_BLEND_DST;
1007 [self setOpacityModifyRGB:YES];
1008 }
1009}
1010
1011-(void) setTexture:(CCTexture2D*)texture
1012{
1013 NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode");
1014
1015 // accept texture==nil as argument
1016 NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument");
1017
1018 [texture_ release];
1019 texture_ = [texture retain];
1020
1021 [self updateBlendFunc];
1022}
1023
1024-(CCTexture2D*) texture
1025{
1026 return texture_;
1027}
1028
1029@end