NSImage does not scale

I am developing a quick application in which I have a method that needs to rescale the @ 2x image for a regular one. The problem is that it does not :(

Why?

-(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; } 
+8
objective-c cocoa macos nsimage
source share
2 answers

You must be very careful about the size attribute of NSImage. This does not necessarily apply to the pixel size of the bitmapRepresentation, it may refer to the displayed size, for example. NSImage may have several bitmapRepresentations for use with different release sizes.

Similarly, changing the size attribute of NSImage does nothing to change bitmapRepresentations

So what you need to do is determine the size at which you want to get your output image, and then draw a new image of that size using bitmapRepresentation from the NSImage source.

Getting this size depends on how you got your input image and what you know about it. For example, if you are sure that your input image has only one bitmapImageRep, you can use this type of thing (as a category on NSImage)

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

Even if you have several bitmapImageReps, the first should be the largest, and if this is the size at which your Retina image was created, it should be the Retina size that you are after.

When you have determined your final size, you can make an image:

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

}

Update

Here is a more elaborate version of the category for obtaining pixel size in NSImage ... do not allow anything about the image, how many images it has, regardless of whether it has a bitmap ImageReps ... it will return the largest pixel sizes it can find. If he cannot find the pixel size of bitMapImageRep, he will use everything he can get, which will most likely limit the size of the window (using eps and 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 

You could #import "NSImage+PixelSize.h" in your current file to make it available.

With this category of images and the resize: method, you must change your method this way:

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

This should fix things for you (disclaimer: I have not tested it on your code)

+10
source share

I changed the use of script i to reduce your images for you :)

 -(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; } 
+2
source share

All Articles