Slicing NSImage horizontally in cocoa - objective-c

I need to slice an NSImage into two equal halves horizontally. Please help me out. Thanks in advance.

This is a trick I just added to my toolkit. I've added it as a category on NSImage. You pass in the source image and the rect from which to slice a new image. Here's the code:
+ (NSImage *) sliceImage:(NSImage *)image fromRect:(NSRect)srcRect {
NSRect targetRect = NSMakeRect(0, 0, srcRect.size.width, srcRect.size.height);
NSImage *result = [[NSImage alloc] initWithSize:targetRect.size];
[result lockFocus];
[image drawInRect:targetRect fromRect:srcRect operation:NSCompositeCopy fraction:1.0];
[result unlockFocus];
return [result autorelease];
}

Related

How to make a screen cut of a scroll window with the whole part not only the visible part on mac

I am writing a sreen cut APP on mac OS X, i can only get the visible part of window like this:
CGImageRef imageRef = CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
self.windowID,
kCGWindowImageBoundsIgnoreFraming);
_windowImage = [[NSImage alloc] initWithCGImage:imageRef size:self.windowFrame.size];
CGImageRelease(imageRef);
But when the window can scroll, i want to get the whole part not only the visible part, does anyone have some advices?
Thanks!
//capture 获取scrollview的截图
NSRect bounds = NSMakeRect(0, 0, [self.scrollView.documentView frame].size.width,[self.scrollView.documentView frame].size.height);
// //只能获取可见部分 only can grap the visible part of the scrollview
// NSBitmapImageRep* captureImage = [(NSView *)self.scrollView.documentView bitmapImageRepForCachingDisplayInRect:bounds];
// [self.scrollView cacheDisplayInRect:bounds toBitmapImageRep:captureImage];
// NSImage *image = [[NSImage alloc] initWithCGImage:[captureImage CGImage] size:captureImage.size];
//can grab the whole part
[self.scrollView lockFocus];
NSImage *image = [[NSImage alloc] initWithData:[(NSView *)self.scrollView.documentView
dataWithPDFInsideRect:bounds]];
[self.scrollView unlockFocus];

Scale Up NSImage and Save

I would like to scale up an image that's 64px to make it 512px (Even if it's blurry or pixelized)
I'm using this to get the image from my NSImageView and save it:
NSData *customimageData = [[customIcon image] TIFFRepresentation];
NSBitmapImageRep *customimageRep = [NSBitmapImageRep imageRepWithData:customimageData];
customimageData = [customimageRep representationUsingType:NSPNGFileType properties:nil];
NSString* customBundlePath = [[NSBundle mainBundle] pathForResource:#"customIcon" ofType:#"png"];
[customimageData writeToFile:customBundlePath atomically:YES];
I've tried setSize: but it still saves it 64px.
Thanks in advance!
You can't use the NSImage's size property as it bears only an indirect relationship to the pixel dimensions of an image representation. A good way to resize pixel dimensions is to use the drawInRect method of NSImageRep:
- (BOOL)drawInRect:(NSRect)rect
Draws the entire image in the specified rectangle, scaling it as needed to fit.
Here is a image resize method (creates a new NSImage at the pixel size you want).
- (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;
}
It's from a more detailed answer I gave here: NSImage doesn't scale
Another resize method that works is the NSImage method drawInRect:fromRect:operation:fraction:respectFlipped:hints
- (void)drawInRect:(NSRect)dstSpacePortionRect
fromRect:(NSRect)srcSpacePortionRect
operation:(NSCompositingOperation)op
fraction:(CGFloat)requestedAlpha
respectFlipped:(BOOL)respectContextIsFlipped
hints:(NSDictionary *)hints
The main advantage of this method is the hints NSDictionary, in which you have some control over interpolation. This can yield widely differing results when enlarging an image. NSImageHintInterpolation is an enum that can take one of five values…
enum {
NSImageInterpolationDefault = 0,
NSImageInterpolationNone = 1,
NSImageInterpolationLow = 2,
NSImageInterpolationMedium = 4,
NSImageInterpolationHigh = 3
};
typedef NSUInteger NSImageInterpolation;
Using this method there is no need for the intermediate step of extracting an imageRep, NSImage will do the right thing...
- (NSImage*) resizeImage:(NSImage*)sourceImage size:(NSSize)size
{
NSRect targetFrame = NSMakeRect(0, 0, size.width, size.height);
NSImage* targetImage = [[NSImage alloc] initWithSize:size];
[targetImage lockFocus];
[sourceImage drawInRect:targetFrame
fromRect:NSZeroRect //portion of source image to draw
operation:NSCompositeCopy //compositing operation
fraction:1.0 //alpha (transparency) value
respectFlipped:YES //coordinate system
hints:#{NSImageHintInterpolation:
[NSNumber numberWithInt:NSImageInterpolationLow]}];
[targetImage unlockFocus];
return targetImage;
}

Resize and Save NSImage?

I have an NSImageView which I get an image for from an NSOpenPanel. That works great.
Now, how can I take that NSImage, half its size and save it as the same format in the same directory as the original as well?
If you can help at all with anything I'd appreciate it, thanks.
Check the ImageCrop sample project from Matt Gemmell:
http://mattgemmell.com/source/
Nice example how to resize / crop images.
Finally you can use something like this to save the result (dirty sample):
// Write to TIF
[[resultImg TIFFRepresentation] writeToFile:#"/Users/Anne/Desktop/Result.tif" atomically:YES];
// Write to JPG
NSData *imageData = [resultImg TIFFRepresentation];
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor];
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
[imageData writeToFile:#"/Users/Anne/Desktop/Result.jpg" atomically:NO];
Since NSImage objects are immutable you will have to:
Create a Core Graphics context the size of the new image.
Draw the NSImage into the CGContext. It should automatically scale it for you.
Create an NSImage from that context
Write out the new NSImage
Don't forget to release any temporary objects you allocated.
There are definitely other options, but this is the first one that came to mind.
+(NSImage*) resize:(NSImage*)aImage scale:(CGFloat)aScale
{
NSImageView* kView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, aImage.size.width * aScale, aImage.size.height* aScale)];
[kView setImageScaling:NSImageScaleProportionallyUpOrDown];
[kView setImage:aImage];
NSRect kRect = kView.frame;
NSBitmapImageRep* kRep = [kView bitmapImageRepForCachingDisplayInRect:kRect];
[kView cacheDisplayInRect:kRect toBitmapImageRep:kRep];
NSData* kData = [kRep representationUsingType:NSJPEGFileType properties:nil];
return [[NSImage alloc] initWithData:kData];
}
Here is a specific implementation
-(NSImage*)resizeImage:(NSImage*)input by:(CGFloat)factor
{
NSSize size = NSZeroSize;
size.width = input.size.width*factor;
size.height = input.size.height*factor;
NSImage *ret = [[NSImage alloc] initWithSize:size];
[ret lockFocus];
NSAffineTransform *transform = [NSAffineTransform transform];
[transform scaleBy:factor];
[transform concat];
[input drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
[ret unlockFocus];
return [ret autorelease];
}
Keep in mind that this is pixel based, with HiDPI the scaling must be taken into account, it is simple to obtain :
-(CGFloat)pixelScaling
{
NSRect pixelBounds = [self convertRectToBacking:self.bounds];
return pixelBounds.size.width/self.bounds.size.width;
}
Apple has source code for downscaling and saving images found here
http://developer.apple.com/library/mac/#samplecode/Reducer/Introduction/Intro.html
Here is some code that makes a more extensive use of Core Graphics than other answers. It's made according to hints in Mark Thalman's answer to this question.
This code downscales an NSImage based on a target image width. It's somewhat nasty, but still useful as an extra sample for documenting how to draw an NSImage in a CGContext, and how to write contents of CGBitmapContext and CGImage into a file.
You may want to add extra error checking. I didn't need it for my use case.
- (void)generateThumbnailForImage:(NSImage*)image atPath:(NSString*)newFilePath forWidth:(int)width
{
CGSize size = CGSizeMake(width, image.size.height * (float)width / (float)image.size.width);
CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width * 4, rgbColorspace, bitmapInfo);
NSGraphicsContext * graphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
[NSGraphicsContext setCurrentContext:graphicsContext];
[image drawInRect:NSMakeRect(0, 0, size.width, size.height) fromRect:NSMakeRect(0, 0, image.size.width, image.size.height) operation:NSCompositeCopy fraction:1.0];
CGImageRef outImage = CGBitmapContextCreateImage(context);
CFURLRef outURL = (CFURLRef)[NSURL fileURLWithPath:newFilePath];
CGImageDestinationRef outDestination = CGImageDestinationCreateWithURL(outURL, kUTTypeJPEG, 1, NULL);
CGImageDestinationAddImage(outDestination, outImage, NULL);
if(!CGImageDestinationFinalize(outDestination))
{
NSLog(#"Failed to write image to %#", newFilePath);
}
CFRelease(outDestination);
CGImageRelease(outImage);
CGContextRelease(context);
CGColorSpaceRelease(rgbColorspace);
}
To resize image
- (NSImage *)scaleImage:(NSImage *)anImage newSize:(NSSize)newSize
{
NSImage *sourceImage = anImage;
if ([sourceImage isValid])
{
if (anImage.size.width == newSize.width && anImage.size.height == newSize.height && newSize.width <= 0 && newSize.height <= 0) {
return anImage;
}
NSRect oldRect = NSMakeRect(0.0, 0.0, anImage.size.width, anImage.size.height);
NSRect newRect = NSMakeRect(0,0,newSize.width,newSize.height);
NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
[newImage lockFocus];
[sourceImage drawInRect:newRect fromRect:oldRect operation:NSCompositeCopy fraction:1.0];
[newImage unlockFocus];
return newImage;
}
}

Converting NSImage to CIImage without degraded quality

I am trying to convert an NSImage to a CIImage. When I do this, there seems to be a huge loss in image quality. I think it is because of the "TIFFRepresentation". Does anyone have a better method? Thanks a lot.
NSImage *image = [[NSImage alloc] initWithData:[someSource dataRepresentation]];
NSData * tiffData = [image TIFFRepresentation];
CIImage *backgroundCIImage = [[CIImage alloc] initWithData:tiffData];
CIContext *ciContext = [[NSGraphicsContext currentContext] CIContext];
[ciContext drawImage:backgroundCIImage atPoint:CGPointZero fromRect:someRect];
Your problem is indeed converting to TIFF. PDF is a vector format, while TIFF is bitmap, so a TIFF will look blurry at larger sizes.
Your best bet is probably to get a CGImage from the NSImage and create the CIImage from that. Either that or just create the CIImage from the original data.
Try replacing the line
NSData * tiffData = [image TIFFRepresentation];
with
NSData * tiffData = [image TIFFRepresentationUsingCompression: NSTIFFCompressionNone factor: 0.0f];
because the documentation states that TIFFRepresentation uses the TIFF compression option associated with each image representation, which might not be NSTIFFCompressionNone. Thus, you should be explicit about wanting the tiffData uncompressed.
I finally solved the problem. Basically, I render the pdf document two times its normal resolution offscreen and then capture the image displayed by the view. For a more detailed image, just increase the scaling factor. Please see the code below for the proof of concept. I didn't show the CIImage but once you get the bitmap, just use the CIImage method to create the CIImage from the bitmap.
NSImage *pdfImage = [[NSImage alloc] initWithData:[[aPDFView activePage] dataRepresentation]];
NSSize size = [pdfImage size];
NSRect imageRect = NSMakeRect(0, 0, size.width, size.height);
imageRect.size.width *= 2; //Twice the scale factor
imageRect.size.height *= 2; //Twice the scale factor
PDFDocument *pageDocument = [[[PDFDocument alloc] init] autorelease];
[pageDocument insertPage:[aPDFView activePage] atIndex:0];
PDFView *pageView = [[[PDFView alloc] init] autorelease];
[pageView setDocument:pageDocument];
[pageView setAutoScales:YES];
NSWindow *offscreenWindow = [[NSWindow alloc] initWithContentRect:imageRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreRetained
defer:NO];
[offscreenWindow setContentView:pageView];
[offscreenWindow display];
[[offscreenWindow contentView] display]; // Draw to the backing buffer
// Create the NSBitmapImageRep
[[offscreenWindow contentView] lockFocus];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:imageRect];
// Clean up and delete the window, which is no longer needed.
[[offscreenWindow contentView] unlockFocus];
[compositeImage TIFFRepresentation]];
NSData *imageData = [rep representationUsingType: NSJPEGFileType properties: nil];
[imageData writeToFile:#"/Users/David/Desktop/out.jpg" atomically: YES];
[offscreenWindow release];

cocoa hello world screensaver

I have been studying NSView and as such I thought I would give a shot at a screen saver. I have been able to display and image in an NSView but I can't seen to modify this example code to display a simple picture in ScreenSaverView.
http://www.mactech.com/articles/mactech/Vol.20/20.06/ScreenSaversInCocoa/
BTW great tutorial that works with Snow Leopard.
I would think to simply display an image I would need something that looked like this...
What am I doing wrong?
//
// try_screensaverView.m
// try screensaver
//
#import "try_screensaverView.h"
#implementation try_screensaverView
- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
{
self = [super initWithFrame:frame isPreview:isPreview];
if (self) {
[self setAnimationTimeInterval:1]; //refresh once per sec
}
return self;
}
- (void)startAnimation
{
[super startAnimation];
NSString *path = [[NSBundle mainBundle] pathForResource:#"leaf" ofType:#"JPG" inDirectory:#""];
image = [[NSImage alloc] initWithContentsOfFile:path];
}
- (void)stopAnimation
{
[super stopAnimation];
}
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
}
- (void)animateOneFrame
{
//////////////////////////////////////////////////////////
//load image and display This does not scale the image
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
}
- (BOOL)hasConfigureSheet
{
return NO;
}
- (NSWindow*)configureSheet
{
return nil;
}
#end
NSRect bounds = [self bounds];
NSSize newSize;
newSize.width = bounds.size.width;
newSize.height = bounds.size.height;
[image setSize:newSize];
I don't know why you're doing this.
NSRect imageRect;
imageRect.origin = NSZeroPoint;
imageRect.size = [image size];
A.k.a. [self bounds].size.
NSRect drawingRect = imageRect;
[image drawInRect:drawingRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
i.e., [image drawInRect:[self bounds] fromRect:[self bounds] operation:NSCompositeSourceOver fraction:1].
If you're trying to draw the image at its natural size, there's no reason to ever send it a setSize: message. Cut out that entire first part, and the rest should work just fine.
If you're trying to fill the screen (which would be scaling, which would contradict the comment), set the drawingRect to [self bounds], not imageRect. This does exactly as it reads:
image,
draw into (the bounds of the view),
from (the image's entire area).
[image
drawInRect:[self bounds]
fromRect:imageRect
⋮
];
Neither the natural-size-fixed-position draw nor the full-screen draw is an effective screen saver. The latter is irredeemable; you can make the former useful by animating the image around the screen.
I had a similar issue, so I'll post my solution. OP was attempting to load image contents via NSBundle's mainBundle. Instead, you'll have more luck fetching the screensaver's bundle and loading files from there, like so:
NSBundle *saverBundle = [NSBundle bundleForClass:[self class]];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:[saverBundle pathForResource:#"image" ofType:#"png"]];
Since you are doing your drawing in animateOneFrame, try removing your overridden drawRect.