2012-10-02 16 views
8

Estoy desarrollando una aplicación rápida en la que tengo un método que debe cambiar la escala de una imagen @ 2x a una normal. El problema es que no :(NSImage no escala

¿Por qué?

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

he aquí una solución que * se * no funcionará: 'setScalesWhenResized:'.Solía ​​ser así como lo hiciste, pero está obsoleto desde Snow Leopard y no funciona desde Lion. –

+0

¿No puedes dibujarlo en un rect pequeño? ¿O pasar un NSAffineTransform como pista? –

+0

@RamyAlZuhouri: Parece que la aplicación pretende reducir la resolución de la imagen y guardar el resultado, para crear activos a 2x y producir 1x activos de la misma. –

Respuesta

10

Hay que ser muy cuidadoso del atributo tamaño de un NSImage. No necesariamente se refiere a las dimensiones en píxeles de la bitmapRepresentation, se podría referirse al tamaño que se muestra por ejemplo. un NSImage puede tener un número de bitmapRepresentations para su uso en diferentes tamaños de salida.

del mismo modo, cambiando el atributo tamaño de un NSImage no puede modificar las bitmapRepresentations

Entonces, ¿qué lo que debe hacer es calcular el tamaño que desea que tenga su imagen de salida, y luego dibujar una nueva imagen en ese tamaño utilizando un mapa de bits Representación del archivo NSI de origen.

Obtener ese tamaño depende de cómo haya obtenido su imagen de entrada y de lo que sabe al respecto. Por ejemplo, si se tiene la certeza de que su imagen de entrada tiene un solo bitmapImageRep puede utilizar este tipo de cosas (como una categoría en NSImage)

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

Incluso si usted tiene un número de bitmapImageReps, el primero debe ser el más grande, y si ese es el tamaño en el que se creó su imagen de Retina, debe ser el tamaño de Retina que está buscando.

Cuando haya elaborado su tamaño final, se puede hacer la imagen:

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

}

actualización

Aquí está una versión más elaborada de un pixel-tamaño- obtener categoría en NSImage ... no asumamos nada de la imagen, cuántos imageReps tiene, si tiene any bitmapImageReps ... esto devolverá las dimensiones de píxeles más grandes i t puede encontrar. Si no puede encontrar las dimensiones de píxel de bitMapImageRep, usará cualquier otra cosa que pueda obtener, lo que probablemente sea una dimensión de recuadro delimitador (utilizado por eps y pdfs).

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 

Usted sería #import "NSImage+PixelSize.h" en su archivo actual para que sea accesible.

Con esta categoría de imagen y el cambio de tamaño: método, deberá modificar su método de este modo:

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

Eso debe arreglar las cosas para usted (condición: que no he probado en su código)

+0

¿Por qué no utilizar 'bestRepresentationForRect: context: hints:' en los tres lugares? –

+1

Creo que podría estar mendigando la pregunta ... BestRepresentationForRect: sugiere que sepamos el tamaño final que estamos buscando, que en este caso puede que no, estamos buscando reducir a la mitad el tamaño de la retina ... mapa de bits. También como un ejercicio para aprender cómo NSImage se junta, no es una mala idea tener una idea de cómo los diferentes imageReps realmente se relacionan entre sí. – foundry

+0

No es circular ya que el rect se encuentra en el espacio de usuario de la imagen de origen. Si desea la imagen completa, el rect es '(NSRect) {NSZeroPoint, image.size}', independientemente de las resoluciones de píxeles (o la falta de ellas) de cualquiera de las representaciones en la imagen, o del tamaño de escala deseado o resolución. Estoy de acuerdo en que entender la naturaleza de los representantes es bueno; sin embargo, agarrar la primera repetición y asumir que será lo que quieres no es un buen medio para ese fin. –

2

he modificado la secuencia de comandos que utilizo para reducir la escala de mis imágenes para ti :)

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