INTERNAL: Displaying large images using a scrollable draw widget with APP_SCROLL set.
Anonym
Topic:
This tech tip examines using a scrollable draw widget with the APP_SCROLL keyword set to render only the necessary portion of an image.Discussion:
Currently, the TV routiune is unable to render images with a dimension greater than 31999:
IDL> tv, dist(32000,300)
TV: Width and Height must be less than 32000.
Execution halted at: $MAIN$
To get around this, a scrollable draw widget with APP_SCROLL set can be used to render only the currently portion of the image in the draw widget. The scroll events are used to determine which portion of the image to display. The following example demonstrates this work-around:Solution:
;
; Name - DISPLAY_LARGE_IMAGE
;
; Calling Syntax - DISPLAY_LARGE_IMAGE, image [,TITLE=string]
; [,XSIZE=value] [,YSIZE=value]
;
; Purpose - For displaying large images.
;
; Modification History - January 2006, Written, Daryl Atencio
;
;----------------------------------------------------------------------
;---------------------------------------------
; DISPLAY_LARGE_IMAGE_CLEANUP
;+
;
; Frees up any heap memory used by the application
;
;-
pro display_large_image_cleanup, tlb
widget_control, tlb, GET_UVALUE = pState
ptr_free, (*pState).pImage
ptr_free, pState
end
;---------------------------------------------
; DISPLAY_LARGE_IMAGE_EVENT
;+
;
; Main event handler
;
;-
pro display_large_image_event, event
widget_control, event.top, GET_UVALUE = pState
case tag_names(event, /STRUCT) of
'WIDGET_BASE': begin
geom = widget_info(event.top, /GEOMETRY)
delta = [geom.xsize,geom.ysize]-(*pState).tlbDims
(*pState).tlbDims = [geom.xsize,geom.ysize]
reCalc = 0B
geom = widget_info((*pState).draw, /GEOMETRY)
newXsize = geom.xsize+delta[0]
if newXsize gt geom.draw_xsize then begin
newXsize = geom.draw_xsize
reCalc = 1B
endif
newYsize = geom.ysize+delta[1]
if newYsize gt geom.draw_ysize then begin
newYsize = geom.draw_ysize
reCalc = 1B
endif
widget_control, (*pState).draw, XSIZE = newXsize, $
YSIZE = newYsize
if reCalc then begin
geom = widget_info(event.top, /GEOMETRY)
(*pState).tlbDims = [geom.xsize,geom.ysize]
endif
end
'WIDGET_DRAW': begin
case event.type of
3: begin
; Scroll bar event
(*pState).xy = [event.x,event.y]
update_display, pState
end
4: update_display, pState ; Expose event
else:
endcase
end
else: ; Do nothing
endcase
end
;---------------------------------------------
; UPDATE_DISPLAY
;+
;
; Routine for updating the draw widget window.
;
;-
pro update_display, pState
geom = widget_info((*pState).draw, /GEOMETRY)
dx = ((*pState).xy[0]+geom.xsize - geom.draw_xsize) > 0
dy = ((*pState).xy[1]+geom.ysize - geom.draw_ysize) > 0
wset, (*pState).winID
case (*pState).trueFlag of
0: tv, (*(*pState).pImage)[((*pState).xy[0]-dx) > 0 : $
((*pState).xy[0]+geom.xsize-dx-1) < geom.draw_xsize, $
((*pState).xy[1]-dy) > 0 : $
((*pState).xy[1]+geom.ysize-dy-1) < geom.draw_ysize]
1: tv, (*(*pState).pImage)[*,((*pState).xy[0]-dx) > 0 : $
((*pState).xy[0]+geom.xsize-dx-1) < geom.draw_xsize, $
((*pState).xy[1]-dy) > 0 : $
((*pState).xy[1]+geom.ysize-dy-1) < geom.draw_ysize], $
TRUE = 1
2: tv, (*(*pState).pImage)[((*pState).xy[0]-dx) > 0 : $
((*pState).xy[0]+geom.xsize-dx-1) < geom.draw_xsize, $
*, $
((*pState).xy[1]-dy) > 0 : $
((*pState).xy[1]+geom.ysize-dy-1) < geom.draw_ysize], $
TRUE = 2
3: tv, (*(*pState).pImage)[((*pState).xy[0]-dx) > 0 : $
((*pState).xy[0]+geom.xsize-dx-1) < geom.draw_xsize, $
((*pState).xy[1]-dy) > 0 : $
((*pState).xy[1]+geom.ysize-dy-1) < geom.draw_ysize,*], $
TRUE = 3
endcase
end
;---------------------------------------------
; DISPLAY_LARGE_IMAGE
;+
;
; Main routine. Initializes widgets and starts event handling.
;
;-
pro display_large_image, image, $
TITLE = title, $
XSIZE = xsize, $
YSIZE = ysize
xSize = n_elements(xsize) gt 0 ? xSize[0] : 500
ySize = n_elements(ysize) gt 0 ? ySize[0] : 500
imgDims = size(image)
case imgDims[0] of
2: begin
trueFlag = 0
imgDims = imgDims[1:2]
end
3: begin
trueFlag = where(imgDims[1:3] eq 3, COMPLEMENT = dimIndex)+1
if trueFlag eq 0 then begin
message, /INFO, 'Invalid image dimensions'
return
endif
imgDims = imgDims[dimIndex+1]
end
else: begin
message, /INFO, 'Invalid image dimensions'
return
end
endcase
tlb = widget_base(COL = 1, /TLB_SIZE_EVENTS, TITLE = title)
draw = widget_draw(tlb, /APP_SCROLL, /EXPOSE, $
XSIZE = imgDims[0], YSIZE = imgDims[1], $
X_SCROLL = xSize0], Y_SCROLL = ySize1], $
RETAIN = 0)
widget_control, tlb, /REALIZE
widget_control, draw, GET_VALUE = winID
tlbGeom = widget_info(tlb, /GEOMETRY)
state = {pImage:ptr_new(image), $
trueFlag:trueFlag, $
draw:draw, $
winID:winID, $
xy:[0,imgDims[1]-(widget_info(draw, /GEOMETRY)).ysize], $
tlbDims:[tlbGeom.xsize,tlbGeom.ysize]}
pState = ptr_new(state)
widget_control, tlb, SET_UVALUE = pState
xmanager, 'display_large_image', tlb, /NO_BLOCK, $
CLEANUP = 'display_large_image_cleanup'
end