Karma Home Page
Next: Dynamic Extensions Up: Karma Programming Manual Previous: Advanced Examples

# Image Display Tool

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 <kvis> programme. The <kvis> 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).

# Include Files

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>
    #include <k_version.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>
    #include <Xkw/Dataclip.h>

# Function and Variable Declarations

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.2.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 (flag data_event_func,
                     (void *object, multi_array *multi_desc,
                      CONST char *domain, CONST char *name) );
    STATIC_FUNCTION (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,
                      double x_im, double y_im, int x_pix, int y_pix) );

# Resources and Options

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"},
    };

# Main function and initialisation

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;

        /*  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 (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  */

# Data Event handers

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, magnifier;
        char *ptr;
        char filename[STRING_LENGTH], stripped_filename[STRING_LENGTH];
        char title[STRING_LENGTH];

        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,
                       XkwNmagnifier, &magnifier,
                       NULL);
        XtVaGetValues (magnifier, XkwNvisibleCanvas, &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  */

# Canvas Event hander

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,
                                    double x_im, double y_im, int x_pix, int y_pix)
    /*  [SUMMARY] Position event callback.
        [PURPOSE] This routine is a position event consumer for a world canvas
        which has a number of ViewableImage objects associated with it. Most
        co-ordinate values are quantised to the nearest image pixel centre.
        <viewimg> The active viewable image.
        <x> The quantised horizontal world co-ordinate of the event.
        <y> The quantised vertical world co-ordinate of the event.
        <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 quantised linear horizontal world co-ordinate (the co-ordinate
        prior to the transform function being called).
        <y_lin> The quantised 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.
        <x_im> The quantised horizontal image pixel co-ordinate. 0.0 is the pixel
        centre.
        <y_im> The quantised vertical image pixel co-ordinate. 0.0 is the pixel
        centre.
        <x_pix> The horizontal screen pixel co-ordinate.
        <y_pix> The vertical screen pixel co-ordinate.
        [NOTE] All co-ordinates are clipped to pixel boundaries.
        [RETURNS] TRUE if the event was consumed, else FALSE indicating that
        the event is still to be processed.
    */
    {
        KWorldCanvas magnifier_canvas;
        Widget image_display = (Widget) *f_info;
        Widget first_track_label, second_track_label, third_track_label, magnifier;
        char pix_string[STRING_LENGTH];
        char world_string[STRING_LENGTH], extra_string[STRING_LENGTH];
        /*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_im, y_im,
                               pix_string, world_string, extra_string);
        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, XkwNmagnifier, &magnifier, NULL);
        XtVaGetValues (magnifier, XkwNvisibleCanvas, &magnifier_canvas, NULL);
        viewimg_set_canvas_attributes (magnifier_canvas,
                                       VIEWIMG_ATT_PAN_CENTRE_X,
                                           (unsigned long) (x_im + 0.5),
                                       VIEWIMG_ATT_PAN_CENTRE_Y,
                                           (unsigned long) (y_im + 0.5),
                                       VIEWIMG_ATT_END);
        kwin_refresh_if_visible (canvas_get_pixcanvas (magnifier_canvas), FALSE);
        return (TRUE);
    }   /*  End Function track_canvas_event  */

# The entire application

Here is the entire application source code.

    /*  kview-small.c

        Main file for  kview-small  (X11 image display tool for Karma).

        Copyright (C) 1997-2005  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   3-AUG-2005: Added #include <k_version.h>
      and took account of API changes.

    */
    #include <stdio.h>
    #include <X11/Xatom.h>
    #include <X11/Intrinsic.h>
    #include <X11/StringDefs.h>
    #include <karma.h>
    #include <k_version.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>
    #include <Xkw/Dataclip.h>

    #define VERSION "1.2.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 (flag data_event_func,
                     (void *object, multi_array *multi_desc,
                      CONST char *domain, CONST char *name) );
    STATIC_FUNCTION (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,
                      double x_im, double y_im, int x_pix, int y_pix) );

    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;

        /*  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 (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 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, magnifier;
        char *ptr;
        char filename[STRING_LENGTH], stripped_filename[STRING_LENGTH];
        char title[STRING_LENGTH];

        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,
                       XkwNmagnifier, &magnifier,
                       NULL);
        XtVaGetValues (magnifier, XkwNvisibleCanvas, &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,
                                    double x_im, double y_im, int x_pix, int y_pix)
    /*  [SUMMARY] Position event callback.
        [PURPOSE] This routine is a position event consumer for a world canvas
        which has a number of ViewableImage objects associated with it. Most
        co-ordinate values are quantised to the nearest image pixel centre.
        <viewimg> The active viewable image.
        <x> The quantised horizontal world co-ordinate of the event.
        <y> The quantised vertical world co-ordinate of the event.
        <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 quantised linear horizontal world co-ordinate (the co-ordinate
        prior to the transform function being called).
        <y_lin> The quantised 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.
        <x_im> The quantised horizontal image pixel co-ordinate. 0.0 is the pixel
        centre.
        <y_im> The quantised vertical image pixel co-ordinate. 0.0 is the pixel
        centre.
        <x_pix> The horizontal screen pixel co-ordinate.
        <y_pix> The vertical screen pixel co-ordinate.
        [NOTE] All co-ordinates are clipped to pixel boundaries.
        [RETURNS] TRUE if the event was consumed, else FALSE indicating that
        the event is still to be processed.
    */
    {
        KWorldCanvas magnifier_canvas;
        Widget image_display = (Widget) *f_info;
        Widget first_track_label, second_track_label, third_track_label, magnifier;
        char pix_string[STRING_LENGTH];
        char world_string[STRING_LENGTH], extra_string[STRING_LENGTH];
        /*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_im, y_im,
                               pix_string, world_string, extra_string);
        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, XkwNmagnifier, &magnifier, NULL);
        XtVaGetValues (magnifier, XkwNvisibleCanvas, &magnifier_canvas, NULL);
        viewimg_set_canvas_attributes (magnifier_canvas,
                                       VIEWIMG_ATT_PAN_CENTRE_X,
                                           (unsigned long) (x_im + 0.5),
                                       VIEWIMG_ATT_PAN_CENTRE_Y,
                                           (unsigned long) (y_im + 0.5),
                                       VIEWIMG_ATT_END);
        kwin_refresh_if_visible (canvas_get_pixcanvas (magnifier_canvas), FALSE);
        return (TRUE);
    }   /*  End Function track_canvas_event  */

Karma Home Page
Next: Dynamic Extensions Up: Karma Programming Manual Previous: Advanced Examples