Skip to content

Instantly share code, notes, and snippets.

@skyfe79
Forked from Farini/NSImageExtensions.swift
Created January 23, 2024 12:01
Show Gist options
  • Save skyfe79/118a45e1438bb7300a752905680c8aed to your computer and use it in GitHub Desktop.
Save skyfe79/118a45e1438bb7300a752905680c8aed to your computer and use it in GitHub Desktop.
NSImage extensions for easy resizing, cropping and saving png images. Version updated for Swift 3. Originally by Raphael Hanneken https://gist.github.com/raphaelhanneken/cb924aa280f4b9dbb480
extension NSImage {
/// Returns the height of the current image.
var height: CGFloat {
return self.size.height
}
/// Returns the width of the current image.
var width: CGFloat {
return self.size.width
}
/// Returns a png representation of the current image.
var PNGRepresentation: Data? {
if let tiff = self.tiffRepresentation, let tiffData = NSBitmapImageRep(data: tiff) {
return tiffData.representation(using: .PNG, properties: [:])
}
return nil
}
/// Copies the current image and resizes it to the given size.
///
/// - parameter size: The size of the new image.
///
/// - returns: The resized copy of the given image.
func copy(size: NSSize) -> NSImage? {
// Create a new rect with given width and height
let frame = NSMakeRect(0, 0, size.width, size.height)
// Get the best representation for the given size.
guard let rep = self.bestRepresentation(for: frame, context: nil, hints: nil) else {
return nil
}
// Create an empty image with the given size.
let img = NSImage(size: size)
// Set the drawing context and make sure to remove the focus before returning.
img.lockFocus()
defer { img.unlockFocus() }
// Draw the new image
if rep.draw(in: frame) {
return img
}
// Return nil in case something went wrong.
return nil
}
/// Copies the current image and resizes it to the size of the given NSSize, while
/// maintaining the aspect ratio of the original image.
///
/// - parameter size: The size of the new image.
///
/// - returns: The resized copy of the given image.
func resizeWhileMaintainingAspectRatioToSize(size: NSSize) -> NSImage? {
let newSize: NSSize
let widthRatio = size.width / self.width
let heightRatio = size.height / self.height
if widthRatio > heightRatio {
newSize = NSSize(width: floor(self.width * widthRatio), height: floor(self.height * widthRatio))
} else {
newSize = NSSize(width: floor(self.width * heightRatio), height: floor(self.height * heightRatio))
}
return self.copy(size: newSize)
}
/// Copies and crops an image to the supplied size.
///
/// - parameter size: The size of the new image.
///
/// - returns: The cropped copy of the given image.
func crop(size: NSSize) -> NSImage? {
// Resize the current image, while preserving the aspect ratio.
guard let resized = self.resizeWhileMaintainingAspectRatioToSize(size: size) else {
return nil
}
// Get some points to center the cropping area.
let x = floor((resized.width - size.width) / 2)
let y = floor((resized.height - size.height) / 2)
// Create the cropping frame.
let frame = NSMakeRect(x, y, size.width, size.height)
// Get the best representation of the image for the given cropping frame.
guard let rep = resized.bestRepresentation(for: frame, context: nil, hints: nil) else {
return nil
}
// Create a new image with the new size
let img = NSImage(size: size)
img.lockFocus()
defer { img.unlockFocus() }
if rep.draw(in: NSMakeRect(0, 0, size.width, size.height),
from: frame,
operation: NSCompositingOperation.copy,
fraction: 1.0,
respectFlipped: false,
hints: [:]) {
// Return the cropped image.
return img
}
// Return nil in case anything fails.
return nil
}
/// Copies and crops an image with an origin and size. Note: Be careful with the size
///
/// - parameter origin: The point where the cropping starts
///
/// - parameter size: The size of the new image.
///
/// - returns: The cropped copy of the given image.
func cropAt(origin:NSPoint, size:NSSize) -> NSImage? {
// Resize the current image, while preserving the aspect ratio.
// TODO: Safely resize the image when origin+size is bigger than image
// guard let resized = self.resizeWhileMaintainingAspectRatioToSize(size: size) else {
// return nil
// }
// Get some points to center the cropping area.
let x = origin.x //floor((resized.width - size.width) / 2)
let y = origin.y //floor((resized.height - size.height) / 2)
// Create the cropping frame.
let frame = NSMakeRect(x, y, size.width, size.height)
// Get the best representation of the image for the given cropping frame.
guard let rep = self.bestRepresentation(for: frame, context: nil, hints: nil) else {
return nil
}
// Create a new image with the new size
let img = NSImage(size: size)
img.lockFocus()
defer { img.unlockFocus() }
if rep.draw(in: NSMakeRect(0, 0, size.width, size.height),
from: frame,
operation: NSCompositingOperation.copy,
fraction: 1.0,
respectFlipped: false,
hints: [:]) {
// Return the cropped image.
return img
}
// Return nil in case anything fails.
return nil
}
/// Saves the PNG representation of the current image to the HD.
///
/// - parameter url: The location url to which to write the png file.
func savePNGRepresentationToURL(url: URL) throws {
if let png = self.PNGRepresentation {
try png.write(to: url, options: .atomicWrite)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment