; $Id: xia.pro,v 2.16 2007/08/20 20:05:07 bdavis Exp $ ; ; Copyright (c) 1992-1999, Research Systems, Inc. All rights reserved. ; Unauthorized reproduction prohibited. ;+ ; NAME: ; XIA ; ; PURPOSE: ; Display an animated sequence of images using X-windows Pixmaps. ; The speed and direction of the display can be adjusted using ; the widget interface. ; ; CATEGORY: ; Animation, Image display, widgets. ; ; CALLING SEQUENCE: ; To initialize: ; XIA, SET = [Sizex, Sizey, Nframes] ; ; To load a single image: ; XIA, IMAGE = Image, FRAME = Frame_Index ; ; To load a single image that is already displayed in an existing window: ; XIA, FRAME = Frame_index, $ ; WINDOW = [Window_Number [, X0, Y0, Sx, Sy]] ; (This technique is much faster than reading back from the window.) ; ; To display the animation after all the images have been loaded: ; XIA [, Rate] ; ; To close and deallocate the pixmap/buffer (which also takes place ; automatically when the user presses the "Done With Animation" ; button or closes the window with the window manager): ; XIA, /CLOSE ; ; OPTIONAL INPUTS: ; Rate: A value between 0 and 100 that represents the speed of the ; animation as a percentage of the maximum display rate. ; The fastest animation is with a value of 100 and the slowest ; is with a value of 0. The default animation rate is 100. ; The animation must be initialized using the SET ; keyword before calling XIA with a rate value. ; ; KEYWORD PARAMETERS: ; BOTTOM: If set, window will be at bottom of screen or parent window ; CLOSE: Set this keyword to delete the offscreen pixwins and Widget, ; freeing storage. ; ; CYCLE: If set, cycle. Normally, frames are displayed going either ; forward or backwards. If CYCLE is set, reverse direction ; after the last frame in either direction is displayed. ; Provide this keyword with the SET keyword. ; ; FRAME: The frame number when loading frames. This keyword only has ; an effect when used in conjunction with the SET keyword. ; FRAME must be set to a number in the range 0 to Nframes-1. ; ; GROUP: The widget ID of the widget that calls XIA. When ; this ID is specified, the death of the caller results in the ; death of XIA. ; ; IMAGE: A single image to be loaded at the animation position given ; by FRAME. The keyword parameter FRAME must also be specified. ; ; KEEP_PIXMAPS: If TRUE, XIA doesn't destroy the animation ; pixmaps when it is killed. Calling it again without ; going through the SET and LOAD steps will cause the same ; animation to play without the overhead of creating ; the pixmaps. ; BLOCK: Set this keyword to have XMANAGER block when this ; application is registered. By default the Xmanager ; keyword NO_BLOCK is set to 1 to provide access to the ; command line if active command line processing is available. ; Note that setting BLOCK for this application will cause ; all widget applications to block, not only this ; application. For more information see the NO_BLOCK keyword ; to XMANAGER. ; ORDER: Set this keyword to display images from the top down instead ; of the default bottom up. This keyword is only used when ; loading images. ; MODAL: If set, then XIA runs in "modal" mode, meaning that ; all other widgets are blocked until the user quits ; XIA. ; MPEG_OPEN: Set this keyword to open an MPEG file. ; MPEG_FILENAME: Set this keyword equal to a string for the desired ; MPEG filename. If not set, idl.mpg is used. ; MPEG_CLOSE: Set this keyword to write the MPEG file. ; NREPS - number of frames to repeat when creating mpeg (1 means no dups) ; SHOWLOAD: Set this keyword (in conjunction with the SET keyword) to ; display each frame and update the frame slider as frames are ; loaded. ; ; SET: This keyword initializes XIA. SET should be equated ; to a 3-element integer vector containing the following ; parameters: ; Sizex, Sizey: The width and height of the images to be ; displayed, in pixels. ; ; Nframes: The number of frames in the animated sequence ; (since XIA is an animation routine, ; Nframes must be at least 2 frames). ; ; TITLE: A string to be used as the title of the widget. If this ; keyword is not specified, the title is set to "XIA" ; This keyword has an effect only when used in conjunction with ; the SET keyword). ; ; TRACK: If set, the frame slider tracks the current frame. Default ; is not to track. Provide this keyword with the SET keyword. ; ; WINDOW: When this keyword is specified, an image is copied from an ; existing window to the animation pixmap. When using X ; windows, this technique is much faster than reading ; from the display and then calling XIA with a 2D ; array. ; ; The value of this parameter is either an IDL window ; number (in which case the entire window is copied), ; or a vector containing the window index and the rectangular ; bounds of the area to be copied, for example: ; WINDOW = [Window_Number, X0, Y0, Sx, Sy] ; ; XOFFSET: The horizontal offset, in pixels from the left of the frame, ; of the image in the destination window. ; ; YOFFSET: The vertical offset, in pixels from the bottom of the frame, ; of the image in the destination window. ; ; OUTPUTS: ; No explicit outputs. ; ; COMMON BLOCKS: ; XIA_COM: a private common block. ; ; SIDE EFFECTS: ; A pixmap and widget are created. ; ; RESTRICTIONS: ; Only a single copy of XIA can run at a time. ; ; PROCEDURE: ; When initialized, this procedure creates an approximately square ; pixmap or memory buffer, large enough to contain Nframes of ; the requested size. Once the images are loaded, using the ; IMAGE and FRAME keywords, they are displayed by copying the images ; from the pixmap or buffer to the visible draw widget. ; ; EXAMPLE: ; Enter the following commands to open the file ABNORM.DAT (a series ; of images of a human heart) and animate the images it contains using ; XIA. For a more detailed example of using XIA, ; see the example in the "Using IDL Widgets" chapter of "IDL Basics". ; Read the images into the variable H by entering: ; ; OPENR, 1, FILEPATH('abnorm.dat', SUBDIR = 'examples/data') ; H = BYTARR(64, 64, 16) ; READU, 1, H ; CLOSE, 1 ; H = REBIN(H, 128, 128, 16) ; ; Initailize XIA with the command: ; ; XIA, SET=[128, 128, 16], /SHOWLOAD ; ; Load the images into XIA and play the animation by entering: ; ; FOR I=0,15 DO XIA, FRAME = I, IMAGE = H[*,*,I] ; XIA ; ; MODIFICATION HISTORY: ; 02-May-06 added nreps keyword ; 14-Apr-06 added xpos & ypos, bottom & right keywords ; 02-Aug-01 modified XInterAnimate to have printing [Bill Davis] ;-------------------------------------------------------------------------------- PRO xintanim_kill_pix ; If there are pixmaps currently open, free them. COMPILE_OPT hidden COMMON XIA_com, topbase, animatebase, pwin, mpegID, nreps i = size(pwin) if (i[0] ne 0) then begin ; Not scalar, so contains valid IDs i = i[i[0] + 2] ; # of elements in pwin FOR j=0, i-1 DO IF pwin[j] GE 0 THEN WDELETE, pwin[j] ;Delete the windows pwin = -1 ;Show nothing there by setting to scalar value endif end ;-------------------------------------------------------------------------------- PRO xintanim_event, ev COMPILE_OPT hidden COMMON xia_local, printer ; get the information structure out of the widget base WIDGET_CONTROL, ev.id, GET_UVALUE=UVALUE ;;;PRINT, ' >>In xintanim_event; UVALUE=', UVALUE if SIZE( UVALUE, /TYPE ) LT 7 then UVALUE='Done' CASE UVALUE of 'Done': widget_control, /destroy, ev.top 'print_bitmap': BEGIN ; this printing is much faster, but low-res filename = '~/IDLbitmap.ps' ; will leave a file with this name if !VERSION.OS EQ "vms" then filename = 'sys$login:IDLbitmap.ps' ;;; print,' (printing a bitmap; this may take a while)' IF ( (!D.NAME EQ 'X') OR (!D.NAME EQ 'MAC') ) THEN WIDGET_CONTROL,/hourglass screendump, filename spawnprint, filename, printer=printer END 'print_rightAspect': BEGIN ; this printing is much faster, but low-res filename = '~/IDLbitmap.ps' ; will leave a file with this name ;;; print,' (printing a bitmap; this may take a while)' IF ( (!D.NAME EQ 'X') OR (!D.NAME EQ 'MAC') ) THEN WIDGET_CONTROL,/hourglass screendump, filename, /Right_Aspect spawnprint, filename, printer=printer END 'Printer_Select': BEGIN wholeStr = ev.value wordarray, wholeStr, words printer = STRTRIM(words[0],2) ;;; if STRPOS( printer, '\') EQ 1 THEN $ ; strip menu indicator ;;; printer = STRMID(printer, 2, 100) END 'saveEPS': BEGIN ; (THIS IS UNTESTED) fileName = DIALOG_PICKFILE( TITLE='Save to an EPS file', $ FILTER='*.epsi;*.eps', /WRITE) IF fileName NE '' THEN BEGIN Device, Get_Visual_Depth=thisDepth IF thisDepth GT 8 THEN BEGIN Device, Decomposed=0 truecolor = 1 ENDIF ELSE truecolor = 0 screenDump = TVRD(true = truecolor) s = [2, !D.X_size, !D.Y_size] ; Set the plotting device to PostScript: SET_PLOT, 'ps' ; Use the DEVICE procedure to make the output encapsulated, ; 8 bits, color, and only as wide and high as it needs to ; be to contain the XWD image: DEVICE, /ENCAPSUL, BITS_PER_PIXEL=8, /COLOR, $ FILENAME=filename, XSIZE=S[1]/1000., $ YSIZE=S[2]/1000., /PREVIEW ; preview will let you see image arrayInfo=SIZE(screenDump) if arrayInfo[0] eq 2 then begin xSize = arrayInfo[1] ySize = arrayInfo[2] endif else begin xSize = arrayInfo[2] ySize = arrayInfo[3] endelse IF Size(screenDump, /N_Dimensions) EQ 2 THEN $ TV, screenDump, XSize=xsize, YSize=ysize ELSE $ TV, screenDump, XSize=xsize, YSize=ysize, TRUE=1 ; Close the file: DEVICE, /CLOSE ; Return plotting to X Windows: SET_PLOT, 'x' ENDIF END 'savegif': BEGIN fileName = DIALOG_PICKFILE( TITLE='Save to a GIF file', $ FILTER='*.gif', /WRITE ) IF fileName NE '' THEN BEGIN TVLCT, r, g, b, /get ; so colors aren't expanded to 256 WRITE_GIF, filename, TVRD(), r, g, b ENDIF END 'saveJPEG': BEGIN fileName = DIALOG_PICKFILE( TITLE='Save to a JPEG file', $ FILTER='*.jpg', /WRITE) IF fileName NE '' THEN BEGIN MK_JPEG, filename=filename ENDIF END 'screenDump': BEGIN if nwords( printer ) GT 0 then begin IF STRUPCASE( printer ) EQ 'POSTSCRIPT' THEN BEGIN SPAWN,'/bin/rm ~/mptsanim.ps' SPAWN,'xwd -name "IDL Animation Widget" | xwdtopnm | pnmtops > ~/mptsanim.ps' dum = DIALOG_MESSAGE( 'Postscript file ~/mptsanim.ps saved', /INFO ) ENDIF ELSE BEGIN SPAWN,'xwd -name "IDL Animation Widget" | lpr -P'+STRTRIM(printer,2) ENDELSE endif else begin SPAWN,'xwd -name "IDL Animation Widget" | lpr' endelse END ENDCASE RETURN END ;------------------------------------------------------------------------- PRO XIA, RATE, SET = SET, IMAGE = IMAGE, FRAME = FRAME, $ ORDER = ORDER, CLOSE = CLOSE, $ SHOWLOAD = SHOWLOAD, GROUP = GROUP, WINDOW = WINDOW, $ XOFFSET = XOFFSET, YOFFSET = YOFFSET, KEEP_PIXMAPS=KEEP_PIXMAPS, $ CYCLE = cycle, TRACK = track, MPEG_OPEN = mpegOpen, $ MPEG_CLOSE = mpegClose, MPEG_FILENAME = mpegFilename, BLOCK=block, $ bottom=bottom, right=right, nreps_in=nreps_in, $ xpos=xpos, ypos=ypos, MODAL = modal COMMON XIA_com, topbase, animatebase, pwin, mpegID, nreps IF N_ELEMENTS(quality) EQ 0 THEN quality=100 ; max IF N_ELEMENTS(block) EQ 0 THEN block=0 ;--------------------- MPEG Write Portion of XIA -------------------- if (KEYWORD_SET(mpegClose)) then begin ; let user know about demo mode limitation. ; mpeg object is disabled in demo mode if (LMGR(/DEMO)) then begin MESSAGE, 'MPEG_CLOSE: Feature disabled for demo mode.' return endif if (OBJ_VALID(mpegID)) then $ MPEG_SAVE, mpegID endif if (KEYWORD_SET(mpegOpen)) then begin ; let user know about demo mode limitation. ; mpeg object is disabled in demo mode if (LMGR(/DEMO)) then begin MESSAGE, 'MPEG_OPEN: Feature disabled for demo mode.' return endif ;;; iframe_gap = getenv('IDL_IFRAME_GAP') ;;; if nwords( iframe_gap ) gt 0 then begin ;;; mpegID = OBJ_NEW('IDLgrMPEG', FILENAME = mpegFilename, $ ;;; IFRAME_GAP=long(iframe_gap) ) ;;; print, 'Using IFRAME_GAP of ', IFRAME_GAP ;;; endif else begin ;;; mpegID = OBJ_NEW('IDLgrMPEG', FILENAME = mpegFilename) ;;; endelse ;; the object will default the filename if passed as undefined mpegID = OBJ_NEW('IDLgrMPEG', FILENAME = mpegFilename) if keyword_set( quality ) then $ mpegID -> IDLgrMPEG::SetProperty, quality=quality ;;;print, " MPEG Quality set to ", quality endif ;--------------------- CLOSE Portion of XIA ------------------------- IF KEYWORD_SET(CLOSE) THEN BEGIN ;; Get rid of the MPEG object if it exists if (OBJ_VALID(mpegID)) then $ MPEG_CLOSE, mpegID if (widget_info(topbase, /valid)) THEN $ WIDGET_CONTROL, topbase, /DESTROY xintanim_kill_pix nreps=1 RETURN ENDIF ;Don't allow two copies of xinternimate to run at once IF xregistered("XIA") THEN begin tmp = dialog_message( /error, [ 'Error in XIA:', ' ', $ 'Only one animation at a time is allowed.' ]) return endif ;---------------------- SET Portion of XIA ------------------------ IF KEYWORD_SET(SET) THEN BEGIN ;This is the first call to XIA. Here the pixmap is ; created and the widgets are initialized. xintanim_kill_pix ;If old pixmap exists, delete IF N_ELEMENTS(nreps_in) gt 0 THEN begin nreps = nreps_in > 1 endif else begin if n_elements( nreps ) eq 0 then nreps=1 ; # of repitions of a frame endelse ;;; IF NOT(KEYWORD_SET(TITLE)) THEN TITLE = "IDL Animation Widget" TITLE = "IDL Animation Widget" if KEYWORD_SET(MODAL) AND N_ELEMENTS(GROUP) GT 0 then begin topbase = WIDGET_BASE(TITLE = TITLE, $ GROUP_LEADER = GROUP, /MODAL, MBAR=wMBarBase, $ xoffset=xpos, yoffset=ypos, /align_right ) endif else begin topbase = WIDGET_BASE(TITLE = TITLE, /COLUMN, MBAR=wMBarBase, $ xoffset=xpos, yoffset=ypos, /align_right ) endelse ; create the file pull-down menu wMenu = WIDGET_BUTTON( wMBarBase, VALUE='File', /MENU) ;;; bmid5 = WIDGET_BUTTON( wMenu, VALUE='Print', $ ;;; UVALUE = 'print_plot' ) dum = WIDGET_BUTTON(wMenu, VALUE="PRINT Bitmap of Graphics", $ UVALUE="print_bitmap") dum = WIDGET_BUTTON(wMenu, VALUE="PRINT Bitmap with Window's Aspect Ratio", $ UVALUE="print_rightAspect") ;;; IF !Version.OS_Family EQ 'unix' THEN $ ;;; bmid5 = WIDGET_BUTTON( wMenu, VALUE='Dump this Window to Printer', $ ;;; UVALUE = 'screenDump' ) Printer_List = PPPL_Printers( ) printer = Printer_List(0) printer_pdmenu = MK_PDMENU( Printer_List ) ; make a submenu printer_pdmenu(0).flags = 0 tempID =WIDGET_BUTTON( wMenu, VALUE='Printer', /MENU) wPrintSelect = CW_PDMENU( tempID, printer_pdmenu, $ UVALUE='Printer_Select',/RETURN_NAME, /MBAR) dum = WIDGET_BUTTON( wMenu, VALUE="Save Image as GIF File...", $ UVALUE="savegif") dum = WIDGET_BUTTON( wMenu, VALUE="Save Image as JPEG File...", $ UVALUE="saveJPEG") dum = WIDGET_BUTTON( wMenu, VALUE="Save Image as EPS File...", $ UVALUE="saveEPS") wQuit = WIDGET_BUTTON( wMenu, VALUE='Dismiss', UVALUE='Done') ; Setting the managed attribute indicates our intention to put this app ; under the control of XMANAGER, and prevents our draw widgets from ; becoming candidates for becoming the default window on WSET, -1. XMANAGER ; sets this, but doing it here prevents our own WSETs at startup from ; having that problem. Don't do this if SHOWLOAD is to be used because ; we want it to grab the window then. IF not KEYWORD_SET(SHOWLOAD) THEN WIDGET_CONTROL, /MANAGED, topbase animatebase = CW_ANIMATE(topbase, set[0], set[1], set[2]*nreps, $ TRACK = KEYWORD_SET(track), CYCLE=KEYWORD_SET(cycle)) if keyword_set( bottom ) then begin widget_bottom, topbase, yborder=60 endif ; If the SHOWLOAD keyword is set, realize things now so the load is seen IF KEYWORD_SET(SHOWLOAD) THEN $ WIDGET_CONTROL, topbase, /REALIZE, /HOURGLASS RETURN ENDIF ;----------------- IMAGE Loading Portion of XIA -------------------- nwindow = N_ELEMENTS(WINDOW) nimage = N_ELEMENTS(image) if (nwindow gt 0) or (nimage gt 0) then begin old_window = !D.WINDOW ;Save old window ; Make sure a widget has been created before trying to load it. if (not widget_info(topbase, /valid)) then MESSAGE, 'Not initialized' IF (N_ELEMENTS(YOFFSET) EQ 0) THEN YOFFSET = 0 IF (N_ELEMENTS(XOFFSET) EQ 0) THEN XOFFSET = 0 if (N_ELEMENTS(WINDOW) gt 0) then begin CW_ANIMATE_LOAD, animatebase, frame=frame, window=window, $ XOFFSET = XOFFSET, YOFFSET = YOFFSET ;; Write to MPEG if needed if (OBJ_VALID(mpegID)) then $ MPEG_PUT, mpegID, FRAME=frame, WINDOW=window endif else begin IF (N_ELEMENTS(ORDER) EQ 0) THEN ORDER = 0 ; loop added to repeat frames [BD] print, ' nreps = ', nreps for n = 0, nreps-1 do begin ifr = frame*nreps + n CW_ANIMATE_LOAD, animatebase, frame=ifr, image=image, $ XOFFSET = XOFFSET, YOFFSET = YOFFSET, ORDER = ORDER ;; Write to MPEG if needed if (OBJ_VALID(mpegID)) then $ MPEG_PUT, mpegID, FRAME=ifr, IMAGE=image, ORDER=order endfor endelse WSET, old_window RETURN ENDIF ;--------------- Register and Run Portion of XIA ------------------- ; If the base is not valid, it means that we have skipped ; calling this routine with the SET keyword. In this case, we can restart ; the last animation if the KEEP_PIXMAP keyword was used to preserve them ; in the previous call. if (not widget_info(topbase, /valid)) then begin s = size(pwin) if (s[0] eq 0) then message, 'No image frames loaded' ; Scan the pixmaps to figure out the image size n = s[s[0] + 2] xs = 0 for i = 0, n-1 do begin if pwin[i] ne -1 then begin old_window = !D.WINDOW wset, pwin[i] xs = !d.x_vsize ys = !d.y_vsize WSET, old_window goto, found ; Like a "break" in C endif endfor found: if (xs ne 0) then begin IF NOT(KEYWORD_SET(TITLE)) THEN TITLE = 'XIA' topbase = WIDGET_BASE(TITLE = TITLE) animatebase = CW_ANIMATE(topbase, xs, ys, n, PIXMAPS=pwin) ; The pixmaps are no longer our responsibility. They will get destroyed ; by CW_ANIMATE as usual unless this invocation of XIA ; specifies the KEEP_PIXMAP keyword. pwin = 0 ; Indicate that we aren't saving them anymore. endif else message, 'No image frames loaded' endif ; At this point, the application must be realized if it isn't already if (not widget_info(topbase, /realized)) then $ WIDGET_CONTROL, topbase, /REALIZE ; Save the pixmaps for later restart if keyword_set(keep_pixmaps) then CW_ANIMATE_GETP, animatebase, pwin if N_ELEMENTS(RATE) EQ 0 THEN RATE = 30 cw_animate_run, animatebase, rate Xmanager, "XIA", topbase, EVENT_HANDLER = "xintanim_event", $ GROUP_LEADER = GROUP, NO_BLOCK=(NOT(FLOAT(block))) END