# E.S.O. - VLT project 
#
# "@(#) $Id: RtdImage.man3,v 1.1.1.1 2009/03/31 14:11:52 cguirao Exp $" 
#
# This file is processed by the ESO/VLT docDoManPages command to
# produce a man page in nroff, TeX and MIF formats. 
# See docDoManPages(1) for a description of the input format.
# 
# who             when       what
# --------------  ---------  ----------------------------------------
# Allan Brighton  20 Jul 95  Created
# Allan Brighton  10 Apr 96  updated 

NAME
RtdImage - The C++ class implementing the rtdimage Tk image type

PARENT CLASS
TkImage

SYNOPSIS
#include "RtdImage.h"

class RtdImageOptions : public TkImageOptions {...};

#define RTD_OPTIONS ...

class RtdImage : public TkImage {
...
public:
    RtdImage(Tcl_Interp*, const char* instname, int argc, char** argv, 
	     Tk_ImageMaster master, const char* imageType,
	     Tk_ConfigSpec* specs = (Tk_ConfigSpec*)NULL, 
	     RtdImageOptions* options = (RtdImageOptions*)NULL);
    
    ~RtdImage();
    
    virtual int call(const char* name, int len, int argc, char* argv[]);

    static int CreateImage(Tcl_Interp*, char *name, int argc, char **argv, 
		    Tk_ImageType*, Tk_ImageMaster, ClientData*);

    static void eventProc(ClientData clientData, XEvent *eventPtr);

    static void motionProc(ClientData clientData);

    int displayImageEvent(int frameId, int type, int width, int height, 
			  int xoffset, int yoffset, const Mem& data);
    
    static int rtd_set_cmap(ClientData, Tcl_Interp* interp, int argc, char** argv);

    int alloccolorsCmd(int argc, char* argv[]);
    int autocutCmd(int argc, char* argv[]);
    int bitpixCmd(int argc, char* argv[]);
    int cameraCmd(int argc, char* argv[]);
    int clearCmd(int argc, char* argv[]);
    int cmapCmd(int argc, char* argv[]);
    int colorrampCmd(int argc, char* argv[]);
    int colorscaleCmd(int argc, char* argv[]);
    int convertCmd(int argc, char* argv[]);
    int cutCmd(int argc, char* argv[]);
    int dispheightCmd(int argc, char* argv[]);
    int dispwidthCmd(int argc, char* argv[]);
    int dumpCmd(int argc, char* argv[]);
    int fitsCmd(int argc, char* argv[]);
    int flipCmd(int argc, char* argv[]);
    int frameidCmd(int argc, char* argv[]);
    int freqCmd(int argc, char *argv[]);
    int getCmd(int argc, char* argv[]);
    int graphdistCmd(int argc, char* argv[]);
    int heightCmd(int argc, char* argv[]);
    int isclearCmd(int argc, char* argv[]);
    int ittCmd(int argc, char* argv[]);
    int maxCmd(int argc, char* argv[]);
    int maxFreqCmd(int argc, char* argv[]);
    int mbandCmd(int argc, char* argv[]);
    int minCmd(int argc, char* argv[]);
    int mmapCmd(int argc, char* argv[]);
    int motioneventCmd(int argc, char* argv[]);
    int objectCmd(int argc, char* argv[]);
    int panCmd(int argc, char* argv[]);
    int perfTestCmd(int argc, char *argv[]);
    int pixtabCmd(int argc, char* argv[]);
    int previewCmd(int argc, char* argv[]);
    int radecboxCmd(int argc, char* argv[]);
    int remoteCmd(int argc, char* argv[]);
    int remoteTclCmd(int argc, char* argv[]);
    int rotateCmd(int argc, char* argv[]);
    int scaleCmd(int argc, char* argv[]);
    int shmCmd(int argc, char* argv[]);
    int spectrumCmd(int argc, char* argv[]);
    int statisticsCmd(int argc, char* argv[]);
    int typeCmd(int argc, char* argv[]);
    int updateCmd(int argc, char* argv[]);
    int viewCmd(int argc, char* argv[]);
    int warpCmd(int argc, char* argv[]);
    int wcssetCmd(int argc, char* argv[]);
    int wcsshiftCmd(int argc, char* argv[]);
    int wcscenterCmd(int argc, char* argv[]);
    int wcsdistCmd(int argc, char* argv[]);
    int wcsequinoxCmd(int argc, char* argv[]);
    int wcsheightCmd(int argc, char* argv[]);
    int wcsradiusCmd(int argc, char* argv[]);
    int wcswidthCmd(int argc, char* argv[]);
    int widthCmd(int argc, char* argv[]);
    int zoomCmd(int argc, char* argv[]);
    int zoomviewCmd(int argc, char* argv[]);

    CoordinateType getCoordinateType(const char* s);

    int convertCoordsStr(int dist_flag, 
			 char* inx_buf, char* iny_buf,
			 char* outx_buf, char* outy_buf,
			 double& x, double& y,
			 char* in_type, char* out_type);

    int convertCoords(int dist_flag, double& x, double& y, 
		      char in_type, char out_type);

    int canvasToScreenCoords(double& x, double& y, int dist_flag);
    int canvasToImageCoords(double& x, double& y, int dist_flag);
    int canvasToWorldCoords(double& x, double& y, int dist_flag);
    int screenToCanvasCoords(double& x, double& y, int dist_flag);
    int screenToImageCoords(double& x, double& y, int dist_flag);
    int screenToWorldCoords(double& x, double& y, int dist_flag);
    int imageToCanvasCoords(double& x, double& y, int dist_flag);
    int imageToScreenCoords(double& x, double& y, int dist_flag);
    int imageToWorldCoords(double& x, double& y, int dist_flag);
    int worldToCanvasCoords(double& x, double& y, int dist_flag);
    int worldToImageCoords(double& x, double& y, int dist_flag);
    int worldToScreenCoords(double& x, double& y, int dist_flag);
    int imageToChipCoords(double& x, double& y, int dist_flag);
    int canvasToChipCoords(double& x, double& y, int dist_flag);
    int screenToChipCoords(double& x, double& y, int dist_flag);
    int worldToChipCoords(double& x, double& y, int dist_flag);
    int chipToImageCoords(double& x, double& y, int dist_flag);
    int chipToCanvasCoords(double& x, double& y, int dist_flag);
    int chipToScreenCoords(double& x, double& y, int dist_flag);
    int chipToWorldCoords(double& x, double& y, int dist_flag);

    static ImageColor* colors();
    int displaymode() const;
    int fitWidth() const;
    int fitHeight() const;
    int subsample() const;
    char* file() const;
    char* newImageCmd() const;
    char* name() const;
    int usexshm() const;
    int usexsync() const;
    int shm_header() const;
    int shm_data() const;
    int min_colors() const;
    int max_colors() const;
    int verbose() const;

    int dispWidth();
    int dispHeight();

    int imageType();

    int isWcs();

    char* cameraPreCmd();
    char* cameraPostCmd();
    ImageData* image();
};


DESCRIPTION
The RtdImage C++ class implements the rtdimage Tk image type.  It is a
subclass of class TkImage, which implements the more general, Tk image
related interface, while RtdImage implements the more specific real-time
display features (see TkImage).

Class RtdImage is not normally accessed from other classes (other than
derived classes) directly, only via the Tcl command interface (see
RtdImage(n)) and the remote control interface (see rtdRemote), so this
description is aimed at those who want to understand the class inorder
to modify it or subclass from it.

The main interface is to the Tcl interpreter and the Tk image
code. The main entry point is through the initialization routine:

	extern "C" int Rtd_Init(Tcl_Interp* interp)

This routine is declared extern "C", since the C routine tclAppInit()
needs to call it (note: tclAppInit() is usually found in the file
tkAppInit.c and is required in any Tcl/Tk application for adding
extensions). To add the rtdimage extension to an application, the
following lines are added to its tclAppInit() routine:

	if (Rtd_Init(interp) == TCL_ERROR)
	    return TCL_ERROR;

Note that, since the rtdimage extension is implemented in C++, main()
also needs to be compiled and linked with a C++ compiler. Since main()
is also included in the tkAppInit.c file in the standard Tk
distribution, you either have to compile tkAppInit.c with a C++
compiler (rename it to tkAppInit.C first) or you have to extract the
main() routine and put it in a separate file (say, main.C) and compile
it with a C++ compiler (In this case, main() is a very simple
two-liner that simply calls Tk_Main() and returns).

Rtd_Init() installs the new image type "rtdimage" so that static
RtdImage member functions are called (from a table of function
pointers) whenever an rtdimage is created, displayed or deleted. The
static member functions are passed a pointer to client data, which is
actually a pointer to the RtdImage class instance (set in the image
create procedure). This pointer is used to access the class member
functions to implement the image display and subcommands.

IMAGE CREATION
When an image of type rtdimage is created (by the Tk "image create"
command), the static method "CreateImage" is called. It creates an
instance of the RtdImage class and sets the client data pointer to the
class instance for later reference (in the display and delete static
methods).

IMAGE DISPLAY HANDLING
Once an image has been created, a static method in the parent class
(TkImage::DisplayImage) is called whenever the image needs to be
displayed or redisplayed. It, in turn calls the non-static RtdImage
method displayImage() with the coordinates of the area of the image
that needs to be redrawn and the X "drawable" to draw to. The display
method updates the XImage for the given area and displays it in the 
given X drawable (see class ImageDisplay).

X SHARED MEMORY
RtdImage attempts to use X shared memory, if the "-usexshm" option is
on (default).  This improves performance, but is only available when
working on the workstation console. This is taken into consideration
in the display routine, when deciding whether or not to use X pixmaps
to cache image data in the X server.

DISPLAY MODES
Two different display modes are supported: 

In displaymode 0, the image is always copied completely to the X
server as needed. This makes scrolling smoother, since fewer trips to
the X server are needed, but is not practical for large images or
greatly magnified images due to memory and bandwidth constraints. This
mode is however used for example, by the pan window, since it must
always display the entire image (at a small size).

In displaymode 1 (default), only the part of the image that is visible
in the image window is considered. This generally means more frequent
trips to the X server, but with less data, so in the end the
performance remains acceptable for any size image.

The "-displaymode" option in the Tcl command controls the setting of
the display mode.

UPDATING THE DISPLAY
Image redisplay can be forced at any time by calling the parent class
method imageChanged().  This is done, for example, when an rtdimage
subcommand modifies the image in some way, for example by rotating or
scaling it. The imageChanged() method tells Tk the "logical" size of
the image, which is independent of the window size or the size of the
XImage and/or Pixmap being used.

SCROLLING AND CANVAS WINDOW
An image of type rtdimage can only be displayed in a canvas
window. The RtdImage class keeps track of the canvas window's
scrolling offsets and uses them to help determine which part of the
image to display. The image handling code (TkImage::GetImage) gets
called for each widget in which the image is displayed, and this is
where the check is made and the canvas window handle is saved for
later reference.

VIEWS
Normally, a Tk image can be displayed in multiple widgets and changes
in size, etc. are propagated.  For the rtdimage, a different scheme
was needed for sharing images, since changes in size should not be
propagated to all instances of the image. For example, a panning
window should display the same image, but at a smaller size and a zoom
window should display the same image at a larger size, etc. The
concept of a "view" of an rtdimage was implemented. This is a simple
array of pointers to RtdImage objects that is updated whenever the
main image is updated.

ADDING NEW OPTIONS
The return value from the "image create rtdimage" command in Tk is the
name of the image and also the name of a new Tcl command. The options
to the create command are the same as the options to the image
"configure" subcommand. These options are kept in a table and can be
fairly easily extended by adding entries to the table and to a simple
class. Derived classes can also use the RTD_OPTIONS macro to avoid
duplicating code.

#define RTD_OPTION(x) Tk_Offset(RtdImageOptions, x)
#define RTD_OPTIONS \
    {TK_CONFIG_BOOLEAN, "-usexshm",     NULL, NULL, "1", RTD_OPTION(usexshm),     0}, \
    {TK_CONFIG_BOOLEAN, "-usexsync",    NULL, NULL, "1", RTD_OPTION(usexsync),    0}, \
    {TK_CONFIG_BOOLEAN, "-verbose",     NULL, NULL, "0", RTD_OPTION(verbose),     0}, \
    {TK_CONFIG_BOOLEAN, "-shm_header",  NULL, NULL, "0", RTD_OPTION(shm_header),  0}, \
    {TK_CONFIG_BOOLEAN, "-shm_data",    NULL, NULL, "0", RTD_OPTION(shm_data),    0}, \
    {TK_CONFIG_INT,     "-displaymode", NULL, NULL, "1", RTD_OPTION(displaymode), 0}, \
    {TK_CONFIG_INT,     "-min_colors",  NULL, NULL, "1", RTD_OPTION(min_colors),  0}, \
    {TK_CONFIG_INT,     "-max_colors",  NULL, NULL, "1", RTD_OPTION(max_colors),  0}, \
    {TK_CONFIG_INT,     "-fitwidth",    NULL, NULL, "0", RTD_OPTION(fitWidth),    0}, \
    {TK_CONFIG_INT,     "-fitheight",   NULL, NULL, "0", RTD_OPTION(fitHeight),   0}, \
    {TK_CONFIG_BOOLEAN, "-subsample",   NULL, NULL, "1", RTD_OPTION(subsample),   0}, \
    {TK_CONFIG_STRING,  "-file",        NULL, NULL, "",  RTD_OPTION(file),        0}, \
    {TK_CONFIG_STRING,  "-newimagecmd", NULL, NULL, "",  RTD_OPTION(newImageCmd), 0}, \
    {TK_CONFIG_STRING,  "-name",        NULL, NULL, "",  RTD_OPTION(name), 0}

For each rtdimage configuration option, you need an entry in the above
table (from RtdImage.C) and a member in the RtdImageOptions class in
RtdImage.h:

class RtdImageOptions : public TkImageOptions {
public:
    int displaymode;		// set mode used to display image: 
				// 0 ==> XImage is size of image, update whole image to pixmap
				// 1 ==> XImage is size of window (default mode)

    int fitWidth;		// fit the image in a window with this width 
    int fitHeight;		// and this height by shrinking the image

    int subsample;		// if true, don't count neighboring pixels when shrinking image
    int usexshm;		// if true, use X shared memory if available. 
    int usexsync;               // if true, use X synchronisation if available. 
    int verbose;		// if true, print program info to stdout 

    int shm_header;		// if true, keep image FITS headers in shared memory
    int shm_data;		// if true, keep image FITS data in shared memory
				// (see RtdRemote remote access interface)

    int min_colors;		// min (max) number of colors to allocate, if this many are
    int max_colors;		// not available, use private colormap.

    char* file;			// name of image file, if any

    char* name;			// name for image (for debugging)

    char* newImageCmd;		// tcl command to evaluate whenever a new (different) 
				// image is loaded (for updates, see camera command)

    int fixUpdateRate;          // flag: user has specified a fixed update rate, as below.
    double userUpdateTime;      // the minimum time between updates, as specified
                                // by the user.

    // constructor
    RtdImageOptions()
	: displaymode(1),
	  fitWidth(0), fitHeight(0),
	  subsample(0),
	  usexshm(1),
          usexsync(1),
          verbose(0),
	  shm_header(0),
	  shm_data(0),
	  min_colors(30),
	  max_colors(60),
	  file(NULL),
	  name(NULL),
	  newImageCmd(NULL),
          fixUpdateRate(0),
          userUpdateTime(0.) {}
};


The values in this class are treated as read-only for the most part by
RtdImage. They are normally only set by Tk_ConfigureWidget when the
image is created or configured. RtdImage accesses these options
through inline functions defined at the end of RtdImage.h:

    // read-only access to configuration options
    static ImageColor* colors() {return colors_;}
    int displaymode() const {return options_->displaymode;}
    int fitWidth() const {return options_->fitWidth;}
    int fitHeight() const {return options_->fitHeight;}
    int subsample() const {return options_->subsample;}
    char* file() const {return options_->file;}
    char* newImageCmd() const {return options_->newImageCmd;}
    char* name() const {return ((options_->name && *options_->name) 
                        ? options_->name : instname_);}
    int usexshm() const {return options_->usexshm;}
    int usexsync() const {return options_->usexsync;}
    int shm_header() const {return options_->shm_header;}
    int shm_data() const {return options_->shm_data;}
    int min_colors() const {return options_->min_colors;}
    int max_colors() const {return options_->max_colors;}
    int verbose() const {return options_->verbose;}

Where options_ is the name of the RtdImageOptions class object.

ADDING NEW IMAGE SUBCOMMANDS
The rtdimage subcommands are also defined in a table. In the
TclCommand class hierarchy, there is a scheme that allows subcommands
to be inherited and extended at any level of the inheritance
hierarchy.  (Options can also be inherited by deriving a subclass of
RtdImageOptions and using the #define RTD_OPTIONS in the option
array.)  The following table is used to declare rtdimage subcommands
in RtdImage.C:

/* 
 * declare a table of image subcommands and the methods that handle them.
 *
 * NOTE: keep this table sorted, so we can use a binary search on it !
 * (select lines in emacs and use M-x sort-lines)
 */
static class RtdImageSubCmds {
public:
    char* name;      // method name
    int (RtdImage::*fptr)(int argc, char* argv[]); // ptr to method
    int min_args;    // minimum number of args
    int max_args;    // maximum number of args
} subcmds_[] = { 
    {"alloccolors", &RtdImage::alloccolorsCmd,  0,  1},
    {"autocut",     &RtdImage::autocutCmd,      0,  2},
    {"bitpix",      &RtdImage::bitpixCmd,       0,  0},
    {"camera",      &RtdImage::cameraCmd,       1,  4},
    {"clear",       &RtdImage::clearCmd,        0,  14},
    {"cmap",        &RtdImage::cmapCmd,         1,  2},
    {"colorramp",   &RtdImage::colorrampCmd,    0,  0},
    {"colorscale",  &RtdImage::colorscaleCmd,   0,  1},
    {"convert",     &RtdImage::convertCmd,      7,  7},
    {"cut",         &RtdImage::cutCmd,          0,  2},
    {"dispheight",  &RtdImage::dispheightCmd,   0,  0},
    {"dispwidth",   &RtdImage::dispwidthCmd,    0,  0},
    {"dump",        &RtdImage::dumpCmd,         1,  5},
    {"fits",        &RtdImage::fitsCmd,         1,  2},
    {"flip",        &RtdImage::flipCmd,         1,  2},
    {"frameid",     &RtdImage::frameidCmd,      0,  0},
    {"get",         &RtdImage::getCmd,          3,  5},
    {"graphdist",   &RtdImage::graphdistCmd,    5,  5},
    {"height",      &RtdImage::heightCmd,       0,  0},
    {"isclear",     &RtdImage::isclearCmd,      0,  0},
    {"itt",         &RtdImage::ittCmd,          1,  2},
    {"max",         &RtdImage::maxCmd,          0,  0},
    {"mband",       &RtdImage::mbandCmd,        6,  6},
    {"min",         &RtdImage::minCmd,          0,  0},
    {"mmap",        &RtdImage::mmapCmd,         0,  7},
    {"motionevent", &RtdImage::motioneventCmd,  0,  1},
    {"object",      &RtdImage::objectCmd,       0,  0},
    {"pan",         &RtdImage::panCmd,          1,  3},
    {"perftest",    &RtdImage::perfTestCmd,     1,  2},
    {"pixtab",      &RtdImage::pixtabCmd,       1,  3},
    {"preview",     &RtdImage::previewCmd,      1,  1},
    {"radecbox",    &RtdImage::radecboxCmd,     3,  3},
    {"remote",      &RtdImage::remoteCmd,       0,  1},
    {"remotetcl",   &RtdImage::remoteTclCmd,    1,  1},
    {"rotate",      &RtdImage::rotateCmd,       0,  1},
    {"scale",       &RtdImage::scaleCmd,        0,  2},
    {"shm",         &RtdImage::shmCmd,          0,  7},
    {"spectrum",    &RtdImage::spectrumCmd,     9,  9},
    {"statistics",  &RtdImage::statisticsCmd,   0,  0},
    {"type",        &RtdImage::typeCmd,         0,  0},
    {"update",      &RtdImage::updateCmd,       0,  1},
    {"userfreq",    &RtdImage::maxFreqCmd,      1,  1},
    {"view",        &RtdImage::viewCmd,         2,  11},
    {"warp",        &RtdImage::warpCmd,         2,  2},
    {"wcscenter",   &RtdImage::wcscenterCmd,    0,  2},
    {"wcsdist",     &RtdImage::wcsdistCmd,      4,  4},
    {"wcsequinox",  &RtdImage::wcsequinoxCmd,   0,  0},
    {"wcsheight",   &RtdImage::wcsheightCmd,    0,  0},
    {"wcsradius",   &RtdImage::wcsradiusCmd,    0,  0},
    {"wcsset",      &RtdImage::wcssetCmd,       0,  11},
    {"wcsshift",    &RtdImage::wcsshiftCmd,     3,  3},
    {"wcswidth",    &RtdImage::wcswidthCmd,     0,  0},
    {"width",       &RtdImage::widthCmd,        0,  0},
    {"zoom",        &RtdImage::zoomCmd,         1,  3},
    {"zoomview",    &RtdImage::zoomviewCmd,     1,  5}
};

The above table maps subcommand name to class method that implements
the command. The additional fields indicate the minimum and maximum
number of arguments the subcommand expects (for the subcommands, argc
is the number of arguments and argv[0] the first argument after the
subcommand name). The parent class (and any future derived classes)
also declare a similar table and also the virtual method "call", that
calls a method in a class, given the method name:

/*
 * Call the given method in this class with the given arguments
 * If the method is not defined here, pass on the search to the
 * parent class. Since this is a virtual function, the search starts
 * in the most specific class.
 */
int RtdImage::call(const char* name, int len, int argc, char* argv[])
{
 ...
}


DERIVING A SUBCLASS OF RTDIMAGE
If you want to derive a class from RtdImage, in order to modify or
extend its behavior in some way, this is the way you would do it:

In tkAppInit.C, replace the call to Rtd_Init with your own copy
of that function: say MyRtd_Init(), so that you can install your
own derived class.  The new function and declarations would look
something like this:

   class MyRtdImage : public RtdImage {
	...
   }

   // image structure needed for Tk images
   static Tk_ImageType myRtdImageType = {
        "rtdimage",			/* name */
        MyRtdImage::CreateImage,	/* createProc */
        TkImage::GetImage,	        /* getProc */
        TkImage::DisplayImage,      	/* displayProc */
        TkImage::FreeImage,	        /* freeProc */
        TkImage::DeleteImage,		/* deleteProc */
        (Tk_ImageType *) NULL		/* nextPtr */
   };


   // called from tkAppInit
   extern "C" int Rtd_Init(Tcl_Interp* /* interp */)  
   {
       Tk_CreateImageType(&myRtdImageType);
       return TCL_OK;
   }


   /*
    * This static method is called by the Tk image code to create
    * a new (MyRtdImage) image.
    */
   int RtdImage::CreateImage(
       Tcl_Interp *interp, // Interpreter for application containing image.
       char *name,     // Name to use for image.
       int argc,       // Number of arguments.
       char **argv,    // Argument strings for options 
		       // (not including image name or type)
       Tk_ImageType *typePtr, // Pointer to our type record (not used). 
       Tk_ImageMaster master, // Token for image, to be used by us in 
			      // later callbacks.
       ClientData *clientDataPtr)// Store manager's token (this ptr) 
				 // for image here,
				 // it will be returned in later callbacks.
   {
       MyRtdImage* im = new MyRtdImage(interp, name, argc, argv, master);
       *clientDataPtr = (ClientData) im;
       return im->status();
   }

After this, you can define methods for new image commands (to be
called from Tcl level), redefine some existing behavior or add
interfaces to C/C++ code or other processes.

To add new Tcl image subcommands, declare a table like the one
described above (each class in the hierarchy that defines subcommands
declares one of these).  For example:

   /* 
    * declare a table of image subcommands and the methods that handle them.
    */
   static class MyRtdImageSubCmds {
   public:
        char* name;      // method name
       int (MyRtdImage::*fptr)(int argc, char* argv[]); // ptr to method
       int min_args;    // minimum number of args
       int max_args;    // maximum number of args
   } subcmds_[] = { 
       {"foo",         &MyRtdImage::fooCmd,          1,  3},
       {"bar",         &MyRtdImage::barCmd,          2,  4},
       ....
   };

Now we need to declare the virtual member function "call" to call the
method to handle the Tcl subcommands (this is the same in every class,
but still needs to be redefined - it probably could also be defined as
a macro):

   /*
    * Call the given method in this class with the given arguments
    * If the method is not defined here, pass on the search to the
    * parent class. Since this is a virtual function, the search starts
    * in the most specific class.
    */
   int MyRtdImage::call(const char* name, int len, int argc, char* argv[])
   {
	...
   }

The rtdimage options can also be extended, if needed, for example:

    class MyRtdImageOptions : public RtdImageOptions {
    public:
      char *grid_tag;               // canvas tag for all grid items
      char *component;              // NDF component to display
  
      MyRtdImageOptions() : grid_tag(NULL), component(NULL)  {}
    };

    #define MYRTD_OPTION(x) Tk_Offset(MyRtdImageOptions, x)
    #define MYRTD_OPTIONS \
        RTD_OPTIONS, \
        {TK_CONFIG_STRING,  "-grid_tag",    NULL, NULL, 
           "ast_grid_item", MYRTD_OPTION(grid_tag), 0}, \
        {TK_CONFIG_STRING,  "-component",   NULL, NULL, 
           "data", MYRTD_OPTION(component), 0}

Each class in the hierarchy should follow these conventions and define
the options as above, so that derived classes can access them and add
to them.

SEE ALSO
TkImage, RtdImage(n), image(n), canvas(n)
--------------------------------------------------------------------

