A layer is simply a bitmap – a chunk of memory that holds the red, green, blue, and alpha values of each pixel. When you send the message setNeedsDisplay to a UIView instance, that method is forwarded to the view’s layer. After the run loop is done processing an event, every layer marked for re-display prepares a CGContextRef. Drawing routines called on this context generate pixels that end up in the layer’s bitmap.
How do drawing routines get called on the layer’s context? After a layer prepares its context, it sends the message drawLayer:inContext: to its delegate. The delegate of an implicit layer is its view, so in the implementation for drawLayer:inContext:, the view sends drawRect: to itself. Therefore, when you see this line at the top of your drawRect: implementations,
- (void)drawRect:(CGRect)r { CGContextRef ctx = UIGraphicsGetCurrentContext(); }
you are getting a pointer to the layer’s context. All of the drawing in drawRect: is filling the layer’s bitmap, which is then copied to the screen.
Need to see this for yourself? Set an Xcode breakpoint in HypnosisView’s drawRect: and check out the stack trace in the debug navigator, as shown in Figure 22.8.
A few paragraphs up, we said that the pixels generated by drawing routines “end up in the layer’s bitmap.” What does that mean? When you want to create a bitmap context in Cocoa Touch (as you did when you created the thumbnails for the possessions), you typically do something like this:
// Create context UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]); ... Do drawing here ... // Get image result UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); // Clean up image context UIGraphicsEndImageContext();
A bitmap context is created and drawn to, and the resulting pixels are stored in a UIImage instance.
The UIGraphics suite of functions provides a convenient way of creating a bitmap CGContextRef and writing that data to a UIImage object:
// Create a color space to use for the context CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // Create a context of appropriate width and height // with 4 bytes per pixel - RGBA CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast); // Make this context the current one UIGraphicsPushContext(ctx); ... Do drawing here ... // Get image result CGImageRef image = CGBitmapContextCreateImage(ctx); UIImage *result = [[[UIImage alloc] initWithCGImage:image] autorelease]; // Clean up image context - make previous context current if one exists UIGraphicsPopContext(); CGImageRelease(image); CGContextRelease(ctx); CGColorSpaceRelease(colorSpace);
A layer creates the same kind of context when it needs to redraw its contents. However, a layer does it a little differently. See the NULL as the first parameter to CGBitmapContextCreate? That is where you pass a data buffer to hold the pixels generated by drawing routines in this context. By passing NULL, we say, “Core Graphics, figure out how much memory is needed for this buffer, create it, and then dispose of it when the context is destroyed.” A CALayer already has a buffer (its contents), so it would call the function as follows:
CGContextRef ctx = CGBitmapContextCreate(myBitmapPixels, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
Then, when this context is drawn to, all of the resulting pixels are immediately written to the bitmap that is the layer.
3.133.142.2