objective-c macos jpeg nsimage

objective c - Cómo cambiar el tamaño de NSImage



objective-c macos (9)

Aquí hay una versión Swift 4 de la respuesta de Thomas Johannesmeyer :

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!)! }

Y Swift 4 versión de la respuesta de 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 }

Tengo NSBitmapImageRep que es W x H tamaño. Y creo NSImage y hago addRepresentation: Entonces necesito cambiar el tamaño de NSImage . Intenté el método setSize pero no funciona. ¿Qué tengo que hacer?


Aquí hay una versión de Swift 3 que mantiene la proporción de la imagen, simplemente configure el tamaño mínimo como el alto o ancho mínimo que desee:

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!)! }


Complete la respuesta de Swift 3 (modificada de @Erik Aigner above ):

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


En realidad no es necesario modificar ningún parámetro de imagen de origen como el size . El siguiente fragmento de código ya está en Swift, pero creo que se puede deducir la versión de 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 }


La respuesta de Thomas Johannesmeyer utilizando lockFocus no funciona como puede pretender en las pantallas Retina / HiDPI: cambia el tamaño de los puntos deseados en la escala nativa de la pantalla, no en píxeles .

  • Si está cambiando el tamaño para mostrarlo en pantalla , use ese método .
  • Si está cambiando el tamaño de un archivo con dimensiones de píxeles exactas, será el doble de grande cuando se ejecute en pantallas Retina (2x DPI).

Este método, creado a partir de varias respuestas, incluidas algunas en esta pregunta relacionada , cambia el tamaño a las dimensiones de píxeles especificadas, independientemente de la pantalla actual 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; }


Para simplemente escalar 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; }

Y para escalar un NSImage basado en NSBitmapImageRep

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

Dibujar directamente sobre NSImage ([NSImage lockFocus], etc.) creará un NSCGImageSnapshotRep no un NSBitmapImageRep.


EDITAR Puede cambiar el tamaño de la imagen usando la siguiente función:

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

En segundo lugar así:

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


Edición: dado que esta respuesta sigue siendo la respuesta aceptada, pero se escribió sin tener en cuenta las pantallas Retina, lo enlazaré con una mejor solución más abajo en el hilo: https://.com/a/38442746/1277898

Debido a que el método de Paresh es totalmente correcto pero está en desuso desde 10.8, publicaré el código de trabajo de 10.8 a continuación. Todo el crédito a la respuesta de Paresh sin embargo.

- (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:NSCompositeCopy fraction:1.0]; [smallImage unlockFocus]; return smallImage; } return nil; }


Respuesta de Marco escrita en 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)