我有一个大小为 W
xH
的 NSBitmapImageRep
。
我创建了一个 NSImage
并调用了 addRepresentation:
。然后我需要调整 NSImage
的大小。
我尝试使用 setSize
方法,但它不起作用。我该怎么办?
我有一个大小为 W
xH
的 NSBitmapImageRep
。
我创建了一个 NSImage
并调用了 addRepresentation:
。然后我需要调整 NSImage
的大小。
我尝试使用 setSize
方法,但它不起作用。我该怎么办?
编辑: 由于这个答案仍然是被接受的答案,但是没有考虑Retina屏幕,我将直接链接到更好的解决方案,该方案在线程的下面:Objective-C Swift 4
因为Paresh的方法完全正确但已经自10.8版本以后被弃用,所以我将发布适用于10.8版本的工作代码。所有功劳归功于Paresh的回答。
- (NSImage *)imageResize:(NSImage*)anImage newSize:(NSSize)newSize {
NSImage *sourceImage = anImage;
[sourceImage setScalesWhenResized:YES];
// Report an error if the source isn't a valid image
if (![sourceImage isValid]){
NSLog(@"Invalid Image");
} else {
NSImage *smallImage = [[NSImage alloc] initWithSize: newSize];
[smallImage lockFocus];
[sourceImage setSize: newSize];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[sourceImage drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, newSize.width, newSize.height) operation:NSCompositingOperationCopy fraction:1.0];
[smallImage unlockFocus];
return smallImage;
}
return nil;
}
sourceImage
大小的副作用吗?https://developer.apple.com/documentation/appkit/nsimage/1519987-size?language=objc 这可能不是调用者想要的。 - wcochranThomas Johannesmeyer使用lockFocus
的回答在Retina/HiDPI屏幕上可能无法按您的意图工作:它会以屏幕本地比例的所需点大小调整大小,而不是像素。
此方法从各种答案中拼凑而成,包括一些在这个相关问题中,无论当前屏幕DPI如何,都会调整大小到指定的像素尺寸:
+ (NSImage *)resizedImage:(NSImage *)sourceImage toPixelDimensions:(NSSize)newSize
{
if (! sourceImage.isValid) return nil;
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:newSize.width
pixelsHigh:newSize.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0
bitsPerPixel:0];
rep.size = newSize;
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:rep]];
[sourceImage drawInRect:NSMakeRect(0, 0, newSize.width, newSize.height) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
[newImage addRepresentation:rep];
return newImage;
}
lockFocus()
方法的建议已经过时;该方法现在已被软弃用。Marco的方法会在屏幕上生成非Retina图像,但您可以通过将NSBitmapImageRep
初始化器的pixelsWide
和pixelsHigh
参数的值添加* 2
来解决这个问题。这使得这种方法在Retina屏幕上能够很好地工作。(我不知道它对于打印图像有什么作用...谁还会在2022年打印东西呢?) - Bryan@Marco的答案使用Swift 4编写:
extension NSImage {
func resized(to newSize: NSSize) -> NSImage? {
if let bitmapRep = NSBitmapImageRep(
bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height),
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0
) {
bitmapRep.size = newSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmapRep)
draw(in: NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height), from: .zero, operation: .copy, fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()
let resizedImage = NSImage(size: newSize)
resizedImage.addRepresentation(bitmapRep)
return resizedImage
}
return nil
}
}
let targetSize = NSSize(width: 256.0, height: 256.0)
let newImageResized = myimage.resized(to: targetSize)
NSGraphicsContext.current.imageInterpolation = .none
。 - hotdogsoup.nlNSBitmapRepresentation
初始化器的pixelsWide
和pixelsHigh
值后添加* 2
即可。缩放后的图像将与其他答案中讨论的旧的lockFocus()
缩放方法所产生的图像相匹配。 - Bryan编辑:您可以使用以下函数调整图像大小:
- (NSImage *)imageResize:(NSImage*)anImage
newSize:(NSSize)newSize
{
NSImage *sourceImage = anImage;
[sourceImage setScalesWhenResized:YES];
// Report an error if the source isn't a valid image
if (![sourceImage isValid])
{
NSLog(@"Invalid Image");
} else
{
NSImage *smallImage = [[[NSImage alloc] initWithSize: newSize] autorelease];
[smallImage lockFocus];
[sourceImage setSize: newSize];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[sourceImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
[smallImage unlockFocus];
return smallImage;
}
return nil;
}
NSData *imageData = [yourImg TIFFRepresentation]; // converting img into data
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData]; // converting into BitmapImageRep
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor]; // any number betwwen 0 to 1
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps]; // use NSPNGFileType if needed
NSImage *resizedImage = [[NSImage alloc] initWithData:imageData]; // image created from data
实际上,修改任何源图像参数(例如大小
)都不是必需的。以下代码片段已经使用Swift编写,但我认为您可以从中推断出Objective-C版本:
func resized(to: CGSize) -> NSImage {
let img = NSImage(size: to)
img.lockFocus()
defer {
img.unlockFocus()
}
if let ctx = NSGraphicsContext.current {
ctx.imageInterpolation = .high
draw(in: NSRect(origin: .zero, size: to),
from: NSRect(origin: .zero, size: size),
operation: .copy,
fraction: 1)
}
return img
}
to:
设置为16x16时,图像变成32x32之外,一切工作正常。可能是视网膜计算的问题?如何纠正这个问题? - hotdogsoup.nl这是Thomas Johannesmeyer的答案的Swift 4版本:
func resize(image: NSImage, w: Int, h: Int) -> NSImage {
var destSize = NSMakeSize(CGFloat(w), CGFloat(h))
var newImage = NSImage(size: destSize)
newImage.lockFocus()
image.draw(in: NSMakeRect(0, 0, destSize.width, destSize.height), from: NSMakeRect(0, 0, image.size.width, image.size.height), operation: NSCompositingOperation.sourceOver, fraction: CGFloat(1))
newImage.unlockFocus()
newImage.size = destSize
return NSImage(data: newImage.tiffRepresentation!)!
}
以下是 Swift 4 版本的 Marco 的答案:
func resize(image: NSImage, w: Int, h: Int) -> NSImage {
let destSize = NSMakeSize(CGFloat(w), CGFloat(h))
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(destSize.width), pixelsHigh: Int(destSize.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0)
rep?.size = destSize
NSGraphicsContext.saveGraphicsState()
if let aRep = rep {
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: aRep)
}
image.draw(in: NSMakeRect(0, 0, destSize.width, destSize.height), from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()
let newImage = NSImage(size: destSize)
if let aRep = rep {
newImage.addRepresentation(aRep)
}
return newImage
}
下面是完整的 Swift 3 解答(修改自 @Erik Aigner 的 上述回答):
extension NSImage {
func resizeImage(width: CGFloat, _ height: CGFloat) -> NSImage {
let img = NSImage(size: CGSize(width:width, height:height))
img.lockFocus()
let ctx = NSGraphicsContext.current()
ctx?.imageInterpolation = .high
self.draw(in: NSMakeRect(0, 0, width, height), from: NSMakeRect(0, 0, size.width, size.height), operation: .copy, fraction: 1)
img.unlockFocus()
return img
}
}
这里有一个Swift 3版本,保持图像比例不变,只需将minimumSize设置为您想要的最小高度或宽度:
func imageResized(image: NSImage) -> NSImage {
let ratio = image.size.height / image.size.width
let width: CGFloat
let height: CGFloat
// We keep ratio of image
if ratio > 1 {
width = minimumSize
height = minimumSize * ratio
} else {
width = minimumSize
height = minimumSize * (1 / ratio)
}
let destSize = NSSize(width: width, height: height)
let newImage = NSImage(size: destSize)
newImage.lockFocus()
image.draw(in: NSRect(x: 0, y: 0, width: destSize.width, height: destSize.height), from: NSRect(x: 0, y: 0, width: image.size.width, height: image.size.height), operation: .sourceOver, fraction: 1.0)
newImage.unlockFocus()
newImage.size = destSize
return NSImage(data: newImage.tiffRepresentation!)!
}
2020 | SWIFT 4和5:
用法:
let resizedImg = someImage.resizedCopy(w: 500.0, h:500.0)
let scaledImg = someImage.scaledCopy( sizeOfLargerSide: 1000.0)
//and bonus:
scaledImg.writePNG(toURL: someUrl )
代码:
extension NSImage {
func scaledCopy( sizeOfLargerSide: CGFloat) -> NSImage {
var newW: CGFloat
var newH: CGFloat
var scaleFactor: CGFloat
if ( self.size.width > self.size.height) {
scaleFactor = self.size.width / sizeOfLargerSide
newW = sizeOfLargerSide
newH = self.size.height / scaleFactor
}
else{
scaleFactor = self.size.height / sizeOfLargerSide
newH = sizeOfLargerSide
newW = self.size.width / scaleFactor
}
return resizedCopy(w: newW, h: newH)
}
func resizedCopy( w: CGFloat, h: CGFloat) -> NSImage {
let destSize = NSMakeSize(w, h)
let newImage = NSImage(size: destSize)
newImage.lockFocus()
self.draw(in: NSRect(origin: .zero, size: destSize),
from: NSRect(origin: .zero, size: self.size),
operation: .copy,
fraction: CGFloat(1)
)
newImage.unlockFocus()
guard let data = newImage.tiffRepresentation,
let result = NSImage(data: data)
else { return NSImage() }
return result
}
public func writePNG(toURL url: URL) {
guard let data = tiffRepresentation,
let rep = NSBitmapImageRep(data: data),
let imgData = rep.representation(using: .png, properties: [.compressionFactor : NSNumber(floatLiteral: 1.0)]) else {
Swift.print("\(self) Error Function '\(#function)' Line: \(#line) No tiff rep found for image writing to \(url)")
return
}
do {
try imgData.write(to: url)
}catch let error {
Swift.print("\(self) Error Function '\(#function)' Line: \(#line) \(error.localizedDescription)")
}
}
}
仅对NSBitmapImageRep进行缩放
static NSBitmapImageRep *i_scale_bitmap(const NSBitmapImageRep *bitmap, const uint32_t width, const uint32_t height)
{
NSBitmapImageRep *new_bitmap = NULL;
CGImageRef dest_image = NULL;
CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRef context = CGBitmapContextCreate(NULL, (size_t)width, (size_t)height, PARAM(bitsPerComponent, 8), PARAM(bytesPerRow, (size_t)(width * 4)), space, kCGImageAlphaPremultipliedLast);
CGImageRef src_image = [bitmap CGImage];
CGRect rect = CGRectMake((CGFloat)0.f, (CGFloat)0.f, (CGFloat)width, (CGFloat)height);
CGContextDrawImage(context, rect, src_image);
dest_image = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(space);
new_bitmap = [[NSBitmapImageRep alloc] initWithCGImage:dest_image];
CGImageRelease(dest_image);
return new_bitmap;
}
针对基于NSBitmapImageRep缩放NSImage的方法
ImageImp *imgimp_create_scaled(const ImageImp *image, const uint32_t new_width, const uint32_t new_height)
{
NSImage *src_image = (NSImage*)image;
NSBitmapImageRep *src_bitmap, *dest_bitmap;
NSImage *scaled_image = nil;
cassert_no_null(src_image);
cassert([[src_image representations] count] == 1);
cassert([[[src_image representations] objectAtIndex:0] isKindOfClass:[NSBitmapImageRep class]]);
src_bitmap = (NSBitmapImageRep*)[[(NSImage*)image representations] objectAtIndex:0];
cassert_no_null(src_bitmap);
dest_bitmap = i_scale_bitmap(src_bitmap, new_width, new_height);
scaled_image = [[NSImage alloc] initWithSize:NSMakeSize((CGFloat)new_width, (CGFloat)new_height)];
[scaled_image addRepresentation:dest_bitmap];
cassert([scaled_image retainCount] == 1);
[dest_bitmap release];
return (ImageImp*)scaled_image;
}
直接在NSImage上绘制(使用[NSImage lockFocus]等)将创建一个NSCGImageSnapshotRep而不是NSBitmapImageRep。