Home > database >  Cocoa: Capture Screen and scale image on saving in Swift
Cocoa: Capture Screen and scale image on saving in Swift

Time:10-25

Below code I am using to capture screen in macOS application,

let img = CGDisplayCreateImage(CGMainDisplayID())

guard let destination = FileManager.default.urls(for: .downloadsDirectory,
    in: .userDomainMask).first?.appendingPathComponent("shot.jpg", isDirectory: false)
else {
    print("Unable to save captured image!")
    return
}
            
let properties: CFDictionary = [
    kCGImagePropertyPixelWidth: "900",
    kCGImagePropertyPixelHeight: "380"
] as CFDictionary
            
if let dest = CGImageDestinationCreateWithURL(destination as CFURL, kUTTypeJPEG, 1, properties) {
    CGImageDestinationAddImage(dest, img!, properties)
    CGImageDestinationFinalize(dest)
}
else {
    print("Unable to create captured image to the destination!")
}

I have to scale the image to particular size while saving. So, I used CFDictionary with width, heigh properties of the image. But It's seems I am doing it as wrong. Please help me to find out correct solution. Thank you!

CodePudding user response:

First, you can't resize using CGImageDestinationCreateWithURL or CGImageDestinationAddImage. If you look at the docs here and here you will notice that neither kCGImagePropertyPixelWidth or kCGImagePropertyPixelHeight is supported.

You will need to resize manually. You can use this tool, or modify it, if you find it helpful. It supports fill (stretch) and fit (scale while keeping the original aspect ratio) content modes. If you specify .fit it will center the drawing in the resulting image. If you specify .fill it will fill the whole space stretching whichever dimension it needs to.

enum ImageResizer {

    enum ContentMode {
        case fill
        case fit
    }

    enum Error: Swift.Error {
        case badOriginal
        case resizeFailed
    }

    static func resize(_ source: CGImage, to targetSize: CGSize, mode: ContentMode) throws -> CGImage {

        let context = CGContext(
            data: nil,
            width: Int(targetSize.width),
            height: Int(targetSize.height),
            bitsPerComponent: source.bitsPerComponent,
            bytesPerRow: 0,
            space: source.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!,
            bitmapInfo: source.bitmapInfo.rawValue
        )

        guard let context = context else {
            throw Error.badOriginal
        }

        let drawingSize: CGSize
        switch mode {
        case .fill:
            drawingSize = targetSize
        case .fit:
            drawingSize = CGSize(width: source.width, height: source.height)
                .scaledToFit(target: targetSize)
        }

        let drawRect = CGRect(origin: .zero, size: targetSize)
            .makeCenteredRect(withSize: drawingSize)

        context.interpolationQuality = .high
        context.draw(source, in: drawRect)

        guard let result = context.makeImage() else {
            throw Error.resizeFailed
        }

        return result
    }
}

ImageResizer depends on these CG extensions for scaling the source image and centering scaled image:

extension CGSize {

    var maxDimension: CGFloat {
        Swift.max(width, height)
    }

    var minDimension: CGFloat {
        Swift.min(width, height)
    }

    func scaled(by scalar: CGFloat) -> CGSize {
        CGSize(width: width * scalar, height: height * scalar)
    }

    func scaleFactors(to target: CGSize) -> CGSize {
        CGSize(
            width: target.width / width,
            height: target.height / height
        )
    }

    func scaledToFit(target: CGSize) -> CGSize {
        return scaled(by: scaleFactors(to: target).minDimension)
    }
}

extension CGRect {
    func makeCenteredRect(withSize size: CGSize) -> CGRect {
        let origin = CGPoint(
            x: midX - size.width / 2.0,
            y: midY - size.height / 2.0
        )
        return CGRect(origin: origin, size: size)
    }
}

Also, make sure you set up permissions if you're going to save to .downloadsDirectory.

  • Related