This is NXImage.m in view mode; [Download] [Up]
/* NXImage - class to load an manipulate images Copyright (C) 1993, Adam Fedor. NXImage.m,v 1.20 1995/06/26 23:02:42 fedor Exp FIXME: [1] findImageNamed: might do weird things if the name has '.''s in it (excluding the extension). [2] Should there be a place to look for system bitmaps? (findImageNamed:). [3] bestRepresentation is not complete */ #include <string.h> #include "NXImage.h" #include "NXBitmapImageRep.h" #include "NXCachedImageRep.h" #include "View.h" #include "Window.h" #include "dpsclient/psops.h" #include <sys/param.h> #include <objc/HashTable.h> #include <objc/List.h> #include <objc/Storage.h> #include "NXBundle.h" #include "stdmacros.h" #include <objc/hashtable.h> typedef struct _rep_data_t { char *fileName; id rep; id cache; id original; BOOL validCache; } rep_data_t; /* Use this typedef for newer versions of the libobjects library */ #if 0 #define NXHashState GNUHashState #endif #ifndef index #define index strchr #define rindex strrchr #endif /* Class variables and functions for class methods */ static HashTable *nameHash; static List *imageReps = NULL; static HashTable *fileHash = NULL; static char **fileTypes; static BOOL syncFile; static HashTable *pboardHash = NULL; static NXAtom *pboardTypes; static BOOL syncPboard; void ** iterate_reps_for_types(List *imageReps, SEL method, HashTable *table); /* Strip the extension from a name */ static char * baseName(const char *name, char *buf) { char *s; buf = NXCopyStringBuffer(name); s = rindex(name, '.'); if (s > rindex(name, '/')) *s = '\0'; return buf; } /* Get the extension from a name */ static const char * extension(const char *name) { char *s; s = rindex(name, '.'); if (s > rindex(name, '/')) return s+1; else return NULL; } /* Find the rep_data_t holding a representation */ rep_data_t *repd_for_rep(Storage *_reps, NXImageRep *rep) { int i, count; rep_data_t *repd; count = [_reps count]; for (i = 0; i < count; i++) { repd = (rep_data_t *)[_reps elementAt:i]; if (repd->rep == rep) return repd; } return NULL; } extern const char *NXImageInstanceName(void); @interface NXImage(ToolKit) - _displayComposite:(int)op fromRect:(const NXRect *)rect toPoint:(const NXPoint *)point; @end @implementation NXImage + findImageNamed:(const char *)aName { // If there is no image with that name, search in the main bundle if (!nameHash || ![nameHash valueForKey:aName]) { const char *ext; char path[MAXPATHLEN+1]; id main; main = [NXBundle mainBundle]; ext = extension(aName); path[0] = '\0'; if (ext) [main getPath:path forResource:aName ofType:ext]; else { // Look for name with any registered extension. NXHashState state; char *key, *value; [self imageFileTypes]; state = [fileHash initState]; while ([fileHash nextState: &state key:(const void **)&key value: (void **)&value]) { [main getPath:path forResource:aName ofType:key]; if (path[0] != '\0') break; } } if (path[0] != '\0') { char buf[MAXPATHLEN+1]; id image = [[NXImage alloc] initFromFile:path]; baseName(aName, buf); if (image) [image setName:buf]; return image; } } return [nameHash valueForKey:aName]; } - init { [self initSize:NULL]; return self; } // Designated initializer for nearly everything. - initSize:(const NXSize *)aSize { const char *instance_name; [super init]; _reps = [[Storage alloc] initCount:0 elementSize:sizeof(rep_data_t) description:@encode(rep_data_t)]; if (aSize) { _size = *aSize; _flags.sizeWasExplicitlySet = YES; } _flags.colorMatchPreferred = YES; _flags.multipleResolutionMatching = YES; // Force linker to link in the category instance_name = NXImageInstanceName(); return self; } - initFromFile:(const char *)fileName { [self init]; return ([self useFromFile:fileName]) ? self : nil; } - initFromStream:(NXStream *)stream { [self init]; return ([self loadFromStream:stream]) ? self : nil; } - initFromImage:(NXImage *)image rect:(const NXRect *)rect { return nil; } - getImage:(NXImage **)image rect:(NXRect *)rect { return nil; } - setSize:(const NXSize *)aSize { _size = *aSize; _flags.sizeWasExplicitlySet = YES; return self; } - getSize:(NXSize *)aSize { if (_size.width == 0) { NXImageRep *rep = [self bestRepresentation]; [rep getSize:&_size]; } *aSize = _size; return self; } - _setDontFreeName:(BOOL)flag { _flags.dontFreeName = flag; return self; } - free { [self representationList]; [_repList freeObjects]; [_repList free]; [_reps free]; if (name && !_flags.dontFreeName) { [nameHash removeKey:name]; NX_FREE(name); } return [super free]; } - copy { int i, count; id copy = [super copy]; [self representationList]; count = [_repList count]; for (i = 0; i < count; i++) { id rep; rep = [[_repList objectAt:i] copy]; [copy useRepresentation:rep]; } [copy _setDontFreeName:YES]; return copy; } - (BOOL)setName:(const char *)string { if (!nameHash) nameHash = [[HashTable alloc] initKeyDesc:"*" valueDesc:"@"]; if (!string || [nameHash isKey:string]) return NO; name = NXCopyStringBuffer(string); [nameHash insertKey:name value:self]; return YES; } - (const char *)name { return name; } - setFlipped:(BOOL)flag { _flags.flipDraw = flag; return self; } - (BOOL)isFlipped { return _flags.flipDraw; } - setScalable:(BOOL)flag { _flags.scalable = flag; return self; } - (BOOL)isScalable { return _flags.scalable; } - setDataRetained:(BOOL)flag { _flags.dataRetained = flag; return self; } - (BOOL)isDataRetained { return _flags.dataRetained; } - setUnique:(BOOL)flag { _flags.uniqueWindow = flag; _flags.uniqueWasExplicitlySet = YES; return self; } - (BOOL)isUnique { return _flags.uniqueWindow; } - setCacheDepthBounded:(BOOL)flag { _flags.unboundedCacheDepth = ~flag; return self; } - (BOOL)isCacheDepthBounded { return ~_flags.unboundedCacheDepth; } - setBackgroundColor:(NXColor)aColor { _color = aColor; return self; } - (NXColor)backgroundColor { return _color; } - setEPSUsedOnResolutionMismatch:(BOOL)flag { _flags.useEPSOnResolutionMismatch = flag; return self; } - (BOOL)isEPSUsedOnResolutionMismatch { return _flags.useEPSOnResolutionMismatch; } - setColorMatchPreferred:(BOOL)flag { _flags.colorMatchPreferred = flag; return self; } - (BOOL)isColorMatchPreferred { return _flags.colorMatchPreferred; } - setMatchedOnMultipleResolution:(BOOL)flag { _flags.multipleResolutionMatching = flag; return self; } - (BOOL)isMatchedOnMultipleResolution { return _flags.multipleResolutionMatching; } /* Make sure any images that were added with useFromFile: are loaded in and added to the representation list */ - _loadImageFilenames { unsigned i, count; rep_data_t *repd; _syncLoad = NO; count = [_reps count]; for (i = 0; i < count; i++) { repd = (rep_data_t *)[_reps elementAt:i]; if (repd->fileName) [self loadFromFile:repd->fileName]; } // Now get rid of them since they are already loaded count = [_reps count]; while (count--) { repd = (rep_data_t *)[_reps elementAt:count]; if (repd->fileName) { NX_FREE(repd->fileName); [_reps removeElementAt:count]; } } return self; } // Cache the bestRepresentation. If the bestRepresentation is not itself // a cache and no cache exists, create one and draw the representation in it // If a cache exists, but is not valid, redraw the cache from the original // image (if there is one). - _doImageCache { NXImageRep *rep; rep_data_t *repd; repd = repd_for_rep(_reps, [self bestRepresentation]); rep = repd->rep; if (repd->cache) rep = repd->cache; if (![rep isKindOf:[NXCachedImageRep class]]) { if ([self lockFocus]) { rep_data_t *cached; NXRect bounds; PSsetgray(NX_WHITE); [_lockedView getBounds:&bounds]; NXRectFill(&bounds); [self drawRepresentation:rep inRect:NULL]; [self unlockFocus]; cached = [_reps elementAt:[_reps count] - 1]; cached->original = rep; cached->validCache = YES; } } else if (!repd->validCache) { if ([self lockFocusOn:rep]) { NXRect bounds; PSsetgray(NX_WHITE); [_lockedView getBounds:&bounds]; NXRectFill(&bounds); repd = repd_for_rep(_reps, rep); [self drawRepresentation:repd->original inRect:NULL]; [self unlockFocus]; repd->validCache = YES; } } return self; } - dissolve:(float)delta toPoint:(const NXPoint *)point { return nil; } - dissolve:(float)delta fromRect:(const NXRect *)rect toPoint:(const NXPoint *)point { return nil; } - composite:(int)op toPoint:(const NXPoint *)point { NXRect rect; NXSetRect(&rect, 0, 0, _size.width, _size.height); return [self composite:op fromRect:&rect toPoint:point]; } - composite:(int)op fromRect:(const NXRect *)rect toPoint:(const NXPoint *)point { [self _doImageCache]; return [self _displayComposite:op fromRect:rect toPoint:point]; } - (BOOL)drawRepresentation:(NXImageRep *)imageRep inRect:(const NXRect *)rect { NXPoint origin; if (rect) origin = rect->origin; else origin.x = origin.y = 0; if (!_flags.scalable) return [imageRep drawAt:&origin]; return [imageRep drawIn:rect]; } - recache { int i, count; count = [_reps count]; for (i = 0; i < count; i++) { rep_data_t *repd; repd = (rep_data_t *)[_reps elementAt:i]; repd->validCache = NO; } return self; } - writeTIFF:(NXStream *)stream { return [self writeTIFF:stream allRepresentations:NO]; } - writeTIFF:(NXStream *)stream allRepresentations:(BOOL)flag { return [self writeTIFF:stream allRepresentations:flag usingCompression:0 andFactor:0]; } - writeTIFF:(NXStream *)stream allRepresentations:(BOOL)flag usingCompression:(int)compression andFactor:(float)aFloat { return nil; } - write:(NXTypedStream *)stream { return self; } - read:(NXTypedStream *)stream { return self; } - finishUnarchiving { if (name && [nameHash valueForKey:name]) { [self free]; return [nameHash valueForKey:name]; } return nil; } - _appendImageRepList:imageList { int i, count; count = [imageList count]; for (i=0; i<count; i++) [self useRepresentation:[imageList objectAt:i]]; return self; } - (BOOL)loadFromStream:(NXStream *)stream { int count; BOOL ok; Class rep; List *imageList = nil; count = [imageReps count]; while (count--) { rep = (Class)[imageReps objectAt:count]; if ([rep canLoadFromStream:stream]) { if ([rep respondsTo:@selector(newListFromStream:)]) imageList = [rep perform:@selector(newListFromStream:) with:(void *)stream]; else { id image; image = [[rep alloc] initFromStream:stream]; if (image) { imageList = [[List alloc] init]; [imageList addObject:image]; } } if (!imageList) return NO; [self _appendImageRepList:imageList]; break; } } ok = (imageList) ? YES : NO; [imageList free]; return ok; } - (BOOL)loadFromFile:(const char *)fileName { int count; BOOL ok; Class rep; List *imageList = nil; [[self class] imageFileTypes]; if (!extension(fileName) || ![fileHash valueForKey:extension(fileName)]) return NO; /* Is this kosher? We just call newListFromFile for each imageRep without checking canLoadFromStream or anything, and just assume we'll get nil back if the imageRep can't handle it. FIXME. */ count = [imageReps count]; while (count-- && !imageList) { rep = (Class)[imageReps objectAt:count]; if ([rep respondsTo:@selector(newListFromFile:)]) imageList = [rep perform:@selector(newListFromFile:) with:(void *)fileName]; else { id image; image = [[rep alloc] initFromFile:fileName]; if (image) { imageList = [[List alloc] init]; [imageList addObject:image]; } } } if (!imageList) return NO; [self _appendImageRepList:imageList]; ok = (imageList) ? YES : NO; [imageList free]; return ok; } - (BOOL)useFromFile:(const char *)fileName { rep_data_t repd; [[self class] imageFileTypes]; if (![fileHash valueForKey:extension(fileName)]) return NO; repd.fileName = NXCopyStringBuffer(fileName); [_reps addElement:&repd]; _syncLoad = YES; return YES; } - (BOOL)useDrawMethod:(SEL)drawMethod inObject:anObject { return NO; } - (BOOL)useRepresentation:(NXImageRep *)imageRepresentation { rep_data_t repd; if (!imageRepresentation) return NO; if (_syncLoad) [self _loadImageFilenames]; repd.fileName = NULL; repd.rep = imageRepresentation; repd.cache = NULL; repd.original = NULL; repd.validCache = NO; [_reps addElement:&repd]; return YES; } - (BOOL)useCacheWithDepth:(int)depth { NXRect rect; NXSize size; NXCachedImageRep *rep; [self getSize:&size]; if (!size.width || !size.height) return NO; NXSetRect(&rect, 0, 0, size.width, size.height); // FIXME: Need to create a window with the proper depth rep = [[NXCachedImageRep alloc] initFromWindow:nil rect:&rect]; [self useRepresentation:rep]; return (rep) ? YES : NO; } - removeRepresentation:(NXImageRep *)imageRepresentation { int i, count; rep_data_t *repd; count = [_reps count]; for (i = 0; i < count; i++) { repd = (rep_data_t *)[_reps elementAt:i]; if (repd->rep == imageRepresentation) [_reps removeElementAt:i]; } return self; } - (BOOL)lockFocus { NXImageRep *rep; if (!(rep = [self bestRepresentation])) { [self useCacheWithDepth:NX_DefaultDepth]; rep = [self lastRepresentation]; if (!rep) return NO; } return [self lockFocusOn:rep]; } - (BOOL)lockFocusOn:(NXImageRep *)imageRepresentation { Window *window; if (![imageRepresentation isKindOf:[NXCachedImageRep class]]) { rep_data_t *repd, *cached; int depth; if (_flags.unboundedCacheDepth) depth = [imageRepresentation bitsPerSample] * [imageRepresentation numColors]; else depth = NX_DefaultDepth; if (![self useCacheWithDepth:depth]) return NO; repd = repd_for_rep(_reps, imageRepresentation); cached = repd_for_rep(_reps, [self lastRepresentation]); repd->cache = cached->rep; cached->original = repd->rep; imageRepresentation = cached->rep; } [(NXCachedImageRep *)imageRepresentation getWindow:&window andRect:NULL]; _lockedView = [window contentView]; [_lockedView lockFocus]; return YES; } - unlockFocus { if (_lockedView) [_lockedView unlockFocus]; _lockedView = nil; return self; } - (NXImageRep *)lastRepresentation { // Reconstruct the repList if it has changed [self representationList]; return [_repList lastObject]; } - (NXImageRep *)bestRepresentation { NXImageRep *rep; rep_data_t *repd; // Make sure we have the images loaded in if (_syncLoad) [self _loadImageFilenames]; if ([_reps count] == 0) return nil; // What's the best representation? FIXME repd = (rep_data_t *)[_reps elementAt:[_reps count]-1]; if (repd->cache) rep = repd->cache; else rep = repd->rep; return rep; } - (List *)representationList { int i, count; if (!_repList) _repList = [[List alloc] init]; if (_syncLoad) [self _loadImageFilenames]; count = [_reps count]; [_repList empty]; for (i = 0; i < count; i++) { rep_data_t *repd; repd = (rep_data_t *)[_reps elementAt:i]; [_repList addObject:repd->rep]; } return _repList; } - setDelegate:(id)anObject { delegate = anObject; return self; } - delegate { return delegate; } + initialize { imageReps = [[List alloc] init]; [imageReps addObject:[NXBitmapImageRep class]]; syncFile = YES; syncPboard = YES; return self; } + (void)registerImageRep:imageRepClass { [imageReps addObject:imageRepClass]; syncFile = YES; syncPboard = YES; } + (void)unregisterImageRep:imageRepClass { [imageReps removeObject:imageRepClass]; syncFile = YES; syncPboard = YES; } + (Class)imageRepForFileType:(const char *)type { [self imageFileTypes]; return (Class)[fileHash valueForKey:type]; } + (Class)imageRepForPasteboardType:(NXAtom)type { [self imagePasteboardTypes]; return (Class)[pboardHash valueForKey:type]; } + (Class)imageRepForStream:(NXStream *)stream { int count; Class rep; count = [imageReps count]; while (count--) { rep = [imageReps objectAt:count]; if ([rep canLoadFromStream:stream]) return rep; } return Nil; } + (const char *const *)imageUnfilteredFileTypes { if (!fileHash) fileHash = [[HashTable alloc] initKeyDesc:"%" valueDesc:"@"]; if (syncFile) { NX_FREE(fileTypes); fileTypes = (char **)iterate_reps_for_types(imageReps, @selector(imageUnfilteredFileTypes), fileHash); syncFile = NO; } return (const char *const *)fileTypes; } + (const char *const *)imageFileTypes { return [self imageUnfilteredFileTypes]; } + (const NXAtom *)imageUnfilteredPasteboardTypes { if (!pboardHash) pboardHash = [[HashTable alloc] initKeyDesc:"%" valueDesc:"@"]; if (syncPboard) { NX_FREE(pboardTypes); pboardTypes = (NXAtom *)iterate_reps_for_types(imageReps, @selector(imageUnfilteredPasteboardTypes), pboardHash); syncPboard = NO; } return (const NXAtom *)pboardTypes; } + (const NXAtom *)imagePasteboardTypes { return [self imageUnfilteredPasteboardTypes]; } @end /* For every image rep, call the specified method to obtain a list of (void) pointers. Add these to a hash table and return the list of all the pointers, with duplicates weeded out. Used by imageUnfilteredPasteboardTypes and imageUnfilteredFileTypes. */ void ** iterate_reps_for_types(List *imageReps, SEL method, HashTable *table) { int count; NXHashState state; void **types; void *key, *value; count = [imageReps count]; while (count--) { id rep; const void *const *pb_list; rep = [imageReps objectAt:count]; pb_list = (const void *const *)[rep perform: method]; while (*pb_list) { if (![table isKey:*pb_list]) [table insertKey:*pb_list value:rep]; pb_list++; } } count = [table count]; NX_MALLOC(types, void*, count+1); state = [table initState]; count = 0; while ([table nextState: &state key: (const void **)&key value: (void **)&value]) types[count++] = key; types[count] = NULL; return types; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.