1 /* Copyright (c) 2006-2007 Christopher J. W. Lloyd <cjwl@objc.net>
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
9 #import <AppKit/NSImage.h>
10 #import <AppKit/NSImageRep.h>
11 #import <AppKit/NSBitmapImageRep.h>
12 #import <AppKit/NSCachedImageRep.h>
13 #import <AppKit/NSPDFImageRep.h>
14 #import <AppKit/NSEPSImageRep.h>
15 #import <AppKit/NSCustomImageRep.h>
16 #import <AppKit/NSPasteboard.h>
17 #import <AppKit/NSColor.h>
18 #import <AppKit/NSGraphicsContextFunctions.h>
19 #import <Foundation/NSKeyedArchiver.h>
20 #import <AppKit/NSRaise.h>
22 // Private class used so the context knows the flipped status of a locked image
23 // 10.4 does something like that - probably for more than just getting the flippiness - 10.6 uses some special NSSnapshotBitmapGraphicsContext
24 @interface NSImageCacheView : NSView {
27 - (id)initWithFlipped:(BOOL)flipped;
29 @implementation NSImageCacheView
30 - (id)initWithFlipped:(BOOL)flipped
32 if ((self = [super init])) {
43 @implementation NSImage
45 +(NSArray *)imageFileTypes {
46 return [self imageUnfilteredFileTypes];
49 +(NSArray *)imageUnfilteredFileTypes {
50 NSMutableArray *result=[NSMutableArray array];
51 NSArray *allClasses=[NSImageRep registeredImageRepClasses];
52 int i,count=[allClasses count];
55 [result addObjectsFromArray:[[allClasses objectAtIndex:i] imageUnfilteredFileTypes]];
60 +(NSArray *)imagePasteboardTypes {
61 return [self imageUnfilteredPasteboardTypes];
64 +(NSArray *)imageUnfilteredPasteboardTypes{
65 NSMutableArray *result=[NSMutableArray array];
66 NSArray *allClasses=[NSImageRep registeredImageRepClasses];
67 int i,count=[allClasses count];
70 [result addObjectsFromArray:[[allClasses objectAtIndex:i] imageUnfilteredPasteboardTypes]];
75 +(BOOL)canInitWithPasteboard:(NSPasteboard *)pasteboard {
76 NSString *available=[pasteboard availableTypeFromArray:[self imageUnfilteredPasteboardTypes]];
78 return (available!=nil)?YES:NO;
81 +(NSArray *)_checkBundles {
82 return [NSArray arrayWithObjects:
83 [NSBundle mainBundle], // Check the main bundle first according to the doc
84 [NSBundle bundleForClass:self],
88 +(NSMutableDictionary *)allImages {
89 NSMutableDictionary *result=[[[NSThread currentThread] threadDictionary] objectForKey:@"__allImages"];
92 result=[NSMutableDictionary dictionary];
93 [[[NSThread currentThread] threadDictionary] setObject:result forKey:@"__allImages"];
99 +imageNamed:(NSString *)name {
100 NSImage *image=[[self allImages] objectForKey:name];
102 NSArray *bundles=[self _checkBundles];
103 int i,count=[bundles count];
105 for(i=0;i<count;i++){
106 NSBundle *bundle=[bundles objectAtIndex:i];
107 NSString *path=[bundle pathForImageResource:name];
110 image=[[[NSImage alloc] initWithContentsOfFile:path] autorelease];
111 [image setName:name];
119 // Cocoa AppKit always returns the same shared cached image
123 -(void)encodeWithCoder:(NSCoder *)coder {
124 NSUnimplementedMethod();
127 -initWithCoder:(NSCoder *)coder {
128 if([coder allowsKeyedCoding]){
129 NSKeyedUnarchiver *keyed=(NSKeyedUnarchiver *)coder;
131 const unsigned char *tiff=[keyed decodeBytesForKey:@"NSTIFFRepresentation" returnedLength:&length];
132 NSBitmapImageRep *rep;
139 rep=[NSBitmapImageRep imageRepWithData:[NSData dataWithBytes:tiff length:length]];
146 _size=NSMakeSize(0,0);
147 _representations=[NSMutableArray new];
149 [_representations addObject:rep];
152 [NSException raise:NSInvalidArgumentException format:@"-[%@ %s] is not implemented for coder %@",isa,sel_getName(_cmd),coder];
157 -initWithSize:(NSSize)size {
160 _representations=[NSMutableArray new];
165 return [self initWithSize:NSMakeSize(0,0)];
168 -initWithData:(NSData *)data {
169 Class repClass = [NSImageRep imageRepClassForData:data];
172 if([repClass respondsToSelector:@selector(imageRepsWithData:)])
173 reps=[repClass performSelector:@selector(imageRepsWithData:) withObject:data];
174 else if([repClass respondsToSelector:@selector(imageRepWithData:)]){
175 NSImageRep *rep=[repClass performSelector:@selector(imageRepWithData:) withObject:data];
178 reps=[NSArray arrayWithObject:rep];
187 _size=NSMakeSize(0,0);
188 _representations=[NSMutableArray new];
190 [_representations addObjectsFromArray:reps];
195 -initWithContentsOfFile:(NSString *)path {
196 NSArray *reps=[NSImageRep imageRepsWithContentsOfFile:path];
204 _size=NSMakeSize(0,0);
205 _representations=[NSMutableArray new];
207 [_representations addObjectsFromArray:reps];
212 -initWithContentsOfURL:(NSURL *)url {
213 NSData *data=[NSData dataWithContentsOfURL:url];
220 return [self initWithData:data];
223 -initWithCGImage:(CGImageRef)cgImage size:(NSSize)size;
225 if (self = [self initWithSize:size]) {
226 NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithCGImage:cgImage] autorelease];
227 [_representations addObject:rep];
232 -initWithPasteboard:(NSPasteboard *)pasteboard {
234 NSString *available=[pasteboard availableTypeFromArray:[[self class] imageUnfilteredPasteboardTypes]];
235 NSData *data = [pasteboard dataForType:available];
240 return [self initWithData:data];
243 -initByReferencingFile:(NSString *)path {
244 return [self initWithContentsOfFile:path];
247 -initByReferencingURL:(NSURL *)url {
248 // Better than nothing
249 return [self initWithContentsOfURL:url];
254 [_backgroundColor release];
255 [_representations release];
259 -copyWithZone:(NSZone *)zone {
260 NSImage *result=NSCopyObject(self,0,zone);
262 result->_name=[_name copy];
263 result->_backgroundColor=[_backgroundColor copy];
264 result->_representations=[_representations mutableCopy];
274 if(_size.width==0.0 && _size.height==0.0){
275 int i,count=[_representations count];
276 NSSize largestSize=NSMakeSize(0,0);
278 for(i=0;i<count;i++){
279 NSImageRep *check=[_representations objectAtIndex:i];
280 NSSize checkSize=[check size];
282 if(checkSize.width*checkSize.height>largestSize.width*largestSize.height)
283 largestSize=checkSize;
292 -(NSColor *)backgroundColor {
293 return _backgroundColor;
304 -(BOOL)scalesWhenResized {
305 return _scalesWhenResized;
308 -(BOOL)matchesOnMultipleResolution {
309 return _matchesOnMultipleResolution;
312 -(BOOL)usesEPSOnResolutionMismatch {
313 return _usesEPSOnResolutionMismatch;
316 -(BOOL)prefersColorMatch {
317 return _prefersColorMatch;
320 -(NSImageCacheMode)cacheMode {
324 -(BOOL)isCachedSeparately {
325 return _isCachedSeparately;
328 -(BOOL)cacheDepthMatchesImageDepth {
329 return _cacheDepthMatchesImageDepth;
332 -(BOOL)isDataRetained {
333 return _isDataRetained;
340 -(BOOL)setName:(NSString *)name {
341 if(_name!=nil && [[NSImage allImages] objectForKey:_name]==self)
342 [[NSImage allImages] removeObjectForKey:_name];
348 if([[NSImage allImages] objectForKey:_name]!=nil)
351 [[NSImage allImages] setObject:self forKey:_name];
355 -(void)setSize:(NSSize)size {
360 -(void)setBackgroundColor:(NSColor *)value {
362 [_backgroundColor release];
363 _backgroundColor=value;
366 -(void)setFlipped:(BOOL)value {
370 -(void)setTemplate:(BOOL)value {
374 -(void)setScalesWhenResized:(BOOL)value {
375 _scalesWhenResized=value;
378 -(void)setMatchesOnMultipleResolution:(BOOL)value {
379 _matchesOnMultipleResolution=value;
382 -(void)setUsesEPSOnResolutionMismatch:(BOOL)value {
383 _usesEPSOnResolutionMismatch=value;
386 -(void)setPrefersColorMatch:(BOOL)value {
387 _prefersColorMatch=value;
390 -(void)setCacheMode:(NSImageCacheMode)value {
394 -(void)setCachedSeparately:(BOOL)value {
395 _isCachedSeparately=value;
398 -(void)setCacheDepthMatchesImageDepth:(BOOL)value {
399 _cacheDepthMatchesImageDepth=value;
402 -(void)setDataRetained:(BOOL)value {
403 _isDataRetained=value;
406 -(void)setDelegate:delegate {
411 NSUnimplementedMethod();
415 -(NSArray *)representations {
416 return _representations;
419 -(void)addRepresentation:(NSImageRep *)representation {
420 if(representation!=nil)
421 [_representations addObject:representation];
424 -(void)addRepresentations:(NSArray *)array {
425 int i,count=[array count];
428 [self addRepresentation:[array objectAtIndex:i]];
431 -(void)removeRepresentation:(NSImageRep *)representation {
432 [_representations removeObjectIdenticalTo:representation];
435 -(NSCachedImageRep *)_cachedImageRepCreateIfNeeded {
436 int count=[_representations count];
439 NSCachedImageRep *check=[_representations objectAtIndex:count];
441 if([check isKindOfClass:[NSCachedImageRep class]]){
446 [_representations removeObjectAtIndex:count];
450 NSCachedImageRep *cached=[[NSCachedImageRep alloc] initWithSize:[self size] depth:0 separate:_isCachedSeparately alpha:YES];
451 [self addRepresentation:cached];
456 -(NSImageRep *)_bestUncachedRepresentationForDevice:(NSDictionary *)device {
457 int i,count=[_representations count];
459 for(i=0;i<count;i++){
460 NSImageRep *check=[_representations objectAtIndex:i];
462 if(![check isKindOfClass:[NSCachedImageRep class]]){
471 -(NSImageRep *)_bestUncachedFallbackCachedRepresentationForDevice:(NSDictionary *)device size:(NSSize)size {
472 int i,count=[_representations count];
473 NSImageRep *best=nil;
475 size.width=ABS(size.width);
476 size.height=ABS(size.height);
478 for(i=0;i<count;i++){
479 NSImageRep *check=[_representations objectAtIndex:i];
481 if(![check isKindOfClass:[NSCachedImageRep class]]){
485 NSSize checkSize=[check size];
486 NSSize bestSize=[best size];
487 float checkArea=checkSize.width*checkSize.height;
488 float bestArea=bestSize.width*bestSize.height;
489 float desiredArea=size.width*size.height;
491 // downsampling is better than upsampling
492 if(bestArea<desiredArea && checkArea>=desiredArea)
494 // downsampling a closer image is better
495 if(checkArea<bestArea && checkArea>=desiredArea)
497 // if we have to upsample, biggest is better
498 if(checkArea>bestArea && bestArea<desiredArea)
507 for(i=0;i<count;i++){
508 NSImageRep *check=[_representations objectAtIndex:i];
510 if([check isKindOfClass:[NSCachedImageRep class]]){
518 -(NSImageRep *)bestRepresentationForDevice:(NSDictionary *)device {
520 device=[[NSGraphicsContext currentContext] deviceDescription];
522 if([device objectForKey:NSDeviceIsPrinter]!=nil){
523 int i,count=[_representations count];
525 for(i=0;i<count;i++){
526 NSImageRep *check=[_representations objectAtIndex:i];
528 if(![check isKindOfClass:[NSCachedImageRep class]])
533 if([device objectForKey:NSDeviceIsScreen]!=nil){
534 NSImageRep *uncached=[self _bestUncachedRepresentationForDevice:device];
535 NSImageCacheMode caching=_cacheMode;
537 if(caching==NSImageCacheDefault){
538 if([uncached isKindOfClass:[NSBitmapImageRep class]])
539 caching=NSImageCacheBySize;
540 else if([uncached isKindOfClass:[NSPDFImageRep class]])
541 caching=NSImageCacheAlways;
542 else if([uncached isKindOfClass:[NSEPSImageRep class]])
543 caching=NSImageCacheAlways;
544 else if([uncached isKindOfClass:[NSCustomImageRep class]])
545 caching=NSImageCacheAlways;
550 case NSImageCacheDefault:
551 case NSImageCacheAlways:
554 case NSImageCacheBySize:
555 if([[uncached colorSpaceName] isEqual:[device objectForKey:NSDeviceColorSpaceName]]){
556 NSSize size=[self size];
558 if((size.width==[uncached pixelsWide]) && (size.height==[uncached pixelsHigh])){
559 int deviceBPS=[[device objectForKey:NSDeviceBitsPerSample] intValue];
561 if(deviceBPS==[uncached bitsPerSample])
567 case NSImageCacheNever:
571 NSCachedImageRep *cached=[self _cachedImageRepCreateIfNeeded];
574 [self lockFocusOnRepresentation:cached];
578 rect.size=[self size];
580 if([self scalesWhenResized]){
581 [self drawRepresentation:uncached inRect:rect];
584 [uncached drawAtPoint:rect.origin];
593 return [_representations lastObject];
597 // This doesn't actually remove the cache, it just marks it as invalid
598 // This is important because you can change the size of a drawn image
599 // and it doesn't destroy the cache. It is recached next time it is drawn.
603 -(void)cancelIncrementalLoad {
604 NSUnimplementedMethod();
607 -(NSData *)TIFFRepresentation {
608 return [self TIFFRepresentationUsingCompression:NSTIFFCompressionNone factor:0.0];
611 -(NSData *)TIFFRepresentationUsingCompression:(NSTIFFCompression)compression factor:(float)factor {
612 NSMutableArray *bitmaps=[NSMutableArray array];
614 for(NSImageRep *check in _representations){
615 if([check isKindOfClass:[NSBitmapImageRep class]]) {
616 [bitmaps addObject:check];
617 } else if([check isKindOfClass:[NSCachedImageRep class]]) {
618 // We don't use the general case else we get flipped results for flipped images since lockFocusOnRepresentation is flipping and the Cache content
619 // is already flipped
620 NSRect r = { .origin = NSZeroPoint, .size = check.size };
622 NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithFocusedViewRect: r];
625 [bitmaps addObject:image];
628 NSSize size=[check size];
629 NSBitmapImageRep *image=[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:size.width pixelsHigh:size.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:32];
631 [self lockFocusOnRepresentation:image];
632 // we should probably use -draw here but not all reps implement it, or not?
636 [bitmaps addObject:image];
642 return [NSBitmapImageRep TIFFRepresentationOfImageRepsInArray:bitmaps usingCompression:compression factor:factor];
646 [self lockFocusOnRepresentation:nil];
649 -(void)lockFocusOnRepresentation:(NSImageRep *)representation {
650 NSGraphicsContext *context=nil;
651 CGContextRef graphicsPort;
653 if(representation==nil){
654 // FIXME: Cocoa doesn't add the cached rep until the unlockFocus, it just creates the drawing context
655 // then snaps the image during unlock and adds it
656 representation=[self _cachedImageRepCreateIfNeeded];
658 [self lockFocusOnRepresentation:representation];
660 id uncached=[self _bestUncachedRepresentationForDevice:nil];
663 rect.size=[self size];
665 // if([self scalesWhenResized])
666 [uncached drawInRect:rect];
667 // drawAtPoint: is not working with NSPDFImageRep
668 // Should probably ditch all the caching stuff anyway as it is deprecated
670 // [uncached drawAtPoint:rect.origin];
676 if([representation isKindOfClass:[NSCachedImageRep class]])
677 context=[NSGraphicsContext graphicsContextWithWindow:[(NSCachedImageRep *)representation window]];
678 else if([representation isKindOfClass:[NSBitmapImageRep class]])
679 context=[NSGraphicsContext graphicsContextWithBitmapImageRep:(NSBitmapImageRep *)representation];
682 [NSException raise:NSInvalidArgumentException format:@"NSImageRep %@ can not be lockFocus'd"];
686 [NSGraphicsContext saveGraphicsState];
687 [NSGraphicsContext setCurrentContext:context];
689 graphicsPort=NSCurrentGraphicsPort();
690 CGContextSaveGState(graphicsPort);
691 CGContextClipToRect(graphicsPort,NSMakeRect(0,0,[representation size].width,[representation size].height));
693 // Some fake view, just so the context knows if it's flipped or not
694 NSView *view = [[[NSImageCacheView alloc] initWithFlipped:[self isFlipped]] autorelease];
695 [[context focusStack] addObject:self];
697 if([self isFlipped]){
698 CGAffineTransform flip={1,0,0,-1,0,[self size].height};
699 CGContextConcatCTM(graphicsPort,flip);
705 // Remove the pushed view
706 [[[NSGraphicsContext currentContext] focusStack] removeLastObject];
708 CGContextRef graphicsPort=NSCurrentGraphicsPort();
710 CGContextRestoreGState(graphicsPort);
712 [NSGraphicsContext restoreGraphicsState];
715 -(BOOL)drawRepresentation:(NSImageRep *)representation inRect:(NSRect)rect {
716 NSColor *bg=[self backgroundColor];
723 return [representation drawInRect:rect];
726 -(void)compositeToPoint:(NSPoint)point fromRect:(NSRect)rect operation:(NSCompositingOperation)operation {
727 [self compositeToPoint:point fromRect:rect operation:operation fraction:1.0];
730 -(void)compositeToPoint:(NSPoint)point fromRect:(NSRect)source operation:(NSCompositingOperation)operation fraction:(float)fraction {
731 /* Compositing is a blitting operation. We simulate it using the draw operation.
733 Compositing does not honor all aspects of the CTM, e.g. it will keep an image upright regardless of the orientation of CTM.
734 To deal with that we use a negative height in a flipped coordinate system.
735 There are probably other cases which are not right here.
738 NSSize size=[self size];
739 NSRect rect=NSMakeRect(point.x,point.y,size.width,size.height);
741 NSGraphicsContext *graphicsContext=[NSGraphicsContext currentContext];
742 CGContextRef context=[graphicsContext graphicsPort];
744 CGContextSaveGState(context);
745 if([[NSGraphicsContext currentContext] isFlipped]){
746 rect.size.height=-rect.size.height;
749 [self drawInRect:rect fromRect:source operation:operation fraction:fraction];
750 CGContextRestoreGState(context);
753 -(void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)operation {
754 [self compositeToPoint:point operation:operation fraction:1.0];
757 -(void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)operation fraction:(float)fraction {
758 [self compositeToPoint:point fromRect:NSZeroRect operation:operation fraction:1.0];
761 -(void)dissolveToPoint:(NSPoint)point fraction:(float)fraction {
762 NSUnimplementedMethod();
765 -(void)dissolveToPoint:(NSPoint)point fromRect:(NSRect)rect fraction:(float)fraction {
766 NSUnimplementedMethod();
769 -(void)drawAtPoint:(NSPoint)point fromRect:(NSRect)source operation:(NSCompositingOperation)operation fraction:(float)fraction {
770 NSSize size=[self size];
772 [self drawInRect:NSMakeRect(point.x,point.y,size.width,size.height) fromRect:source operation:operation fraction:fraction];
775 -(void)drawInRect:(NSRect)rect fromRect:(NSRect)source operation:(NSCompositingOperation)operation fraction:(float)fraction {
777 // Keep a lid on any intermediate allocations while producing caches
778 NSAutoreleasePool *pool=[NSAutoreleasePool new];
779 NSImageRep *any=[[[self _bestUncachedFallbackCachedRepresentationForDevice:nil size:rect.size] retain] autorelease];
780 NSImageRep *cachedRep=nil;
781 CGContextRef context;
782 NSRect fullRect = { .origin = NSZeroPoint, .size = self.size };
783 BOOL drawFullImage = (NSIsEmptyRect(source) || NSEqualRects(source, fullRect));
784 BOOL canCache = drawFullImage && !_isFlipped;
787 // If we're drawing the full image unflipped then we can just draw from a cached rep or a bitmap rep (assuming we have one)
788 if([any isKindOfClass:[NSCachedImageRep class]] ||
789 [any isKindOfClass:[NSBitmapImageRep class]]) {
795 // Looks like we need to create a cached rep for this image
796 NSImageRep *uncached=any;
797 NSSize uncachedSize=[uncached size];
798 BOOL useSourceRect=NSIsEmptyRect(source)?NO:YES;
799 NSSize cachedSize=useSourceRect?source.size:uncachedSize;
801 // Create a cached image rep to hold our image
802 NSCachedImageRep *cached=[[[NSCachedImageRep alloc] initWithSize:cachedSize depth:0 separate:YES alpha:YES] autorelease]; // remember that pool we created earlier
804 // a non-nil object passed here means we need to manually add the rep
805 [self lockFocusOnRepresentation: cached];
807 context=NSCurrentGraphicsPort();
809 // move to the origin of the source rect - remember we've locked focus so we've got a fresh CTM to work with
810 CGContextTranslateCTM(context,-source.origin.x,-source.origin.y);
813 // Flip the CTM so the image is drawn the right way up in the cache
814 CGContextTranslateCTM(context, 0, uncachedSize.height);
815 CGContextScaleCTM(context, 1, -1);
817 // Draw into the new cache rep
818 [self drawRepresentation:uncached inRect:NSMakeRect(0,0,uncachedSize.width,uncachedSize.height)];
822 // And keep it if it makes sense
824 [self addRepresentation: cached];
830 // OK now we've got a rep we can draw
832 context=NSCurrentGraphicsPort();
834 CGContextSaveGState(context);
836 if (CGContextSupportsGlobalAlpha(context) == NO) {
837 // That should really be done by setting the context alpha - and the compositing done in the context implementation
839 // fraction is accomplished with a 1x1 alpha mask
840 // FIXME: could use a float format image to completely preserve fraction
841 uint8_t bytes[1]={ MIN(MAX(0,fraction*255),255) };
842 CGDataProviderRef provider=CGDataProviderCreateWithData(NULL,bytes,1,NULL);
843 CGImageRef mask=CGImageMaskCreate(1,1,8,8,1,provider,NULL,NO);
845 CGContextClipToMask(context,rect,mask);
846 CGImageRelease(mask);
847 CGDataProviderRelease(provider);
850 CGContextSetAlpha(context, fraction);
852 [[NSGraphicsContext currentContext] setCompositingOperation:operation];
854 [self drawRepresentation: cachedRep inRect:rect];
856 CGContextRestoreGState(context);
861 -(NSString *)description {
862 NSSize size=[self size];
864 return [NSString stringWithFormat:@"<%@[0x%lx] name: %@ size: { %f, %f } representations: %@>", [self class], self, _name, size.width, size.height, _representations];
869 @implementation NSBundle(NSImage)
871 -(NSString *)pathForImageResource:(NSString *)name {
872 NSString *extension = [name pathExtension];
873 if (extension && extension.length) {
874 NSString *baseName=[name stringByDeletingPathExtension];
875 return [self pathForResource:baseName ofType:extension];
877 NSArray *types=[NSImage imageFileTypes];
878 int i,count=[types count];
880 for(i=0;i<count;i++){
881 NSString *type=[types objectAtIndex:i];
882 NSString *path=[self pathForResource:name ofType:type];
888 return [self pathForResource:[name stringByDeletingPathExtension] ofType:[name pathExtension]];