【问题标题】:NSImage doesn't scaleNSImage 无法缩放
【发布时间】:2012-09-23 17:31:19
【问题描述】:

我正在开发一个快速应用程序,其中我有一种方法可以将@2x 图像重新缩放为常规图像。问题是它没有:(

为什么?

-(BOOL)createNormalImage:(NSString*)inputRetinaImagePath {

    NSImage *inputRetinaImage = [[NSImage alloc] initWithContentsOfFile:inputRetinaImagePath];



    NSSize size = NSZeroSize;
    size.width = inputRetinaImage.size.width*0.5;
    size.height = inputRetinaImage.size.height*0.5;

    [inputRetinaImage setSize:size];


    NSLog(@"%f",inputRetinaImage.size.height);


    NSBitmapImageRep *imgRep = [[inputRetinaImage representations] objectAtIndex: 0];

    NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];

    NSString *outputFilePath = [[inputRetinaImagePath substringToIndex:inputRetinaImagePath.length - 7] stringByAppendingString:@".png"];

    NSLog([@"Normal version file path: " stringByAppendingString:outputFilePath]);
    [data writeToFile:outputFilePath atomically: NO];
    return true;
}

【问题讨论】:

  • 这是一个行不通的解决方案:setScalesWhenResized:。这曾经是您执行此操作的方式,但自 Snow Leopard 以来已被弃用,并且不适用于 Lion。
  • 你不能把它画成一个更小的矩形吗?或者传递一个 NSAffineTransform 作为提示?
  • @RamyAlZuhouri:听起来该应用程序旨在降低图像的分辨率并保存结果,以创建 2 倍的资产并从中生成 1 倍的资产。
  • 但是您是明确地绘制图像还是将其分配给图像?
  • @RamyAlZuhouri:根据问题中的代码,两者都没有。调整大小的图像永远不会出现在方法之外,既不会出现在属性中,也不会出现在 ivar 中,也不会出现在其他对象中。

标签: objective-c macos cocoa nsimage


【解决方案1】:

你必须非常小心 NSImage 的 size 属性。它不一定指的是 bitmapRepresentation 的像素尺寸,它可以指例如显示的大小。一个 NSImage 可能有许多 bitmapRepresentations 用于不同的输出大小。

同样,改变 NSImage 的 size 属性不会改变 bitmapRepresentations

所以你需要做的是计算出你希望输出图像的大小,然后使用来自源 NSImage 的 bitmapRepresentation 以该大小绘制一个新图像。

获得该尺寸取决于您如何获得输入图像以及您对它的了解。例如,如果您确信您的输入图像只有一个 bitmapImageRep,您可以使用这种类型的东西(作为 NSImage 上的一个类别)

  - (NSSize) pixelSize
{
    NSBitmapImageRep* bitmap = [[self representations] objectAtIndex:0];
    return NSMakeSize(bitmap.pixelsWide,bitmap.pixelsHigh);
}

即使您有多个 bitmapImageRep,第一个也应该是最大的,如果这是创建 Retina 图像的尺寸,那么它应该是您所追求的 Retina 尺寸。

确定最终尺寸后,您可以制作图像:

- (NSImage*) resizeImage:(NSImage*)sourceImage size:(NSSize)size
{

    NSRect targetFrame = NSMakeRect(0, 0, size.width, size.height);     
    NSImage* targetImage = nil;
    NSImageRep *sourceImageRep =
    [sourceImage bestRepresentationForRect:targetFrame
                                   context:nil
                                     hints:nil];

    targetImage = [[NSImage alloc] initWithSize:size];

    [targetImage lockFocus];
    [sourceImageRep drawInRect: targetFrame];
    [targetImage unlockFocus];

return targetImage; 

}

更新

这里是 NSImage 上像素大小获取类别的更详细版本...让我们假设图像没有什么,它有多少 imageReps,它是否有 any bitmapImageReps...这将返回它可以找到的最大像素尺寸。如果它找不到 bitMapImageRep 像素尺寸,它将使用它可以得到的任何其他东西,这很可能是边界框尺寸(由 eps 和 pdf 使用)。

NSImage+PixelSize.h

#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>

@interface NSImage (PixelSize)

- (NSInteger) pixelsWide;
- (NSInteger) pixelsHigh;
- (NSSize) pixelSize;

@end

NSImage+PixelSize.m

#import "NSImage+PixelSize.h"

@implementation NSImage (Extensions)

- (NSInteger) pixelsWide
{
    /*
     returns the pixel width of NSImage.
     Selects the largest bitmapRep by preference
     If there is no bitmapRep returns largest size reported by any imageRep.
     */
    NSInteger result = 0;
    NSInteger bitmapResult = 0;

    for (NSImageRep* imageRep in [self representations]) {
        if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
            if (imageRep.pixelsWide > bitmapResult)
                bitmapResult = imageRep.pixelsWide;
        } else {
            if (imageRep.pixelsWide > result)
                result = imageRep.pixelsWide;
        }
    }
    if (bitmapResult) result = bitmapResult;
    return result;

}

- (NSInteger) pixelsHigh
{
    /*
     returns the pixel height of NSImage.
     Selects the largest bitmapRep by preference
     If there is no bitmapRep returns largest size reported by any imageRep.
     */
    NSInteger result = 0;
    NSInteger bitmapResult = 0;

    for (NSImageRep* imageRep in [self representations]) {
        if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
            if (imageRep.pixelsHigh > bitmapResult)
                bitmapResult = imageRep.pixelsHigh;
        } else {
            if (imageRep.pixelsHigh > result)
                result = imageRep.pixelsHigh;
        }
    }
    if (bitmapResult) result = bitmapResult;
    return result;
}

- (NSSize) pixelSize
{
    return NSMakeSize(self.pixelsWide,self.pixelsHigh);
}

@end

您可以在当前文件中#import "NSImage+PixelSize.h" 以使其可访问。

使用此图像类别和 resize: 方法,您可以这样修改您的方法:

//size.width = inputRetinaImage.size.width*0.5;
//size.height = inputRetinaImage.size.height*0.5;
size.width  = inputRetinaImage.pixelsWide*0.5;
size.height = inputRetinaImage.pixelsHigh*0.5;

//[inputRetinaImage setSize:size];
NSImage* outputImage = [self resizeImage:inputRetinaImage size:size];

//NSBitmapImageRep *imgRep = [[inputRetinaImage representations] objectAtIndex: 0];
NSBitmapImageRep *imgRep = [[outputImage representations] objectAtIndex: 0];

应该为你解决问题(附带条件:我没有在你的代码上测试过)

【讨论】:

  • 为什么不在三个地方都使用bestRepresentationForRect:context:hints:
  • 我认为这可能是在乞求问题... BestRepresentationForRect:表明我们知道我们所追求的最终尺寸——在这种情况下我们可能不知道,我们希望将视网膜的尺寸减半。 ..位图。同样作为学习 NSImage 如何连接在一起的练习,掌握各种 imageReps 之间的实际关系是不是一个坏主意?
  • 它不是圆形的,因为矩形在源图像的用户空间中。如果你想要整个图像,那么矩形是(NSRect){ NSZeroPoint, image.size },不管图像中任何表示的像素分辨率(或缺少像素分辨率),或者所需的缩放大小或分辨率。我同意理解代表的性质是好的。然而,抓住第一个代表并假设它会是你想要的并不是达到这个目的的好方法。
  • @Peter - 首先我能感谢你给我奖金,非常感谢,我相信我的回答仍然可以做一些改进。关于 image.size,我同意你的观点,但它确实绕过了这里的问题,即我们正试图获得像素尺寸(无论如何我解释这个问题)......而对 rect/size 的引用正在得到在显示尺寸 - 特别是 image.size 不一定与图像的“原始”像素大小有任何关系。我认为这是值得努力的一点,因为它可能会造成如此多的混乱。
  • @peterhosey,关于你的最后一点 - 抓取第一个 imageReps 是一个伪劣的解决方案 - 我同意,它对源图像做出了太多假设,这就是为什么我用更详细的像素大小类别......我不应该这么仓促!
【解决方案2】:

我修改了我用来为你缩小图像的脚本:)

-(BOOL)createNormalImage:(NSString*)inputRetinaImagePath {

    NSImage *inputRetinaImage = [[NSImage alloc] initWithContentsOfFile:inputRetinaImagePath];

    //determine new size
    NSBitmapImageRep* bitmapImageRep = [[inputRetinaImage representations] objectAtIndex:0];
    NSSize size = NSMakeSize(bitmapImageRep.pixelsWide * 0.5,bitmapImageRep.pixelsHigh * 0.5);

    NSLog(@"size = %@", NSStringFromSize(size));

    //get CGImageRef
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)[inputRetinaImage TIFFRepresentation], NULL);
    CGImageRef oldImageRef =  CGImageSourceCreateImageAtIndex(source, 0, NULL);
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(oldImageRef);
    if (alphaInfo == kCGImageAlphaNone) alphaInfo = kCGImageAlphaNoneSkipLast;

    // Build a bitmap context
    CGContextRef bitmap = CGBitmapContextCreate(NULL, size.width, size.height, 8, 4 * size.width, CGImageGetColorSpace(oldImageRef), alphaInfo);

    // Draw into the context, this scales the image
    CGContextDrawImage(bitmap, CGRectMake(0, 0, size.width, size.height), oldImageRef);

    // Get an image from the context
    CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);

    //this does not work in my test.
    NSString *outputFilePath = [[inputRetinaImagePath substringToIndex:inputRetinaImagePath.length - 7] stringByAppendingString:@".png"];

    //but this does!
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* docsDirectory = [paths objectAtIndex:0];
    NSString *newfileName = [docsDirectory stringByAppendingFormat:@"/%@", [outputFilePath lastPathComponent]];

    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:newfileName];
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
    CGImageDestinationAddImage(destination, newImageRef, nil);

    if (!CGImageDestinationFinalize(destination)) {
        NSLog(@"Failed to write image to %@", newfileName);
    }

    CFRelease(destination);

    return true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-26
    • 2015-03-01
    • 2019-08-16
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多