This chapter takes you through a complete visualisation application, describing the various sections in the programme. Finally, the entire application souce code is presented at the end of the chapter.
This tool can load an image (stored as a 2-dimensional array) and display it. The tool includes a file browser, a magnifying window, a colourmap editor, zoom controls and much more, all in less than 500 lines of code. This tool (called ``kview-small'') is in fact a stripped-down version of the <kview> programme. The <kview> programme can also display TrueColour images and movies. In addition, it can display a profile window when a 3-dimensional array is loaded (you simply point at the displayed image and the corresponding array of values down the third axis is drawn in another window).
This section shows all the include files that this application will need.
#include <stdio.h> #include <X11/Xatom.h> #include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <karma.h> #define NEW_WIN_SCALE #include <k_event_codes.h> #include <karma_foreign.h> #include <karma_viewimg.h> #include <karma_iarray.h> #include <karma_xtmisc.h> #include <karma_dsxfr.h> #include <karma_conn.h> #include <karma_wcs.h> #include <karma_ds.h> #include <karma_im.h> #include <karma_hi.h> #include <karma_ic.h> #include <karma_r.h> #include <Xkw/ImageDisplay.h> #include <Xkw/Filewin.h>
This section shows all the variables required by the application. The various functions are also declared prior to their use. Note the use of the ``EXTERN_FUNCTION'' and ``STATIC_FUNCTION'' macros, which are portable to K&R C, ANSI C, and C++.
#define VERSION "1.1.0" /* Local functions */ EXTERN_FUNCTION (void setup_comms, (Display *display) ); EXTERN_FUNCTION (flag process_image, (multi_array *multi_desc, iarray *image_arr, double *min, double *max, KWorldCanvas pseudo_canvas, KWorldCanvas mag_pseudo_canvas, ViewableImage *image, ViewableImage *magnified_image) ); /* Public data */ char title_name[STRING_LENGTH] = "Unknown"; /* Private data */ static Widget main_shell = NULL; static Widget image_display = NULL; static ViewableImage image = NULL; static ViewableImage magnified_image = NULL; static iarray pseudo_arr = NULL; static double pseudo_scale = 1.0; static double pseudo_offset = 0.0; /* Private functions */ STATIC_FUNCTION (void fileselect_cbk, (Widget w, XtPointer client_data, XtPointer call_data) ); STATIC_FUNCTION (flag data_event_func, (void *object, multi_array *multi_desc, CONST char *domain, CONST char *name) ); STATIC_FUNCTION (track_canvas_event, (ViewableImage vimage, double x, double y, void *value, unsigned int event_code, void *e_info, void **f_info, double x_lin, double y_lin, unsigned int value_type) );
This section shows the list of resources (such as default colours and fonts) used for the application. These are stored in the ``fallback_resources'' variable.
The ``Options'' variable stores a list of command-line options. The X Intrinsics toolkit uses this variable to parse the command line.
String fallback_resources[] = { "Kview-small*pseudoColourCanvas*background: black", "Kview-small*pseudoColourCanvas*foreground: white", "Kview-small*Command*background: grey70", "Kview-small*Repeater*background: grey70", "Kview-small*Ktoggle*background: grey80", "Kview-small*closeButton*background: grey90", "Kview-small*ChoiceMenu.background: turquoise", "Kview-small*ExclusiveMenu.background: turquoise", "Kview-small*Value*background: #d0a0a0", "Kview-small*ImageDisplay*quit*background: orange", "Kview-small*SimpleSlider.foreground: Sea Green", "Kview-small*ImageDisplay*trackLabel0*font: 8x13bold", "Kview-small*ImageDisplay*trackLabel1*font: 8x13bold", "Kview-small*ImageDisplay*trackLabel2*font: 8x13bold", "Kview-small*ImageDisplay*zoomMenu*font: 10x20", "Kview-small*ImageDisplay*crosshairMenu*font: 10x20", "Kview-small*ImageDisplay*exportMenu*theMenu*font: 10x20", "Kview-small*ImageDisplay*theMenu*font: 10x20", "Kview-small*ImageDisplay.overlayMenu.menuButton.font: 7x13bold", "Kview-small*ImageDisplay.Command.font: 7x13bold", "Kview-small*ImageDisplay.Ktoggle.font: 7x13bold", "Kview-small*ImageDisplay.ChoiceMenu.font: 7x13bold", "Kview-small*ImageDisplay.exportMenu.menuButton.font: 7x13bold", "Kview-small*ImageDisplay*zoomMenu*Unzoom*foreground: red", "Kview-small*SimpleSlider.borderWidth: 0", "Kview-small*ZoomPolicy*showIntensityReset: True", "Kview-small*ZoomPolicy*resetIntensityToggle.state: True", "Kview-small*ZoomPolicy*autoIntensityScale: False", "Kview-small*font: 9x15bold", "Kview-small*borderColor: black", "Kview-small*background: aquamarine", "Kview-small*foreground: black", NULL }; static XrmOptionDescRec Options[] = { {"-private_cmap", ".topForm.multiCanvas.pseudoColourCanvas.forceNewCmap", XrmoptionNoArg, (XPointer) "True"}, {"-num_colours", ".topForm.cmapSize", XrmoptionSepArg, (XPointer) NULL}, {"-cmap_master", ".topForm.cmapMaster", XrmoptionSepArg, (XPointer) NULL}, {"-fullscreen", ".topForm.fullscreen", XrmoptionNoArg, (XPointer) "True"}, {"-verbose", "*verbose", XrmoptionNoArg, (XPointer) "True"}, };
This section shows the main function and the setup_comms function. Together, these two functions initialise the application.
The first thing the main function does is to use the im package to initialise a few basic things like the name of the module (application), the version of the module and the version of the library.
int main (int argc, char **argv) { KWorldCanvas wc_pseudo; flag controlled; XtAppContext app_context; Widget filewin, filepopup; Display *dpy; extern Widget main_shell, image_display; /* Initialise module */ im_register_module_name ("kview-small"); im_register_module_version_date (VERSION); im_register_lib_version (KARMA_VERSION);
Next main calls the xtmisc_init_app_initialise function which is a variant of the XtAppInitialize function in the X Intrinsics (Xt) library. XtAppInitialize will initialise Xt, create an application context, open and initialise a display and create the initial application shell instance (these words are taken from the ``X Toolkit Intrinsics Reference Manual (volume 5 of the O'Reilly X manuals). What the xtmisc_init_app_initialise function does beyond this is some basic initialisation of Karma event management. The other important thing it does is to determine if there are enough colourcells available in the default colourmap. If not, the application is given it's own colourmap: this is very handy when there is another application running that takes a lot of colourcells (like a WWW browser).
/* Start up Xt */ main_shell = xtmisc_init_app_initialise (&app_context, "Kview-small", Options, XtNumber (Options), &argc, argv, fallback_resources, XTMISC_INIT_ATT_MIN_CCELLS, 100, XTMISC_INIT_ATT_COMMS_SETUP, TRUE, XTMISC_INIT_ATT_CONTROLLED, &controlled, XTMISC_INIT_ATT_END, NULL); xtmisc_set_icon (main_shell, ic_write_kimage_icon); dpy = XtDisplay (main_shell); setup_comms (dpy); XtVaSetValues (main_shell, XtNtitle, title_name, NULL);
The next interesting thing that main does is to create an ``ImageDisplay'' widget. This widget does most of the hard work in the application. It creates a file browser, colourmap editor, zoom controls, display canvas and much more. All you have to worry about is what to do when a file is selected by the user, and how you want to display data.
image_display = XtVaCreateManagedWidget ("topForm", imageDisplayWidgetClass, main_shell, XtNborderWidth, 0, XkwNenableAnimation, FALSE, XkwNnumTrackLabels, 3, NULL);
Now main manipulates the file browser widget to register a callback function for file selection. Ordinarily, the file browser will handle directory selection internally and change directory, so there is no need to register a callback for directory selection. In this application, however we need to trap the directory selection before the browser changes directory, because the directory may in fact be a ``Miriad'' image file (Miriad is an astronomical data reduction package). Miriad datasets are in fact directories with several files therein. We can turn the XkwNtrapDirectoryDatasets resource on to do this for us.
filepopup = XtNameToWidget (image_display, "filewinPopup"); filewin = XtNameToWidget (filepopup, "form.selector"); XtVaSetValues (filepopup, XkwNfilenameTester, XkwFilewinStandardFileTester_nD, NULL); XtAddCallback (filepopup, XkwNfileSelectCallback, fileselect_cbk, NULL); XtVaSetValues (filewin, XkwNforwardSyntheticEvents, True, XkwNtrapDirectoryDatasets, True, NULL);
Next main registers the function that should be called when new data is available for processing/displaying, using the ds_event_register_func function. The data may come from a disc file, or it may come from a network connection. In either case, a single callback mechanism is available.
ds_event_register_func (data_event_func, NULL);
The main function now ``realises'' the main image display widget and attempts to get the PseudoColour world canvas the widget has created. The viewimg_register_position_event_func function is used to register a callback function which is called whenever events occurr on the PseudoColour canvas.
Finally main might popup the file browser. It then goes into the Xt event loop, never to return therefrom.
XtRealizeWidget (main_shell); XtVaGetValues (image_display, XkwNpseudoColourCanvas, &wc_pseudo, NULL); if (wc_pseudo == NULL) { fprintf (stderr, "No PseudoColour visual available\n"); exit (RV_UNDEF_ERROR); } viewimg_register_position_event_func (wc_pseudo, ( flag (*) () ) track_canvas_event, (void *) image_display); if (!controlled) XtPopup (filepopup, XtGrabNone); XtAppMainLoop (app_context); return (RV_OK); } /* End Function main */
The setup_comms function will try to make the module run as a Karma server (able to receive network connections), and will construct an appropriate title-bar string.
void setup_comms (Display *display) /* This routine will initialise the communications system. The display the module is connected to must be pointed to by display . NOTE: conn_initialise MUST be called first. The routine returns nothing. */ { int def_port_number; unsigned int server_port_number; char hostname[STRING_LENGTH]; extern char module_name[STRING_LENGTH + 1]; extern char module_version_date[STRING_LENGTH + 1]; /* Get default port number */ if ( ( def_port_number = r_get_def_port ( module_name, DisplayString (display) ) ) < 0 ) { fprintf (stderr, "Could not get default port number\n"); return; } r_gethostname (hostname, STRING_LENGTH); server_port_number = def_port_number; if ( !conn_become_server (&server_port_number, CONN_MAX_INSTANCES) ) { fprintf (stderr, "Module not operating as Karma server\n"); sprintf (title_name, "%s v%s @%s", module_name, module_version_date, hostname); } else { fprintf (stderr, "Port allocated: %d\n", server_port_number); /* Register the protocols */ dsxfr_register_connection_limits (1, -1); sprintf (title_name, "%s v%s @%s:%u", module_name, module_version_date, hostname, server_port_number); } } /* End Function setup_comms */
This section shows the functions which are called in response to files or directories being selected, and when new data is available for processing/display.
The fileselect_cbk function is called by the file browser whenever the user selects a file. This function will attempt to load the specified filename. If successful, it will dispatch a data event using the ds_event_dispatch function. The data structure may be then ``deallocated'' safely. If any callback function wanted the data, the attachment count on the data structure would be incremented, so calling ds_dealloc_multi only results in the attachment count being decremented.
static void fileselect_cbk (Widget w, XtPointer client_data, XtPointer call_data) { char *filename = (char *) call_data; multi_array *multi_desc; if ( ( multi_desc = foreign_guess_and_read (filename, K_CH_MAP_LOCAL, FALSE, NULL, FA_GUESS_READ_END) ) == NULL ) { fprintf (stderr, "Error reading file: \"%s\"\n", filename); return; } ds_event_dispatch (multi_desc, "FILE", filename); ds_dealloc_multi (multi_desc); } /* End Function fileselect_cbk */
Some of the hard work is done in the data_event_func function. The function obtains the PseudoColour main and magnifier canvases from the Image Display widget and passes this information, as well as the data structure and other information to the process_image function. The function then does a few cosmetic things like changing the title-bar and telling the Image Display widget the name of the file that was loaded (if the data came from a network connection, a fake name is given). A few more housekeeping operations to inform the intensity control widget of the new array, setting which canvas is visible and registering the data scaling information with the viewimg package are then performed.
static flag data_event_func (void *object, multi_array *multi_desc, CONST char *domain, CONST char *name) /* [SUMMARY] Process an event. <object> The object information pointer. <multi_desc> The multi_array descriptor. <domain> The domain from where the event was generated. <name> The name of the event in its domain. [RETURNS] TRUE if further callbacks should not be called, else FALSE. */ { KWorldCanvas pseudo_canvas, mag_pseudo_canvas; double min, max; Widget izoomwinpopup; char *ptr; char filename[STRING_LENGTH], stripped_filename[STRING_LENGTH]; char title[STRING_LENGTH]; extern ViewableImage image, magnified_image; extern iarray pseudo_arr; extern double pseudo_scale, pseudo_offset; extern Widget main_shell, image_display; if (strcmp (domain, "FILE") == 0) filename[0] = '\0'; else { strcpy (filename, domain); strcat (filename, "::"); } strcat (filename, name); strcpy (stripped_filename, filename); if ( ( ptr = strrchr (stripped_filename, '.') ) != NULL ) { if (strcmp (ptr, ".kf") == 0) *ptr = '\0'; } XtVaGetValues (image_display, XkwNpseudoColourCanvas, &pseudo_canvas, XkwNmagnifierPseudoColourCanvas, &mag_pseudo_canvas, NULL); izoomwinpopup = XtNameToWidget (image_display, "izoomwinpopup"); if ( !process_image (multi_desc, &pseudo_arr, &min, &max, pseudo_canvas, mag_pseudo_canvas, &image, &magnified_image) ) return (FALSE); sprintf (title, "%s file: %s\n", title_name, filename); XtVaSetValues (main_shell, XtNtitle, title, NULL); XtVaSetValues (image_display, XkwNimageName, stripped_filename, NULL); XkwDataclipNewArray (izoomwinpopup, pseudo_arr, min, max, TRUE); XtVaSetValues (image_display, XkwNvisibleCanvas, pseudo_canvas, NULL); iarray_get_data_scaling (pseudo_arr, &pseudo_scale, &pseudo_offset); viewimg_set_attributes (image, VIEWIMG_VATT_DATA_SCALE, pseudo_scale, VIEWIMG_VATT_DATA_OFFSET, pseudo_offset, VIEWIMG_VATT_END); viewimg_set_attributes (magnified_image, VIEWIMG_VATT_DATA_SCALE, pseudo_scale, VIEWIMG_VATT_DATA_OFFSET, pseudo_offset, VIEWIMG_VATT_END); return (TRUE); } /* End Function data_event_func */
The rest of the work is done in the process_image function, which first tries to create an ``Intelligent Array'' using the iarray_create_and_setup function. This function will also find any astronomical projection information, compute the minumum and maximum, and reject data of the wrong type or dimensionality. If it succeeds, the canvas_use_astro_transform is used to tell the canvas package that the ``astro_transform'' variable may contain astronomical co-ordinate transformation information. If this variable is not NULL, co-ordinate transformations are performed automatically. After cleaning up old data, the function tries to create ViewableImage objects from the Intelligent Array. If this succeeds, then the function will update the value range attributes (the viewimg package can compute these automatically, but a little time is saved if the ranges are computed only once). Finally, the ViewableImage objects are made active so that they may be seen.
flag process_image (multi_array *multi_desc, iarray *image_arr, double *min, double *max, KWorldCanvas pseudo_canvas, KWorldCanvas mag_pseudo_canvas, ViewableImage *image, ViewableImage *magnified_image) /* [PURPOSE] This routine will process an image and display it. <multi_desc> The multi_array descriptor. <image_arr> The image array is written here. <min> The minimum image value is written here. <max> The maximum image value is written here. <pseudo_canvas> The PseudoColour canvas. <image> If an image is loaded the ViewableImage is written here. If no image is loaded, NULL is written here. The value written here must be preserved between calls. <magnified_image> If an image is loaded the magnified ViewableImage is written here. If no image is loaded, NULL is written here. The value written here must be preserved between calls. [RETURNS] TRUE on success, else FALSE. */ { KwcsAstro astro_projection; /*static char function_name[] = "process_image";*/ if ( !iarray_create_and_setup (image_arr, multi_desc, TRUE, 2, NONE, min, max, TRUE, &astro_projection) ) { return (FALSE); } if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; canvas_use_astro_transform (pseudo_canvas, NULL); if ( ( *image = viewimg_create_from_iarray (pseudo_canvas, *image_arr, FALSE) ) == NULL ) { fprintf (stderr, "Error getting ViewableImage from Iarray\n"); iarray_dealloc (*image_arr); *image_arr = NULL; return (FALSE); } if ( ( *magnified_image = viewimg_create_from_iarray (mag_pseudo_canvas, *image_arr, FALSE) ) == NULL ) { fprintf (stderr, "Error getting ViewableImage from Iarray\n"); iarray_dealloc (*image_arr); *image_arr = NULL; if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; return (FALSE); } viewimg_set_attributes (*image, VIEWIMG_VATT_VALUE_MIN, *min, VIEWIMG_VATT_VALUE_MAX, *max, VIEWIMG_ATT_END); viewimg_set_attributes (*magnified_image, VIEWIMG_VATT_VALUE_MIN, *min, VIEWIMG_VATT_VALUE_MAX, *max, VIEWIMG_ATT_END); if ( !viewimg_make_active (*image) || !viewimg_make_active (*magnified_image) ) { fprintf (stderr, "Error making ViewableImage(s) active\n"); iarray_dealloc (*image_arr); *image_arr = NULL; if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; return (FALSE); } canvas_use_astro_transform (pseudo_canvas, astro_projection); return (TRUE); } /* End Function process_image */
This section shows the function which handles canvas events. This function rejects all events except pointer (mouse) moves. The convenience function viewimg_track_compute is used to compute strings that should be displayed above the image. These strings are then dutifully displayed in their corresponding label widgets. Finally, the mouse position is used to update the magnifier pan position.
static flag track_canvas_event (ViewableImage vimage, double x, double y, void *value, unsigned int event_code, void *e_info, void **f_info, double x_lin, double y_lin, unsigned int value_type) /* [PURPOSE] This routine is a position event consumer for a world canvas which has a number of ViewableImage objects associated with it. <viewimg> The active viewable image. <x> The horizontal world co-ordinate of the event. <y> The vertical world co-ordinate of the event. These values will have been transformed by the registered transform function (see canvas_register_transform_func ) for the associated world canvas. <value> A pointer to the data value in the viewable image corresponding to the event co-ordinates. <event_code> The arbitrary event code. <e_info> The arbitrary event information. <f_info> The arbitrary function information pointer. <x_lin> The linear horizontal world co-ordinate (the co-ordinate prior to the transform function being called). <y_lin> The linear vertical world co-ordinate (the co-ordinate prior to the transform function being called). <value_type> The type of the data value. This may be K_DCOMPLEX or K_UB_RGB. [RETURNS] TRUE if the event was consumed, else FALSE indicating that the event is still to be processed. */ { KWorldCanvas magnifier_canvas; unsigned long pointer_x_index, pointer_y_index; Widget image_display = (Widget) *f_info; Widget first_track_label, second_track_label, third_track_label; char pix_string[STRING_LENGTH]; char world_string[STRING_LENGTH], extra_string[STRING_LENGTH]; extern iarray pseudo_arr; /*static char function_name[] = "track_canvas_event";*/ if (event_code != K_CANVAS_EVENT_POINTER_MOVE) return (FALSE); viewimg_track_compute (vimage, value, value_type, x, y, x_lin, y_lin, pix_string, world_string, extra_string, &pointer_x_index, &pointer_y_index); first_track_label = XtNameToWidget (image_display, "trackLabel0"); second_track_label = XtNameToWidget (image_display, "trackLabel1"); third_track_label = XtNameToWidget (image_display, "trackLabel2"); XtVaSetValues (first_track_label, XtNlabel, pix_string, NULL); XtVaSetValues (second_track_label, XtNlabel, world_string, NULL); XtVaSetValues (third_track_label, XtNlabel, extra_string, NULL); XtVaGetValues (image_display, XkwNmagnifierVisibleCanvas, &magnifier_canvas, NULL); viewimg_set_canvas_attributes (magnifier_canvas, VIEWIMG_ATT_PAN_CENTRE_X, pointer_x_index, VIEWIMG_ATT_PAN_CENTRE_Y, pointer_y_index, VIEWIMG_ATT_END); kwin_refresh_if_visible (canvas_get_pixcanvas (magnifier_canvas), FALSE); return (TRUE); } /* End Function track_canvas_event */
Here is the entire application source code.
/* kview-small.c Main file for kview-small (X11 image display tool for Karma). Copyright (C) 1997 Richard Gooch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Richard Gooch may be reached by email at karma-request@atnf.csiro.au The postal address is: Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. */ /* This Karma module will enable on-screen display of images. This module runs on an X11 server. Written by Richard Gooch 11-JAN-1997: Copied from <kview>. Last updated by Richard Gooch 20-NOV-1997 */ #include <stdio.h> #include <X11/Xatom.h> #include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <karma.h> #define NEW_WIN_SCALE #include <k_event_codes.h> #include <karma_foreign.h> #include <karma_viewimg.h> #include <karma_iarray.h> #include <karma_xtmisc.h> #include <karma_dsxfr.h> #include <karma_conn.h> #include <karma_wcs.h> #include <karma_ds.h> #include <karma_im.h> #include <karma_hi.h> #include <karma_ic.h> #include <karma_r.h> #include <Xkw/ImageDisplay.h> #include <Xkw/Filewin.h> #define VERSION "1.1.0" /* Local functions */ EXTERN_FUNCTION (void setup_comms, (Display *display) ); EXTERN_FUNCTION (flag process_image, (multi_array *multi_desc, iarray *image_arr, double *min, double *max, KWorldCanvas pseudo_canvas, KWorldCanvas mag_pseudo_canvas, ViewableImage *image, ViewableImage *magnified_image) ); /* Public data */ char title_name[STRING_LENGTH] = "Unknown"; /* Private data */ static Widget main_shell = NULL; static Widget image_display = NULL; static ViewableImage image = NULL; static ViewableImage magnified_image = NULL; static iarray pseudo_arr = NULL; static double pseudo_scale = 1.0; static double pseudo_offset = 0.0; /* Private functions */ STATIC_FUNCTION (void fileselect_cbk, (Widget w, XtPointer client_data, XtPointer call_data) ); STATIC_FUNCTION (flag data_event_func, (void *object, multi_array *multi_desc, CONST char *domain, CONST char *name) ); STATIC_FUNCTION (track_canvas_event, (ViewableImage vimage, double x, double y, void *value, unsigned int event_code, void *e_info, void **f_info, double x_lin, double y_lin, unsigned int value_type) ); String fallback_resources[] = { "Kview-small*pseudoColourCanvas*background: black", "Kview-small*pseudoColourCanvas*foreground: white", "Kview-small*Command*background: grey70", "Kview-small*Repeater*background: grey70", "Kview-small*Ktoggle*background: grey80", "Kview-small*closeButton*background: grey90", "Kview-small*ChoiceMenu.background: turquoise", "Kview-small*ExclusiveMenu.background: turquoise", "Kview-small*Value*background: #d0a0a0", "Kview-small*ImageDisplay*quit*background: orange", "Kview-small*SimpleSlider.foreground: Sea Green", "Kview-small*ImageDisplay*trackLabel0*font: 8x13bold", "Kview-small*ImageDisplay*trackLabel1*font: 8x13bold", "Kview-small*ImageDisplay*trackLabel2*font: 8x13bold", "Kview-small*ImageDisplay*zoomMenu*font: 10x20", "Kview-small*ImageDisplay*crosshairMenu*font: 10x20", "Kview-small*ImageDisplay*exportMenu*theMenu*font: 10x20", "Kview-small*ImageDisplay*theMenu*font: 10x20", "Kview-small*ImageDisplay.overlayMenu.menuButton.font: 7x13bold", "Kview-small*ImageDisplay.Command.font: 7x13bold", "Kview-small*ImageDisplay.Ktoggle.font: 7x13bold", "Kview-small*ImageDisplay.ChoiceMenu.font: 7x13bold", "Kview-small*ImageDisplay.exportMenu.menuButton.font: 7x13bold", "Kview-small*ImageDisplay*zoomMenu*Unzoom*foreground: red", "Kview-small*SimpleSlider.borderWidth: 0", "Kview-small*ZoomPolicy*showIntensityReset: True", "Kview-small*ZoomPolicy*resetIntensityToggle.state: True", "Kview-small*ZoomPolicy*autoIntensityScale: False", "Kview-small*font: 9x15bold", "Kview-small*borderColor: black", "Kview-small*background: aquamarine", "Kview-small*foreground: black", NULL }; static XrmOptionDescRec Options[] = { {"-private_cmap", ".topForm.multiCanvas.pseudoColourCanvas.forceNewCmap", XrmoptionNoArg, (XPointer) "True"}, {"-num_colours", ".topForm.cmapSize", XrmoptionSepArg, (XPointer) NULL}, {"-cmap_master", ".topForm.cmapMaster", XrmoptionSepArg, (XPointer) NULL}, {"-fullscreen", ".topForm.fullscreen", XrmoptionNoArg, (XPointer) "True"}, {"-verbose", "*verbose", XrmoptionNoArg, (XPointer) "True"}, }; int main (int argc, char **argv) { KWorldCanvas wc_pseudo; flag controlled; XtAppContext app_context; Widget filewin, filepopup; Display *dpy; extern Widget main_shell, image_display; /* Initialise module */ im_register_module_name ("kview-small"); im_register_module_version_date (VERSION); im_register_lib_version (KARMA_VERSION); /* Start up Xt */ main_shell = xtmisc_init_app_initialise (&app_context, "Kview-small", Options, XtNumber (Options), &argc, argv, fallback_resources, XTMISC_INIT_ATT_MIN_CCELLS, 100, XTMISC_INIT_ATT_COMMS_SETUP, TRUE, XTMISC_INIT_ATT_CONTROLLED, &controlled, XTMISC_INIT_ATT_END, NULL); xtmisc_set_icon (main_shell, ic_write_kimage_icon); dpy = XtDisplay (main_shell); setup_comms (dpy); XtVaSetValues (main_shell, XtNtitle, title_name, NULL); image_display = XtVaCreateManagedWidget ("topForm", imageDisplayWidgetClass, main_shell, XtNborderWidth, 0, XkwNenableAnimation, FALSE, XkwNnumTrackLabels, 3, NULL); filepopup = XtNameToWidget (image_display, "filewinPopup"); filewin = XtNameToWidget (filepopup, "form.selector"); XtVaSetValues (filepopup, XkwNfilenameTester, XkwFilewinStandardFileTester_nD, NULL); XtAddCallback (filepopup, XkwNfileSelectCallback, fileselect_cbk, NULL); XtVaSetValues (filewin, XkwNforwardSyntheticEvents, True, XkwNtrapDirectoryDatasets, True, NULL); ds_event_register_func (data_event_func, NULL); XtRealizeWidget (main_shell); XtVaGetValues (image_display, XkwNpseudoColourCanvas, &wc_pseudo, NULL); if (wc_pseudo == NULL) { fprintf (stderr, "No PseudoColour visual available\n"); exit (RV_UNDEF_ERROR); } viewimg_register_position_event_func (wc_pseudo, ( flag (*) () ) track_canvas_event, (void *) image_display); if (!controlled) XtPopup (filepopup, XtGrabNone); XtAppMainLoop (app_context); return (RV_OK); } /* End Function main */ void setup_comms (Display *display) /* This routine will initialise the communications system. The display the module is connected to must be pointed to by display . NOTE: conn_initialise MUST be called first. The routine returns nothing. */ { int def_port_number; unsigned int server_port_number; char hostname[STRING_LENGTH]; extern char module_name[STRING_LENGTH + 1]; extern char module_version_date[STRING_LENGTH + 1]; /* Get default port number */ if ( ( def_port_number = r_get_def_port ( module_name, DisplayString (display) ) ) < 0 ) { fprintf (stderr, "Could not get default port number\n"); return; } r_gethostname (hostname, STRING_LENGTH); server_port_number = def_port_number; if ( !conn_become_server (&server_port_number, CONN_MAX_INSTANCES) ) { fprintf (stderr, "Module not operating as Karma server\n"); sprintf (title_name, "%s v%s @%s", module_name, module_version_date, hostname); } else { fprintf (stderr, "Port allocated: %d\n", server_port_number); /* Register the protocols */ dsxfr_register_connection_limits (1, -1); sprintf (title_name, "%s v%s @%s:%u", module_name, module_version_date, hostname, server_port_number); } } /* End Function setup_comms */ static void fileselect_cbk (Widget w, XtPointer client_data, XtPointer call_data) { char *filename = (char *) call_data; multi_array *multi_desc; if ( ( multi_desc = foreign_guess_and_read (filename, K_CH_MAP_LOCAL, FALSE, NULL, FA_GUESS_READ_END) ) == NULL ) { fprintf (stderr, "Error reading file: \"%s\"\n", filename); return; } ds_event_dispatch (multi_desc, "FILE", filename); ds_dealloc_multi (multi_desc); } /* End Function fileselect_cbk */ static flag data_event_func (void *object, multi_array *multi_desc, CONST char *domain, CONST char *name) /* [SUMMARY] Process an event. <object> The object information pointer. <multi_desc> The multi_array descriptor. <domain> The domain from where the event was generated. <name> The name of the event in its domain. [RETURNS] TRUE if further callbacks should not be called, else FALSE. */ { KWorldCanvas pseudo_canvas, mag_pseudo_canvas; double min, max; Widget izoomwinpopup; char *ptr; char filename[STRING_LENGTH], stripped_filename[STRING_LENGTH]; char title[STRING_LENGTH]; extern ViewableImage image, magnified_image; extern iarray pseudo_arr; extern double pseudo_scale, pseudo_offset; extern Widget main_shell, image_display; if (strcmp (domain, "FILE") == 0) filename[0] = '\0'; else { strcpy (filename, domain); strcat (filename, "::"); } strcat (filename, name); strcpy (stripped_filename, filename); if ( ( ptr = strrchr (stripped_filename, '.') ) != NULL ) { if (strcmp (ptr, ".kf") == 0) *ptr = '\0'; } XtVaGetValues (image_display, XkwNpseudoColourCanvas, &pseudo_canvas, XkwNmagnifierPseudoColourCanvas, &mag_pseudo_canvas, NULL); izoomwinpopup = XtNameToWidget (image_display, "izoomwinpopup"); if ( !process_image (multi_desc, &pseudo_arr, &min, &max, pseudo_canvas, mag_pseudo_canvas, &image, &magnified_image) ) return (FALSE); sprintf (title, "%s file: %s\n", title_name, filename); XtVaSetValues (main_shell, XtNtitle, title, NULL); XtVaSetValues (image_display, XkwNimageName, stripped_filename, NULL); XkwDataclipNewArray (izoomwinpopup, pseudo_arr, min, max, TRUE); XtVaSetValues (image_display, XkwNvisibleCanvas, pseudo_canvas, NULL); iarray_get_data_scaling (pseudo_arr, &pseudo_scale, &pseudo_offset); viewimg_set_attributes (image, VIEWIMG_VATT_DATA_SCALE, pseudo_scale, VIEWIMG_VATT_DATA_OFFSET, pseudo_offset, VIEWIMG_VATT_END); viewimg_set_attributes (magnified_image, VIEWIMG_VATT_DATA_SCALE, pseudo_scale, VIEWIMG_VATT_DATA_OFFSET, pseudo_offset, VIEWIMG_VATT_END); return (TRUE); } /* End Function data_event_func */ flag process_image (multi_array *multi_desc, iarray *image_arr, double *min, double *max, KWorldCanvas pseudo_canvas, KWorldCanvas mag_pseudo_canvas, ViewableImage *image, ViewableImage *magnified_image) /* [PURPOSE] This routine will process an image and display it. <multi_desc> The multi_array descriptor. <image_arr> The image array is written here. <min> The minimum image value is written here. <max> The maximum image value is written here. <pseudo_canvas> The PseudoColour canvas. <image> If an image is loaded the ViewableImage is written here. If no image is loaded, NULL is written here. The value written here must be preserved between calls. <magnified_image> If an image is loaded the magnified ViewableImage is written here. If no image is loaded, NULL is written here. The value written here must be preserved between calls. [RETURNS] TRUE on success, else FALSE. */ { KwcsAstro astro_projection; /*static char function_name[] = "process_image";*/ if ( !iarray_create_and_setup (image_arr, multi_desc, TRUE, 2, NONE, min, max, TRUE, &astro_projection) ) { return (FALSE); } if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; canvas_use_astro_transform (pseudo_canvas, NULL); if ( ( *image = viewimg_create_from_iarray (pseudo_canvas, *image_arr, FALSE) ) == NULL ) { fprintf (stderr, "Error getting ViewableImage from Iarray\n"); iarray_dealloc (*image_arr); *image_arr = NULL; return (FALSE); } if ( ( *magnified_image = viewimg_create_from_iarray (mag_pseudo_canvas, *image_arr, FALSE) ) == NULL ) { fprintf (stderr, "Error getting ViewableImage from Iarray\n"); iarray_dealloc (*image_arr); *image_arr = NULL; if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; return (FALSE); } viewimg_set_attributes (*image, VIEWIMG_VATT_VALUE_MIN, *min, VIEWIMG_VATT_VALUE_MAX, *max, VIEWIMG_ATT_END); viewimg_set_attributes (*magnified_image, VIEWIMG_VATT_VALUE_MIN, *min, VIEWIMG_VATT_VALUE_MAX, *max, VIEWIMG_ATT_END); if ( !viewimg_make_active (*image) || !viewimg_make_active (*magnified_image) ) { fprintf (stderr, "Error making ViewableImage(s) active\n"); iarray_dealloc (*image_arr); *image_arr = NULL; if (*image != NULL) viewimg_destroy (*image); *image = NULL; if (*magnified_image != NULL) viewimg_destroy (*magnified_image); *magnified_image = NULL; return (FALSE); } canvas_use_astro_transform (pseudo_canvas, astro_projection); return (TRUE); } /* End Function process_image */ static flag track_canvas_event (ViewableImage vimage, double x, double y, void *value, unsigned int event_code, void *e_info, void **f_info, double x_lin, double y_lin, unsigned int value_type) /* [PURPOSE] This routine is a position event consumer for a world canvas which has a number of ViewableImage objects associated with it. <viewimg> The active viewable image. <x> The horizontal world co-ordinate of the event. <y> The vertical world co-ordinate of the event. These values will have been transformed by the registered transform function (see canvas_register_transform_func ) for the associated world canvas. <value> A pointer to the data value in the viewable image corresponding to the event co-ordinates. <event_code> The arbitrary event code. <e_info> The arbitrary event information. <f_info> The arbitrary function information pointer. <x_lin> The linear horizontal world co-ordinate (the co-ordinate prior to the transform function being called). <y_lin> The linear vertical world co-ordinate (the co-ordinate prior to the transform function being called). <value_type> The type of the data value. This may be K_DCOMPLEX or K_UB_RGB. [RETURNS] TRUE if the event was consumed, else FALSE indicating that the event is still to be processed. */ { KWorldCanvas magnifier_canvas; unsigned long pointer_x_index, pointer_y_index; Widget image_display = (Widget) *f_info; Widget first_track_label, second_track_label, third_track_label; char pix_string[STRING_LENGTH]; char world_string[STRING_LENGTH], extra_string[STRING_LENGTH]; extern iarray pseudo_arr; /*static char function_name[] = "track_canvas_event";*/ if (event_code != K_CANVAS_EVENT_POINTER_MOVE) return (FALSE); viewimg_track_compute (vimage, value, value_type, x, y, x_lin, y_lin, pix_string, world_string, extra_string, &pointer_x_index, &pointer_y_index); first_track_label = XtNameToWidget (image_display, "trackLabel0"); second_track_label = XtNameToWidget (image_display, "trackLabel1"); third_track_label = XtNameToWidget (image_display, "trackLabel2"); XtVaSetValues (first_track_label, XtNlabel, pix_string, NULL); XtVaSetValues (second_track_label, XtNlabel, world_string, NULL); XtVaSetValues (third_track_label, XtNlabel, extra_string, NULL); XtVaGetValues (image_display, XkwNmagnifierVisibleCanvas, &magnifier_canvas, NULL); viewimg_set_canvas_attributes (magnifier_canvas, VIEWIMG_ATT_PAN_CENTRE_X, pointer_x_index, VIEWIMG_ATT_PAN_CENTRE_Y, pointer_y_index, VIEWIMG_ATT_END); kwin_refresh_if_visible (canvas_get_pixcanvas (magnifier_canvas), FALSE); return (TRUE); } /* End Function track_canvas_event */