How to resize NSImage?

笑着哭i 提交于 2019-11-26 18:09:22

问题


I have an NSBitmapImageRep which is WxH size.

I create NSImage and call addRepresentation:. Then I need to resize the NSImage.

I tried setSize method but it doesn't work. What should I do?


回答1:


Edit: Since this answer is still the accepted answer, but was written without Retina screens in mind, I will straight up link to a better solution further down the thread: Objective-C Swift 4


Because the method of Paresh is totally correct but deprecated since 10.8 I'll post the working 10.8 code below. All credit to Paresh's answer though.

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



回答2:


Thomas Johannesmeyer's answer using lockFocus doesn't work as you may intend on Retina/HiDPI screens: it resizes to the desired points in the screen's native scale, not pixels.

  • If you're resizing for display on screen, use that method.
  • If you're resizing for a file with exact pixel dimensions, it'll be twice as large when running on Retina (2x DPI) screens.

This method, cobbled together from various answers including some in this related question, resizes to the specified pixel dimensions regardless of current screen 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;
}



回答3:


EDIT You can resize image using below function:

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

Secondly like this:

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



回答4:


Actually it is not necessary to modify any source image parameters like size. The following snippet is already in Swift, but I think you can infer the Objective-C version from it:

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
}



回答5:


@Marco's answer written in 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)



回答6:


Here's a Swift 4 version of Thomas Johannesmeyer's answer:

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

And Swift 4 version of Marco's answer:

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
}



回答7:


Complete Swift 3 answer (modified from @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
    }
}



回答8:


Here is a Swift 3 version keeping ratio of image, just set minimumSize as the minimum height or width you want:

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



回答9:


For just scaling 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;
}

And for scaling a NSImage based on 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;
}

Draw directly over NSImage ([NSImage lockFocus], etc) will create a NSCGImageSnapshotRep not a NSBitmapImageRep.



来源:https://stackoverflow.com/questions/11949250/how-to-resize-nsimage

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!