Henrik
Henrik

Reputation: 4044

Blend two NSImages using Swift on OS X

I have two instances of NSImage. I want image2 to be placed on top of image1, with a certain level of opacity. They are of matching dimensions. Neither of the images need to be visible in the UI.

How can I correctly set up a graphics context and draw images to it, one with full opacity and another semi-transparent?

I have been reading several answers here but I find it complicated, especially since most seem to be for either Objective-C or only apply to iOS. Any pointers are appriciated. If this can be accomplished without needing a CGContext at all that would be even better.

func blendImages(image1: NSImage, image2: NSImage, alpha: CGFloat) -> CGImage {

    // Create context
    var ctx: CGContextRef = CGBitmapContextCreate(0, inputImage.size.width, inputImage.size.height, 8, inputImage.size.width*4, NSColorSpace.genericRGBColorSpace(), PremultipliedLast)
    let area = CGRectMake(0, 0, inputImage.size.width, inputImage.size.height)
    CGContextScaleCTM(ctx, 1, -1)

    // Draw image1 in context

    // Draw image2 with alpha opacity
    CGContextSetAlpha(ctx, CGFloat(0.5))

    // Create CGImage from context
    let outputImage = CGBitmapContextCreateImage(ctx)

    return outputImage
}

Elsewhere I have this extension to get CGImages from my NSImages:

extension NSImage {
    var CGImage: CGImageRef {
        get {
            let imageData = self.TIFFRepresentation
            let source = CGImageSourceCreateWithData(imageData as! CFDataRef, nil)
            let maskRef = CGImageSourceCreateImageAtIndex(source, 0, nil)
            return maskRef
        }
    }
}

Upvotes: 1

Views: 1365

Answers (2)

Dean Liu
Dean Liu

Reputation: 511

Thanks Henrik, I needed this for Obj-C. Here's that version in case someone also needs:

-(CGImageRef) mergeImage:(NSImage*)a andB:(NSImage*)b fraction:(float)fraction{
    NSBitmapImageRep *bitmap = (NSBitmapImageRep*)[[a representations] objectAtIndex:0];
    NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:ctx];

    CGRect rect = CGRectMake(0, 0, bitmap.size.width, bitmap.size.height);
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:ctx];
    [b drawInRect:rect fromRect:rect operation:NSCompositeSourceOver fraction:fraction];
    [NSGraphicsContext restoreGraphicsState];

    return [bitmap CGImage];
}

Upvotes: 0

Henrik
Henrik

Reputation: 4044

I managed to solve this with NSGraphicsContext.

func mergeImagesAB(pathA: String, pathB: String, fraction: CGFloat) -> CGImage {

    guard let imgA = NSImage(byReferencingFile: pathA),
         let imgB = NSImage(byReferencingFile: pathB),
         let bitmap = imgA.representations[0] as? NSBitmapImageRep,
         let ctx = NSGraphicsContext(bitmapImageRep: bitmap)
         else {fatalError("Failed to load images.")}

    let rect = NSRect(origin: CGPoint(x: 0, y: 0), size: bitmap.size)

    NSGraphicsContext.saveGraphicsState()
    NSGraphicsContext.setCurrentContext(ctx)
    imgB.drawInRect(rect, fromRect: rect, operation: .CompositeSourceOver, fraction: fraction)
    NSGraphicsContext.restoreGraphicsState()

    guard let result = bitmap.CGImage
        else {fatalError("Failed to create image.")}

    return result;
}

Upvotes: 3

Related Questions