Merging/stacking two images with Cocoa/OSX

For a quick 'n dirty solution you can use the NSImage drawing APIs:

NSImage *background = [NSImage imageNamed:@"background"];
NSImage *overlay = [NSImage imageNamed:@"overlay"];

NSImage *newImage = [[NSImage alloc] initWithSize:[background size]];
[newImage lockFocus];

CGRect newImageRect = CGRectZero;
newImageRect.size = [newImage size];

[background drawInRect:newImageRect];
[overlay drawInRect:newImageRect];

[newImage unlockFocus];

CGImageRef newImageRef = [newImage CGImageForProposedRect:NULL context:nil hints:nil];

If you don't like that, most of the CGContext APIs you'd expect are available cross platform—for drawing with a little more control. Similarly, you could look into NSGraphicsContext.


If anyone, like me, needs a Swift version.

This is a functional Swift 5 version:

let background = NSImage(named: "background")
let overlay = NSImage(named: "overlay")

let newImage = NSImage(size: background.size)
newImage.lockFocus()

var newImageRect: CGRect = .zero
newImageRect.size = newImage.size

background.draw(in: newImageRect)
overlay.draw(in: newImageRect)

newImage.unlockFocus()

I wish I had the time to do the same with the CGContext example.


This is pretty easy when you render to a CGContext.

If you want an image as a result, you can create and render to a CGBitmapContext, then request the image after render.

General flow, with common details and contextual info omitted:

CGImageRef CreateCompositeOfImages(CGImageRef pBackground,
                                   const CGRect pBackgroundRect,
                                   CGImageRef pForeground,
                                   const CGRect pForegroundRect)
{
  // configure context parameters
  CGContextRef gtx = CGBitmapContextCreate( %%% );

  // configure context

  // configure context to render background image
  // draw background image
  CGContextDrawImage(gtx, pBackgroundRect, pBackground);

  // configure context to render foreground image
  // draw foreground image
  CGContextDrawImage(gtx, pForegroundRect, pForeground);

  // create result
  CGImageRef result = CGBitmapContextCreateImage(gtx);

  // cleanup

  return result;
}

You would need to create a CGImage from your PNG.

Additional APIs you may be interested in using:

  • CGContextSetBlendMode
  • CGContextSetAllowsAntialiasing
  • CGContextSetInterpolationQuality.

I know a lot of people will generally advise you to use higher level abstractions (i.e. AppKit and UIKit), but CoreGraphics is a great library for rendering in both of those contexts. If you are interested in graphics implementations which are easy to use in both OS X and iOS, CoreGraphics is a good choice to base your work upon if you are comfortable working with those abstractions.