CCRenderTexture and threads warning on OS X - macos

I am getting this warning with Cocos2D 2.0 on OS X:
-[CCRenderTexture initWithWidth:height:pixelFormat:depthStencilFormat:] : cocos2d: WARNING. CCRenderTexture is running on its own thread. Make sure that an OpenGL context is being used on this thread!
Here's the code that I believe is causing the warning:
- (id) initWithObject: (CCNode *) object mask: (CCSprite *) mask {
NSAssert(object != nil, #"Invalid sprite for object");
NSAssert(mask != nil, #"Invalid sprite for mask");
if((self = [super init])) {
_objectSprite = object;
_maskSprite = mask;
// Set up the burn sprite that will "knock out" parts of the darkness layer depending on the alpha value of the pixels in the image.
[_maskSprite setBlendFunc: (ccBlendFunc) { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }];
// Get window size, we want masking over entire screen don't we?
CGSize size = [[CCDirector sharedDirector] winSize];
// Create point with middle of screen
CGPoint screenMid = ccp(size.width * 0.5f, size.height * 0.5f);
// Create the rendureTextures for the mask
_masked = [CCRenderTexture renderTextureWithWidth: size.width height: size.height];
[[_masked sprite] setBlendFunc: (ccBlendFunc) { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }];
// Set render textures at middle of screen
_masked.position = screenMid;
// Add the masked object to the screen
[self addChild: _masked];
[[CCDirector sharedDirector] setAlphaBlending:YES];
}
return self;
}
Please help, I just cannot figure this out.

In ccConfig.h, find a line that says
#define CC_DIRECTOR_MAC_THREAD CC_MAC_USE_DISPLAY_LINK_THREAD
This is where the use of threads is defined. You can try using the main thread option (CC_MAC_USE_MAIN_THREAD), or use GCD to make sure all code is actually executed on -[CCDirector runningThread]. This will pretty much fix it.

Related

Detecting and ignoring touch on non-transparent part of MKOverlay drawn Image in iOS8

I have a overlay image (.png) on my map that consists of a transparent bit in the middle, and colored sides so the user can only focus on the middle part. However do to the shape of that middle bit, quite a bit is visible at some sides.
I'm trying to detect a tap on the OverlayView so I can ignore it and only accept touches in the designated area.
I followed the following tut at Ray Wenderlich's site for adding the overlay:
The image overlay is drawn like this:
#implementation PVParkOverlayView
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage {
self = [super initWithOverlay:overlay];
if (self) {
_overlayImage = overlayImage;
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGImageRef imageReference = self.overlayImage.CGImage;
//UIImage *imageTest = _overlayImage;
MKMapRect theMapRect = self.overlay.boundingMapRect;
CGRect theRect = [self rectForMapRect:theMapRect];
//orientation testing
//CGContextRotateCTM(context, 0);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -theRect.size.height);
CGContextDrawImage(context, theRect, imageReference);
}
I have a gesture recognizer on my mapview and am trying to detect the tap there:
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint tapPoint = [gestureRecognizer locationInView:self.mapView];
CLLocationCoordinate2D tapCoord = [self.mapView convertPoint:tapPoint toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
for (id<MKOverlay> overlay in self.mapView.overlays) {
if([overlay isKindOfClass:[PVParkOverlay class]]){
NSLog(#"overlay is present");
/*
MKPolygon *polygon = (MKPolygon*) overlay;
CGMutablePathRef mpr = CGPathCreateMutable();
MKMapPoint *polygonPoints = polygon.points;
for (int p=0; p < polygon.pointCount; p++){
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}
if(CGPathContainsPoint(mpr , NULL, mapPointAsCGP, FALSE)){
// ... found it!
NSLog(#"I've found it!");
}
//CGPathRelease(mpr);
*/
}
}
I know that the overlay is there, but since it is a drawn image I can't find a way to convert this to polygon points to use this code (if even possible).
Any other methods I can use for this?
I also found following sample code but the viewForOverlay method is deprecated:
- (void)mapTapped:(UITapGestureRecognizer *)recognizer
{
MKMapView *mapView = (MKMapView *)recognizer.view;
id<MKOverlay> tappedOverlay = nil;
for (id<MKOverlay> overlay in mapView.overlays)
{
MKOverlayView *view = [mapView viewForOverlay:overlay];
if (view)
{
// Get view frame rect in the mapView's coordinate system
CGRect viewFrameInMapView = [view.superview convertRect:view.frame toView:mapView];
// Get touch point in the mapView's coordinate system
CGPoint point = [recognizer locationInView:mapView];
// Check if the touch is within the view bounds
if (CGRectContainsPoint(viewFrameInMapView, point))
{
tappedOverlay = overlay;
break;
}
}
}
NSLog(#"Tapped view: %#", [mapView viewForOverlay:tappedOverlay]);
}

Assertion failure in +[CCSprite spriteWithSpriteFrameName:]?

I'm getting an Assertion failure in +[CCSprite spriteWithSpriteFrameName:] for some reason.
I'm basically trying to "updatePinkBerries" or in other words, creating more sprite 'berries' to be added to the screen. They are the 'enemies' in my game.
This is my code:
- (id) init
{
if((self = [super init]))
{
CCLOG(#"%# init", NSStringFromClass([self class]));
self.touchEnabled = YES;
[self scheduleUpdate];
}
return self;
}
-(void)updatePinkBerries:(ccTime)dt
{
CGSize winSize = [CCDirector sharedDirector].winSize;
double curTime = CACurrentMediaTime();
if (curTime > _nextPinkBerrySpawn)
{
// Figure out the next time to spawn an asteroid
float randSecs = randomValueBetween(0.20, 1.0); _nextPinkBerrySpawn = randSecs + curTime;
// Figure out a random Y value to spawn at
float randY = randomValueBetween(0.0, winSize.height);
// Figure out a random amount of time to move from right to left
float randDuration = randomValueBetween(2.0, 10.0);
// Create a new asteroid sprite
CCSprite *pinkBerry = [CCSprite spriteWithSpriteFrameName:#"ship.png"]; [_batchNode addChild:pinkBerry];
// Set its position to be offscreen to the right
pinkBerry.position = ccp(winSize.width + pinkBerry.contentSize.width/2, randY);
// Move it offscreen to the left, and when it's done, call removeNode
[pinkBerry runAction:
[CCSequence actions:
[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width- pinkBerry.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(removeNode:)], nil]];
}
}
EDIT: FULL ASSERTION LOG:
2013-03-31 19:39:09.225 Berry Muncher-iOS[19550:c07] -[CCFileUtils
fullPathForFilename:resolutionType:] : cocos2d: Warning: File not
found: Sprites.plist 2013-03-31 19:39:09.225 Berry
Muncher-iOS[19550:c07] cocos2d: CCSpriteFrameCache: Trying to use file
'Sprites.png' as texture 2013-03-31 19:39:09.425 Berry
Muncher-iOS[19550:c07] cocos2d: CCSpriteFrameCache: Frame 'ship.png'
not found 2013-03-31 19:39:09.426 Berry Muncher-iOS[19550:c07] ***
Assertion failure in +[CCSprite spriteWithSpriteFrameName:],
/Users/Suraya/Desktop/Kobold2D/Kobold2D-2.1.0/Kobold2D/libs/cocos2d-iphone/cocos2d/CCSprite.m:105
EDIT: screenshot of my plist
I recognize this from a Ray Wanderlich tutorial. Because he has different artwork than you might use, you should create your own CCSpriteBatchNode and accompanying plist file so item names from his project don't get mixed up with yours.

Huge drop in fps with transparent EAGLView

I have a Cocos2d project and I want a constant background throughout the app. In the applicationDidFinishLaunching method of its delegate, I have replaced the line:
I have changed the pixelFormat of glView from kEAGLColorFormatRGB565 to kEAGLColorFormatRGBA8. When I make that change, glView becomes transparent and I can see through it, but the fps drops dramatically. If I don't make that change, the view doesn't become transparent, but I don't see the huge drop in fps. I'm talking about a significant drop in fps, from 59.0-60.0 to about 35.0-42.0.
I am using this code right below the addSubview line above to make the view transparent:
director.openGLView.opaque = NO;
The whole applicationDidFinishLaunching method looks like this:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
// Init the window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Try to use CADisplayLink director
// if it fails (SDK < 3.1) use the default director
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
CCDirector *director = [CCDirector sharedDirector];
// Init the View Controller
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
//
// Create the EAGLView manually
// 1. Create a RGB565 format. Alternative: RGBA8
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
//
EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGBA8 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
// attach the openglView to the director
[director setOpenGLView:glView];
glView.opaque = NO;
// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
//
// VERY IMPORTANT:
// If the rotation is going to be controlled by a UIViewController
// then the device orientation should be "Portrait".
//
// IMPORTANT:
// By default, this template only supports Landscape orientations.
// Edit the RootViewController.m file to edit the supported orientations.
//
#if GAME_AUTOROTATION == kGameAutorotationUIViewController
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
#else
[director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
#endif
[director setAnimationInterval:1.0/60];
[director setDisplayFPS:YES];
// make the OpenGLView a child of the view controller
[viewController setView:glView];
//Required in iOS6, recommended in 4 and 5
[window setRootViewController:viewController];
// make the View Controller a child of the main window, needed for iOS 4 and 5
[window addSubview: viewController.view];
[window makeKeyAndVisible];
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
// You can change anytime.
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// Removes the startup flicker
[self removeStartupFlicker];
// Run the intro Scene
[[CCDirector sharedDirector] runWithScene: [Intro scene]];
}
Any ideas as to why this is happening? I can provide more code if need be.
I forgot to mention one other code change that I did. In CCDirector.m setGLDefaultValuesI changed this line from this:
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
To this:
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Anytime you add an alpha channel with transparency, the OpenGL engine is going to have to perform some form (albeit default) alpha blending. This is math per pixel that it wasnt having to do in the non-RGBA (i.e. RGB 565) case. Anytime you add to the math needed by the engine frame rates are going to drop.
This is to be expected. The RGBA8888 framebuffer uses twice as much memory and the GPU needs to perform more work to render into this framebuffer. This is why RGB565 is the default format.

CoreGraphics drawRect in a cocos2d project EXEC BAD ACCESS

I got a interesting riddle here. My cocos2d project uses a UIView to display nicer popups than the regular alert view. To be more flexible in the size of the popup, I draw the background in the drawRect method.
But first the hierarchy how I implement cocos2d and UIKit:
Every UIKit element is added to the RootViewController. Every CCNode to the EAGLView. (If someone has a better solution to mix those world, don't wait to tell me!) Unfortunately is every UIKit view in front of cocos nodes.
Everything works fine when I add the first popup to the RootViewController. But if I remove the first popup and add a new one to the RootViewController occurs a bad access.
It crashes only in combination with cocos2d.
I use the code from Ray Wenderlichs CoreGraphics 101 tutorial.
context and strokeColor are not nil.
Another important infos: I use ARC and am supporting iOS 4.2 and above.
The complete code can be found at raywenderlich.com or below
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
CGRect rectFor1PxStroke(CGRect rect)
{
return CGRectMake(rect.origin.x + 0.5, rect.origin.y + 0.5, rect.size.width - 1, rect.size.height -1);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frame = CGRectInset(self.bounds, 2, 2);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 3.0, shadowColor);
CGRect strokeRect = CGRectInset(frame, -2.0, -2.0);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetLineWidth(context, 2.0);
CGContextStrokeRect(context, rectFor1PxStroke(strokeRect));
drawLinearGradient(context, frame, gradient1, gradient2);
}
ARC releases the CGColorRef named strokeColor. One fix is to replace it with an CGFloat array and use CGContextSetStrokeColor instead CGContextSetStrokeColorWithColor
This answer solves this issue:
App crashes when using __bridge for CoreGraphics gradient on ARC
I agree with zeiteisen ,ARC releases CGColorRef and the easiest way to solve this by
replacing CGContextSetStrokeColorWithColor with
CGContextSetStrokeColor(context, components)
Change UIColor of strokeColor to 'RGB CGFloat components array' as following:
static CGFloat red, green, blue, alpha;
- (void)getRGBComponents:(CGFloat [4])components forColor:(UIColor *)color {
[color getRed:&red green:&green blue:&blue alpha:&alpha];//To fetch CGFloat RGB components
for (int component = 0; component < 4; component++) {
switch (component) {
case 0:
components[component] = red;
break;
case 1:
components[component] = green;
break;
case 2:
components[component] = blue;
break;
case 3:
components[component] = alpha;
break;
default:
break;
}
}
}
Use this method like this:
CGFloat components[4];
UIColor *strokeColor=[UIColor greyColor];//My color
[self getRGBComponents:components forColor:strokeColor];//'components' will be substituted in CGContextSetStrokeColor
NSLog(#"%f %f %f %f", components[0], components[1], components[2],components[3]);

Copying the drawn contents of one UIView to another

I'd like to take a UITextView and allow the user to enter text into it and then trigger a copy of the contents onto a quartz bitmap context. Does anyone know how I can perform this copy action? Should I override the drawRect method and call [super drawRect] and then take the resulting context and copy it? If so, does anyone have any reference to sample code to copy from one context to another?
Update: from reading the link in the answer below, I put together this much to attempt to copy my UIView contents into a bitmap context, but something is still not right. I get my contents mirrored across the X axis (i.e. upside down). I tried using CGContextScaleCTM() but that seems to have no effect.
I've verified that the created UIImage from the first four lines do properly create a UIImage that isn't strangely rotated/flipped, so there is something I'm doing wrong with the later calls.
// copy contents to bitmap context
UIGraphicsBeginImageContext(mTextView.bounds.size);
[mTextView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setNeedsDisplay];
// render the created image to the bitmap context
CGImageRef cgImage = [image CGImage];
CGContextScaleCTM(mContext, 1.0, -1.0); // doesn't seem to change result
CGContextDrawImage(mContext, CGRectMake(
mTextView.frame.origin.x,
mTextView.frame.origin.y,
[image size].width, [image size].height), cgImage);
Any suggestions?
Here is the code I used to get a UIImage of UIView:
#implementation UIView (Sreenshot)
- (UIImage *)screenshot{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
/* iOS 7 */
BOOL visible = !self.hidden && self.superview;
CGFloat alpha = self.alpha;
BOOL animating = self.layer.animationKeys != nil;
BOOL success = YES;
if ([self respondsToSelector:#selector(drawViewHierarchyInRect:afterScreenUpdates:)]){
//only works when visible
if (!animating && alpha == 1 && visible) {
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
}else{
self.alpha = 1;
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
self.alpha = alpha;
}
}
if(!success){ /* iOS 6 */
self.alpha = 1;
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
self.alpha = alpha;
}
UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
#end
You can use in iOS 7 and later:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

Resources