X
21 Rate this article:
No rating

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