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.