Question

I would like to render to an offscreen bitmap (or array of RGBA values) and then blit those to a UIView during in the view's drawRect function. I would prefer to do full 32-bit rendering (including alpha channel), but would also be content with 24-bit rendering.

Would anyone mind pointing me in the right direction with some code snippets or relevant APIs?

Also, I know exactly how to do this using OpenGL - I would just prefer to do this work in Core Graphics itself.

Was it helpful?

Solution

To render into an offscreen context and save it as a CGImageRef:

void *bitmapData = calloc(height, bytesPerLine);
CGContextRef offscreen = CGBitmapContextCreate(..., bitmapData, ...)
// draw stuff into offscreen
CGImageRef image = CGBitmapContextCreateImage(offscreen);
CFRelease(offscreen);
free(bitmapData);

To draw it on the screen:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, rect, image);
}

You could also just save the image in the view's layer's contents property (view.layer.contents = image), or use a UIImageView.

OTHER TIPS

You can use a CGBitmapContext. You can generate an image from a CGBitmapContext and draw it during your drawRect.

Use CGDataProviderCreateWithData and CGImageCreate if you don't need the bitmap context and just want the CGImageRef.

For future reference, here is a full example in Swift 2.1 of rendering to an offscreen bitmap and displaying it on the screen.

Note that once you have created the bitmap context you can keep drawing more content into it, and updating the view when you want. This is great if you want to do a lengthy drawing operation on a background thread and periodically show progress to the user.

View Controller:

import UIKit

class ViewController: UIViewController {
    @IBOutlet var myView: MyView!
    var bitmapContext: CGContext?

    override func viewDidLoad() {
        super.viewDidLoad()
        createBitmapContext()
        drawContentIntoBitmap()
        myView.update(from: bitmapContext)
        releaseBitmapContext()
    }

    func createBitmapContext() {
        bitmapContext = CGBitmapContextCreate(
            nil,                                                        // auto-assign memory for the bitmap
            Int (myView.bounds.width * UIScreen.mainScreen().scale),    // width of the view in pixels
            Int (myView.bounds.height * UIScreen.mainScreen().scale),   // height of the view in pixels
            8,                                                          // 8 bits per colour component
            0,                                                          // auto-calculate bytes per row
            CGColorSpaceCreateDeviceRGB(),                              // create a suitable colour space
            CGImageAlphaInfo.PremultipliedFirst.rawValue)               // use quartz-friendly byte ordering
    }

    func drawContentIntoBitmap() {
        CGContextScaleCTM(bitmapContext, UIScreen.mainScreen().scale, UIScreen.mainScreen().scale)  // convert to points dimensions
        CGContextSetStrokeColorWithColor (bitmapContext, UIColor.redColor().CGColor)
        CGContextSetLineWidth (bitmapContext, 5.0)
        CGContextStrokeEllipseInRect (bitmapContext, CGRectMake(50, 50, 100, 100))
    }

    func releaseBitmapContext() {
        bitmapContext = nil // in Swift, CGContext and CGColorSpace objects are memory managed by automatic reference counting
    }
}

Subclass of UIView:

import UIKit

class MyView: UIView {     
    var cgImage: CGImage?

    func update(from bitmapContext: CGContext?) {
        cgImage = CGBitmapContextCreateImage(bitmapContext)
        setNeedsDisplay()
    }

    override func drawRect(rect: CGRect) {
        let displayContext = UIGraphicsGetCurrentContext()
        CGContextDrawImage(displayContext, bounds, cgImage)
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top