; docformat = 'rst' ;+ ; Simple image viewing/processing application. This example illustrates: ; ; * using a single event handler for all events ; * resizing ; * using a pixmap ; * using a scrolling draw widget ; ; To simplify the application, only indexed color images are used. ;- ;+ ; Loads the current image into the pixmap. If the current image is a new image, ; use the NEW keyword to create a new pixmap (of a possibly different size). ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; ; :Keywords: ; new : in, optional, type=boolean ; set to create a new pixmap (of a possibly different size) ;- pro mg_ximage_loadimage, pstate, new=new compile_opt strictarr ; setup pixmap to be drawn to if (keyword_set(new)) then begin (*pstate).imageDims = size(*(*pstate).image, /dimensions) if ((*pstate).pixmapId gt 0L) then wdelete, (*pstate).pixmapId window, /free, /pixmap, $ xsize=(*pstate).imageDims[0], ysize=(*pstate).imageDims[1] (*pstate).pixmapId = !d.window endif else begin wset, (*pstate).pixmapId endelse ; draw to the pixmap tvscl, *(*pstate).image end ;+ ; Refresh the graphics display. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ;- pro mg_ximage_refreshgraphics, pstate compile_opt strictarr if (n_elements((*pstate).image) eq 0L) then return wset, (*pstate).winId ; [src_x, src_y, cols, rows, dest_x, dest_y, src_id] device, copy=[(*pstate).position, (*pstate).screenDims, 0L, 0L, (*pstate).pixmapId] end ;+ ; Set the status message. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; msg : in, required, type=string ; message to use as status ;- pro mg_ximage_setstatus, pstate, msg compile_opt strictarr status = widget_info((*pstate).tlb, find_by_uname='status') widget_control, status, set_value=msg end ;+ ; Resize the widget program. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; x : in, required, type=long ; new xsize of tlb ; y : in, required, type=long ; new ysize of tlb ;- pro mg_ximage_resize, pstate, x, y compile_opt strictarr ; find widget identifiers for the widgets we need to resize status = widget_info((*pstate).tlb, find_by_uname='status') draw = widget_info((*pstate).tlb, find_by_uname='draw') ; calculate new sizes based on the difference to the old size (*pstate).screenDims += ([x, y] - (*pstate).tlbSize) (*pstate).screenDims <;= (*pstate).imageDims ; set the new sizes widget_control, draw, $ scr_xsize=(*pstate).screenDims[0], $ scr_ysize=(*pstate).screenDims[1] widget_control, status, scr_xsize=(*pstate).screenDims[0] tlbG = widget_info((*pstate).tlb, /geometry) (*pstate).tlbSize = [tlbG.scr_xsize, tlbG.scr_ysize] ; make sure to refresh the graphics mg_ximage_refreshgraphics, pstate end ;+ ; Query the user for a file to open and open it. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ;- pro mg_ximage_open, pstate compile_opt strictarr filename = dialog_pickfile(dialog_parent=(*pstate).tlb, $ path=(*pstate).lastDirectory, $ get_path=path) if (filename eq '') then return (*pstate).lastDirectory = path mg_ximage_openfile, pstate, filename end ;+ ; Open a specified image file. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; filename : in, required, type=string ; filename of image file to open ;- pro mg_ximage_openfile, pstate, filename compile_opt strictarr im = read_image(filename) *(*pstate).image = im *(*pstate).originalImage = im mg_ximage_loadimage, pstate, /new mg_ximage_refreshgraphics, pstate mg_ximage_setstatus, pstate, string(filename, format='(%"Opened %s.")') end ;+ ; Query user for location to save image and save it. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ;- pro mg_ximage_save, pstate compile_opt strictarr saved = dialog_write_image(*(*pstate).image, $ dialog_parent=(*pstate).tlb, $ path=(*pstate).lastDirectory, $ options=options) if (~saved) then return (*pstate).lastDirectory = file_dirname(options.filename) mg_ximage_setstatus, pstate, string(options.filename, format='(%"Saved %s.")') end ;+ ; Perform an image processing operation on the image. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; uname : in, required, type=string ; name of operation to perform ;- pro mg_ximage_dooperation, pstate, uname compile_opt strictarr case uname of 'sobel': *(*pstate).image = sobel(*(*pstate).image) 'roberts': *(*pstate).image = roberts(*(*pstate).image) 'smooth': *(*pstate).image = smooth(*(*pstate).image, 5, /edge_truncate) 'revert': *(*pstate).image = *(*pstate).originalImage endcase mg_ximage_loadimage, pstate mg_ximage_refreshgraphics, pstate mg_ximage_setstatus, pstate, string(uname, format='(%"Performed %s operation.")') end ;+ ; Hande draw widget events. ; ; :Params: ; pstate : in, required, type=pointer ; pointer to state structure ; event : in, required, type=structure ; DRAW_WIDGET event structure ;- pro mg_ximage_drawevent, pstate, event compile_opt strictarr case event.type of 3: begin ; viewport moved (*pstate).position = [event.x, event.y] mg_ximage_refreshgraphics, pstate end 4: begin ; expose mg_ximage_refreshgraphics, pstate end else: ; other types shouldn't occur since we didn't set them when creating ; the WIDGET_DRAW endcase end ;+ ; Event handler for MG_XIMAGE. ; ; :Params: ; event : in, required, type=structure ; any event generated by the program ;- pro mg_ximage_event, event compile_opt strictarr widget_control, event.top, get_uvalue=pstate uname = widget_info(event.id, /uname) case uname of 'tlb': mg_ximage_resize, pstate, event.x, event.y 'open': mg_ximage_open, pstate 'save': mg_ximage_save, pstate 'exit': widget_control, event.top, /destroy 'sobel': mg_ximage_dooperation, pstate, uname 'roberts': mg_ximage_dooperation, pstate, uname 'smooth': mg_ximage_dooperation, pstate, uname 'revert': mg_ximage_dooperation, pstate, uname 'draw': mg_ximage_drawevent, pstate, event endcase end ;+ ; Free resources of the widget program. ; ; :Params: ; tlb : in, required, type=long ; widget identifier of the top-level base ;- pro mg_ximage_cleanup, tlb compile_opt strictarr widget_control, tlb, get_uvalue=pstate wdelete, (*pstate).pixmapId ptr_free, (*pstate).originalImage, (*pstate).image, pstate end ;+ ; Simple program to view and do some basic image processing operations on an ; image. ; ; :Params: ; im : in, optional, type=2D image array ; image to show and manipulate ;- pro mg_ximage, im compile_opt strictarr screenDims = [400, 400] imageDims = n_elements(im) gt 0L ? size(im, /dimensions) : [400, 400] ; create widget hierarchy tlb = widget_base(title='Image processing application', /column, $ mbar=menubar, uname='tlb', $ /tlb_size_events) fileMenu = widget_button(menubar, value='File', /menu) openItem = widget_button(filemenu, value='Open...', uname='open') saveItem = widget_button(filemenu, value='Save...', uname='save') exitItem = widget_button(filemenu, value='Exit', uname='exit', /separator) operationsMenu = widget_button(menubar, value='Operations', /menu) sobelItem = widget_button(operationsMenu, value='Sobel', uname='sobel') robertsItem = widget_button(operationsMenu, value='Roberts', uname='roberts') smoothItem = widget_button(operationsMenu, value='Smooth', uname='smooth') revertItem = widget_button(operationsMenu, value='Revert to original', $ uname='revert', /separator) draw = widget_draw(tlb, uname='draw', $ xsize=imageDims[0], ysize=imageDims[1], $ /app_scroll, retain=0, $ x_scroll_size=screenDims[0], y_scroll_size=screenDims[1], $ /viewport_events, /expose_events) status = widget_label(tlb, value='Ready.', scr_xsize=screenDims[0], $ uname='status', /align_left) ; realize the hierarchy widget_control, tlb, /realize widget_control, draw, get_value=winId ; save the current size of the realized geometry to make resizing easier tlbG = widget_info(tlb, /geometry) ; set up data for event handlers state = { tlb: tlb, $ originalImage: ptr_new(im), $ tlbSize: [tlbG.scr_xsize, tlbG.scr_ysize], $ screenDims: screenDims, $ imageDims: imageDims, $ position: lonarr(2), $ image: ptr_new(im), $ winId: winId, $ pixmapId: -1L, $ lastDirectory: filepath('', subdir=['examples', 'data']) $ } pstate = ptr_new(state, /no_copy) widget_control, tlb, set_uvalue=pstate ; draw graphics mg_ximage_loadimage, pstate, /new mg_ximage_refreshgraphics, pstate ; set scrollbars appropriately for viewing the lower left of the image widget_control, draw, set_draw_view=(*pstate).position ; start XMANAGER xmanager, 'mg_ximage', tlb, /no_block, $ event_handler='mg_ximage_event', cleanup='mg_ximage_cleanup' end