home *** CD-ROM | disk | FTP | other *** search
- #import "MovieView.h"
-
- /*
- * Movie 2.51 - 5/7/92 pjf
- *
- * Differences between 2.5 and 2.51:
- * - the save: method actually has a prayer of working when the
- * user saves on top of an existing movie and the old copy can't be renamed.
- *
- * Differences between 2.0 and 2.5:
- * - buttons to control cache depth
- *
- * - turn off multiframe .tiffs by default (define BC_VERSION_1 if you
- * have a .tiff movie and are too lazy to use tiffutil to turn
- * it into an .anim directory)
- *
- * - now able to save movie (currently-selected depth)
- *
- */
-
- @implementation MovieView
-
- - initFrame:(const NXRect *) frameRect
- {
- const char *x;
- [(self = [super initFrame:frameRect]) allocateGState];
- state = STOPPED;
- mode = LOOP;
- maxw = maxh = -1.0;
- movieFrame=NULL;
- anim = nil;
- pingDuringDisplay=NO;
- x=NXGetDefaultValue("Movie","DefaultDepth");
- if (!x) dmode=D_DEF; /* use default depth */
- else switch(atoi(x)) {
- default:
- case 0: dmode=D_DEF; break;
- case 2: dmode=D_2; break;
- case 8: dmode=D_8; break;
- case 12: dmode=D_12; break;
- case 24: dmode=D_24; break;
- };
- updateControls=NO;
- return self;
- }
-
- - drawSelf:(const NXRect *) rects :(int)count
- {
- NXImage *image;
- NXPoint origin = {0.0,0.0};
-
- if (!movieFrame) return nil;
- image = movieFrame[currentFrameIndex].image;
- if (!image) return self;
- [image composite:NX_COPY toPoint:&origin];
- if (pingDuringDisplay) NXPing();
- if ((currentFrameIndex == 0) ||
- ((mode == BOUNCE)&&(currentFrameIndex == numFrames-1))) {
- if (state != STOPPED) {
- double t=[anim getDoubleRealTime]+[anim getSyncTime];
- double afps=numFrames/(t-oldt);
- [actualFpsText setDoubleValue:afps];
- oldt=t;
- }
- }
- if (updateControls) {
- [fNumSlider setIntValue:currentFrameIndex];
- [fNumText setIntValue:currentFrameIndex];
- };
- return self;
- }
-
-
- - (BOOL)open:sender
- {
- #ifdef BC_VERSION_1
- const char *const types[] = { "tiff", "anim", (const char *) NULL };
- #else
- const char *const types[] = { "anim", (const char *) NULL };
- #endif
- id pan = [OpenPanel new];
- const char *const *filenames;
- char filename[FILENAME_MAX];
-
- if (![pan runModalForTypes:types]) return NO;
- if ((filenames = [pan filenames]) == NULL) return NO;
- sprintf(filename,"%s/%s", [pan directory], filenames[0]);
- strcpy(moviePath,filename);
- return [self openFile:filename];
- }
-
- - setUpWindowStuff:(char *)filename;
- {
- char ptitle[FILENAME_MAX];
- Window *w=[self window];
- char *ptr=rindex(filename,'/')+1;
- if (ptr == (char *)1) ptr=filename;
- sprintf(ptitle,"Controls for %s",ptr);
- [panel setTitle:ptitle];
- [panel setNextResponder:w];
- [w sizeWindow:maxw:maxh]; /* will recache images */
- [w setTitleAsFilename:filename];
- [w setMiniwindowIcon:"movieDoc.tiff"];
-
- [[fNumSlider setMaxValue:numFrames-1] setEnabled:YES];
- [nFramesText setIntValue:numFrames-1];
-
- [depthButtons selectCellAt:(int)dmode:0];
- [panel orderFront:self];
- [w makeKeyAndOrderFront:self];
- [self display];
- return self;
- }
-
- // openFile: returns YES if the frames were successfully read, NO if not.
- - (BOOL)openFile:(char *)filename
- {
- char *ptr=rindex(filename,'.');
- List *bitmaps;
-
- if (!ptr) {
- NXRunAlertPanel(NULL,"Impossible filename %s",NULL,NULL,NULL,filename);
- return NO;
- };
-
- /* get the bitmaps */
- if (!strcmp(ptr,".anim")) { /* the file is an Icon-style .anim directory */
- char buf[FILENAME_MAX];
- char *ptr2;
- *ptr='\0'; /* clobber extension */
- ptr2=1+rindex(filename,'/'); /* danger danger */
- if (ptr2 == (char *)1) ptr2=filename; /* if not /full/path/name */
- sprintf(buf,"%s.anim/%s",filename,ptr2);
- bitmaps = [self openAnimDirectory:buf];
- }
- #ifdef BC_VERSION_1
- else if (!strcmp(ptr,".tiff")) { /* a slew o' TIFFs in one file */
- bitmaps = [NXBitmapImageRep newListFromFile:filename];
- }
- #endif
- else { /* this shouldn't happen */
- NXRunAlertPanel(NULL,"Impossible filename %s",NULL,NULL,NULL,filename);
- return NO;
- };
-
- if (!bitmaps) {
- NXRunAlertPanel(NULL,"Couldn't get bitmaps from %s",NULL,NULL,NULL,
- filename);
- return NO;
- };
-
- [self allocateFrames:bitmaps];
- [bitmaps free]; /* does not free elements */
- [self setUpWindowStuff:filename];
- return YES;
- }
-
-
- - (List *)openAnimDirectory:(char *)filenameRoot
- {
- List *bitmaps = [[List alloc] init];
- int i=1;
- while (1) {
- char buf[FILENAME_MAX];
- NXBitmapImageRep *newbitmap;
- sprintf(buf,"%s.%d.tiff",filenameRoot,i++);
- if ((access(buf,R_OK)) == -1) break;
- newbitmap = [[NXBitmapImageRep alloc] initFromFile:buf];
- if (!newbitmap) {
- NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL,
- buf);
- [[bitmaps freeObjects] free];
- return nil;
- }
- else
- [bitmaps addObject:newbitmap];
- };
- return bitmaps;
- }
-
- NXWindowDepth deps[] = {
- NX_DefaultDepth, NX_TwoBitGrayDepth,
- NX_EightBitGrayDepth, NX_TwelveBitRGBDepth,
- NX_TwentyFourBitRGBDepth /*,NX_PurinaCatChow__ChowChowChowDepth*/
- };
-
- // set up Frame data structures and find max frame size
- - allocateFrames:(List *)frames
- {
- int i;
- numFrames=[frames count];
- NX_MALLOC(movieFrame,movieFrameStruct,numFrames);
- for(i=0;i<numFrames;i++) {
- NXImage *nxi;
- NXBitmapImageRep *bm=[frames objectAt:i];
- NXSize sz;
- [bm getSize:&sz];
- movieFrame[i].original=bm;
- nxi=movieFrame[i].image=[[NXImage alloc] initSize:&sz];
- [nxi setUnique:YES]; /* make caches disjoint */
- [nxi setBackgroundColor:NX_COLORBLACK];
- /* keep track of largest frame */
- if (sz.width > maxw) maxw=sz.width;
- if (sz.height > maxh) maxh=sz.height;
- };
- return self;
- }
-
- /*****************************************************************
- *****************************************************************/
- - superviewSizeChanged:(NXSize *)old
- {
- [anim stopEntry];
- [super superviewSizeChanged:old];
- [self recache];
- [self renderFrames];
- [self display];
- [anim resetRealTime];
- [anim startEntry];
- return self;
- }
-
- - renderFrames
- {
- int i;
- NXRect r;
- [self getBounds:&r];
- for(i=0;i<numFrames;i++)
- if ([movieFrame[i].image lockFocus]) {
- [movieFrame[i].original drawIn:&r];
- [movieFrame[i].image unlockFocus];
- }
- else {
- fprintf(stderr,"Barf: couldn't lockFocus on image %d\n",
- movieFrame[i].image);
- abort();
- };
- return self;
- }
-
- - recache
- // assume depth & size both changed
- //
- // Appkit bug? Can one render down from 24 bit color to 2 bit gray?
- //
- {
- NXRect r;
- int i;
- [self getBounds:&r];
- [self freeCaches];
- for(i=0;i<numFrames;i++) {
- movieFrame[i].image=[[NXImage alloc] initSize:&r.size];
- [movieFrame[i].image useCacheWithDepth:deps[(int)dmode]];
- };
- return self;
- }
-
- - save:sender
- {
- const char *type = "anim"; // will only save in .anim format.
- SavePanel *sp = [SavePanel new];
- [sp setDelegate:self];
- [sp setRequiredFileType:type];
- if ([sp runModal]) { // OK was hit
- int i;
- char cwd[MAXPATHLEN];
- /* if directory exists, rename it with a wiggle in back. */
- if (access([sp filename],F_OK) == 0) {
- /* I could do this with a couple of calls to system(), but noooo,
- * I had to do it the had way. yeccch. */
- char *buf=malloc(strlen([sp filename]+2));
- sprintf(buf,"%s~",[sp filename]);
- if (!getwd(cwd)) {
- NXRunAlertPanel("FATAL","Couldn't get current directory.",NULL,NULL,NULL);
- abort();
- };
- if (rename([sp filename],buf) == -1) {
- // sledgehammer time.
- struct direct *de;
- DIR *dp;
- chdir([sp filename]);
- dp=opendir(".");
- while(de=readdir(dp)) unlink(de->d_name);
- closedir(dp);
- chdir(cwd);
- unlink([sp filename]);
- };
- };
- mkdir([sp filename],0755);
- chdir([sp filename]);
- for(i=0;i<numFrames;i++) {
- char buf3[MAXPATHLEN];
- char buf2[MAXPATHLEN];
- char *ptr;
- int fd;
- NXStream *s;
- strcpy(buf3,[sp filename]);
- ptr=rindex(buf3,'/')+1;
- *(rindex(ptr,'.'))='\0';
- sprintf(buf2,"./%s.%d.tiff",ptr,i+1);
- fd=open(buf2,O_WRONLY|O_CREAT,0644);
- s=NXOpenFile(fd,NX_WRITEONLY);
- [movieFrame[i].image writeTIFF:s];
- NXClose(s);
- close(fd);
- };
- chdir(cwd);
- };
- return self;
- }
-
- - (BOOL) panelValidateFilenames:sender
- {
- if (!strcmp([sender filename],moviePath)) {
- NXRunAlertPanel("Save","Cannot overwrite original movie",NULL,NULL,NULL);
- return NO;
- };
- return YES;
- }
-
- - freeCaches
- {
- int i;
- for(i=0;i<numFrames;i++) [movieFrame[i].image free];
- return self;
- }
-
- - freeOriginals
- {
- int i;
- for(i=0;i<numFrames;i++) [movieFrame[i].original free];
- return self;
- }
-
- - free
- {
- [self freeCaches];
- [self freeOriginals];
- [self freeGState];
- [anim free];
- anim=nil;
- return [super free];
- }
-
- - copy:sender
- {
- char *buffer;
- NXStream *stream;
- int length, maxLength;
- Pasteboard *pasteboard = [Pasteboard new];
- runState s=state;
- [anim stopEntry];
- if (s!=STOPPED) [self stop:self];
- [pasteboard declareTypes:&NXPostScriptPboardType num:1 owner:self];
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- [self copyPSCodeInside:&bounds to:stream];
- NXFlush(stream);
- NXGetMemoryBuffer(stream, &buffer, &length, &maxLength);
- [pasteboard writeType:NXPostScriptPboard data:buffer length:length];
- NXCloseMemory(stream, NX_FREEBUFFER);
- switch(s) {
- case STOPPED: break;
- case FORWARD:
- case REVERSE: [anim startEntry]; break;
- };
- return self;
- }
-
- - tick:sender
- {
- int end=(state == FORWARD) ? numFrames-1 : 0;
-
- switch(mode) {
- case ONCE: {
- if (currentFrameIndex == end) {
- [self stop:self];
- return self;
- }
- else
- currentFrameIndex += (int)state;
- break; /* not needed */
- };
- case LOOP: {
- int next = currentFrameIndex + (int)state;
- if (((state == FORWARD)&&(next>end)) ||
- ((state == REVERSE)&&(next<end))) {
- currentFrameIndex = (state < 0) ? numFrames-1 : 0;
- }
- else
- currentFrameIndex = next;
- break;
- };
- case BOUNCE: {
- int next = currentFrameIndex + (int)state;
- if (((state == FORWARD)&&(next>end)) ||
- ((state == REVERSE)&&(next<end))) {
- if (state == FORWARD) [self selectStateButton:REV];
- if (state == REVERSE) [self selectStateButton:FWD];
- state *= -1;
- currentFrameIndex += (int)state;
- }
- else
- currentFrameIndex = next;
- break;
- };
- };
- [self display];
- return self;
- }
-
- /*****************************************************************
- *****************************************************************/
-
- - fwd:sender
- {
-
- if (numFrames < 2) return self;
- if (state != STOPPED) [self stop:self];
- state = FORWARD;
- [self move:sender];
- return self;
- }
-
- - rev:sender
- {
- if (numFrames < 2) return self;
- if (state != STOPPED) [self stop:self];
- state = REVERSE;
- [self move:sender];
- return self;
- }
-
- - move:sender
- {
- double period = 1.0/[fpsSlider floatValue];
- if (numFrames < 2) { /* duh */
- [self selectStateButton:STOP];
- return self;
- };
- anim = [[Animator alloc] initChronon:period adaptation:0.05 /*?*/
- target:self action:@selector(tick:)
- autoStart:YES eventMask:0];
- if (state == FORWARD) [self selectStateButton:FWD];
- if (state == REVERSE) [self selectStateButton:REV];
- [fNumText setStringValue:""];
- [fNumSlider setEnabled:NO];
- oldt=[anim getSyncTime];
- return self;
- }
-
- - stop:sender
- {
-
- if (numFrames < 2) return self;
- switch(state) {
- case FORWARD:
- case REVERSE: [anim free]; anim=nil;
- case STOPPED: break;
- }
- state = STOPPED;
- [self selectStateButton:STOP];
- [fNumText setIntValue:currentFrameIndex];
- [fNumSlider setEnabled:YES];
- [fNumSlider setIntValue:currentFrameIndex];
- return self;
- }
-
- - fwdStep:sender
- {
- [self step:(int) FORWARD];
- return self;
- }
-
- - revStep:sender
- {
- [self step:(int) REVERSE];
- return self;
- }
-
- - step:(int) direction
- {
- if (numFrames < 2) return self;
- if (state != STOPPED) [self stop:self];
- if ((currentFrameIndex = (currentFrameIndex + direction) % numFrames) < 0)
- currentFrameIndex = numFrames + currentFrameIndex;
- [self selectStateButton:STOP];
- [fNumText setIntValue:currentFrameIndex];
- [fNumSlider setEnabled:YES];
- [fNumSlider setIntValue:currentFrameIndex];
- return [self display];
- }
-
- - expand2x:sender
- {
- NXRect r;
- [self getBounds:&r];
- r.size.width *= 2.0;
- r.size.height *= 2.0;
- [[self window] sizeWindow:r.size.width :r.size.height];
- return self;
- }
-
- - reduce50pct:sender
- {
- NXRect r;
- [self getBounds:&r];
- r.size.width *= 0.5;
- r.size.height *= 0.5;
- [[self window] sizeWindow:r.size.width :r.size.height];
- return self;
- }
-
- - restore:sender
- {
- [[self window] sizeWindow:maxw :maxh];
- return self;
- }
-
-
- - modeButtonsChanged:sender
- {
- mode = (runMode)[sender selectedRow];
- return self;
- }
-
- - fNumSliderChanged:sender
- {
- if (currentFrameIndex == [sender intValue]) return self;
- currentFrameIndex=[sender intValue];
- [self stop:self];
- [self display];
- return self;
- }
-
- - fpsSliderChanged:sender
- {
- [fpsText setFloatValue:[sender floatValue]];
- switch(state) {
- case FORWARD:
- case REVERSE: {
- double period = 1.0/[fpsSlider floatValue];
- [anim free];
- anim = [[Animator alloc] initChronon:period adaptation:0.05
- target:self action:@selector(tick:)
- autoStart:YES eventMask:0];
- break;
- };
- case STOPPED: break;
- };
- return self;
- }
-
- - pingButtonChanged:sender
- {
- switch([sender selectedRow]) {
- case 0: pingDuringDisplay=NO; break;
- case 1: pingDuringDisplay=YES; break;
- };
- return self;
- }
-
- - selectStateButton:(runState)b
- {
- [stateButtons selectCellAt:0:((int)b)];
- return self;
- }
-
- - depthButtonsChanged:sender
- {
- dmode=(depthMode)[sender selectedRow];
- [anim stopEntry];
- [self recache];
- [self renderFrames];
- [self display];
- [anim resetRealTime];
- [anim startEntry];
- return self;
- }
-
- - updateCheckBoxChanged:sender
- {
- updateControls = !updateControls;
- return self;
- }
-
-
- // Window's delegate methods
-
- - windowWillClose:sender
- {
- [panel close];
- [self free];
- return self;
- }
-
- -windowDidMiniaturize:sender
- {
- [panel orderOut:sender];
- [anim stopEntry];
- return self;
- }
-
- -windowDidDeminiaturize:sender
- {
- [panel orderFront:sender];
- [anim resetRealTime];
- [anim startEntry];
- return self;
- }
- @end
-