I'm using the following code to resize an NSImage
by preserving the aspect ratio. But
this extension method keeps reducing the image size when I try to increment the same
func resizeMaintainingAspectRatio(width: CGFloat, height: CGFloat) -> NSImage? {
let ratioX = (width/(NSScreen.main?.backingScaleFactor)!) / size.width
let ratioY = (height/(NSScreen.main?.backingScaleFactor)!) / size.height
var ratio = ratioX < ratioY ? ratioX : ratioY
let newHeight = size.height * ratio
let newWidth = size.width * ratio
let canvasSize = CGSize(width: newWidth, height: newHeight)
let img = NSImage(size: canvasSize)
img.lockFocus()
let context = NSGraphicsContext.current
context?.imageInterpolation = .high
draw(in: NSRect(origin: .zero, size: NSSize(width: newWidth,height: newHeight)), from: NSRect(origin: .zero, size: size) , operation: .copy, fraction: 1)
img.unlockFocus()
return img
}
Usage:
let w = CGFloat(logoimage!.size.width) 10
let h=CGFloat(logoimage!.size.height) 10
logoimage=originallogoimage?.resizeMaintainingAspectRatio(width: w, height: h)
CodePudding user response:
This is because NSImage
's member size
does not give you the actual physical dimension of the image, but rather its backing store resolution on the screen which is screen resolution dependent (downscaled by the -backingScaleFactor
).
In your code, while your logoimage
's underlying image representation still holds the actual physical dimension, you downscaled its size by the -backingScaleFactor
(which scales down the actual dimension of the image) to get the ratio, causing the resultant image being even smaller as the new image uses a downscaled size derived by an already-downscaled size. This is redundant, for the conversion between the image's physical pixel resolution and the backing store resolution happens automatically.
By simply removing backingScaleFactor
from the code, here's the code for resizedMaintainingAspectRatio(width:height:)
:
extension NSImage {
func resizedMaintainingAspectRatio(width: CGFloat, height: CGFloat) -> NSImage {
let ratioX = width / size.width
let ratioY = height / size.height
let ratio = ratioX < ratioY ? ratioX : ratioY
let newHeight = size.height * ratio
let newWidth = size.width * ratio
let newSize = NSSize(width: newWidth, height: newHeight)
let image = NSImage(size: newSize)
image.lockFocus()
let context = NSGraphicsContext.current
context!.imageInterpolation = .high
draw(in: NSRect(origin: .zero, size: newSize), from: NSZeroRect, operation: .copy, fraction: 1)
image.unlockFocus()
return image
}
}
Since lockFocus()
is deprecated after macOS 13.0, you can use init(size:flipped:drawingHandler:)
instead:
extension NSImage {
func resizedMaintainingAspectRatio(width: CGFloat, height: CGFloat) -> NSImage {
let ratioX = width / size.width
let ratioY = height / size.height
let ratio = ratioX < ratioY ? ratioX : ratioY
let newHeight = size.height * ratio
let newWidth = size.width * ratio
let newSize = NSSize(width: newWidth, height: newHeight)
let image = NSImage(size: newSize, flipped: false) { destRect in
NSGraphicsContext.current!.imageInterpolation = .high
self.draw(in: destRect, from: NSZeroRect, operation: .copy, fraction: 1)
return true
}
return image
}
}
Now the image should be resized to the correct size by doing the following:
logoimage = logoimage!.resizedMaintainingAspectRatio(width: logoimage!.size.width 10,
height: logoimage!.size.height 10)