How to resize NSImage?

前端 未结 10 1646
萌比男神i
萌比男神i 2020-12-02 21:21

I have an NSBitmapImageRep which is WxH size.

I create NSImage and call addRepresentation:. Then I n

相关标签:
10条回答
  • 2020-12-02 21:26

    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;
    }
    
    0 讨论(0)
  • 2020-12-02 21:27

    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
        }
    }
    
    0 讨论(0)
  • 2020-12-02 21:27

    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.

    0 讨论(0)
  • 2020-12-02 21:29

    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;
    }
    
    0 讨论(0)
  • 2020-12-02 21:32

    @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)
    
    0 讨论(0)
  • 2020-12-02 21:39

    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
    }
    
    0 讨论(0)
提交回复
热议问题