This is SurfImageDecoder.m in view mode; [Download] [Up]
/* ** Copyright (c) 1995 Netsurfer Inc. All Rights Reserved. ** ** Author: <bbum@friday.com> */ /* This object is included in the MiscKit by permission from the author ** and its use is governed by the MiscKit license, found in the file ** "LICENSE.rtf" in the MiscKit distribution. Please refer to that file ** for a list of all applicable permissions and restrictions. */ #import <appkit/appkit.h> #import "SurfImageDecoder.h" #import <objc/objc-runtime.h> #ifdef VERBOSE #undef VERBOSE #undef METHOD #undef METHODnl #endif #define VERBOSE if(_SIDFlags.verboseMode) #define METHOD fprintf(stderr, "[%s %s%s] ", \ object_getClassName(self), \ (self == [self class]) ? "+" : "-", \ SELNAME(_cmd)) #define METHODnl fprintf(stderr, "[%s %s%s]\n", \ object_getClassName(self), \ (self == [self class]) ? "+" : "-", \ SELNAME(_cmd)) #define BOOL2STR(_b_) (_b_ ? "Yes": "No") @implementation SurfImageDecoder /*" * A controller for image decoding. This class implements a generic image * decoder that can be used to decde any of a number of different image * formats depending on what decoder classes have been registered with the * instance. * * Given an arbitrary path or stream, an instance of #SurfImageDecoder * will determine the type of the image, attempt to decode it, and, upon * success, will return an instance of #NXImage containing the decoded * image. * * Instances are designed to be used in a multi-threaded context. Any * instance can be decoding image in any thread of execution while other * instances are simultaneously decoding images. A single instance * %{cannot} decode multiple images from different threads; to attempt * this guarantees catastrophic failure of the environment. * * #{Registrering Custom Decoders} * * A class can register to be a potential decoder by calling * #{SurfImageDecoder}'s #{-addDecoderClass:} method. When instantiated, * an instance of SurfImageDecoder automatically registers the * #{SurfGIFDecoder} and #{SurfJPEGDecoder} classes. * * #{How Image Formats Are Determined} * * The #{-loadFromStream:...} family of methods determines the contents of * the stream by calling {+canLoadFromStream:} within each potentially * eligible decoder's class. The first decoder to return YES decodes the * stream. * * #{-loadFromFile:...} and friends try to determine the image type by * looking up the file's extension in the #{decoderByType} HashTable. If * not found, the stream is mapped into memory and passed to * #{-loadFromStream:...}. * * Note: the #{-lastImageCorrupt} method assumes that the last decoder used * still exists and is still viable; ie: don't call #{-lastImageCorrupt} * if you are using external decoders that may have been freed between the * time an image decoding attempt was made and the time that * #{-lastImageCorrupt} is called!. * "*/ + sharedInstance /*" * Returns the shared instance of SurfImageDecoder. Should only be used in * applications that require a single image decoder. "*/ { static id sharedInstance = nil; if (!sharedInstance) { sharedInstance = [self alloc]; [sharedInstance init]; } return sharedInstance; } - init /*" * Designated intializer. Creates entries in %decoderByType for each * decoder type, but does not allocate/initialize any decoders. "*/ { VERBOSE { METHOD; } decoderByType = [[HashTable allocFromZone:[self zone]] initKeyDesc:"*" valueDesc:"@"]; decoderClassByType = [[HashTable allocFromZone:[self zone]] initKeyDesc:"*" valueDesc:"@"]; decoderList = [[List allocFromZone:[self zone]] init]; _decoderReferences = [[HashTable allocFromZone:[self zone]] initKeyDesc:"@" valueDesc:"i"]; [self addDecoderClass: objc_lookUpClass("SurfGIFDecoder")]; [self addDecoderClass: objc_lookUpClass("SurfJPEGDecoder")]; [self addDecoderClass: objc_lookUpClass("SurfTIFFDecoder")]; [self addDecoderClass: objc_lookUpClass("SurfPNGDecoder")]; return self; } - free /*" * Frees the image decoder and all specific type decoders. "*/ { [[decoderByType empty] free]; [[decoderClassByType empty] free]; [[decoderList freeObjects] free]; NX_FREE((char **) _unfilteredFileTypes); [[_decoderReferences empty] free]; return [super free]; } - (const char *const *)imageUnfilteredFileTypes /*" * Returns NULL terminated array of strings containing all of the image * types an instance of SurfImageDecoder can decode. "*/ { if(!_unfilteredFileTypes) { NXHashState theState; const char *type; id value; int index; _unfilteredFileTypes = NXZoneMalloc([self zone], sizeof(char *) * ([decoderByType count] + 1)); theState = [decoderByType initState]; index = 0; while([decoderByType nextState:&theState key: (void **) &type value: (void **) &value]) { _unfilteredFileTypes[index] = type; index++; } _unfilteredFileTypes[index] = NULL; } return _unfilteredFileTypes; } - (BOOL)canLoadFromStream:(NXStream *)aStream /*" * Returns YES if the decoder thinks it can decode the data in aStream. * This forwards aStream to each specific format decoding class, if any * return YES, this method immediately returns YES. * * The current position in the stream is unchanged. "*/ { return [self decoderForStream: aStream] ? YES : NO; } - decoderForType:(const char *) imageType /*" * Returns the decoder for images of type imageType. This can be used to * customize the particular image decoder. "*/ { if(!imageType) return nil; VERBOSE { METHODnl; fprintf(stderr, "{%s}\n", imageType); } return [decoderByType valueForKey: NXUniqueString(imageType)]; } - (Class) decoderClassForType:(const char *) imageType /*" * Returns the decoder class for images of type imageType. This can be * used to customize the particular image decoder. "*/ { if(!imageType) return nil; VERBOSE { METHODnl; fprintf(stderr, "{%s}\n", imageType); } return [decoderClassByType valueForKey: NXUniqueString(imageType)]; } - decoderForStream:(NXStream *) aStream /*" * Returns the decoder object that should be used to decode aStream. If an * appropriate decoder object is not available, returns nil. "*/ { /* ----------------------------------------------------------------------- * Traverse decoderList: call +canLoadFromStream: on each. If one * returns YES, return that instance. * ----------------------------------------------------------------------- */ id *ida; id *max; ida = NX_ADDRESS(decoderList); max = ida + [decoderList count]; for(;ida<max;ida++) /* * if is the implementor's responsibility to ensure that the * stream's seek is unaltered upon return!!! */ if([[*ida class] canLoadFromStream: aStream]) return *ida; return nil; } - (Class) decoderClassForStream:(NXStream *) aStream /*" * Returns the decoder Class used to decode aStream. If no class is * available, returns nil. "*/ { return [[self decoderForStream: aStream] class]; } - addDecoderClass: aClass /*" * Adds aClass as a possible decoder. This is accomplished by retrieving * the types accepted by the decoder class by calling * #{+imageUnfilteredFileTypes}. The returned types are appended to the * list of types that can be converted by this image converter object. An * instance of the class is allocated and an entry is created in * %hashedExtensions for each type the decoder can decode. If a decoder * that can decode that particular type already exists in * %hashedExtensions, it is replaced (but not freed). "*/ { id newDecoder; int refCount; const char * const *decoderTypes; VERBOSE { METHOD; } /* * check to see if aClass is defined and responds to some set of methods * that define it to be a decoder. Should we create a protocol that * defines decoder-ness? */ if(!aClass) return nil; /* * Set the _lastDecoder to nil. This prevents anyone from doing * something that causes the _lastDecoder to be sent a message when the * _lastDecoder has already been freed! */ _lastDecoder = nil; /* * allocate instance of decoder */ newDecoder = [[aClass allocFromZone:[self zone]] init]; /* * add to _decoderReferences hashtable */ refCount = 0; /* * ask decoder for +imageUnfilteredFileTypes; traverse array of types */ decoderTypes = [aClass imageUnfilteredFileTypes]; for (;*decoderTypes;decoderTypes++) { const char *theType = *decoderTypes; int oldRefCount; id oldDecoder; VERBOSE { METHODnl; fprintf(stderr, "Adding type: {%s}\n", theType); } /* * insert decoder and decoder class in decoderByType hash; with type * as key. */ oldDecoder = [decoderByType valueForKey: theType]; [decoderByType insertKey: theType value: newDecoder]; [decoderClassByType insertKey: theType value: aClass]; /* * decremement reference count of old decoder-- if it is zero, free * it. */ oldRefCount = (int) [_decoderReferences valueForKey: oldDecoder]; --oldRefCount; if(oldRefCount == 0) { [_decoderReferences removeKey: oldDecoder]; [oldDecoder free]; } else if (oldRefCount != -1) { [_decoderReferences insertKey: oldDecoder value:(void *)oldRefCount]; } /* * increment reference count for new decoder. */ refCount++; } // set reference count [_decoderReferences insertKey:newDecoder value:(void *)refCount]; // add to decoderList [decoderList addObject:newDecoder]; /* * propagate configuration (imageDepth) */ [newDecoder setImageDepth:imageDepth]; [newDecoder setVerboseMode:_SIDFlags.verboseMode]; [newDecoder setErrorDelegate:self]; NX_FREE(_unfilteredFileTypes); _unfilteredFileTypes = NULL; [self imageUnfilteredFileTypes]; return self; } - decodeFromFile:(const char *) filePath /*" * Attempts to decode the data in filePath as an image. Uses the * filePath's extension to determine the image type. If it fails to find a * decoder given the filePath's extension, the method maps the filePath and * calls {-decodeFromStream:} in hopes that one of the image decoders will * decide it can decode the stream contents. * * Returns the decoded image upon success and nil upon failure. This * methdo is a cover for #{-decodeFromFile:intoImage:withDecoder:} "*/ { return [self decodeFromFile: filePath intoImage: nil withDecoder: nil]; } - decodeFromFile:(const char *) filePath intoImage: anImage /*" * Attempts to decode the contents of filePath in the same fashion as * #{-decodeFromFile:}. Instead of allocating an instance of NXImage, this * method will append the newly decoded image in an NXBitmapImageRep using * the #{-useRepresentation:} method. * * Returns the decoded image upon success or nil upon failure. This * methdo is a cover for #{-decodeFromFile:intoImage:withDecoder:} "*/ { return [self decodeFromFile: filePath intoImage: anImage withDecoder: nil]; } - decodeFromFile:(const char *) filePath intoImage: anImage withDecoder: aDecoder /*" * Attempts to decode the contents of filePath using aDecoder. If anImage * is defined, the decoded representation will be appended to anImage using * the #{-useRepresentation:} method. If aDecoder is not defined, the * method will try to identify the decoder to be used from filePath's * extension. If that fails, this method will map the file and pass * control #{-decodeFromStream:...}. * * Returns the decoded image upon success or nil upon failure. "*/ { NXStream *stream; if (!aDecoder) { // grab the file's suffix // look up decoder } stream = NXMapFile(filePath, NX_READONLY); if(!stream) return nil; VERBOSE { METHODnl; fprintf(stderr, "%s\n", filePath); } if(aDecoder) { _lastDecoder = aDecoder; anImage = [aDecoder decodeFromStream: stream intoImage: anImage]; } else anImage = [self decodeFromStream: stream intoImage: anImage withDecoder: nil]; NXCloseMemory(stream, NX_FREEBUFFER); return anImage; } - decodeFromStream:(NXStream *) aStream /*" * This method is a cover for #{-decodeFromStream:intoImage:withDecoder:}. "*/ { return [self decodeFromStream: aStream intoImage: nil withDecoder: nil]; } - decodeFromStream:(NXStream *) aStream intoImage:anImage /*" * This method is a cover for #{-decodeFromStream:intoImage:withDecoder:}. "*/ { return [self decodeFromStream: aStream intoImage: nil withDecoder: nil]; } - decodeFromStream:(NXStream *) aStream intoImage:anImage withDecoder:aDecoder /*" * Decodes the contents of aStream into the image anImage using the decoder * aDecoder. If anImage is defined, the decoded image will be appended * using the #{-useRepresentation:} method. If aDecoder is not defined, * this method determines the decoder using the #{-decoderForStream:} * method. * * Returns the decoded image upon success, or nil upon failure. "*/ { if(!aStream) return nil; aDecoder = aDecoder ? aDecoder : [self decoderForStream: aStream]; _lastDecoder = aDecoder; if(!aDecoder) return nil; return [aDecoder decodeFromStream: aStream intoImage: anImage]; } - (void) setImageDepth:(NXWindowDepth) aDepth /*" * Sets the target image depth to aDepth. Some of the image decoders can * optimize the decoding process to a specific depth. This method will * propagate aDepth to all decoders using the #{setImageDepth} method; * whether or not a decoder can actually decode to that depth is entirely * up to the decoder. * * Generally, image decoders default to producing 24-bit deep images. "*/ { id *ida; id *max; imageDepth = aDepth; ida = NX_ADDRESS(decoderList); max = ida + [decoderList count]; for(;ida<max;ida++) [*ida setImageDepth: imageDepth]; } - (NXWindowDepth) imageDepth /*" * Returns %{imageDepth}. "*/ { return imageDepth; } - (BOOL) lastImageCorrupt /*" * Returns YES if last the image that was decoded contained some kind of * error. "*/ { return _lastDecoder ? [_lastDecoder lastImageCorrupt] : YES; } - decoderList /*" * Returns the decoderList List. "*/ { return decoderList; } - decoderByType /*" * Returns the decoderByType HashTable. "*/ { return decoderByType; } - decoderClassByType /*" * Returns the decoderClassByType HashTable. "*/ { return decoderClassByType; } - (BOOL) verboseMode /*" * Returns YES if verbose mode is enabled. "*/ { return _SIDFlags.verboseMode; } - (void) setVerboseMode:(BOOL) aFlag /*" * Enabled/disables verbose mode. Verbose mode spews a HUGE quantity of * state information to stderr. "*/ { id *ida; id *max; _SIDFlags.verboseMode = aFlag; ida = NX_ADDRESS(decoderList); max = ida + [decoderList count]; for(;ida<max;ida++) [*ida setVerboseMode: _SIDFlags.verboseMode]; } - (void) setSpewToStderr:(BOOL) aFlag /*" * If aFlag is YES, error messages geneerated by decoders will be printed * via NXLogError(). "*/ { _SIDFlags.spewStderr = aFlag; } - (BOOL) spewToStderr /*" * Returns YES if error messages will be printed to the console/stderr. "*/ { return _SIDFlags.spewStderr; } - (void) setErrorDelegate:(id <SurfErrorDelegate>) aDelegate /*" * Sets the error delegate to aDelegate. aDelegate's implementation of * #{-decoder:spewMessage:withSeverity:} will be invoked whenever a decoder * generates an error message. "*/ { errorDelegate = aDelegate; } - errorDelegate /*" * Returns the image decoder's error delegate. "*/ { return errorDelegate; } - (void) decoder: aDecoder spewMessage:(const char *) errMsg withSeverity:(SurfErrorSeverity) aSeverity /*" * Invoked whenever a decoder generates an error. If #errorDelegate has * been set, it will be notified of the error message via the * #{-decoder:spewMessage:withSeverity:} method. If #spewToStderr has been * enabled, the message will be printed via the #{NXLogError()} function. "*/ { if(errorDelegate) [errorDelegate decoder:aDecoder spewMessage:errMsg withSeverity:aSeverity]; if(_SIDFlags.spewStderr) NXLogError("*** Image Decoder Msg *** {%s}", errMsg); } - (void)printForDebugger:(NXStream *)aStream /*" * Prints a summary of the decoders state to aStream. "*/ { } - (void) dumpTypes /*" * Pretty-prints file extensions to stderr. "*/ { char ** typeArray = (char **) [self imageUnfilteredFileTypes]; int count; count = 0; while(*typeArray) { typeArray++; count++; } NXLogError("Can decode images with %d different extensions:\n", count); typeArray = (char **) [self imageUnfilteredFileTypes]; while (*typeArray) { NXLogError("\t'%s'", *typeArray); typeArray++; } } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.