X
4129

How to Programatically Save a .png Image of an iPlot Visualization

Note: IDL 8.x has now introduced a new set of graphical functions, based on iTools, that are more flexible and easy to use. For the case explained below, the new graphics functions make is very easy to implement programatically. We recommend the user to experiment with those new graphic functions as well. 

Topic

iTools certainly has many different ways of programatically implementing the functionality of outputting its visualization graphics to a '.png' or other image file format. All have one important trick, though. The default visualization of any iPlot includes a red border near the edge of the draw window that looks out of place when the plot image is displayed outside of the iTools GUI. For optimal output this red border needs to be hidden before a snapshot of the plot visualization is made. The method shown in this Help Article may be the very easiest for setting up that snapshot and outputting the result to a .png or other similar image format file.

Discussion

If you are wanting to output your iPlot's visualization graphics to a high-resolution printer, then the approach described here will not be suitable. But for any application, where you simply want to programatically take a snapshot of the current plot window and store it or share it for display on other computer monitors, you should find this approach perfectly satisfying. The basic algorithm that describes the code displayed below is as follows:

  1. Get the ID of and the direct Handle of the iTool GUI.
  2. Use that to get the Window object of the iTool.
  3. Use the Window object to get the View object of the window.
  4. This next step is not as intuitive as the above: Get all the children of the View object. In an initial iPlot Visualization it turns out that there are three different graphics hierarchies on display. They are identifiable only by their class types. The object of the 'IDLitgrLayer' class is a container for the graphics that are used to display the data and its routine labels and delimiters (like borders, axes and their direct-associated text). The object of the IDLitgrAnnotateLayer class is a container for extra annotations a user may have inserted (like colorbars) or drawn or typed onto the iPlot visualization. Finally, the specific "surprise" object we are looking for, the one of the 'IDLgrView' class, actually contains nothing more than the red border that borders all iTools views by default. This has no simple property on the iTools Visualization Browser thay you can turn off. Rather, unless you implement these programmatic steps, you would only be able to turn these off on the GUI in the 'File->Print Preview...' wizard.
  5. Perhaps the red border is always and will always be the third object in this container. But, with no documentation promising that, we cannot rely on that. For this reason, we loop through the object array to find the index of the first object that is of that 'IDLGRVIEW' class. We get the handle on that "red border view object" with that index.
  6. Through that handle we set its 'HIDE' property to 'true'.
  7. We also need to make sure that the image file snapshot is not displaying the scene with "selected" items. When the iPlot visualization starts, for example, the dataspace object is selected by default, and its "pull handles" are displayed in green. To keep that out of the PNG output, we need to "deselect" that object and any other obects that a user may have focused on right before the snapshot. The IDLitWindow::GetSelectedItems and IDLitVisualization::Select mthods provide a solution here.
  8. Now we can use the long-standing IDL graphics object 'IDLgrBuffer' to draw a "pixmap" of the orignal full View object from above in a memory buffer. It is safer to read from this IDLgrBuffer, because the "backing store" of the iTools window can sometimes contain snapshots of windows that are temporarily overlaid over our iTools GUI. We get the DIMENSIONS property of the original Window object above and use those to initialize our 'IDLgrBuffer' object.
  9. We then use the IDLgrBuffer's Draw method with the original View object from above.
  10. Now we do a simple Read( ) from the IDLgrBuffer object. This returns an IDLgrImage object.
  11. The 3D pixel-interleaved RGB image data is in the 'DATA' property of this Image object.
  12. After we GetProperty on 'DATA', that output is ready to feed directly to IDL's WRITE_PNG.
  13. To restore iTools to its normal state, we reset the 'HIDE' property of the "red border view object" back to 'false'.

I should add a few tips about routines that I have used to find iTools programming commands that are either not fully documented in our manuals or are not easy to remember:

    help, [object], /OBJECT

is particulalry useful in finding the availability of commands like 'GetCurrentWindow()', 'GetCurrentView()'. To find properties of an object

    [object]->GetProperty, ALL=props
    help, props, /STRUCT

and

    id = oTool->GetFullIdentifier([object])
    itpropertyreport, oTool, id

are both useful. Finally, sometimes it is useful to find parent or children objects in iTools; its hierarchy has so many layers. Therefore, these two calls are good to remember:

    [object]->GetProperty, PARENT=oParent

and:

    oChildObjects = [object]->Get(/ALL)

To conclude, below is the code that accomplishes the above 12-step algorithm:

 

; Make a colorful iPlot
iplot, findgen(100), COLOR=[255,0,0], BACKGROUND_COLOR=[255,255,0]
idTool = itgetcurrent(TOOL=oTool)   ; Get the initial "handle" on the iTool
oWin = oTool->GetCurrentWindow()   ; Get the top-level window object
oView = oWin->GetCurrentView()    ; Get the top-level view object

; This undocumented red border object was not easy to find in the iTools
; source code. It is its own distinct IDLgrView object, one of three
; children to the top-level iPlot View object.
temp = oView->Get(/ALL)
help, temp
;TEMP OBJREF = Array[3]
print, transpose(temp)
; ; This is the "data layer"
; ; ... the "annotation layer"
; ; the red border
; Let's be careful how we dynamically find the right object
for i = 0, n_elements(temp)-1 do $   ; Find the right index
	if obj_class(temp[i]) eq 'IDLGRVIEW' then break
oBorder = temp[i]
oBorder->SetProperty, HIDE=1   ; This turns off the display of the border

; Turn off all currently displayed graphics used to denote an object
; is selected, by "deselecting" all selected objects
objectList = oWin->GetSelectedItems()
if obj_valid(objectList[0]) then $
	for i = 0, n_elements(objectList)-1 do $
		objectList[i]->Select, /UNSELECT

; Now we are ready to save a nice clean image to PNG. The basic algorithm
; is draw the iTools' view to an IDLgrBuffer off-screen pixmap, read that
; pixmap into a 3D RGB image array, and write that array to a PNG file.
oWin->GetProperty, DIMENSIONS=winDims
oPixmap = obj_new('IDLgrBuffer', DIMENSIONS=winDims)
oPixmap->Draw, oView
oImage = oPixmap->Read()   ; Gets an image "object"
oImage->GetProperty, DATA=iplotSnapshotImage   ; Gets the image RGB data
obj_destroy, oPixmap   ; Clean up
write_png, 'myIPLOTimage.png', iplotSnapshotImage

; restore original window state
oBorder->SetProperty, HIDE=0