2012-10-02 21 views
8

Tôi đang phát triển một ứng dụng nhanh trong đó tôi có phương thức nên rescale một hình ảnh @ 2x thành hình ảnh thông thường. Vấn đề là nó không :(NSImage không quy mô

Tại sao?

-(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; 
} 
+0

Dưới đây là một giải pháp mà * sẽ không * làm việc: 'setScalesWhenResized:'.Đó từng là cách bạn đã làm điều này, nhưng nó không được chấp nhận từ Snow Leopard và không hoạt động như Lion. –

+0

Bạn không thể chỉ vẽ nó trong một rect nhỏ hơn? Hoặc chuyển NSAffineTransform làm gợi ý? –

+0

@RamyAlZuhouri: Có vẻ như ứng dụng này có nghĩa là giảm độ phân giải của hình ảnh và lưu kết quả, để tạo nội dung ở 2x và sản xuất 1x nội dung từ cùng một. –

Trả lời

10

Bạn phải rất cảnh giác với các thuộc tính kích thước của một NSImage. Nó không nhất thiết phải đề cập đến kích thước pixel của bitmapRepresentation, nó có thể tham khảo các kích thước hiển thị ví dụ. một NSImage có thể có một số bitmapRepresentations để sử dụng với kích thước đầu ra khác nhau.

Tương tự như vậy, thay đổi thuộc tính kích thước của một NSImage không có gì để làm thay đổi bitmapRepresentations

Vậy điều gì bạn cần làm là làm việc ra kích thước bạn muốn hình ảnh đầu ra của bạn được, và sau đó vẽ một hình ảnh mới ở kích thước đó bằng cách sử dụng một bitmapRepresentation từ nguồn NSImage.

Việc nhận kích thước đó phụ thuộc vào cách bạn đã có được hình ảnh đầu vào và những gì bạn biết về nó. Ví dụ, nếu bạn tự tin rằng hình ảnh đầu vào của bạn chỉ có một bitmapImageRep bạn có thể sử dụng loại điều (như một phạm trù trên NSImage)

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

Thậm chí nếu bạn có một số bitmapImageReps, là đầu tiên nên lớn nhất, và nếu đó là kích thước mà hình ảnh Retina của bạn đã được tạo ra tại, nó phải là kích thước Retina bạn đang sau.

Khi bạn đã tìm ra kích thước cuối cùng của bạn, bạn có thể làm cho hình ảnh:

- (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; 

}

cập nhật

Đây là một phiên bản phức tạp hơn của một pixel-size- nhận được danh mục trên NSImage ... hãy giả sử không có gì về hình ảnh, có bao nhiêu imageRep mà nó có, cho dù có bất kỳ bitmapImageReps ... điều này sẽ trả về kích thước pixel lớn nhất i t có thể tìm thấy. Nếu nó không thể tìm thấy kích thước pixel bitMapImageRep, nó sẽ sử dụng bất kỳ thứ gì khác mà nó có thể nhận được, mà rất có thể sẽ là giới hạn kích thước hộp (được sử dụng bởi eps và 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 

Bạn sẽ #import "NSImage+PixelSize.h" trong tập tin hiện tại của bạn để làm cho nó dễ tiếp cận.

Với điều này loại hình ảnh và thay đổi kích cỡ: phương pháp, bạn sẽ thay đổi phương pháp của bạn như sau:

//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]; 

Đó nên sửa chữa điều cho bạn (điều kiện: Tôi đã không thử nghiệm nó trên mã của bạn)

+0

Tại sao không sử dụng 'bestRepresentationForRect: ngữ cảnh: gợi ý:' trong cả ba địa điểm? –

+1

Tôi nghĩ rằng nó có thể được đặt câu hỏi ... BestRepresentationForRect: cho thấy chúng ta biết kích thước cuối cùng chúng ta đang sau - trong trường hợp này chúng ta có thể không, chúng ta đang tìm cách giảm một nửa kích thước của võng mạc ... bitmap. Cũng như một bài tập về cách thức mà NSImage gắn kết với nhau, nó không phải là một ý tưởng tồi để nắm bắt được cách các imageRep khác nhau thực sự liên quan với nhau như thế nào? – foundry

+0

Nó không tròn vì rect nằm trong không gian người dùng của ảnh nguồn. Nếu bạn muốn toàn bộ hình ảnh, thì rect là '(NSRect) {NSZeroPoint, image.size}', bất kể độ phân giải pixel (hoặc thiếu độ phân giải) của bất kỳ biểu diễn nào trong hình ảnh hoặc kích thước được chia tỷ lệ mong muốn hoặc độ phân giải. Tôi đồng ý rằng sự hiểu biết bản chất của đại diện là tốt; Tuy nhiên, lấy đại diện đầu tiên và giả định rằng nó sẽ là những gì bạn muốn không phải là một phương tiện tốt để kết thúc đó. –

2

tôi sửa đổi kịch bản tôi sử dụng để thu hẹp hình ảnh của tôi cho bạn :)

-(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; 
}