Skip to content

Instantly share code, notes, and snippets.

@raphaelhanneken
Last active August 24, 2023 01:04
Show Gist options
  • Star 37 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save raphaelhanneken/cb924aa280f4b9dbb480 to your computer and use it in GitHub Desktop.
Save raphaelhanneken/cb924aa280f4b9dbb480 to your computer and use it in GitHub Desktop.
NSImage extensions for easy resizing, cropping and saving png images.
//
// NSImageExtensions.swift
//
import Cocoa
extension NSImage {
/// The height of the image.
var height: CGFloat {
return size.height
}
/// The width of the image.
var width: CGFloat {
return size.width
}
/// A PNG representation of the image.
var PNGRepresentation: Data? {
if let tiff = self.tiffRepresentation, let tiffData = NSBitmapImageRep(data: tiff) {
return tiffData.representation(using: .png, properties: [:])
}
return nil
}
// MARK: Resizing
/// Resize the image to the given size.
///
/// - Parameter size: The size to resize the image to.
/// - Returns: The resized image.
func resize(withSize targetSize: NSSize) -> NSImage? {
let frame = NSRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height)
guard let representation = self.bestRepresentation(for: frame, context: nil, hints: nil) else {
return nil
}
let image = NSImage(size: targetSize, flipped: false, drawingHandler: { (_) -> Bool in
return representation.draw(in: frame)
})
return image
}
/// Copy the image and resize it to the supplied size, while maintaining it's
/// original aspect ratio.
///
/// - Parameter size: The target size of the image.
/// - Returns: The resized image.
func resizeMaintainingAspectRatio(withSize targetSize: NSSize) -> NSImage? {
let newSize: NSSize
let widthRatio = targetSize.width / self.width
let heightRatio = targetSize.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.resize(withSize: newSize)
}
// MARK: Cropping
/// Resize the image, to nearly fit the supplied cropping size
/// and return a cropped copy the image.
///
/// - Parameter size: The size of the new image.
/// - Returns: The cropped image.
func crop(toSize targetSize: NSSize) -> NSImage? {
guard let resizedImage = self.resizeMaintainingAspectRatio(withSize: targetSize) else {
return nil
}
let x = floor((resizedImage.width - targetSize.width) / 2)
let y = floor((resizedImage.height - targetSize.height) / 2)
let frame = NSRect(x: x, y: y, width: targetSize.width, height: targetSize.height)
guard let representation = resizedImage.bestRepresentation(for: frame, context: nil, hints: nil) else {
return nil
}
let image = NSImage(size: targetSize,
flipped: false,
drawingHandler: { (destinationRect: NSRect) -> Bool in
return representation.draw(in: destinationRect)
})
return image
}
// MARK: Saving
/// Save the images PNG representation the the supplied file URL:
///
/// - Parameter url: The file URL to save the png file to.
/// - Throws: An unwrappingPNGRepresentationFailed when the image has no png representation.
func savePngTo(url: URL) throws {
if let png = self.PNGRepresentation {
try png.write(to: url, options: .atomicWrite)
} else {
throw NSImageExtensionError.unwrappingPNGRepresentationFailed
}
}
}
/// Exceptions for the image extension class.
///
/// - creatingPngRepresentationFailed: Is thrown when the creation of the png representation failed.
enum NSImageExtensionError: Error {
case unwrappingPNGRepresentationFailed
}
@zsong
Copy link

zsong commented May 3, 2022

Resizing != cropping

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