调整大小并保存NSImage?

5

我有一个NSImageView,我从NSOpenPanel中获取了一张图片。这很好。

现在,我该如何将该NSImage的大小减半,并将其保存为相同格式的文件,保存到与原始文件相同的目录中呢?

如果您能提供任何帮助,我将不胜感激,谢谢。

7个回答

8

请查看Matt Gemmell的ImageCrop示例项目:
http://mattgemmell.com/source/

这是一个不错的示例,展示了如何调整/裁剪图像。
最终你可以使用类似以下代码保存结果(样例代码):

// Write to TIF
[[resultImg TIFFRepresentation] writeToFile:@"/Users/Anne/Desktop/Result.tif" atomically:YES];

// Write to JPG
NSData *imageData = [resultImg  TIFFRepresentation];
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor];
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
[imageData writeToFile:@"/Users/Anne/Desktop/Result.jpg" atomically:NO];

Matt Gemmell的代码中存在一个错误,即在裁剪方式上:如果您有一张640x480的图像,并告诉它的实现将其裁剪为240x240,则它通过将源图像裁剪到480x480然后缩小到240x240来到达240x240目标图像,而不是真正地从源图像中裁剪出240x240块。如果有人需要修复,请给我发消息。我已经通过电子邮件向他发送了修复程序,但没有得到回应。 - Andreas Zollmann
@AndreasZollmann 我不认为那是一个 bug。如果你需要调整到不同的宽高比,你就需要做出一个选择。我会认为在大多数情况下,你会想要保持宽高比。 - henrikstroem

4

由于NSImage对象是不可变的,您需要执行以下操作:

  1. 创建一个大小为新图像的Core Graphics上下文。
  2. 将NSImage绘制到CGContext中。它应该会自动为您缩放。
  3. 从该上下文创建一个NSImage
  4. 写出新的NSImage
  5. 不要忘记释放您分配的任何临时对象。

肯定还有其他选项,但这是我想到的第一种方法。


请问您能详细说明一下CGContext吗?我可能有点理解错误。将NSImageView中的图像放入NSData中,然后转换为NSBitmapImageRep,再将其放入NSGraphicsContext中,这样做对吗?在哪个阶段应该更改其大小?这是正确的还是错误的? - Josh Kahane
核心图形(Quartz)是一个大主题。我不知道@Anne答案中的技巧。那少得多代码。我会先尝试那个。否则就有Apple的Quartz文档。 - Mark

3
+(NSImage*) resize:(NSImage*)aImage scale:(CGFloat)aScale
{
    NSImageView* kView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, aImage.size.width * aScale, aImage.size.height* aScale)];
    [kView setImageScaling:NSImageScaleProportionallyUpOrDown];
    [kView setImage:aImage];

    NSRect kRect = kView.frame;
    NSBitmapImageRep* kRep = [kView bitmapImageRepForCachingDisplayInRect:kRect];
    [kView cacheDisplayInRect:kRect toBitmapImageRep:kRep];

    NSData* kData = [kRep representationUsingType:NSJPEGFileType properties:nil];
    return [[NSImage alloc] initWithData:kData];
}

当在后台线程上调用时,这可能会给您带来麻烦,并且速度非常慢:您创建了一个图像视图,该视图又会渲染成新图像(这是缩放发生的地方)。这已经很糟糕了,但然后您将该图像序列化为JPEG(失去细节),然后再次读取它。那部分完全没有必要,只会浪费资源。 - DarkDust

2
这里有一个具体的实现。
-(NSImage*)resizeImage:(NSImage*)input by:(CGFloat)factor
{    
    NSSize size = NSZeroSize;      
    size.width = input.size.width*factor;
    size.height = input.size.height*factor; 

    NSImage *ret = [[NSImage alloc] initWithSize:size];
    [ret lockFocus];
    NSAffineTransform *transform = [NSAffineTransform transform];
    [transform scaleBy:factor];  
    [transform concat]; 
    [input drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];    
    [ret unlockFocus];        

    return [ret autorelease];
}

请记住这是以像素为基础的,对于HiDPI屏幕需要考虑缩放比例,获取方式很简单:

-(CGFloat)pixelScaling
{
    NSRect pixelBounds = [self convertRectToBacking:self.bounds];
    return pixelBounds.size.width/self.bounds.size.width;
}

1

这个链接现在说:“重要提示:此示例代码可能不代表当前开发的最佳实践”;-) - hnh

0

调整图像大小

- (NSImage *)scaleImage:(NSImage *)anImage newSize:(NSSize)newSize
{
    NSImage *sourceImage = anImage;
    if ([sourceImage isValid])
    {
        if (anImage.size.width == newSize.width && anImage.size.height == newSize.height && newSize.width <= 0 && newSize.height <= 0) {
            return anImage;
        }

        NSRect oldRect = NSMakeRect(0.0, 0.0, anImage.size.width, anImage.size.height);
        NSRect newRect = NSMakeRect(0,0,newSize.width,newSize.height);
        NSImage *newImage = [[NSImage alloc] initWithSize:newSize];

        [newImage lockFocus];
        [sourceImage drawInRect:newRect fromRect:oldRect operation:NSCompositeCopy fraction:1.0];
        [newImage unlockFocus];

        return newImage;
    }
}

0

这里有一些代码,它比其他答案更广泛地使用了核心图形技术。它是根据Mark Thalman's answer中对这个问题的提示制作的。

该代码基于目标图像宽度缩小一个NSImage。它有点讨厌,但作为文档如何在CGContext中绘制NSImage,以及如何将CGBitmapContextCGImage的内容写入文件的额外示例仍然很有用。

您可能想添加额外的错误检查。对于我的用例,我不需要它。

- (void)generateThumbnailForImage:(NSImage*)image atPath:(NSString*)newFilePath forWidth:(int)width
{
    CGSize size = CGSizeMake(width, image.size.height * (float)width / (float)image.size.width);
    CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();

    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
    CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width * 4, rgbColorspace, bitmapInfo);
    NSGraphicsContext * graphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
    [NSGraphicsContext setCurrentContext:graphicsContext];

    [image drawInRect:NSMakeRect(0, 0, size.width, size.height) fromRect:NSMakeRect(0, 0, image.size.width, image.size.height) operation:NSCompositeCopy fraction:1.0];

    CGImageRef outImage = CGBitmapContextCreateImage(context);
    CFURLRef outURL = (CFURLRef)[NSURL fileURLWithPath:newFilePath];
    CGImageDestinationRef outDestination = CGImageDestinationCreateWithURL(outURL, kUTTypeJPEG, 1, NULL);
    CGImageDestinationAddImage(outDestination, outImage, NULL);
    if(!CGImageDestinationFinalize(outDestination))
    {
        NSLog(@"Failed to write image to %@", newFilePath);
    }
    CFRelease(outDestination);
    CGImageRelease(outImage);
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorspace);
}

1
graphicsContextWithGraphicsPort:flipped:自(OS X v10.10)起已被弃用。 - Victor Laskin
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Ivan Vučica
只需使用graphicsContextWithCGContext而不是graphicsContextWithGraphicsPort。 - geowar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接