home *** CD-ROM | disk | FTP | other *** search
/ Altsys Virtuoso 2.0K / virtuoso_20k.iso / DemoApps / Graphics / Multimedia / Movie / Source / MovieView.m < prev    next >
Encoding:
Text File  |  1993-01-26  |  14.2 KB  |  611 lines

  1. #import "MovieView.h"
  2.  
  3. /*
  4.  * Movie 2.51 - 5/7/92 pjf
  5.  *
  6.  * Differences between 2.5 and 2.51:
  7.  * - the save: method actually has a prayer of working when the
  8.  *   user saves on top of an existing movie and the old copy can't be renamed.
  9.  *
  10.  * Differences between 2.0 and 2.5:
  11.  * - buttons to control cache depth
  12.  *
  13.  * - turn off multiframe .tiffs by default (define BC_VERSION_1 if you
  14.  *   have a .tiff movie and are too lazy to use tiffutil to turn
  15.  *   it into an .anim directory)
  16.  *
  17.  * - now able to save movie (currently-selected depth)
  18.  *
  19.  */
  20.  
  21. @implementation MovieView
  22.  
  23. - initFrame:(const NXRect *) frameRect
  24. {
  25.   const char *x;
  26.   [(self = [super initFrame:frameRect]) allocateGState];
  27.   state = STOPPED;
  28.   mode = LOOP;
  29.   maxw = maxh = -1.0;
  30.   movieFrame=NULL;
  31.   anim = nil;
  32.   pingDuringDisplay=NO;
  33.   x=NXGetDefaultValue("Movie","DefaultDepth");
  34.   if (!x) dmode=D_DEF; /* use default depth */
  35.   else switch(atoi(x)) {
  36.     default:
  37.     case 0: dmode=D_DEF; break;
  38.     case 2: dmode=D_2; break;
  39.     case 8: dmode=D_8; break;
  40.     case 12: dmode=D_12; break;
  41.     case 24: dmode=D_24; break;
  42.     };
  43.   updateControls=NO;
  44.   return self;
  45. }
  46.  
  47. - drawSelf:(const NXRect *) rects :(int)count
  48. {
  49.   NXImage *image;
  50.   NXPoint origin = {0.0,0.0};
  51.   
  52.   if (!movieFrame) return nil;
  53.   image = movieFrame[currentFrameIndex].image;
  54.   if (!image) return self;
  55.   [image composite:NX_COPY toPoint:&origin];
  56.   if (pingDuringDisplay) NXPing();
  57.   if ((currentFrameIndex == 0) ||
  58.       ((mode == BOUNCE)&&(currentFrameIndex == numFrames-1))) {
  59.     if (state != STOPPED) {
  60.       double t=[anim getDoubleRealTime]+[anim getSyncTime];
  61.       double afps=numFrames/(t-oldt);
  62.       [actualFpsText setDoubleValue:afps];
  63.       oldt=t;
  64.       }
  65.     }
  66.   if (updateControls) {
  67.     [fNumSlider setIntValue:currentFrameIndex];
  68.     [fNumText setIntValue:currentFrameIndex];
  69.     };
  70.   return self;
  71. }
  72.  
  73.  
  74. - (BOOL)open:sender
  75. {
  76. #ifdef BC_VERSION_1
  77.   const char *const types[] = { "tiff", "anim", (const char *) NULL };
  78. #else
  79.   const char *const types[] = { "anim", (const char *) NULL };
  80. #endif
  81.   id pan = [OpenPanel new];
  82.   const char *const *filenames;
  83.   char filename[FILENAME_MAX];
  84.  
  85.   if (![pan runModalForTypes:types]) return NO;
  86.   if ((filenames = [pan filenames]) == NULL) return NO;
  87.   sprintf(filename,"%s/%s", [pan directory], filenames[0]);
  88.   strcpy(moviePath,filename);
  89.   return [self openFile:filename];
  90. }
  91.  
  92. - setUpWindowStuff:(char *)filename;
  93. {
  94.   char ptitle[FILENAME_MAX];
  95.   Window *w=[self window];
  96.   char *ptr=rindex(filename,'/')+1;
  97.   if (ptr == (char *)1) ptr=filename;
  98.   sprintf(ptitle,"Controls for %s",ptr);
  99.   [panel setTitle:ptitle];
  100.   [panel setNextResponder:w];
  101.   [w sizeWindow:maxw:maxh]; /* will recache images */
  102.   [w setTitleAsFilename:filename];
  103.   [w setMiniwindowIcon:"movieDoc.tiff"];
  104.  
  105.   [[fNumSlider setMaxValue:numFrames-1] setEnabled:YES];
  106.   [nFramesText setIntValue:numFrames-1];
  107.  
  108.   [depthButtons selectCellAt:(int)dmode:0];
  109.   [panel orderFront:self];
  110.   [w makeKeyAndOrderFront:self];
  111.   [self display];
  112.   return self;
  113. }
  114.  
  115. // openFile: returns YES if the frames were successfully read, NO if not.
  116. - (BOOL)openFile:(char *)filename
  117. {
  118.   char *ptr=rindex(filename,'.');
  119.   List *bitmaps;
  120.  
  121.   if (!ptr) {
  122.     NXRunAlertPanel(NULL,"Impossible filename %s",NULL,NULL,NULL,filename);
  123.     return NO;
  124.     };
  125.  
  126.   /* get the bitmaps */
  127.   if (!strcmp(ptr,".anim")) {  /* the file is an Icon-style .anim directory */
  128.     char buf[FILENAME_MAX];
  129.     char *ptr2;
  130.     *ptr='\0'; /* clobber extension */
  131.     ptr2=1+rindex(filename,'/'); /* danger danger */
  132.     if (ptr2 == (char *)1) ptr2=filename; /* if not /full/path/name */
  133.     sprintf(buf,"%s.anim/%s",filename,ptr2);
  134.     bitmaps = [self openAnimDirectory:buf];
  135.     }
  136. #ifdef BC_VERSION_1
  137.   else if (!strcmp(ptr,".tiff")) { /* a slew o' TIFFs in one file */
  138.     bitmaps = [NXBitmapImageRep newListFromFile:filename];
  139.     }
  140. #endif
  141.   else { /* this shouldn't happen */
  142.     NXRunAlertPanel(NULL,"Impossible filename %s",NULL,NULL,NULL,filename);
  143.     return NO;
  144.     };
  145.  
  146.   if (!bitmaps) {
  147.     NXRunAlertPanel(NULL,"Couldn't get bitmaps from %s",NULL,NULL,NULL,
  148.                     filename);
  149.     return NO;
  150.     };
  151.  
  152.   [self allocateFrames:bitmaps];
  153.   [bitmaps free]; /* does not free elements */
  154.   [self setUpWindowStuff:filename];
  155.   return YES;
  156. }
  157.  
  158.  
  159. - (List *)openAnimDirectory:(char *)filenameRoot
  160. {
  161.   List *bitmaps = [[List alloc] init];
  162.   int i=1;
  163.   while (1) {
  164.     char buf[FILENAME_MAX];
  165.     NXBitmapImageRep *newbitmap;
  166.     sprintf(buf,"%s.%d.tiff",filenameRoot,i++);
  167.     if ((access(buf,R_OK)) == -1) break;
  168.     newbitmap = [[NXBitmapImageRep alloc] initFromFile:buf];
  169.     if (!newbitmap) {
  170.       NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL,
  171.                     buf);
  172.       [[bitmaps freeObjects] free];
  173.       return nil;
  174.       }
  175.     else
  176.       [bitmaps addObject:newbitmap];
  177.     };
  178.   return bitmaps;
  179. }
  180.  
  181. NXWindowDepth deps[] = {
  182.   NX_DefaultDepth, NX_TwoBitGrayDepth,
  183.   NX_EightBitGrayDepth, NX_TwelveBitRGBDepth,
  184.   NX_TwentyFourBitRGBDepth /*,NX_PurinaCatChow__ChowChowChowDepth*/
  185.   };
  186.  
  187. // set up Frame data structures and find max frame size
  188. - allocateFrames:(List *)frames
  189. {
  190.   int i;
  191.   numFrames=[frames count];
  192.   NX_MALLOC(movieFrame,movieFrameStruct,numFrames);
  193.   for(i=0;i<numFrames;i++) {
  194.     NXImage *nxi;
  195.     NXBitmapImageRep *bm=[frames objectAt:i];
  196.     NXSize sz;
  197.     [bm getSize:&sz];
  198.     movieFrame[i].original=bm;
  199.     nxi=movieFrame[i].image=[[NXImage alloc] initSize:&sz];
  200.     [nxi setUnique:YES]; /* make caches disjoint */
  201.     [nxi setBackgroundColor:NX_COLORBLACK];
  202.     /* keep track of largest frame */
  203.     if (sz.width > maxw) maxw=sz.width;
  204.     if (sz.height > maxh) maxh=sz.height;
  205.     };
  206.   return self;
  207. }
  208.  
  209. /*****************************************************************
  210.  *****************************************************************/
  211. - superviewSizeChanged:(NXSize *)old
  212. {
  213.   [anim stopEntry];
  214.   [super superviewSizeChanged:old];
  215.   [self recache];
  216.   [self renderFrames];
  217.   [self display];
  218.   [anim resetRealTime];
  219.   [anim startEntry];
  220.   return self;
  221. }
  222.  
  223. - renderFrames
  224. {
  225.   int i;
  226.   NXRect r;
  227.   [self getBounds:&r];
  228.   for(i=0;i<numFrames;i++)
  229.     if ([movieFrame[i].image lockFocus]) {
  230.       [movieFrame[i].original drawIn:&r];
  231.       [movieFrame[i].image unlockFocus];
  232.       }
  233.     else {
  234.       fprintf(stderr,"Barf: couldn't lockFocus on image %d\n",
  235.               movieFrame[i].image);
  236.       abort();
  237.       };
  238.   return self;
  239. }
  240.  
  241. - recache
  242. // assume depth & size both changed 
  243. //
  244. // Appkit bug?  Can one render down from 24 bit color to 2 bit gray?
  245. //
  246. {
  247.   NXRect r;
  248.   int i;
  249.   [self getBounds:&r];
  250.   [self freeCaches];
  251.   for(i=0;i<numFrames;i++) {
  252.     movieFrame[i].image=[[NXImage alloc] initSize:&r.size];
  253.     [movieFrame[i].image useCacheWithDepth:deps[(int)dmode]];
  254.     };
  255.   return self;
  256. }
  257.  
  258. - save:sender
  259. {
  260.   const char *type = "anim"; // will only save in .anim format.
  261.   SavePanel *sp = [SavePanel new];
  262.   [sp setDelegate:self];
  263.   [sp setRequiredFileType:type];
  264.   if ([sp runModal]) { // OK was hit
  265.     int i;
  266.     char cwd[MAXPATHLEN];
  267.     /* if directory exists, rename it with a wiggle in back. */
  268.     if (access([sp filename],F_OK) == 0) {
  269.       /* I could do this with a couple of calls to system(), but noooo,
  270.        * I had to do it the had way. yeccch. */
  271.       char *buf=malloc(strlen([sp filename]+2));
  272.       sprintf(buf,"%s~",[sp filename]);
  273.       if (!getwd(cwd)) {
  274.         NXRunAlertPanel("FATAL","Couldn't get current directory.",NULL,NULL,NULL);
  275.         abort();
  276.         };
  277.       if (rename([sp filename],buf) == -1) {
  278.         // sledgehammer time.
  279.         struct direct *de;
  280.         DIR *dp;
  281.         chdir([sp filename]);
  282.         dp=opendir(".");
  283.         while(de=readdir(dp)) unlink(de->d_name);
  284.         closedir(dp);
  285.         chdir(cwd);
  286.         unlink([sp filename]);
  287.         };
  288.       };
  289.     mkdir([sp filename],0755);
  290.     chdir([sp filename]);
  291.     for(i=0;i<numFrames;i++) {
  292.       char buf3[MAXPATHLEN];
  293.       char buf2[MAXPATHLEN];
  294.       char *ptr;
  295.       int fd;
  296.       NXStream *s;
  297.       strcpy(buf3,[sp filename]);
  298.       ptr=rindex(buf3,'/')+1;
  299.       *(rindex(ptr,'.'))='\0';
  300.       sprintf(buf2,"./%s.%d.tiff",ptr,i+1);
  301.       fd=open(buf2,O_WRONLY|O_CREAT,0644);
  302.       s=NXOpenFile(fd,NX_WRITEONLY);
  303.       [movieFrame[i].image writeTIFF:s];
  304.       NXClose(s);
  305.       close(fd);
  306.       };
  307.     chdir(cwd);
  308.     };
  309.   return self;
  310. }
  311.  
  312. - (BOOL) panelValidateFilenames:sender
  313. {
  314.   if (!strcmp([sender filename],moviePath)) {
  315.     NXRunAlertPanel("Save","Cannot overwrite original movie",NULL,NULL,NULL);
  316.     return NO;
  317.     };
  318.   return YES;
  319. }
  320.  
  321. - freeCaches
  322. {
  323.   int i;
  324.   for(i=0;i<numFrames;i++) [movieFrame[i].image free];
  325.   return self;
  326. }
  327.  
  328. - freeOriginals
  329. {
  330.   int i;
  331.   for(i=0;i<numFrames;i++) [movieFrame[i].original free];
  332.   return self;
  333. }
  334.  
  335. - free
  336. {
  337.   [self freeCaches];
  338.   [self freeOriginals];
  339.   [self freeGState];
  340.   [anim free];
  341.   anim=nil;
  342.   return [super free];
  343. }
  344.  
  345. - copy:sender
  346. {
  347.   char *buffer;
  348.   NXStream *stream;
  349.   int length, maxLength;
  350.   Pasteboard *pasteboard = [Pasteboard new];
  351.   runState s=state;
  352.   [anim stopEntry];
  353.   if (s!=STOPPED) [self stop:self];
  354.   [pasteboard declareTypes:&NXPostScriptPboardType num:1 owner:self];
  355.   stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  356.   [self copyPSCodeInside:&bounds to:stream];
  357.   NXFlush(stream);
  358.   NXGetMemoryBuffer(stream, &buffer, &length, &maxLength);
  359.   [pasteboard writeType:NXPostScriptPboard data:buffer length:length];
  360.   NXCloseMemory(stream, NX_FREEBUFFER);
  361.   switch(s) {
  362.     case STOPPED: break;
  363.     case FORWARD: 
  364.     case REVERSE: [anim startEntry]; break;
  365.     };
  366.   return self;
  367. }
  368.  
  369. - tick:sender
  370. {
  371.   int end=(state == FORWARD) ? numFrames-1 : 0;
  372.  
  373.   switch(mode) {
  374.     case ONCE: {
  375.       if (currentFrameIndex == end) {
  376.         [self stop:self];
  377.         return self;
  378.         }
  379.       else
  380.         currentFrameIndex += (int)state;
  381.       break; /* not needed */
  382.       };
  383.     case LOOP: {
  384.       int next = currentFrameIndex + (int)state;
  385.       if (((state == FORWARD)&&(next>end)) ||
  386.           ((state == REVERSE)&&(next<end))) {
  387.         currentFrameIndex = (state < 0) ? numFrames-1 : 0;
  388.         }
  389.       else
  390.         currentFrameIndex = next;
  391.       break;
  392.       };
  393.     case BOUNCE: {
  394.       int next = currentFrameIndex + (int)state;
  395.       if (((state == FORWARD)&&(next>end)) ||
  396.           ((state == REVERSE)&&(next<end))) {
  397.         if (state == FORWARD) [self selectStateButton:REV];
  398.         if (state == REVERSE) [self selectStateButton:FWD];
  399.         state *= -1;
  400.         currentFrameIndex += (int)state;
  401.         }
  402.       else
  403.         currentFrameIndex = next;
  404.       break;
  405.       };
  406.     };
  407.   [self display];
  408.   return self;
  409. }
  410.  
  411. /*****************************************************************
  412.  *****************************************************************/
  413.  
  414. - fwd:sender
  415. {
  416.  
  417.   if (numFrames < 2) return self;
  418.   if (state != STOPPED) [self stop:self];
  419.   state = FORWARD;
  420.   [self move:sender];
  421.   return self;
  422. }
  423.  
  424. - rev:sender
  425. {
  426.   if (numFrames < 2) return self;
  427.   if (state != STOPPED) [self stop:self];
  428.   state = REVERSE;
  429.   [self move:sender];
  430.   return self;
  431. }
  432.  
  433. - move:sender
  434. {
  435.   double period = 1.0/[fpsSlider floatValue];
  436.   if (numFrames < 2) { /* duh */
  437.     [self selectStateButton:STOP];
  438.     return self;
  439.     };
  440.   anim = [[Animator alloc] initChronon:period adaptation:0.05 /*?*/
  441.                            target:self action:@selector(tick:)
  442.                            autoStart:YES eventMask:0];
  443.   if (state == FORWARD) [self selectStateButton:FWD];
  444.   if (state == REVERSE) [self selectStateButton:REV];
  445.   [fNumText setStringValue:""];
  446.   [fNumSlider setEnabled:NO];
  447.   oldt=[anim getSyncTime];
  448.   return self;
  449. }
  450.  
  451. - stop:sender
  452. {
  453.  
  454.   if (numFrames < 2) return self;
  455.   switch(state) {
  456.     case FORWARD:
  457.     case REVERSE: [anim free]; anim=nil;
  458.     case STOPPED: break;
  459.     }
  460.   state = STOPPED;
  461.   [self selectStateButton:STOP];
  462.   [fNumText setIntValue:currentFrameIndex];
  463.   [fNumSlider setEnabled:YES];
  464.   [fNumSlider setIntValue:currentFrameIndex];
  465.   return self;
  466. }
  467.  
  468. - fwdStep:sender
  469. {
  470.   [self step:(int) FORWARD];
  471.   return self;
  472. }
  473.  
  474. - revStep:sender
  475. {
  476.   [self step:(int) REVERSE];
  477.   return self;
  478. }
  479.  
  480. - step:(int) direction
  481. {
  482.   if (numFrames < 2) return self;
  483.   if (state != STOPPED) [self stop:self];
  484.   if ((currentFrameIndex = (currentFrameIndex + direction) % numFrames) < 0)
  485.     currentFrameIndex = numFrames + currentFrameIndex;
  486.   [self selectStateButton:STOP];
  487.   [fNumText setIntValue:currentFrameIndex];
  488.   [fNumSlider setEnabled:YES];
  489.   [fNumSlider setIntValue:currentFrameIndex];
  490.   return [self display];
  491. }
  492.  
  493. - expand2x:sender
  494. {
  495.   NXRect r;
  496.   [self getBounds:&r];
  497.   r.size.width *= 2.0;
  498.   r.size.height *= 2.0;
  499.   [[self window] sizeWindow:r.size.width :r.size.height];
  500.   return self;
  501. }
  502.  
  503. - reduce50pct:sender
  504. {
  505.   NXRect r;
  506.   [self getBounds:&r];
  507.   r.size.width *= 0.5;
  508.   r.size.height *= 0.5;
  509.   [[self window] sizeWindow:r.size.width :r.size.height];
  510.   return self;
  511. }
  512.  
  513. - restore:sender
  514. {
  515.   [[self window] sizeWindow:maxw :maxh];
  516.   return self;
  517. }
  518.  
  519.  
  520. - modeButtonsChanged:sender
  521. {
  522.   mode = (runMode)[sender selectedRow];
  523.   return self;
  524. }
  525.  
  526. - fNumSliderChanged:sender
  527. {
  528.   if (currentFrameIndex == [sender intValue]) return self;
  529.   currentFrameIndex=[sender intValue];
  530.   [self stop:self];
  531.   [self display];
  532.   return self;
  533. }
  534.  
  535. - fpsSliderChanged:sender
  536. {
  537.   [fpsText setFloatValue:[sender floatValue]];
  538.   switch(state) {
  539.     case FORWARD: 
  540.     case REVERSE: {
  541.       double period = 1.0/[fpsSlider floatValue];
  542.       [anim free];
  543.       anim = [[Animator alloc] initChronon:period adaptation:0.05
  544.                                target:self action:@selector(tick:)
  545.                                autoStart:YES eventMask:0];
  546.       break;
  547.       };
  548.     case STOPPED: break;
  549.     };
  550.   return self;
  551. }
  552.  
  553. - pingButtonChanged:sender
  554. {
  555.   switch([sender selectedRow]) {
  556.     case 0: pingDuringDisplay=NO; break;
  557.     case 1: pingDuringDisplay=YES; break;
  558.     };
  559.   return self;
  560. }
  561.  
  562. - selectStateButton:(runState)b
  563. {
  564.   [stateButtons selectCellAt:0:((int)b)];
  565.   return self;
  566. }
  567.  
  568. - depthButtonsChanged:sender
  569. {
  570.   dmode=(depthMode)[sender selectedRow];
  571.   [anim stopEntry];
  572.   [self recache];
  573.   [self renderFrames];
  574.   [self display];
  575.   [anim resetRealTime];
  576.   [anim startEntry];
  577.   return self;
  578. }
  579.  
  580. - updateCheckBoxChanged:sender
  581. {
  582.   updateControls = !updateControls;
  583.   return self;
  584. }
  585.  
  586.  
  587. // Window's delegate methods
  588.  
  589. - windowWillClose:sender
  590. {
  591.   [panel close];
  592.   [self free];
  593.   return self;
  594. }
  595.  
  596. -windowDidMiniaturize:sender
  597. {
  598.   [panel orderOut:sender];
  599.   [anim stopEntry];
  600.   return self;
  601. }
  602.  
  603. -windowDidDeminiaturize:sender
  604. {
  605.   [panel orderFront:sender];
  606.   [anim resetRealTime];
  607.   [anim startEntry];
  608.   return self;
  609. }
  610. @end
  611.