diff options
Diffstat (limited to 'AppKit/GTMNSImage+Scaling.m')
-rw-r--r-- | AppKit/GTMNSImage+Scaling.m | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/AppKit/GTMNSImage+Scaling.m b/AppKit/GTMNSImage+Scaling.m new file mode 100644 index 0000000..57687fd --- /dev/null +++ b/AppKit/GTMNSImage+Scaling.m @@ -0,0 +1,168 @@ +// +// GTMNSImage+Scaling.m +// +// Scales NSImages to a variety of sizes for drawing +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSImage+Scaling.h" +#import "GTMGeometryUtils.h" + +@implementation NSImage (GTMNSImageScaling) + +- (NSImageRep *)gtm_bestRepresentationForSize:(NSSize)size { + NSImageRep *bestRep = [self gtm_representationOfSize:size]; + if (bestRep) { + return bestRep; + } + NSArray *reps = [self representations]; + + CGFloat repDistance = CGFLOAT_MAX; + + NSImageRep *thisRep; + NSEnumerator *repEnum = [reps objectEnumerator]; + while ((thisRep = [repEnum nextObject])) { + CGFloat thisDistance; + thisDistance = MIN(size.width - [thisRep size].width, + size.height-[thisRep size].height); + + if (repDistance < 0 && thisDistance > 0) continue; + if (ABS(thisDistance) < ABS(repDistance) + || (thisDistance < 0 && repDistance > 0)) { + repDistance = thisDistance; + bestRep = thisRep; + } + } + + if (!bestRep) { + bestRep = [self bestRepresentationForDevice:nil]; + } + + return bestRep; +} + +- (NSImageRep *)gtm_representationOfSize:(NSSize)size { + NSArray *reps = [self representations]; + + NSImageRep *thisRep; + NSEnumerator *repEnum = [reps objectEnumerator]; + while ((thisRep = [repEnum nextObject])) { + if (NSEqualSizes([thisRep size], size)) { + return thisRep; + } + } + return nil; +} + +- (BOOL)gtm_createIconRepresentations { + [self setFlipped:NO]; + [self gtm_createRepresentationOfSize:NSMakeSize(16, 16)]; + [self gtm_createRepresentationOfSize:NSMakeSize(32, 32)]; + [self setScalesWhenResized:NO]; + return YES; +} + +- (BOOL)gtm_createRepresentationOfSize:(NSSize)size { + if ([self gtm_representationOfSize:size]) { + return NO; + } + + NSBitmapImageRep *bestRep = + (NSBitmapImageRep *)[self gtm_bestRepresentationForSize:size]; + + NSRect drawRect = GTMNSScaleRectToRect(GTMNSRectOfSize([bestRep size]), + GTMNSRectOfSize(size), + GTMScaleProportionally, + GTMRectAlignCenter); + + if ([bestRep respondsToSelector:@selector(CGImage)]) { + CGImageRef imageRef = (CGImageRef)[bestRep performSelector:@selector(CGImage)]; + + CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); + if (!cspace) return NO; + + CGContextRef smallContext = + CGBitmapContextCreate(NULL, + size.width, + size.height, + 8, // bits per component + size.width * 4, // bytes per pixel + cspace, + kCGBitmapByteOrder32Host + | kCGImageAlphaPremultipliedLast); + CFRelease(cspace); + + if (!smallContext) return NO; + + + CGContextDrawImage(smallContext, GTMNSRectToCGRect(drawRect), imageRef); + + CGImageRef smallImage = CGBitmapContextCreateImage(smallContext); + + if (smallImage) { + NSBitmapImageRep *cgRep = + [[[NSBitmapImageRep alloc] initWithCGImage:smallImage] autorelease]; + [self addRepresentation:cgRep]; + CGImageRelease(smallImage); + } else { + CGContextRelease(smallContext); + return NO; + } + CGContextRelease(smallContext); + return YES; + } else { + // This functionality is here to allow it to work under Tiger + // It can probably only be called safely from the main thread + NSImage* scaledImage = [[NSImage alloc] initWithSize:size]; + [scaledImage lockFocus]; + NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; + [graphicsContext setImageInterpolation:NSImageInterpolationHigh]; + [graphicsContext setShouldAntialias:YES]; + [bestRep drawInRect:drawRect]; + NSBitmapImageRep* iconRep = + [[[NSBitmapImageRep alloc] initWithFocusedViewRect: + NSMakeRect(0, 0, size.width, size.height)] autorelease]; + [scaledImage unlockFocus]; + [scaledImage release]; + [self addRepresentation:iconRep]; + return YES; + } + return NO; +} + +- (void)gtm_removeRepresentationsLargerThanSize:(NSSize)size { + NSEnumerator *e = [[self representations] reverseObjectEnumerator]; + NSImageRep *thisRep; + while((thisRep = [e nextObject]) ) { + if ([thisRep size].width > size.width + && [thisRep size].height > size.height) + [self removeRepresentation:thisRep]; + } +} + +- (NSImage *)gtm_duplicateOfSize:(NSSize)size { + NSImage *duplicate = [[self copy] autorelease]; + [duplicate gtm_shrinkToSize:size]; + [duplicate setFlipped:NO]; + return duplicate; +} + +- (void)gtm_shrinkToSize:(NSSize)size { + [self gtm_createRepresentationOfSize:size]; + [self setSize:size]; + [self gtm_removeRepresentationsLargerThanSize:size]; +} +@end |