#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(); };
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.
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).
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).
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.
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.
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.
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.
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.
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.
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[]) { ... }
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.
Please send questions or comments to abrighto@eso.org.
Copyright © 1998 ESO - European Southern Observatory