import Foundation import AppKit extension CGImage { func toGrayscale() -> CGImage? { let rect = CGRect(x: 0, y: 0, width: width, height: height) let grayColorSpace = CGColorSpaceCreateDeviceGray() let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) let ccgContext = CGContext(data: nil, width: width, height: height, bitsPerComponent: 16, bytesPerRow: width * 2, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue) ccgContext?.setFillColor(.black) ccgContext?.fill(rect) ccgContext?.draw(self, in: rect) return ccgContext?.makeImage() } // we really only handle 90 degree turns, which is totally fine func rotate(angle : CGFloat, flipHorizontally : Bool, flipVertically : Bool) -> CGImage? { let newWidth: CGFloat = CGFloat(height) let newHeight: CGFloat = CGFloat(width) let ctx = CGContext(data: nil, width: Int(newWidth), height: Int(newHeight), bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace!, bitmapInfo: bitmapInfo.rawValue)! ctx.translateBy(x: newWidth / 2, y: newHeight / 2) ctx.rotate(by: -angle) if flipVertically { ctx.scaleBy(x: 1.0, y: -1.0) } if flipHorizontally { ctx.scaleBy(x: -1.0, y: 1.0) } let dstRect = CGRect(x: -width / 2, y: -height / 2, width: width, height: height) ctx.draw(self, in: dstRect) return ctx.makeImage() } } extension NSImage { func imageRotatedByDegreess(degrees:CGFloat) -> NSImage { var imageBounds = NSMakeRect(0.0, 0.0, size.width, size.height) let pathBounds = NSBezierPath(rect: imageBounds) var transform = NSAffineTransform() transform.rotate(byDegrees: degrees) pathBounds.transform(using: transform as AffineTransform) let rotatedBounds:NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y, pathBounds.bounds.size.width, pathBounds.bounds.size.height ) let rotatedImage = NSImage(size: rotatedBounds.size) imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2) imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2) transform = NSAffineTransform() transform.translateX(by: +(NSWidth(rotatedBounds) / 2 ), yBy: +(NSHeight(rotatedBounds) / 2)) transform.rotate(byDegrees: degrees) transform.translateX(by: -(NSWidth(rotatedBounds) / 2 ), yBy: -(NSHeight(rotatedBounds) / 2)) rotatedImage.lockFocus() transform.concat() self.draw(in: imageBounds, from: .zero, operation: .copy, fraction: 1.0) rotatedImage.unlockFocus() return rotatedImage } func flipHorizontally() -> NSImage { let flipedImage = NSImage(size: size) flipedImage.lockFocus() let transform = NSAffineTransform() transform.translateX(by: size.width, yBy: 0.0) transform.scaleX(by: -1.0, yBy: 1.0) transform.concat() let rect = NSMakeRect(0, 0, size.width, size.height) self.draw(at: .zero, from: rect, operation: .sourceOver, fraction: 1.0) flipedImage.unlockFocus() return flipedImage } func flipVertically() -> NSImage { let flipedImage = NSImage(size: size) flipedImage.lockFocus() let transform = NSAffineTransform() transform.translateX(by: 0.0, yBy: size.height) transform.scaleX(by: 1.0, yBy: -1.0) transform.concat() let rect = NSMakeRect(0, 0, size.width, size.height) self.draw(at: .zero, from: rect, operation: .sourceOver, fraction: 1.0) flipedImage.unlockFocus() return flipedImage } } extension NSView { class func fromNib() -> T? { var topLevelObjects : NSArray? guard Bundle.main.loadNibNamed("\(self)", owner: nil, topLevelObjects: &topLevelObjects) else { return nil } return topLevelObjects!.first(where: { $0 is T }) as? T } }