#include #include #include #include #include #include #include #include #include #include #include #include #include #include "annotate.h" static int initialized = 0; static View* currentView = NULL; static View testView; static Annotation testAnnotation = { 200, 200, 300, 300 }; static int audioDevice = -1; Display* display; GC foregroundGC = 0; GC backgroundGC = 0; GC widgetGC = 0; #define JPEG_WIDGETS_ARRAY_SIZE 100 #define JPEG_WINDOWS_ARRAY_SIZE JPEG_WIDGETS_ARRAY_SIZE static Widget jpegWidgets[ JPEG_WIDGETS_ARRAY_SIZE ]; static Window jpegWindows[ JPEG_WINDOWS_ARRAY_SIZE ]; static int jpegWidgetCount = 0; static int jpegWindowCount = 0; static long MillisecondsPassed( struct timeval time1, struct timeval time2) /* Returns the difference in milleseconds between time1 and time2, */ { return( ( ( time2.tv_sec - time1.tv_sec) * 1000) + ( ( time2.tv_usec - time1.tv_usec ) / 1000 ) ); } ImageData* AnnotateOpenJFIF( char* fileName ) /* Opens a JFIF file and places info concerning it in an ImageData record. */ { ImageData* imageData; FILE* filePtr; jpheader jpegHeader; char* audioBuffer; int* frameOffsets; int qTableSize; char* qTable; int cImageSize; char* cImageData; XPlxCImage* cImage; imageData = ( ImageData * ) malloc( sizeof( ImageData ) ); if ( !imageData ) { printf( "Unable to allocate ImageData record\n" ); return NULL; } if ( !( filePtr = fopen( fileName, "r" ) ) ) { printf( "Unable to open %s for reading\n", fileName ); free( ( char* ) imageData ); return NULL; } fread( (char *) &jpegHeader, sizeof( jpheader ), 1, filePtr ); if ( jpegHeader.tracks > 0 ) { audioBuffer = (char *) malloc( jpegHeader.audioslice ); if ( !audioBuffer ) printf( "Unable to malloc audio buffer.\n" ); } frameOffsets = ( int * ) malloc( jpegHeader.frames * sizeof( int ) ); if ( !frameOffsets ) { printf( "Unable to allocate frame offset list\n" ); fclose( filePtr ); if( audioBuffer ) free( audioBuffer ); free( ( char * ) imageData ); return NULL; } fseek( filePtr, jpegHeader.indexbuf, 0 ); fread( frameOffsets, sizeof( int ), jpegHeader.frames, filePtr ); qTableSize = MakeQTables( jpegHeader.qfactor, ( unsigned char** ) &qTable ); cImageSize = jpegHeader.width * jpegHeader.height * 3 / 5; cImageData = ( char* ) malloc( cImageSize ); cImage = XPlxCreateCImage( display, cImageData, cImageSize, jpegHeader.width, jpegHeader.height ); imageData->filePtr = filePtr; imageData->jpegHeader = jpegHeader; imageData->audioBuffer = audioBuffer; imageData->playAudioBuffer = 0; imageData->frameOffsets = frameOffsets; imageData->currentFrame = 0; imageData->direction = 1; imageData->still = 0; imageData->loop = 0; imageData->bounce = 0; imageData->qTableSize = qTableSize; imageData->qTable = qTable; imageData->cImageSize = cImageSize; imageData->cImageData = cImageData; imageData->cImage = cImage; imageData->millisecondsPerFrame = (long int) ( 1000 / ( float ) ( jpegHeader.fps ) ); imageData->mask = 0; imageData->maskGC = 0; return imageData; } void AnnotateUpdateJPEGWidget( Widget jpegWidget, XPlxCImage* cImage ) /* Updates the JPEG image being displayed by widget. */ { XPlxCImage* jpegDataPtr; XtVaGetValues( jpegWidget, XtNjpegData, &jpegDataPtr, NULL ); memcpy( jpegDataPtr->data, cImage->data, cImage->size ); jpegDataPtr->size = cImage->size; jpegDataPtr->width = cImage->width; jpegDataPtr->height = cImage->height; XtVaSetValues( jpegWidget, XtNputNewImage, True, NULL ); } void AnnotateUpdateJPEGWindow( Window window, XPlxCImage* cImage ) /* Updates the JPEG image being displayed by an X window. */ { XPlxPutCImage( display, window, backgroundGC, cImage, 0, 0, cImage->width, cImage->height, 0, 0, cImage->width, cImage->height, 0 ); /* Just to be sure. */ AnnotateRender( display, window, foregroundGC ); } void AnnotateAttachCurrentViewAndWindow( Window window ) { XGCValues gcVals; ImageData* imageData; gcVals.foreground = XWhitePixel(display,DefaultScreen(display)); gcVals.background = 0; backgroundGC = XCreateGC( display, window, 0, &gcVals); foregroundGC = XCreateGC( display, window, 0 , &gcVals); XPlxVideoTag( display, window, foregroundGC, PLX_GRAPHICS_24 ); XPlxVideoTag( display, window, backgroundGC, PLX_VIDEO); XPlxPutTable( display, window, backgroundGC, currentView->imageData->qTable, currentView->imageData->qTableSize, 1 ); imageData = currentView->imageData; if ( !imageData->mask ) { imageData->mask = XCreatePixmap( display, window, imageData->jpegHeader.width, imageData->jpegHeader.height, 1 ); AnnotateRender( display, window, foregroundGC ); XPlxVideoTag( display, window, backgroundGC, PLX_VIDEO_OVR ); XSetClipMask( display, foregroundGC, imageData->mask ); } } void AnnotateAttachCurrentViewAndWidget( Widget widget ) { XPlxCImage* jpegData; XtVaSetValues( widget, XtNwidth, ( Dimension ) currentView->imageData->jpegHeader.width, XtNheight, ( Dimension ) currentView->imageData->jpegHeader.height, NULL ); XtVaGetValues( widget, XtNjpegData, &jpegData, NULL ); jpegData->width = currentView->imageData->jpegHeader.width; jpegData->height = currentView->imageData->jpegHeader.height; } View* AnnotateSetView( View* newView ) /* Sets a new view (image & annotations) to be displayed in attached JPEG widgets. The attached widgets are updated and notified to refresh the image and so, indirectly, the annotations as well. */ { View* oldView; int count; if ( !newView->imageData && !( newView->imageData = AnnotateOpenJFIF( newView->jfifFileName))) return oldView; oldView = currentView; currentView = newView; if ( currentView ) { for ( count = 0; count < jpegWindowCount; count++ ) AnnotateAttachCurrentViewAndWindow( jpegWindows[ count ] ); for ( count = 0; count < jpegWidgetCount; count++ ) AnnotateAttachCurrentViewAndWidget( jpegWidgets[ count]); } return oldView; } void AnnotateRender( Display* display, Drawable drawable, GC gc ) /* Renders the currentView's annotations using the X parameters above. */ { int count; Annotation* annotations; static char* labelString = "Hello"; if ( !currentView ) return; annotations = currentView->annotations; for ( count = 0; count < currentView->annotationCount; count++ ) { XDrawLine( display, drawable, gc, annotations[ count ].descriptionCoord.x, annotations[ count ].descriptionCoord.y, annotations[ count ].equipCoord.x, annotations[ count ].equipCoord.y ); XDrawString( display, drawable, gc, annotations[ count ].descriptionCoord.x, annotations[ count ].descriptionCoord.y, labelString, strlen( labelString ) ); } } static void AnnotateDrawMethod( Widget widget, XEvent* eventPtr, Region region ) /* Called by attached JPEG widgets when they update. Not efficient */ { AnnotateRender( display , XtWindow( widget ), widgetGC ); } int AnnotateAttachJPEGWidget( Widget jpegWidget ) /* Adds a JPEG widget to the list of widgets to be managed via the annotation routines. These widgets will be set up to display the current view. */ { XGCValues gcVals; XColor allocated; XColor exact; Colormap cmap; if ( jpegWidgetCount >= JPEG_WIDGETS_ARRAY_SIZE ) return 0; jpegWidgets[ jpegWidgetCount++ ] = jpegWidget; XtVaSetValues( jpegWidget, XtNdrawMethod, AnnotateDrawMethod, NULL ); if ( !widgetGC ) { XtVaGetValues( jpegWidget, XtNcolormap, &cmap, NULL ); XAllocNamedColor( display, cmap, "white", &allocated, &exact ); gcVals.foreground = allocated.pixel; widgetGC = XCreateGC( display, XtWindow( jpegWidget ), GCForeground, &gcVals ); } AnnotateAttachCurrentViewAndWidget( jpegWidget ); return 1; } int AnnotateDetachJPEGWidget( Widget jpegWidget ) /* Removes jpegWidget from the list of widgets being managed by the annotate routines. */ { int count; for ( count = 0 ; count < jpegWidgetCount; count++ ) if ( jpegWidgets[ count ] == jpegWidget ) break; if ( count < jpegWidgetCount ) { XtVaSetValues( jpegWidgets[ count ], XtNdrawMethod, NULL, NULL ); for ( ; count < jpegWidgetCount - 1; count++ ) jpegWidgets[ count ] = jpegWidgets[ count + 1 ]; jpegWidgetCount--; return 1; } else return 0; } int AnnotateAttachJPEGWindow( Window jpegWindow ) /* Adds an X window to the list of windows to be managed via the annotation routines. */ { XGCValues gcVals; XColor allocated; XColor exact; Colormap cmap; XWindowAttributes attributes; if ( jpegWindowCount >= JPEG_WINDOWS_ARRAY_SIZE ) return 0; jpegWindows[ jpegWindowCount++ ] = jpegWindow; if ( !foregroundGC ) { XGetWindowAttributes( display, jpegWindow, &attributes ); cmap = attributes.colormap; XAllocNamedColor( display, cmap, "white", &allocated, &exact ); gcVals.foreground = allocated.pixel; foregroundGC = XCreateGC( display, jpegWindow, GCForeground, &gcVals ); backgroundGC = XCreateGC( display, jpegWindow, GCBackground, &gcVals ); } if ( currentView ) AnnotateAttachCurrentViewAndWindow( jpegWindow ); return 1; } int AnnotateDetachJPEGWindow( Window jpegWindow ) /* Removes jpegWindow from the list of windows being managed by the annotate routines. */ { int count; for ( count = 0; count < jpegWindowCount; count++ ) if ( jpegWindows[ count ] == jpegWindow ) break; if ( count < jpegWindowCount ) { for ( ; count < jpegWindowCount - 1; count++ ) jpegWindows[ count ] = jpegWindows[ count ] +1; jpegWindowCount--; return 1; } else return 0; } int AnnotateLoadCurrentFrame( ImageData* imageData ) /* Reads info for the current frame into the ImageData audio & visual buffers. */ { unsigned char instruction; int frameSize; fseek( imageData->filePtr, imageData->frameOffsets[ imageData->currentFrame ], 0 ); fread( &instruction, 1, 1, imageData->filePtr ); if ( ( instruction & 0x00ff ) == LOAD_AUDIO_0 ) { if ( imageData->audioBuffer ) { fread( imageData->audioBuffer, 1, imageData->jpegHeader.audioslice, imageData->filePtr ); imageData->playAudioBuffer = 1; fread( &instruction, 1, 1, imageData->filePtr ); } else { printf( "Found audio frame in non-audio JPEG\n" ); return 0; } } if ( ( instruction & 0x00ff ) == LOAD_JPEG ) { fread( &frameSize, 4, 1, imageData->filePtr ); fread( imageData->cImageData, frameSize, 1, imageData->filePtr ); imageData->cImage->size = frameSize; } else { printf( "Bad instruction encountered in JPEG\n" ); return 0; } return 1; } void AnnotateShowCurrentFrame( ImageData* imageData ) /* Show the current frame of the current view in all windows and widgets. */ { int count; for ( count = 0; count < jpegWidgetCount; count++ ) AnnotateUpdateJPEGWidget( jpegWidgets[ count ],imageData->cImage); for ( count = 0; count < jpegWindowCount; count ++ ) AnnotateUpdateJPEGWindow( jpegWindows[ count ],imageData->cImage); } void AnnotatePlayCurrentFrame( ImageData* imageData ) /* Play the current frame of the current view in all windows and widgets. This includes audio. */ { if ( audioDevice > -1 && imageData->playAudioBuffer ) { write( audioDevice, imageData->audioBuffer, imageData->jpegHeader.audioslice ); imageData->playAudioBuffer = 0; } AnnotateShowCurrentFrame( imageData ); } void AnnotateCheckTiming() /* Check the timing of the current frame. Pause if we're early. */ { struct timeval currentTime; int nextFrameTime; long elapsedTime; ImageData* imageData; imageData = currentView->imageData; if ( imageData->currentFrame == 0 ) gettimeofday( &imageData->startTime, NULL ); gettimeofday( ¤tTime, NULL ); elapsedTime = MillisecondsPassed( imageData->startTime, currentTime ); nextFrameTime = imageData->millisecondsPerFrame * ( imageData->currentFrame + 1 ); if ( nextFrameTime > elapsedTime ) XPlxSleep( ( nextFrameTime - elapsedTime ) * 1000 ); /* else /* printf( "We missed a frame\n" ); */ } void AnnotatePlayNextFrame() /* Play the next frame of the current movie, using timing and looping. */ { ImageData* imageData; imageData = currentView->imageData; if ( AnnotateLoadCurrentFrame( imageData ) ) AnnotatePlayCurrentFrame( imageData ); imageData->currentFrame++; if ( imageData->currentFrame >= imageData->jpegHeader.frames - 1 ) imageData->currentFrame = 0; } void AnnotatePlay() /* Play the current movie in all windows and widgets. */ { while(1) { AnnotateCheckTiming(); AnnotatePlayNextFrame(); } } int AnnotateInitialize( Display* theDisplay, char* audioDeviceName ) /* Initialize the annotation playback system. */ { XGCValues gcVals; if ( !initialized ) { display = theDisplay; audioDevice = open( audioDeviceName, O_WRONLY, 0 ); initialized = 1; /* Testing stuff */ testView.jfifFileName = "/export/home/scott/movie/plx_20_150.jpg"; testView.annotations = &testAnnotation; testView.annotationCount = 1; testView.imageData = NULL; AnnotateSetView( &testView ); } return initialized; }