This is GraphicImport.m in view mode; [Download] [Up]
/*
* (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* (b) If this Sample Code is distributed as part of the Display PostScript
* System Software Development Kit from Adobe Systems Incorporated,
* then this copy is designated as Development Software and its use is
* subject to the terms of the License Agreement attached to such Kit.
*
* (c) If this Sample Code is distributed independently, then the following
* terms apply:
*
* (d) This file may be freely copied and redistributed as long as:
* 1) Parts (a), (d), (e) and (f) continue to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* (e) PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
* CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
* AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
* ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
* OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
* WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
* WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
* DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS.
*/
/*
* GraphicImport.m
*
* This subclass of handles much of the overhead for imported
* files such as TIFF files and EPS files. Subclasses of this object
* provide the specific methods that differentiate the two.
*
* Version: 2.0
* Author: Ken Fromm
* History:
* 03-20-91 Created the file.
*/
#import "GraphicImport.h"
#import "DocView.h"
#import "DrawingView.h"
#import "DrawingViewWraps.h"
#import "NXBitmapImageRepSub.h"
#import "NXEPSImageRepSub.h"
#import "ResourcePanel.h"
#import "rotateprocs.h"
#import <appkit/Font.h>
#import <appkit/NXImage.h>
#import <appkit/Panel.h>
#import <appkit/nextstd.h>
#import <objc/List.h>
#import <dpsclient/wraps.h>
#import <streams/streamsimpl.h>
#import <mach.h>
static const char *ImageErrorString = "PostScript errors have been encountered in this file. The text of the errors can be found in the console output.";
@implementation GraphicImport
/*
* Initializes a new GraphicImport object associated with the file.
* If an EPS file, then check the resources. Three routes are possible.
* The first makes everything as ok. The second cancels the import.
* The third makes the file as unimageable.
*/
- initFromFile:(const char *) file
{
char *end;
ResourceList resourceList;
self = [super init];
gflags.new = YES;
gflags.dirty = YES;
[self setFilename:file];
image = [[NXImage alloc] init];
if (image)
{
end = strrchr(file, '.');
if (end)
{
if (strncmp(end, ".ps", 3) == 0 || strncmp(end, ".eps", 4) == 0)
{
imagerep = [[NXEPSImageRepSub alloc] initFromFile:file];
if (imagerep)
{
/* Check the epsf file and list any that are unavailable. */
if (![imagerep checkResources:&resourceList] ||
![self listUnavailableResources:&resourceList])
{
[imagerep free];
imagerep = NULL;
}
else
[imagerep getBoundingBox:&bounds];
}
else
Notify("Import Error", "Unable to open file.");
}
else if (strncmp(end, ".tiff", 5) == 0)
{
imagerep = [[NXBitmapImageRepSub alloc] initFromFile:file];
if (imagerep)
{
bounds.origin.x = bounds.origin.y = 0.0;
[imagerep getSize:&bounds.size];
}
else
Notify("Import Error", "Unable to open file.");
}
}
}
else
Notify("Import Error", "Unable to open file.");
if (!imagerep)
{
[self free];
self = nil;
}
return self;
}
/*
* Create a new object from a stream.
*/
- initFromStream:(NXStream *) stream
{
int len, maxlen;
char *data;
ResourceList resourceList;
self = [super init];
gflags.new = YES;
gflags.dirty = YES;
image = [[NXImage alloc] init];
if (image)
{
NXGetMemoryBuffer(stream, &data, &len, &maxlen);
if (strncmp(data, "%!PS-Adobe-", 11) == 0)
{
imagerep = [[NXEPSImageRepSub alloc] initFromStream:stream];
if (imagerep)
{
/* Check the epsf file and list any that are unavailable. */
if (![imagerep checkResources:&resourceList] ||
![self listUnavailableResources:&resourceList])
{
[imagerep free];
imagerep = NULL;
}
else
[imagerep getBoundingBox:&bounds];
}
else
Notify("Import Error", "Unable to open file.");
}
else
{
imagerep = [[NXBitmapImageRepSub alloc] initFromStream:stream];
if (imagerep)
{
bounds.origin.x = bounds.origin.y = 0.0;
[imagerep getSize:&bounds.size];
}
else
Notify("Import Error", "Unable to open file.");
}
}
else
Notify("Import Error", "Unable to open file.");
if (!imagerep)
{
[self free];
self = nil;
}
return self;
}
- (BOOL) listUnavailableResources:(ResourceList *) resourceList;
{
BOOL unavailable;
int i, tag;
tag = NX_OKTAG;
unavailable = NO;
for (i = 0; i < RES_NUMTYPES; i++)
unavailable = unavailable | (resourceList->types[i] != NULL);
if (unavailable)
{
tag = [[NXApp resourcePanel] runModalWithList:resourceList andName:filename];
if (tag == NX_CANCELTAG)
{
[imagerep free];
imagerep = NULL;
}
else if (tag == NX_ALERTOTHER)
gflags.unimageable = YES;
}
for (i = 0; i < RES_NUMTYPES; i++)
[resourceList->types[i] free];
return (tag != NX_CANCELTAG);
}
/*
* Used when printing. Add the resources used in this file
* to the list passed in if the file is in the rectangle.
*/
- addResources:(Resource *) resourceDoc for:(NXRect *) r
{
if (!r || IntersectsRotatedRect(r, &bounds, rotation))
[imagerep addResources:resourceDoc forFile:filename];
return self;
}
- free
{
[image free];
[imagerep free];
return [super free];
}
- freeTemp
{
return [super free];
}
- copyTemp
{
id new;
new = [super copy];
[new setFilename:filename];
[new setImage:image];
[new setImageRep:imagerep];
return new;
}
- setFilename:(const char *) file
{
if (file)
filename = NXUniqueString(file);
return self;
}
/* Used when producing a copy of this object. */
- setImage:anImage
{
image = anImage;
return self;
}
/* Used when producing a copy of this object. */
- setImageRep:anImageRep
{
imagerep = anImageRep;
return self;
}
- setSelected:(BOOL) flag
{
gflags.selected = flag;
return self;
}
- (BOOL) selected
{
return (BOOL) gflags.selected;
}
- setImageable:(BOOL) flag
{
gflags.unimageable = !flag;
return self;
}
- (BOOL) imageable
{
return (BOOL) !gflags.unimageable;
}
- (BOOL) error
{
return gflags.error;
}
/*
* pt_num is the changing control point. pt holds the relative change in each coordinate.
* The relative is needed and not the absolute because the closest inside control point
* changes when one of the outside points change.
*/
- setPoint:(int) pt_num :(const NXPoint *) pt
{
int row, col;
NXPoint aPt, cPt, oldOrigin;
if (pt->x || pt->y)
{
gflags.dirty = YES;
aPt = *pt;
cPt.x = cPt.y = 0.0;
RotatePoint(&aPt, &cPt, -rotation);
oldOrigin = bounds.origin;
col = pt_num % 3;
switch (col)
{
case LEFT:
bounds.origin.x += aPt.x;
bounds.size.width -= aPt.x;
break;
case RIGHT:
bounds.size.width += aPt.x;
break;
}
row = pt_num/3;
switch (row)
{
case TOP:
bounds.size.height += aPt.y;
break;
case BOTTOM:
bounds.origin.y += aPt.y;
bounds.size.height -= aPt.y;
break;
}
RotatePoint(&bounds.origin, &oldOrigin, rotation);
}
return self;
}
- setSize:(const NXSize *) aSize
{
if (bounds.size.width != aSize->width && bounds.size.height != aSize->height)
{
gflags.dirty = YES;
bounds.size = *aSize;
}
return self;
}
- setOrigin:(const NXPoint *) pt
{
bounds.origin = *pt;
return self;
}
- setBounds:(const NXRect *) aRect
{
[self setSize:&aRect->size];
[self setOrigin:&aRect->origin];
return self;
}
/*
* Return the dimensions to the files original size.
* Keep the same upper left corner.
*/
- setOriginalSize
{
NXSize original;
NXRect boundsNew;
[imagerep getSize:&original];
if (bounds.size.width != original.width || bounds.size.height != original.height)
{
gflags.dirty = YES;
boundsNew.origin.x = bounds.origin.x;
boundsNew.origin.y = bounds.origin.y + bounds.size.height - original.height;
boundsNew.size = original;
RotatePoint(&boundsNew.origin, &bounds.origin, rotation);
bounds = boundsNew;
}
return self;
}
/*
* Return the dimensions to the files original ratio.
* Use its mimimum dimension as the guide to figure
* the other dimension keeping the same upper left corner.
*/
- setOriginalRatio
{
float aspect, aspectOrig;
NXSize original;
NXRect boundsNew;
[imagerep getSize:&original];
aspect = bounds.size.width/bounds.size.height;
aspectOrig = original.width/original.height;
if (aspect != aspectOrig)
{
gflags.dirty = YES;
boundsNew.size = bounds.size;
if (aspect > aspectOrig)
boundsNew.size.width = original.width * (bounds.size.height/original.height);
else
boundsNew.size.height = original.height * (bounds.size.width/original.width);
if (boundsNew.size.width < SIZE_MIN || boundsNew.size.height < SIZE_MIN)
{
if (aspectOrig > 1.0)
{
boundsNew.size.width = SIZE_MIN * aspectOrig;
boundsNew.size.height = SIZE_MIN;
}
else
{
boundsNew.size.width = SIZE_MIN;
boundsNew.size.height = SIZE_MIN/aspectOrig;
}
}
boundsNew.origin.x = bounds.origin.x;
boundsNew.origin.y = bounds.origin.y + bounds.size.height - boundsNew.size.height;
RotatePoint(&boundsNew.origin, &bounds.origin, rotation);
bounds = boundsNew;
return self;
}
return nil;
}
/* Rotate the object about the point. */
- rotateAboutPoint:(NXPoint *) aPoint withAngle:(float) angle
{
if (angle != 0.0)
{
gflags.dirty = YES;
rotation += angle;
RotatePoint(&bounds.origin, aPoint, angle);
}
return self;
}
/* The pt argument holds the relative point change. */
- moveAll:(const NXPoint *) pt
{
bounds.origin.x += pt->x;
bounds.origin.y += pt->y;
return self;
}
/* Given the point number, return the point. */
- getPoint:(int) pt_num :(NXPoint *) pt
{
pt->x = bounds.origin.x + (pt_num % 3) * (bounds.size.width/2.0);
pt->y = bounds.origin.y + bounds.size.height - (pt_num/3) * (bounds.size.height/2.0);
RotatePoint(pt, &bounds.origin, rotation);
return self;
}
/*
* Returns the bounding box of the file. Factors in the rotation.
*/
- getBounds:(NXRect *)aRect
{
RotateRectBounds(aRect, &bounds, &bounds.origin, rotation);
return self;
}
/*
* Return the rectangle that should be used for scrolling purposes.
* When the rectangle passes out of the visible rectangle then
* the screen should scroll.
*/
- getScrollRect:(NXRect *)aRect forPtNum:(int) pt_num
{
if (pt_num == -1)
{
[self getBounds:aRect];
}
else
{
[self getPoint:pt_num :&aRect->origin];
aRect->size.width = aRect->size.height = 0;
}
return self;
}
/* Constrain to ANGLES if shift key is held down. */
- constrainAngle:(float *)angle withFlags:(int) flags
{
if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK)
*angle = rint((rotation + *angle) / M_PI * ANGLES) * M_PI / ANGLES - rotation;
return self;
}
/*
* This method constains the point to the bounds of the view. The
* constaining is dependent on the control point that has been selected.
*/
- constrainPoint:(NXPoint *)aPt forPtNum:(int *) pt_num
inRect:(const NXRect *) viewRect withFlags:(int) flags
{
int row, col,
directionx, directiony;
float aspect_ratio;
NXSize proposed, original;
NXPoint oldOrigin;
aPt->x = MAX(viewRect->origin.x, aPt->x);
aPt->x = MIN(viewRect->origin.x + viewRect->size.width, aPt->x);
aPt->y = MAX(viewRect->origin.y, aPt->y);
aPt->y = MIN(viewRect->origin.y + viewRect->size.height, aPt->y);
RotatePoint(aPt, &bounds.origin, -rotation);
oldOrigin = bounds.origin;
[imagerep getSize:&original];
col = *pt_num % 3;
row = *pt_num/3;
directionx = directiony = 0;
switch (col)
{
case LEFT:
proposed.width = bounds.origin.x + bounds.size.width - aPt->x;
directionx =1;
break;
case MIDDLE:
proposed.width = bounds.size.width;
aPt->x = bounds.origin.x + bounds.size.width/2.0;
break;
case RIGHT:
proposed.width = aPt->x - bounds.origin.x;
directionx = -1;
break;
}
switch (row)
{
case TOP:
proposed.height = aPt->y - bounds.origin.y;
directiony = -1;
break;
case MIDDLE:
proposed.height = bounds.size.height;
aPt->y = bounds.origin.y + bounds.size.height/2.0;
break;
case BOTTOM:
proposed.height = bounds.origin.y + bounds.size.height - aPt->y;
directiony = 1;
break;
}
/* Constrain to the ratio of width vs. height. If new use the original ratio. */
if ((flags & NX_SHIFTMASK) == NX_SHIFTMASK || gflags.new)
{
if (col != MIDDLE && row != MIDDLE)
{
if (gflags.new)
aspect_ratio = original.width/original.height;
else
aspect_ratio = bounds.size.width/bounds.size.height;
if (proposed.width < SIZE_MIN || proposed.height < SIZE_MIN)
{
if (aspect_ratio > 1.0)
{
aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN * aspect_ratio);
aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN);
}
else
{
aPt->x = aPt->x + directionx * (proposed.width - SIZE_MIN);
aPt->y = aPt->y + directiony * (proposed.height - SIZE_MIN / aspect_ratio);
}
}
else
{
if (proposed.width/proposed.height > aspect_ratio)
aPt->x = aPt->x + directionx *
(proposed.width - proposed.height * aspect_ratio);
else
aPt->y = aPt->y + directiony *
(proposed.height - proposed.width / aspect_ratio);
}
}
else
{
[self getPoint:*pt_num :aPt];
RotatePoint(aPt, &bounds.origin, -rotation);
}
}
else
{
if (proposed.width < SIZE_MIN)
aPt->x = aPt->x + (proposed.width - SIZE_MIN) * directionx;
if (proposed.height < SIZE_MIN)
aPt->y = aPt->y + (proposed.height - SIZE_MIN) * directiony;
}
RotatePoint(aPt, &bounds.origin, rotation);
return self;
}
/*
* Check for a control point hit. No need to perform the hit detection in
* the server since its a simple rectangle intersection check. Return the
* point number hit in the pt_num argument. The numbering starts in
* the lower left and goes left to right and bottom to top.
*/
- hitControl:(const NXRect *) hitRect :(int *) pt_num forSize:(float) size
{
int i, j;
float halfc, halfw, halfh, halfhitw, halfhith;
NXRect knobRect, mouseRect;
mouseRect = *hitRect;
if (rotation != 0.0)
{
halfhitw= mouseRect.size.width/2;
halfhith = mouseRect.size.width/2;
NXInsetRect(&mouseRect, halfhitw, halfhith);
RotatePoint(&mouseRect.origin, &bounds.origin, -rotation);
NXInsetRect(&mouseRect, -halfhitw, -halfhith);
};
knobRect.size.width = knobRect.size.height = size;
halfc = knobRect.size.width/2;
halfw = bounds.size.width/2;
halfh = bounds.size.height/2;
for (i=0; i < 3; i ++)
{
for (j = 0; j < 3; j++)
{
if (!(i == MIDDLE && j == MIDDLE))
{
knobRect.origin.x = bounds.origin.x + j * halfw - halfc;
knobRect.origin.y = bounds.origin.y + bounds.size.height - i * halfh - halfc;
if (NXIntersectsRect(&mouseRect, &knobRect))
{
*pt_num = i * 3 + j;
return self;
}
}
}
}
return nil;
}
/*
* Check for hit dectection on the object. Since the hitpoint and the
* object is a rectangle just perform a rectangle intersection check.
* (The hit point is adjusted for rotation so that the intersection check
* can be used even on a rotated epsf file.)
*/
- hitObject:(UPath *) hitUpath ifNotSelected:(BOOL) flag
{
float halfhitx, halfhity;
NXRect hitRect;
if (!flag || !gflags.selected)
{
NXSetRect(&hitRect, hitUpath->pts[0], hitUpath->pts[1],
hitUpath->pts[2] - hitUpath->pts[0],
hitUpath->pts[3] - hitUpath->pts[1]);
if (rotation != 0.0)
{
halfhitx= hitRect.size.width/2;
halfhity = hitRect.size.width/2;
NXInsetRect(&hitRect, halfhitx, halfhity);
RotatePoint(&hitRect.origin, &bounds.origin, -rotation);
NXInsetRect(&hitRect, -halfhitx, -halfhity);
}
if (NXIntersectsRect(&hitRect, &bounds))
return self;
}
return nil;
}
/*
* Place the point locations and the chararacters for the control
* points into the user path description passed in. In this case, the
* xyshow operator is used instead of a user path. But because the
* xyshow operator takes the same data format as the user path,
* the buffers for the user paths are used. The position of the point
* is calculated relative to the position of the previous point. If
* this is the first set of points in the description then put the
* absolute values in place, otherwise use the lastPoint
* argument to calculate the displacement.
*/
- putControlPoints:(UPath *)buffer forRect:(NXRect *)r :(NXPoint *) lastPoint
{
int i, j;
NXPoint pt;
NXRect bRect;
[self getBounds:&bRect];
if (!r || NXIntersectsRect(r, &bRect))
{
i = buffer->num_ops;
for (j = 0; j < PTS_GRAPHIC; j++)
buffer->ops[i++] = 'a';
buffer->num_ops += PTS_GRAPHIC;
i = buffer->num_pts;
if (i == 0)
{
[self getPoint:0 :&pt];
buffer->pts[i++] = pt.x;
buffer->pts[i++] = pt.y;
*lastPoint = pt;
j = 1;
}
else
j = 0;
for ( ; j < PTS_GRAPHIC + 1; j ++)
{
if (j != 4)
{
[self getPoint:j :&pt];
buffer->pts[i++] = pt.x - lastPoint->x;
buffer->pts[i++] = pt.y - lastPoint->y;
*lastPoint = pt;
}
}
buffer->num_pts += PTS_GRAPHIC*2;
}
return self;
}
/*
* Draw a box around the file. If imaging write "imaging file..." if not
* then draw cross hatches. Ideally we would like to draw the file
* name in the box but obtaining the font metrics for the font is a
* non-trivial exercise that is incorporated into this application.
*/
- drawBoxforRect:(NXRect *) r imaging:(BOOL) imageFlag
{
char *string;
NXSize strSize;
PSgsave();
NXRectClip(r);
PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
PSsetgray(NX_LTGRAY);
PSrectfill(0.0, 0.0, bounds.size.width, bounds.size.height);
if (imageFlag)
{
strSize.width = 70;
strSize.height = 12;
string = "Imaging file...";
}
else
{
PSWSetLine(2.0, NX_DKGRAY);
PSWStrokeX(0.0, 0.0, bounds.size.width, bounds.size.height);
PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
/* Insert code to show the file name. */
strSize.width = bounds.size.width;
strSize.height = bounds.size.height;
string = (char *) filename;
}
if (string && bounds.size.width > strSize.width && bounds.size.height > strSize.height)
{
PSsetgray(NX_BLACK);
PSmoveto((bounds.size.width - strSize.width)/2, (bounds.size.height - strSize.height)/2);
PSselectfont("Helvetica", 12);
PSshow(string);
}
PSgrestore();
return self;
}
/*
* Draws the TIFF or EPS file. Sets up for drawing in a window if drawing
* onscreen and the image is smaller than the maximum window size
* permitted. Makes the window transparent and scales it to the right
* size to match the scale of the document.
*/
- drawObject:(NXRect *) r withFlags:(int) flags inView:view
{
BOOL useImage;
float scale;
NXRect rect, frame;
if (!r || IntersectsRotatedRect(r, &bounds, rotation))
{
if (flags & REDRAWFLAG)
{
scale = [[view superview] scale];
PSgsave();
PSWTranslateRotate(bounds.origin.x, bounds.origin.y, rotation * ANGLE);
PSWSetLine(1.0/scale, NX_LTGRAY);
PSrectstroke(0.0, 0.0, bounds.size.width, bounds.size.height);
PSgrestore();
}
else if (gflags.unimageable || ![NXApp imagingFlag])
{
[self drawBoxforRect:r imaging:NO];
gflags.new = NO;
}
else if (NXDrawingStatus == NX_PRINTING ||
NXDrawingStatus == NX_COPYING ||
[view image])
{
[imagerep drawIn:&bounds with:(rotation*180/M_PI)];
}
else
{
useImage = YES;
/* Get the frame of the epsf file. */
RotateRectBounds(&frame, &bounds, &bounds.origin, rotation);
scale = [[view superview] scale];
/*
* Size the cache. Use the NXImage if the size is less than
* a predetermined size.
*/
[image getSize:&rect.size];
if (frame.size.width*scale != rect.size.width ||
frame.size.height*scale != rect.size.height)
{
gflags.dirty = YES;
rect.size.width = frame.size.width * scale;
rect.size.height = frame.size.height * scale;
if (rect.size.width < IMAGE_MAX && rect.size.height < IMAGE_MAX)
{
[image setSize:&rect.size];
[image getSize:&rect.size];
}
else
useImage = NO;
}
if (gflags.dirty)
{
/*
* Draw the gray box that tells the user
* the file is being imaged.
*/
[view lockFocus];
[self drawBoxforRect:r imaging:YES];
[view unlockFocus];
[[view window] flushWindow];
NXPing();
/*
* Setup the drawing so that it goes into an NXImage.
* Some prep work needs to be done to set the
* position.
*/
if (useImage)
{
rect.origin.x = rect.origin.y = 0;
[image lockFocus];
PSsetalpha(0.0);
PSsetgray(NX_WHITE);
NXRectFill(&rect);
PSsetalpha(1.0);
PSscale(scale, scale);
PStranslate(-frame.origin.x, -frame.origin.y);
}
if ([NXApp tracingFlag])
DPSTraceContext(DPSGetCurrentContext(), YES);
/*
* Draw the file into a subclass of NXEPSImageRep.
* Take the rotation into account.
*/
gflags.error = ![imagerep drawIn:&bounds with:(rotation*180/M_PI)];
if ([NXApp tracingFlag])
DPSTraceContext(DPSGetCurrentContext(), NO);
if (useImage)
[image unlockFocus];
/*
* Display an error panel if one occurs. If it is
* a newly imported file, it will not be
* imported. If it is an existing file,
* will be marked as unimageable.
*/
if (gflags.error)
{
gflags.unimageable = YES;
[self displayError];
if (!gflags.new)
[self drawBoxforRect:r imaging:NO];
}
}
/*
* Composite the buffer to the currently focused view.
*/
if (useImage && !gflags.error)
{
rect.origin.x = rect.origin.y = 0;
rect.size.width = frame.size.width * scale;
rect.size.height = frame.size.height * scale;
[image composite:NX_SOVER fromRect:&rect toPoint:&frame.origin];
}
gflags.new = NO;
gflags.dirty = NO;
}
}
return self;
}
/*
* Write out the bounds, rotation, filename and imagerep.
*/
- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteRect(stream, &bounds);
NXWriteType(stream, "f", &rotation);
NXWriteType(stream, "%", &filename);
NXWriteObject(stream, imagerep);
return self;
}
/*
* Read the bounds, rotation, filename and imagerep.
*/
- read:(NXTypedStream *)stream
{
[super read:stream];
NXReadRect(stream, &bounds);
NXReadType(stream, "f", &rotation);
NXReadType(stream, "%", &filename);
imagerep = NXReadObject(stream);
return self;
}
- awake
{
gflags.new = YES;
gflags.dirty = YES;
image = [[NXImage alloc] init];
return self;
}
- displayError
{
NXRunAlertPanel("Imaging Error", ImageErrorString, "OK", NULL, NULL);
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.