Skip to content

Instantly share code, notes, and snippets.

@musa11971
Created December 13, 2019 13:43
Show Gist options
  • Save musa11971/62abcfda9ce3bb17f54301fdc84d8323 to your computer and use it in GitHub Desktop.
Save musa11971/62abcfda9ce3bb17f54301fdc84d8323 to your computer and use it in GitHub Desktop.
Getting average color in NSImage using Swift (extension) - Used in https://github.com/musa11971/Music-Bar/
//
// NSImage+Music-Bar.swift
// Music Bar
//
// Created by Musa Semou on 13/12/2019.
// Copyright © 2019 Musa Semou. All rights reserved.
//
import AppKit
extension NSImage {
/// Returns the average color that is present in the image.
var averageColor: NSColor? {
// Image is not valid, so we cannot get the average color
if !isValid {
return nil
}
// Create a CGImage from the NSImage
var imageRect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
let cgImageRef = self.cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
// Create vector and apply filter
let inputImage = CIImage(cgImage: cgImageRef!)
let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height)
let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector])
let outputImage = filter!.outputImage!
var bitmap = [UInt8](repeating: 0, count: 4)
let context = CIContext(options: [.workingColorSpace: kCFNull!])
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: nil)
return NSColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
}
}
@Peter-Schorn
Copy link

Peter-Schorn commented Dec 7, 2020

For those without ultra-ultra-ultra wide monitors:

extension NSImage {
    
    var averageColor: NSColor? {
        
        // Image is not valid, so we cannot get the average color
        if !isValid { return nil }
        
        // Create a CGImage from the NSImage
        var imageRect = CGRect(
            x: 0,
            y: 0,
            width: self.size.width,
            height: self.size.height
        )
        let cgImageRef = self.cgImage(
            forProposedRect: &imageRect,
            context: nil,
            hints: nil
        )
        
        // Create vector and apply filter
        let inputImage = CIImage(cgImage: cgImageRef!)
        let extentVector = CIVector(
            x: inputImage.extent.origin.x,
            y: inputImage.extent.origin.y,
            z: inputImage.extent.size.width,
            w: inputImage.extent.size.height
        )
        
        let filter = CIFilter(
            name: "CIAreaAverage",
            parameters: [
                kCIInputImageKey: inputImage,
                kCIInputExtentKey: extentVector
            ]
        )
        let outputImage = filter!.outputImage!
        
        var bitmap = [UInt8](repeating: 0, count: 4)
        let context = CIContext(options: [.workingColorSpace: kCFNull!])
        context.render(
            outputImage,
            toBitmap: &bitmap,
            rowBytes: 4,
            bounds: CGRect(x: 0, y: 0, width: 1, height: 1),
            format: .RGBA8,
            colorSpace: nil
        )
        
        return NSColor(
            red: CGFloat(bitmap[0]) / 255,
            green: CGFloat(bitmap[1]) / 255,
            blue: CGFloat(bitmap[2]) / 255,
            alpha: CGFloat(bitmap[3]) / 255
        )
        
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment