Skip to content

Instantly share code, notes, and snippets.

@MaciejGad
Created March 24, 2017 11:49
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save MaciejGad/11d8469b218817290ee77012edb46608 to your computer and use it in GitHub Desktop.
Save MaciejGad/11d8469b218817290ee77012edb46608 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
}
/// 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)
}
}
}
@stormychel
Copy link

Awesome, thank you for updating and sharing this!

@marshallino16
Copy link

Using tiff for PNGRepresentation will result to a large Data object.

I'm using this :

var PNGRepresentation: Data? {
  guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil)
  else { return nil }
  let imageRep = NSBitmapImageRep(cgImage: cgImage)
  imageRep.size = self.size
  return imageRep.representation(using: .jpeg, properties: [.compressionFactor : 0.8])
}

CompressionFactor can be an optional param.

@SF-Simon
Copy link

Using tiff for PNGRepresentation will result to a large Data object.

I'm using this :

var PNGRepresentation: Data? {
  guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil)
  else { return nil }
  let imageRep = NSBitmapImageRep(cgImage: cgImage)
  imageRep.size = self.size
  return imageRep.representation(using: .jpeg, properties: [.compressionFactor : 0.8])
}

CompressionFactor can be an optional param.

It doesn't make any difference, and you use a data that was originally JPG as PNG.

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