I want to pre-render some graphics into CGLayer for fast drawing in future.
I found that CGLayerCreateWithContext requires a CGContext parameter. It can be easily found in drawRect: method. But I need to create a CGLayer outside of drawRect:. Where should I get CGContext?
Should I simply create temporary CGBitmapContext and use it?
UPDATE:
I need to create CGLayer outside of drawRect: because I want to initialize CGLayer before it is rendered. It is possible to init once on first drawRect call but it's not beautiful solution for me.
There is no reason to do it outside of drawRect: and in fact there are some benefits to doing it inside. For example, if you change the size of the view the layer will still get made with the correct size (assuming it is based on your view's graphics context and not just an arbitrary size). This is a common practice, and I don't think there will be a benefit to creating it outside. The bulk of the CPU cycles will be spent in CGContextDrawLayer anyway.
You can create it by this function, you can render your content in the render block
typedef void (^render_block_t)(CGContextRef);
- (CGLayerRef)rendLayer:(render_block_t) block {
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
CGContextRef context = UIGraphicsGetCurrentContext();
CGLayerRef cgLayer = CGLayerCreateWithContext(context, CGSizeMake(100, 100), nil);
block(CGLayerGetContext(cgLayer));
UIGraphicsEndImageContext();
return cgLayer;
}
I wrote it few days ago. I use it to draw some UIImages in mutable threads.
You can download the code on https://github.com/PengHao/GLImageView/
the file path is GLImageView/GLImageView/ImagesView.m
Related
I've seen a lot of examples where ones get new UIImage with modifications from input UIImage. It looks like:
- (UIImage *)imageByDrawingCircleOnImage:(UIImage *)image
{
// begin a graphics context of sufficient size
UIGraphicsBeginImageContext(image.size);
// draw original image into the context
[image drawAtPoint:CGPointZero];
// get the context for CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// draw there
I have similar problem, but I really want to modify input image. I suppose it would work faster since I would't draw original image every time. But I could not find any samples of it. How can I get image context of original image, where it's already drawn?
UIImage is immutable for numerous reasons (most of them around performance and memory). You must make a copy if you want to mess with it.
If you want a mutable image, just draw it into a context and keep using that context. You can create your own context using CGBitmapContextCreate.
That said, don't second-guess the system too much here. UIImage and Core Graphics have a lot of optimizations in them and there's a reason you see so many examples that copy the image. Don't "suppose it would work faster." You really have to profile it in your program.
i'm trying to create a signature with touch inputs using a GLKView.
But now i need a UIImage to be below the signature.
Short: I want to draw lines above a UIImage using a custom GLKView.
The problem is that my line gets drawn below the image every time, no matter if i set opaque to NO and insertSubview: belowSubview..
Otherwise i tried with the help of textures but i have no idea how to do this..
I do not want to use a GLKViewController if it is possible ;)
Thanks in advance!
Update:
I found my problem and now i get the result that i wanted to have.
Inside the GLKView in the Constructor i initiate the EAGLContext.
I forgot to set the context to self.
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (context) {
self.opaque = NO; // set view transparent
self.context = context; // set the context to itself
}
Although setting opaque to NO is not a good solution it is the only efficient solution for my task.
There are a couple of ways worth looking at to do this.
One is to look at view containment instead of layering — make the GLKView a subview of the view you're drawing a UIImage in.
The other is to draw the image in your GLKView using OpenGL ES. It's a little more work, but not too hard if you look over the documentation and the answers already here on SO. And it has some extra benefits: since both the background image and your drawing are going into the same framebuffer, you can control blending in GL. Here's some tips for getting started:
Use GLKTextureLoader to get your image into an OpenGL ES texture.
Set up a GLKBaseEffect instance for drawing with your texture. Don't forget to tell it to prepareToDraw.
Draw a quad using the texture and effect. This answer has a pretty decent starting point for doing that.
After drawing the background image, draw your signature and it'll be on top of the image.
I found many examples how to blit an array of ints onto the UIView in drawRect, but the simplest one still puzzling me. This works OK, but still three questions:
~ why two contexts?
~ why push/pop context?
~ can avoid copy? (Apple docs say that CGBitmapContextCreateImage copy memory block)
- (void)drawRect:(CGRect)rect {
CGColorSpaceRef color = CGColorSpaceCreateDeviceRGB();
int PIX[9] = { 0xff00ffff,0xff0000ff,0xff00ff00,
0xff0000ff,0xff00ffff,0xff0000ff,
0xff00ff00,0xff0000ff,0xff00ffff};
CGContextRef context = CGBitmapContextCreate((void*)PIX,3,3,8,4*3,color,kCGImageAlphaPremultipliedLast);
UIGraphicsPushContext(context);
CGImageRef image = CGBitmapContextCreateImage(context);
UIGraphicsPopContext();
CGContextRelease(context);
CGColorSpaceRelease(color);
CGContextRef c=UIGraphicsGetCurrentContext();
CGContextDrawImage(c, CGRectMake(0, 0, 10, 10), image);
CGImageRelease(image);
}
The method is drawing the array to 3x3 sized image, then drawing that image onto a 10x10 size in the current context, which is this case would be your UIView's CALayer.
UIGraphicsPushContext lets you set the CGContext that you are currently drawing to. So before the first call, your current CGContext is the UIView, then you push the new CGContext which is the image that is being drawn to.
The UIGraphicsPopContext call restores the previous context, which is the UIView, then you get a reference to that context, and draw the created image to it using this line:
CGContextDrawImage(c, CGRectMake(0, 0, 10, 10), image);
As far as avoiding the copy operation, the docs say that it is sometime a copy on write, but they don't specify when those conditions occur:
The CGImage object returned by this function is created by a copy operation. Subsequent changes to the bitmap graphics context do not affect the contents of the returned image. In some cases the copy operation actually follows copy-on-write semantics, so that the actual physical copy of the bits occur only if the underlying data in the bitmap graphics context is modified. As a consequence, you may want to use the resulting image and release it before you perform additional drawing into the bitmap graphics context. In this way, you can avoid the actual physical copy of the data.
In Quartz2D, can I draw any shapes without using drawRect:(CGRect)rect method?
Yes. If you want to draw to a bitmap (as one example) and produce a CGImage, you could certainly create a CGBitmapContext, then use CoreGraphics as usual using that as your context.
If you want to draw to the display, do your work from within drawRect:, using the supplied graphics context.
I'm trying to draw an animated growing line using Quartz 2d, by adding points to an existing line, gradually over time. I started drawing a new line, In the drawRect method of a UIView, by obtaining the CGContextRef, setting its draw properties, and moving the first point to (0,0).
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context,[UIColor blueColor].CGColor);
CGContextSetLineWidth(context, 2);
CGContextMoveToPoint(context,0,0);
later, in my next drawRect call, i tried extending that line, by again, obtaining the CGContextRef, and adding a new point to it.
GContextRef context= UIGraphicsGetCurrentContext();
CGContextAddLineToPoint(context,x,y);
but it seems that the current CGContextRef doesn't have any record of my previous CGContextMoveToPoint command from the last drawRect call, therefore doesn't have any reference that i already started drawing a line.
Am i doing something wrong here? is there a way refering an already drawn line?
You basically need to treat each call to drawRect as if it was starting from scratch. Even if you are only asked to update a subrect of the view, you should assume that any state associated with the graphics context, such as drawing position and colours, will have been reset. So in your case, you need to keep track of the start position and redraw the whole line each time.
I think the better approach is to animate some thin UIView. Look my answer here.
If you need more than just horizontal line, rotate that view. I think it's better for the performance.