UIImage vs NSImage: Drawing to an off screen image in iOS - objective-c

In mac osx (cocoa), It is very easy to make a blank image of a specific size and draw to it off screen:
NSImage* image = [[NSImage alloc] initWithSize:NSMakeSize(64,64)];
[image lockFocus];
/* drawing code here */
[image unlockFocus];
However, in iOS (cocoa touch) there does not seem to be equivalent calls for UIImage. I want to use UIImage (or some other equivalent class) to do the same thing. That is, I want to make an explicitly size, initially empty image to which I can draw using calls like UIRectFill(...) and [UIBezierPath stroke].
How would I do this?

CoreGraphics is needed here, as UIImage does not have high level functions like what you explained..
UIGraphicsBeginImageContext(CGSizeMake(64,64));
CGContextRef context = UIGraphicsGetCurrentContext();
// drawing code here (using context)
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

You can do that as follows:
UIGraphicsBeginImageContext(CGSizeMake(64, 64));
//Drawing code here
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Here is Apple's Graphics and Drawing reference.

Something like this:
UIGraphicsBeginImageContextWithOptions(mySize, NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
[myImage drawInRect:myImageRect];
[myText drawAtPoint:myOrigin withFont:myFont];
UIGraphicsPopContext();
UIImage *myNewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Related

How to take my view as a picture osx

I want to take my NSView(With layers) as an image can I do so in OSX ?
in iOS I would do the following
-(UIImage*) makeImage//snapshot the view
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
I tried this solution from 1 of the SO answers
[[NSImage alloc] initWithData:[view dataWithPDFInsideRect:[view bounds]]];
but it didn't work because I have layers in my NSView.
I started from converting UI to NS
and now I have warnings on every line :)
-(NSImage*) makeImage :(RMBlurredView*)view
{
UIGraphicsBeginImageContext(view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
NSImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
I am not familiar with OSX can some 1 help me to convert this code to OSX ?
drawing to the image is tad different than in iOS:
you create a blank image, LOCK on to it, so all drawing goes to that graphics context
NSImage *image = [[NSImage alloc] initWithSize:self.view.bounds.size];
[image lockFocus];
you draw your layer same as on iOS
CGContextRef ctx = [NSGraphicsContext currentContext].graphicsPort;
[self.view.layer renderInContext:ctx];
unlock image
[image unlockFocus];
NOTE this is only if your view is layered. it doesn't work for non-layered views!
your view -as shown above- is layer backed so all's fine ;)

Saving a UIImage from a UIView not working properly

I'm attempting to save the contents of a UIView (and it's subviews) into a UIImage.
Here is the code I am using:
+(UIImage *)imageWithView:(UIView *)view {
UIGraphicsBeginImageContextWithOptions([view bounds].size, YES, 0);
[[view layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
It works great... the first time. However, if I then modify subviews of the view and call this method again, I get the old UIImage every time.
It's my understanding that UIGraphicsEndImageContext() does in fact pop the bitmap image off of the stack.
How can I make this imageWithView: method work, taking into account the current state of the view's subviews?
I hope this helps:
UIGraphicsBeginImageContext(self.view.frame.size);
[testView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Snap functionality in iOS

I have a parent UIView which contains 3 subviews, here i have one UIButton to take snap of the whole view. i have done this using
UIGraphicsBeginImageContext(CGSizeMake(320,480));
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view.layer renderInContext:context];
here i need to capture only the 1st subview which name is subview1. how can i do this?
Capture SubView like this:
UIGraphicsBeginImageContext(subview1.size);
[subview1.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

How to capture a specific size of the self.view

I have the self.view, but I only want to capture 300x320 of it.
got this code:
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
UIImage * imgPrintScreen = [[UIImage alloc]init];
imgPrintScreen = viewImage;
What do I need to change here in order to do so?
Thanks allot!
Just change the size that you want to capture, instead of using the entire bounds, use the size you want. The render will start at the origin of the view and be clipped when it falls outside the bounds. If you need to change where the view is actually clipped, simply translate the context after starting the image context:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 320), YES, 0.);
[self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
If your view was, for example, 600x320 and you wanted to capture the middle 300 points in width, you'd translate the context 150 points to the left:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 320), YES, 0.);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, -150.f, 0.f);
[self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

How to darken a PNG?

I may point out that Drawing and Rendering in Objective-C is my weakness. Now, here's my problem.
I want to add a 'Day/Night' feature to my game. It has got lots of objects on a map. Every object is a UIView containing some data in variables and some UIImageViews: the sprite, and some of the objects have a hidden ring (used to show selection).
I want to be able to darken the content of the UIView, but I can't figure out how. The sprite is a PNG with transparency. I've just managed to add a black rectangle behind the sprite using this:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextSetRGBFillColor(ctx, 0, 0, 0, 0.5);
CGContextFillRect(ctx, rect);
CGContextRestoreGState(ctx);
As I've read, this should be done in the drawRect method. Help please!
If you want to understand better my scenario, the App where I'm trying to do this is called 'Kipos', at the App Store.
Floris497's approach is a good strategy for a blanket darkening for more than one image at a time (probably more what you're after in this case). But here's a general purpose method to generate darker UIImages (while respecting alpha pixels):
+ (UIImage *)darkenImage:(UIImage *)image toLevel:(CGFloat)level
{
// Create a temporary view to act as a darkening layer
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
UIView *tempView = [[UIView alloc] initWithFrame:frame];
tempView.backgroundColor = [UIColor blackColor];
tempView.alpha = level;
// Draw the image into a new graphics context
UIGraphicsBeginImageContext(frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawInRect:frame];
// Flip the context vertically so we can draw the dark layer via a mask that
// aligns with the image's alpha pixels (Quartz uses flipped coordinates)
CGContextTranslateCTM(context, 0, frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextClipToMask(context, frame, image.CGImage);
[tempView.layer renderInContext:context];
// Produce a new image from this context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
UIGraphicsEndImageContext();
[tempView release];
return toReturn;
}
The best way would be to add a core image filter to the layer that darkened it. You could use CIExposureAdjust.
CIFilter *filter = [CIFilter filterWithName:#"CIExposureAdjust"];
[filter setDefaults];
[filter setValue:[NSNumber numberWithFloat:-2.0] forKey:#"inputEV"];
view.layer.filters = [NSArray arrayWithObject:filter];
Here is how to do it:
// inputEV controlls the exposure, the lower the darker (e.g "-1" -> dark)
-(UIImage*)adjustImage:(UIImage*)image exposure:(float)inputEV
{
CIImage *inputImage = [[CIImage alloc] initWithCGImage:[image CGImage]];
UIImageOrientation originalOrientation = image.imageOrientation;
CIFilter* adjustmentFilter = [CIFilter filterWithName:#"CIExposureAdjust"];
[adjustmentFilter setDefaults];
[adjustmentFilter setValue:inputImage forKey:#"inputImage"];
[adjustmentFilter setValue:[NSNumber numberWithFloat:-1.0] forKey:#"inputEV"];
CIImage *outputImage = [adjustmentFilter valueForKey:#"outputImage"];
CIContext* context = [CIContext contextWithOptions:nil];
CGImageRef imgRef = [context createCGImage:outputImage fromRect:outputImage.extent] ;
UIImage* img = [[UIImage alloc] initWithCGImage:imgRef scale:1.0 orientation:originalOrientation];
CGImageRelease(imgRef);
return img;
}
Remember to import:
#import <QuartzCore/Quartzcore.h>
And add CoreGraphics and CoreImage frameworks to your project.
Tested on iPhone 3GS with iOS 5.1
CIFilter is available starting from iOS 5.0.
draw a UIView (a black one) over it and set "User interaction enabled" to NO
hope you can do something with this.
then use this to make it dark
[UIView animateWithDuration:2
animations:^{nightView.alpha = 0.4;}
completion:^(BOOL finished){ NSLog(#"done making it dark"); ]; }];
to make it light
[UIView animateWithDuration:2
animations:^{nightView.alpha = 0.0;}
completion:^(BOOL finished){ NSLog(#"done making it light again"); ]; }];