Render a layer on top of a graphics context - ios

In core Graphics, I am hoping to be able to draw updates to a UIView without having to redraw the entire image each time. The initial image is drawn from a CGImageRef:
CGImageRef image = CGBitmapContextCreateImage(ctxImage);
CGContextDrawImage(context, _screenRect, image);
Initially I was adding the new sections to an off screen context with the 'full image' and then creating a new CGImageRef from it before re-drawing.
CGContextDrawImage(tmp_Context,targetRect,_section);
CGImageRef image = CGBitmapContextCreateImage(tmp_Context);
CGContextDrawImage(context, _screenRect, image);
The problem with this is that in every frame update I need to redraw the entire image to the screen rather than somehow superimposing just the sections. This would yield a large performance increase if possible but not sure how to go about it. Possibly with CGLayer?
edit: I have been trying to rapidly update the view in a for loop as I receive small sections, the problem is drawRect isn't getting called to do any updates maybe because the for loop is going too fast and continually interrupting previous calls?
drawLayer = CGLayerCreateWithContext(ctxImage, targetRect.size, nil);
layerContext = CGLayerGetContext(drawLayer);
CGContextDrawImage(layerContext,targetRect,ipegSection);
CGContextDrawLayerAtPoint(currentScreen, targetRect.origin, drawLayer);
// Update the UI
[targetView setNeedsDisplay];

Can't you do (?) :
CGContextDrawImage(context, targetRect, _section);
Like this you draw only your section on the current context, not draw it on another context then redraw the screen entirely.

Related

renderInContext creating black background on background thread

I'm drawing a view using renderInContext to create a screenshot for storage in core-data.
I'm using this 'standard' code...
UIGraphicsBeginImageContextWithOptions( myview.bounds.size, YES, 0 );
CGContextRef ctx = UIGraphicsGetCurrentContext();
[myview.layer renderInContext:ctx];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Originally, I was doing this on the main thread which worked fine. However, as the view and its subviews became more complex (possibly hundreds of subviews with their own draw routines) the UI became too slow. So, I moved the rendering into a background thread.
This works except for the background color of the view 'myview' is black, which isn't what I've set it to...white.
With experimentation, I've noticed that if I pause my background thread for a second or two, the rendering is complete, with my background the required color. This is kind-of-ok but as the view becomes more complex, the pause needs to be longer in order to get the correct image of the view and it's not really correct to have a time delay in which I need to 'up' as the view gets more complex.
Has anyone got any suggestions how to resolve?
For info purposes, I've managed to fix this.
When I needed to render in the background, I was recreating my view and its subviews programmatically but the main view was not immediately calling drawRect when I called setNeedsDisplay...(of course!). So my background thread sometimes ran before the main view had rendered.
By forcing the view to render itself (with the code below) immediately, I could 'synchronise' everything and get the correct thumbnails.
CALayer *layer = self.layer;
[layer setNeedsDisplay];
[layer displayIfNeeded];
Hope this helps anyone else.
Alternative:
UIGraphicsBeginImageContextWithOptions(myview.bounds.size, YES, 0);
[myview drawViewHierarchyInRect:_captureView.bounds afterScreenUpdates:YES];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

iOS: Adding an outline/stroke to an image context with a transparent background

The images that goes through here are PNGs of different shapes with a transparent background. In addition to merging them (which works fine), I'd like to give the new image a couple of pixels thick outline. But I can't seem to manage that.
(So just to clarify, I'm after an outline around the actual shapes in the context, not a rectangle around the entire image.)
+ (UIImage *)mergeBackgroundImage:(UIImage *)backgroundImage withOverlayingImage:(UIImage *)overlayImage{
UIGraphicsBeginImageContextWithOptions(backgroundImage.size, NO, backgroundImage.scale);
[backgroundImage drawInRect:CGRectMake(0, 0, backgroundImage.size.width, backgroundImage.size.height)];
[overlayImage drawInRect:CGRectMake(backgroundImage.size.width - overlayImage.size.width, backgroundImage.size.height - overlayImage.size.height, overlayImage.size.width, overlayImage.size.height)];
//Add stroke.
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Thanks for your time!
Markus
If you make a CALayer who's backing is set to a CGImage of your image, you can then use it as a masking layer for your layer that requires an outline1. And once you've done that, you can render your layer into another context, and then get another UIImage from that.
// edit: Something like what's describe in this answer.

Taking a Screenshot (UIImage) from UIView takes far too long

I have the following method to take a screenshot (UIImage) of a UIView which is far too slow
+ (UIImage *)imageWithView:(UIView *)view
{
CGSize size = view.bounds.size;
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
return image;
}
On my iPad I now have an app that needs this method to make a copy of a view that is drag&dropped. This view is one with rounded corners and therefore is not opaque (which not makes a difference to if I would set the isOpaque param to YES I found out)...
Also the view that is screenshotted contains a UITableView with quite some complex entries in it...
Do you have any suggestions on how I can improve the speed of the screenshotting. Right now, for a bit bigger tableview (maybe 20 entries) it takes about 1 second (!!!)
And the view is already on screen, rendered correctly... so I just need the Pixels to but into an UIImageView...
I need to support iOS 6+.
I use this same code to take a screenshot of a really complex views. I think your bottleneck is using a big image for the drag&drop. Maybe you can resize the UIImage.
In my case the performance in a iPad2 is about 100ms for screenshot.

Use stretchable UIImage in CGContext

I'm searching a way to draw stretchable image as background of my custom cell background view. I would like to use drawRect method and draw an image stretched exactly as it would be stretched with stretchableImageWithLeftCapWidth in a UIImageView... how can i continue this code to make it happen ?
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage *bgImg =[[UIImage imageNamed:#"bg_table_top"]stretchableImageWithLeftCapWidth:3 topCapHeight:0];
//How to draw the image stretched as the self.bounds size ?
....
}
Any reason not to let UIImageView do this? (Include one as a child of your custom cell.) It's true that reducing child views can be a performance improvement in tables, but UIImageView is also pretty good at getting good performance when drawing images.
My guess is otherwise you're going to have to do multiple draw calls, in order to get the ends and middle drawn correctly.

invalid context error when drawing a UIImage

I am trying to draw a UIImage to the context of my UIView. I've used this code with the context stuff commented in and out ...
- (void)drawRect:(CGRect)rect
{
//UIGraphicsBeginImageContextWithOptions(rect.size,YES,[[UIScreen mainScreen] scale]);
//UIGraphicsBeginImageContext(rect.size);
UIImage *newImage = [UIImage imageNamed:#"character_1_0001.png"];
//[newImage drawAtPoint:CGPointMake(200, 200)];
[newImage drawInRect:rect];
//UIGraphicsEndImageContext();
}
As I understand it I shouldn't need to set the image context to do this and indeed without it I do see the image drawn in the view but I also get this error in the log ...
<Error>: CGContextSaveGState: invalid context 0x0
If I uncomment the UIGraphicsBeginImageContext lines I don't get anything drawn and no error.
Do I need to use the default graphics context and declare this somehow?
I need to draw the image because I want to add to it on the context and can't just generate a load of UIImageView objects.
It sounds like you are calling drawRect: directly. So it is getting called once from your call, and once from the genuine drawing loop.
With the context creation present, you do all the drawing in a new context then discard it, so you never see anything
With the context creation missing, you get the error from the manually called drawRect:, and the actual drawing takes place in the genuine drawRect: so you see your image.
Don't call drawRect: directly. Call setNeedsDisplay and drawRect: will be called for you at the appropriate point.
I discovered what was happening. The ViewController host of my custom UIView was instantiating a CALayer object. This was some old code that was previously being used as the drawing layer for a bitmap from an AVCaptureSession that I am using to generate my image.
I'm now not using the CALayer and am instead just drawing a slice of each frame of video onto the default graphics context in my view.
For some reason the instantiation of this CALayer was causing the conflict in the context.
Now it is not being used I only need to use the following code to draw the image ...
- (void)drawRect:(CGRect)rect
{
UIImage *newImage = [UIImage imageNamed:#"character_1_0001.png"];
[newImage drawInRect:rect];
}
Now I just do the same thing with an image that I pass in as a CGImageRef and adjust the rect ... hopefully.

Resources