Table of Contents Previous Chapter

CHAPTER 9 Interactive Graphics Capability (IGC)


The Interactive Graphics Capability (IGC) is a process that displays meteorological data (satellite images, station plots, radar, gridded model graphics, soundings, etc...) inside an X window.

An IGC process provides some user interface, but the majority of its interface comes from the UI process which is described in Chapter8.

The actual conversion from data to its pictorial form is done by Depictable objects which are described in detail in Chapter10.

This chapter will first discuss the software and hardware issues that influenced the design of the IGC. Then it will describe the IGC process environment, including what the major objects are, what they do, and how they interact with each other. Finally, this chapter will provide step by step descriptions of how the IGC implements its major features such as loading and displaying a product, zooming, looping, sampling, etc.

9.1 IGC Design Drivers

All workstations developed at FSL prior to the WFO-Advanced were carefully crafted around highly specialized display hardware. The display hardware not only performed many powerful display functions, such as zooming and panning, but also had a unique architecture that supported animation from local display memory and high speed disks. Because of this specialized hardware many display functions were easy to implement, fast in performance, and required very little of the host's processing resources.

FSL's previous workstations used a single large window to display all meteorological data: contours, images, and plots, whether they were generated in advance or interactively by means of an application. However, there was a desire on the part of a number of meteorologists to have an additional display window for the purpose of monitoring weather in a particular area or to display the output of an application, such as a skew-T or a time-series plot. A proposed solution was to create an additional small window along one side of the screen strictly for applications. This would allow the forecaster to look at applications without losing the display in the large window. For closer inspection, the user could transfer the contents of the application window to the large window. At the same time, the contents of the large display would be transferred to the small application window so it would still be visible. This approach, suggested initially for the UNIX PC workstation became easy to implement on a UNIX workstation with the X window system.

The first prototype of this approach was implemented on a Sun Sparc 2 and consisted of one large square window and four small windows to the left side of the large window. For simplicity, a single process controlled and managed the information in all the windows. Since the Sparc2 had only an eight bit (256 colors) lookup table, the images and graphics in the five windows had to share a common lookup table. Before an image was loaded into a window the value of each image pixel was converted to correspond to the appropriate color in the lookup table. As long as all the images and graphics used similar colors this approach provided reasonably good results. However, if one of the images was displayed in the gray scale then the number of available colors and gray scale values became unacceptable. The experience with this prototype resulted in two significant recommendations: 1) increase the number of available colors by using a 24-bit color system, and 2) reduce the complexity of managing five windows by creating a separate process (IGC) for each window.

A prototype was tested on a Sun Sparc 2 GTX workstation with 24 bits of color. The five windows now shared over 16 million colors, more than adequate for all workstation displays. Each pixel of an image was converted and stored as 24 bits of data plus eight bits of zeros to fill a 32-bit computer word. The drawback to this approach was the four-fold increase in computer memory requirements. For an animation sequence of 32 1000x1000-pixel images, the memory requirement rose from 32 to 128 megabytes. This larger size resulted in slower loading and animation rates because considerably more bytes had to be transferred into and out of memory. An interesting side effect on the GTX was the sequential changing of display colors as the red, green and blue values were transferred from memory to the screen.

The introduction of Hewlett Packard workstations with the CRX24 display controller provided additional options for implementing multiple display windows. In addition to its true 24-bit color, the CRX24 supports four separate 8-bit color tables: one table for the image underlay, a second private table for overlays, a third for overlays needed by the X window manager (and also the default), and a fourth for transparent displays. Instead of deciding on either 8 bits or 24 bits of color, it is now possible to consider a hybrid approach using 8-bit and 24-bit colors for the display windows. The suggested approach uses 8-bit colors in the large window and 24-bit colors in the smaller windows. This satisfies the requirement for a full range of 256 colors in all the display windows. Using this approach, the large window requires approximately 32 megabytes and each small window approximately 5 megabytes of memory for a 32-frame animation sequence, for a total of 52 megabytes for all five windows. If additional savings in memory is desired, the maximum animation length in the small windows can be reduced to a smaller number. The total memory requirement for this hybrid approach, even when considering that the host workstation has to drive two monitors, is considered reasonable. Aside from these memory considerations, this hybrid approach also provides acceptable load and animation performance since the images are relatively small in size.

Although this approach adequately addresses the memory and some basic performance concerns, it also results in increased software complexity since the same data must be loaded differently depending on whether they are loaded to an 8-bit or 24-bit window. The display for each small window is prepared in a 24-bit (i.e. 32-bit computer word) pixmap in the host memory. When a satellite image with a graphic overlay is desired, the 8-bit satellite pixels are converted and mapped to a 24-bit pixmap and the vector graphic is then merged with the image in the pixmap. To remove the graphics from the displayed image, the satellite image is reloaded into the pixmap. To avoid the load time from the database, a second pixmap exists for each frame that contains only the image. When interactively moving a graphics line in the small window this second pixmap refreshes the image each time the line is moved. Because the windows are small (200 by 200 pixels), the performance in these small windows is good.

To load the same image and graphics combination described above into the large window requires a different technique. Images are loaded into an 8-bit underlay window and vector graphics are added by using the transparent overlay window. Each vector graphic is prepared as a bit map and combined with other graphics in an 8-bit pixmap. A technique known as "stipple fill" is employed to draw additional graphics into the pixmap. Although not practically useful, as many as 256 graphics can be drawn into the pixmap and displayed in the overlay window.

The complexity of the IGC is increased further by a requirement to subdivide the display window into four separate panels and load each panel with a set of products. This is useful if the user wants to look at radar reflectivity at four different elevation tilts or view different satellite channels. As long as the same color table is needed for all four panels, the large display window can adequately display all products using the 8-bit color architecture. One can assure that the 8-bit color architecture suffices by carefully defining the products that can be loaded into the four panels in advance. However, if the user is given the capability to modify the pre-defined four-panel display by loading new data into a panel then the possibility of exceeding the number of available colors is high. To assure that sufficient colors are always available for all panels, the IGC needs to be able to concurrently support the 24-bit color mode. A special algorithm is implemented that assigns either 8 bits or 24 bits of color to a panel based on the data displayed in the panel. In order to retain good performance, the algorithm tries to assign 8-bit color tables to as many panels as possible and re-examines the assignments each time a new product is loaded.

9.2 Process Environment

9.2.1 Child Process

The common invocation of the IGC is as a child process, invoked by the user interface process (UI process). The parent process is responsible for defining the area in which an IGC will display. For the D2D workspace, the UI will create five X windows and position them inside the UI's application window. All of these windows will be created using the underlay planes of the graphics hardware, and will be capable of displaying images. Four small windows are created with a 24-bit visual, while the large window is created with an 8-bit visual. With this scheme, five images with different color requirements can be displayed simultaneously.

9.2.1.1 Invocation Arguments

The UI process invokes an IGC process for each of the five display areas, passing the area's X window ID as a process argument. The IGC is allowed to draw into that window, solicit events for that window, and create additional child windows to be displayed on top of that window. However, the UI process is the owner of that window, and is the only process that can destroy it. The UI process can also tell an IGC to use a different window for its layout during a swap operation which is described in detail in Section 9.6.5, "Swapping".

The UI process assigns an ID to each IGC which is also passed as a program argument. Currently the ID is an integer in the range 0..4. The ID is used by the UI process to identify which IGC process is sending an IPC message to the UI.

The UI also informs the IGC whether it is running in a small or large display area through a process argument. An IGC will exhibit the following differences depending on whether it is running in a large or small display area:

The last argument of interest is the IPC target of the parent process. An IGC needs to send messages to the UI process in order to inform the parent of its state, so that the UI can keep its UI widgets in sync with what is being displayed.

9.2.1.2 User Interface

As a child process, the IGC is dependent upon the UI process for its user interface. Most of the UI process widgets affect only the IGC in the large display area. Since the IGC is in a different process, commands to initiate IGC actions are in the form of IPC messages.

However, an IGC running as a child process does provide some of its own user interface through the use of the mouse. An IGC can receive these X events directly. These events are enough to manage context-sensitive pop-up menus and some dialog boxes, such as a dialog to choose an overlay graphic's color.

9.2.2 Stand-Alone Process

The IGC can be run by itself without having to run the UI process. The advantages of this approach is that the start-up time for a single IGC is much less than start-up time for five IGC processes and a UI process. Also, it is slightly easier to connect a debugger to a stand-alone process. However, without the UI process, the menu bar, control bar, and dialog boxes (including the volume browser) are replaced by a far-less elegant, harder to use keyboard interface. Thus, running as a stand-alone process is useful for developers but not practical for end users.

9.2.2.1 Invocation Arguments

An IGC requires an X window that defines its display area. The UI normally has the responsibility of creating that window. However, in stand-alone mode, the IGC has to create its own window, which will be a window with window manager decorations whose parent is the root window. If the IGC process is invoked with the "trueColor" argument, then this window will be a 24-bit window. If the IGC_Process is invoked with no arguments, then the window will be an eight-bit window.

9.2.2.2 User Interface

Some of the functionality that the UI process provides is implemented in a keyboard interface. For example, to load a product, the user would type in "[<A product key>]". The complete key bindings are described in the file, igc/KeyLegend.Doc.

The user interface provided by the mouse that is available to the IGC as a child process is also available as a stand alone process.

9.2.3 Thread of Execution

Like many display applications, an IGC process performs initialization, waits for and processes events, destroys its objects, and then terminates.

9.2.3.1 Initialization

An IGC does the following during process initialization:

9.2.3.2 Event Processing

After initialization, an IGC enters an event dispatch loop which is managed by the EventDispatcher object. Execution will remain inside this loop until the EventDispatcher receives an event indicating that the process should be terminated. If the IGC is a child process, this event will be an IPC message from the UI process. If the IGC is a stand-alone process, this event will be an <Alt Q> entered from the keyboard.

9.2.3.3 Termination

After exiting the event dispatch loop, the IGC will destruct the UI_WorkspaceManager, IGC_Impl, and EventDispatcher objects. The IGC_Impl object will then destruct most of the important IGC objects. The EventDispatcher needs to be destructed last, since other IGC objects need to refer to it, upon their destruction.

Destructing objects is important, since the IGC may have started some child extension processes which need to be terminated. Also, the IGC may have registered with the NotificationServer process and will need to un-register itself.

9.3 Overview of the Main Objects

This description of the objects that provide the IGC functionality will have the following format:

9.3.1 EventDispatcher

9.3.1.1 Process Context

Only one EventDispatcher object exists in an IGC process. The process's main module is responsible for the construction and deletion of this object. A pointer to the X server connection is needed to construct this object since the EventDispatcher will be making Xlib calls directly.

9.3.1.2 Responsibilities

Listens for the following kinds of events by polling, and dispatches these kinds of events to multiple clients. Polling is inefficient! It would be preferable to use the UNIX select system call, but DMQ doesn't support file descriptors.

9.3.2 IGC_Impl

9.3.2.1 Process Context

Only one IGC_Impl object exists in an IGC process. The process's main module is responsible for the construction and deletion of this object. The following is needed to construct this object.

9.3.2.2 Inheritance

IGC_Impl inherits from the IGC_ABC class. This object is the implementation portion of a "Proxy" design pattern. The proxy portion of this pattern is the IGC object that lives inside the UI process. The IGC_ABC class ensures that the public interface between the proxy and the implementation remains consistent.

9.3.2.3 Responsibilities

9.3.3 UI_WorkspaceManager

9.3.3.1 Process Context

Only one UI_WorkspaceManager object exists in an IGC process. The process's main module is responsible for the construction and deletion of this object. The following is needed to construct this object.

9.3.3.2 Responsibilities

UI_WorkspaceManager's primary responsibility is to be a proxy to the UI process. In other words, any IGC object that needs to send information to the UI process will do so by calling a public method of this object. This object is qualified to send messages to the UI since it knows the UI's IPC address and the ID of this IGC.

9.3.4 LoopingMgr

9.3.4.1 Process Context

Only one LoopingMgr object exists in an IGC process. The IGC_Impl is responsible for the construction and deletion of this object.

9.3.4.2 Inheritance

LoopingMgr inherits from the TimerEventClient class so that it can receive a callback from the EventDispatcher when a timer expires.

9.3.4.3 Responsibilities

9.3.5 DisplayMgr

9.3.5.1 Process Context

Only one DisplayMgr object exists in an IGC process. The IGC_Impl is responsible for the construction and deletion of this object. The following is needed to construct this object.

9.3.5.2 Modes

Most often, an IGC will run with a single display panel. But sometimes it is useful to divide the display into four equal portions, such as for displaying four different tilts of radar.

The DisplayMgr implements a single panel layout by constructing a single DisplayPanel object, passing the main layout window as an argument. When switching to a four-panel layout, that DisplayPanel object will be destructed.

The implementation of a four-panel layout is slightly more complicated. This object will create four sub-windows on top of the main layout window. These windows will not be transparent, so the main layout window will not be visible. These four windows will equally subdivide the area of the parent window, and will have the same depth (8 or 24 bits) as the parent window. This object will then create four DisplayPanel objects, each receiving one of those four sub-windows as a constructor argument. When switching to a single-panel layout, the four sub-windows and the four DisplayPanel objects will be destructed.

9.3.5.3 Responsibilities

9.3.6 DisplayPanel

9.3.6.1 Process Context

Either one or four of these DisplayPanel objects exist at one time during the life of the IGC process, depending on the mode of the DisplayMgr (see Section9.3.5.2). The DisplayMgr object is responsible for the construction and deletion of these objects. The ID of the X window that this panel object will use as the image window is required for construction.

9.3.6.2 Inheritance

Inherits from the DisplayAreaEventClient class so that it can receive a callback from the EventDispatcher whenever an Xlib event occurs inside the panel windows.

Inherits from the TimerEventClient class so that it can receive a callback from the EventDispatcher when a timer expires. This is used to distinguish button clicks from button drags.

Inherits from the DisplayPanelActionsClient class so that it can receive a callback from the PopUpActionMgr whenever the user selects an option from a pop-up that pertains to this display panel.

9.3.6.3 Modes:

This object can run using either a pseudo-color (8-bit) display algorithm or a true color (24-bit) display algorithm.

In 8-bit mode, this object will use two X windows mapped to the same screen space. The bottom window is the same window that was passed in at construction time. This window will be used to display images using a private X colormap, utilizing all 256 colors. This window is created with the visual that uses the underlay (image) planes of the H-CRX24 hardware. The top window will be created by this object, and will be a child window of the bottom window. In order to see the bottom window, the top window will have a transparent background. This window will be used to display graphic products (non-images) and legends. This window is created with the visual that uses the overlay planes and the transparency feature of the H-CRX24 hardware.

Here are some of the advantages of using 8-bit mode with a two window architecture:

However, there are these disadvantages:

In 24-bit mode, this object will use the window passed in at construction time as the only panel window. Thus, all graphics and legends will be displayed directly on top of the image in the same window.

Here are the advantages of the 24-bit mode.

And there are also disadvantages:

9.3.6.4 Responsibilities

9.3.7 ViewMgr

9.3.7.1 Process Context

Only one ViewMgr object exists in an IGC process. The IGC_Impl is responsible for the construction and deletion of this object.

9.3.7.2 Responsibilities

9.3.8 FrameSeq

9.3.8.1 Process Context

Only one FrameSeq object exists in an IGC process. The IGC_Impl is responsible for the construction and deletion of this object.

9.3.8.2 Inheritance

Inherits from IdleStateClient class so that it can receive a callback from the EventDispatcher whenever there is no other events to process.

Inherits from OverlayActionsClient class so that it can receive a callback from the PopUpActionMgr whenever the user selects an option from a pop-up that pertains to a particular overlay.

9.3.8.3 Responsibilities

9.3.9 Frame

9.3.9.1 Process Context

During the entire life of the IGC process, the number of Frame objects that exist is equal to the maximum frame count (which is currently 32, for a large IGC). The FrameSeq object is responsible for the construction and deletion of these objects.

9.3.9.2 Responsibilities

9.3.10 GraphicDepictTuple

9.3.10.1 Process Context

The number of GraphicDepictTuple objects that exist in an IGC_Process depends on the number of graphic (non-image) products that are loaded. The number of tuples for each graphic product is equal to the number of unique times from the time-matched inventory. The FrameSeq object is responsible for the construction and deletion of these objects. The following is needed to construct these objects:

9.3.10.2 Inheritance

GraphicDepictTuple inherits from the DepictTuple class because graphics and images have a number of characteristics in common, and a few that differ. Also, it is convenient for the Frame and FrameSeq objects to think of just DepictTuple objects, rather than GraphicDepictTuple or ImageDepictTuple objects.

9.3.10.3 Responsibilities

9.3.11 ImageDepictTuple

9.3.11.1 Process Context

The number of ImageDepictTuple objects that exist in an IGC_Process depends on the number of image products that are loaded. The number of tuples for each image product is equal to the number of unique times from the time-matched inventory. The FrameSeq object is responsible for the construction and deletion of these objects. The following is needed to construct these objects:

9.3.11.2 Inheritance

ImageDepictTuple inherits from the DepictTuple class because graphics and images have a number of characteristics in common, and a few that differ. Also, it is convenient for the Frame and FrameSeq objects to think of just DepictTuple objects, rather than GraphicDepictTuple or ImageDepictTuple objects.

9.3.11.3 Responsibilities

9.3.12 GraphicDepictSeq

9.3.12.1 Process Context

The number of GraphicDepictSeq objects that exist in an IGC_Process is the number of graphic (non-image) products that are loaded. The FrameSeq object is responsible for the construction and deletion of these objects. The following is needed to construct these objects:

9.3.12.2 Inheritance

Inherits from the DepictSeq class because graphics and images have a number of characteristics in common, and a few that differ. Also, it is convenient for the FrameSeq objects to think of just DepictSeq objects, rather than GraphicDepictSeq or ImageDepictSeq objects.

9.3.12.3 Responsibilities

9.3.13 ImageDepictSeq

9.3.13.1 Process Context

The number of ImageDepictSeq objects that exist in an IGC_Process is the number of image products that are loaded. The FrameSeq object is responsible for the construction and deletion of these objects. The following is needed to construct these objects:

9.3.13.2 Inheritance

Inherits from the DepictSeq class because graphics and images have a number of characteristics in common, and a few that differ. Also, it is convenient for the FrameSeq objects to think of just DepictSeq objects, rather than GraphicDepictSeq or ImageDepictSeq objects.

9.3.13.3 Responsibilities

9.4 Walk-through of Loading a Product

9.4.1 Load of a product to an empty display

Initial Situation: No time varying products are loaded, but static products (maps /topo image, etc.) may be loaded and displayed.

  1. User selects product from the user interface of the fxa process. An IPC message is sent containing a depict key, load mode, inventory time, forecast time, and a combine flag. The IGC_Impl object receives this message and then calls FrameSeq::load.
  2. Checks for an implicit clear. Product may require a different map projection than the one being used. If so, then all the existing products are unloaded.
  3. If combine flag is not set, and the new product is an image, unloads any images that are being displayed in the panel in which the new image will be displayed.
  4. Obtains an inventory from dataMgmt.
  5. Calls the routine that time-matches primary sequences, passing the preferred frame count.
  6. Assigns each time on the time match list to the corresponding frame's nominal frame time.
  7. Copies the pointers to the static (map background) tuples in the first frame to the rest of the frames, and increases the use count of these static tuples.
  8. Creates a new DepictSeq object, and adds it to the beginning of the load list. Passes to the DepictSeq constructor the load information that was received from the UI process.
  9. For each time in the time match list, checks to see if there is a DepictTuple object with the same time and sequence. If not, creates one, and adds it to the tuple table. Then adds a pointer to that tuple to the corresponding frame, and increases the tuple's use count.
  10. Analyzes the image color table requirements for each display panel. If the IGC layout is single panel, then this step is skipped. If all the panels have the same color table requirement, then nothing further needs to be done. If there is more than one requirement, the requirement with the most panels, (or in the case of a tie, the combo requirement) will be used to determine the panels that can be displayed in 8-bit pseudo color. All the other panels will be converted to 24-bit true color.
  11. Redraws the current frame, and marks all other frames as needing redrawing.

9.4.2 Load of a product to a non-empty display

Initial Situation: One or more time varying products have already been loaded.

  1. Follows steps 1 - 4 described in Section9.4.1.
  2. Calls the routine that time matches overlay sequences, passing the nominal frame times.
  3. Tries to find a DepictSeq object with the same load attributes. If not, create one, and add to the load list before all the static products but after all the existing time-varying products.
  4. Follows steps 9 - 11 described in Section9.4.1.

9.4.3 Image combo load

Same steps as the single image, except that an existing image is not unloaded. Rather, its DepictTuple and DepictSeq objects are expanded to include the new image as the second image.

9.4.4 Multi-load

A Multi-load is a way of loading multiple products with one load command. This mechanism is used for families, radar combos, and four panel configurations.

When a multi-load command arrives from the UI, the IGC uses that (single) key to look up the following information in a dataMgmt table:

The IGC then loads each product, but defers the display and rendering until all the products are processed.

9.4.5 Product update (auto-update)

The next four steps are also executed whenever the user changes the preferred frame count.

9.5 Walk-through of Displaying a Product

9.5.1 Displaying the current frame

The current frame needs to be displayed whenever any of the following occurs:

To display the current frame, the following steps are taken:

  1. Every tuple in the current frame is examined. Image tuples are processed first followed by graphic tuples. This is because if the display panel is 24 bits, the image needs to be drawn first, so graphics will be drawn on top of it. For 8-bit panels, the ordering is not important, since two different windows are used.
  2. For each tuple that is visible (the tuple's DepictSeq object maintains the visibility flag), the following is done:
    · Renders the tuple, if necessary. The ViewMgr's zoom and density parameters are compared with the tuple's zoom and density parameters the last time it was rendered. If they are the same, then this tuple does not need to be rendered. The details of rendering a tuple are covered in Section9.5.3 and Section9.5.4.
    · Copies the tuple's drawable to every affected display panel. (The DepictSeq object also maintains the panel information).

    For an image drawable, the DisplayPanel object will take that drawable directly and assign it as the image window's background pixmap. The panel object will also receive a pointer to the ColorTable object which is instructed to load the image's 256 colors into the image window's private colormap. Loading the color table is not necessary for 24-bit panels.

    For a graphic drawable, the 1-bit pixmap (bitmap) is copied into the DisplayPanel object's backing graphics pixmap corresponding to the current frame. This copy is done with a stipple fill in order to preserve what was drawn by previous tuples. The color of the tuple is applied when the tuple's pixmap is copied into the panel's pixmap.

  3. Once all the tuples have been examined, every DisplayPanel object will do the following:
    · Obtain a list of tuple objects from the current frame. It will display the legends of each tuple, with the first legend displayed in the lower right corner of the display panel and the others stacked on top. If the tuple is visible, then the legend is displayed in the color of the tuple, otherwise the legend is displayed in gray.
    · Assign the graphics backing pixmap corresponding to the current frame to the graphics window's background pixmap.

9.5.2 Preparing frames in the background

As mentioned earlier, one goal of the IGC is to support fast looping speeds, up to 20 frames per second. In order to achieve this goal, each frame's picture needs to be rendered into a pixmap (or two pixmaps in the case of 8-bit display panels). The advantage of using a pixmap is that it can be displayed into a window rather quickly, and it can be filled off screen, behind the user's back, while the IGC process is waiting for events. If the user wants to step or loop to a frame that hasn't been rendered to a pixmap yet, then the user will notice a delay. The length of the delay depends on how many products are in the frame, and how long it takes for each product's depictable to produce its picture. So, the trade-off is to perform as much rendering as possible while the workstation is idle, but still respond promptly to the user's actions.

Here is how those pixmaps are prepared in the background.

  1. When the EventDispatcher doesn't have any X, IPC, or timer events to process, it calls a method in the FrameSeq object. This method counts the total number of frames that need to be redrawn, then figures out which pie cursor the DisplayMgr object should display based on the ratio between frames needing redrawing and the total number of frames. If no frames need to be processed, then the default cursor is displayed.
  2. One of those Frame objects is then instructed to prepare a single tuple as described in Section9.5.1 step 2. However, instead of copying the tuple's bitmap or pixmap to the X window, it is copied to a pixmap corresponding to the frame that is being processed.
  3. If that frame has processed its last tuple, then the legends for that frame are drawn into the pixmap corresponding to that frame, and the frame is marked as no longer needing redrawing.

9.5.3 Rendering a graphic tuple

Graphic rendering involves passing a drawable (which can be thought of as a blank canvas) and having the depictable (the artist) paint a graphic representation of the data into the canvas. Since the drawable is only one bit deep, the depictable does not have to deal with color. The following steps are taken by the GraphicDepictTuple object:

  1. Tells the DisplayMgr object to change the cursor to its busy representation (wrist-watch).
  2. Clears the drawable by setting all the pixels to 0.
  3. Instructs the depictable to paint into the drawable by passing the depictable the following information:
    · data needed to use Xlib, such as the pointer to the display connection, the drawable, and the graphics context;
    · the extent (width and height) of the drawable;
    · zoom, picture density, and line texture data obtained from the ViewMgr object.;
    · scale and map projection data obtained from the ViewMgr object.
  4. Restores the cursor.

9.5.4 Rendering an image tuple

Image rendering is similar to graphic rendering, except that the canvas is a byte (8-bit) buffer instead of an X drawable. Each ImageDepictable object (there might be two in the case of image combo) will fill its byte buffer with each pixel representing a color index between 0 and 255. The display panel will expand and/or combine the buffers, and then instruct Xlib to copy the buffer into the tuple's drawable. The details differ depending on whether it is a single or combo image, and whether the display panel is 24 or 8 bits deep.

9.5.4.1 Rendering a single 8-bit image

The following steps are performed by an ImageDepictTuple object:

  1. Tells the DisplayMgr object to change the cursor to its busy representation (wrist-watch).
  2. Allocates a buffer which is an array of bytes (char) whose size is the width * height of the tuple's drawable.
  3. Instructs the ImageDepictable object to paint into the byte buffer by passing the following information:
    · a pointer to the buffer;
    · the extent (width and height) of the drawable;
    · zoom, picture density, and line texture data;
    · scale and map projection data.
  4. Passes the byte buffer to Xlib, to copy it into the tuple's 8-bit pixmap.
  5. De-allocates the memory allocated in step 2.
  6. Restores the cursor.

9.5.4.2 Rendering a single 24-bit image

The following steps are performed by an ImageDepictTuple object:

  1. Executes the first three steps described in "Rendering a single 8-bit image."
  2. Allocates a second byte buffer, which uses 4 bytes for every pixel. Thus, the size of this buffer is 4 times that of the buffer used as the depictable's canvas.
  3. For each byte of the buffer filled by the Image Depictable object, the tuple looks up the red, green, and blue intensity values using the image color table and pixel index represented by that byte. The three color components are then multiplied by the image brightness value, a float between 0 and 1 that is maintained by the DisplayMgr object. Four bytes are copied into the second byte buffer: a blank (0) byte, and one byte for each of the red, green, and blue values.
  4. Passes the second buffer to Xlib, so it can be copied into the tuple's 24-bit pixmap.
  5. De-allocates both byte buffers.
  6. Restores the cursor.

9.5.4.3 Rendering an 8-bit image combo

The following steps are performed by an ImageDepictTuple object:

  1. Tells the DisplayMgr object to change the cursor to its busy representation (wrist-watch).
  2. Allocates a buffer which is an array of bytes (char) whose size is the width * height of the tuple's drawable.
  3. Creates a ComboDepictable object, by passing pointers to the two ImageDepictable objects owned by this tuple. This step needs to be done only when rendering for the first time or whenever one of the image depictable objects is replaced.
  4. Instructs the ComboDepictable object to paint into the byte buffer by passing the depictable the following information:
    · a pointer to the byte buffer;
    · the extent (width and height) of the drawable;
    · zoom, picture density, and line texture data;
    · scale and map projection data.
    It is important to understand how the combo depictable fills its byte buffer in order to fully understand the image combination algorithm. For each pixel or byte, the combo depictable asks each of its two depictables for the color value (an 8-bit number between 0 and 255) of that pixel. The two pixel values are quantized into one value using the algorithm depicted in Figure9.1. That combined pixel value is then written at the appropriate position in the byte buffer.
  5. Passes the byte buffer to Xlib, so it can copy the buffer into the tuple's 8-bit pixmap.
  6. De-allocates the memory allocated in step 2
  7. Restores the cursor.
Figure 9.1 Combining two 8-bit pixels into one.

9.5.4.4 Rendering a combo 24-bit image

The following steps are performed by an ImageDepictTuple object:

  1. Tells the DisplayMgr object to change the cursor to its busy representation (wrist-watch).
  2. Allocates three byte buffers which are arrays of bytes (char). Two of these byte buffers will be used as a drawing canvas for the tuple's two image depictables. Their sizes are the (width * height) of the tuple's drawable. The third byte buffer, four times the size of the others, will be used to send the combined data to X.
  3. Instructs both Image Depictable objects to paint into their byte buffers by passing the depictables the following information:
    · a pointer to the byte buffer;
    · the extent (width and height) of the drawable;
    · zoom, picture density, and line texture data;
    · scale and map projection data;
    · a flag indicating whether the color bar should be displayed left justified. One depictable will have this set to true, while it will be false (right side) for the other.
  4. For each byte of the buffers filled by the depictables, the tuple looks up the red, green, and blue intensity values using the image color table and pixel index represented by that byte. The two colors are then combined into a new color using a linear interpolation based on the image fade value, a float between 0 and 1 that is maintained by the DisplayMgr object. The three components of the new color are then multiplied by the image brightness value. Four bytes are copied into the third byte buffer: a blank (0) byte and one byte for each of the red, green, and blue values.
  5. Passes this third buffer to Xlib, to be copied into the tuple's 24-bit pixmap.
  6. De-allocates the buffers allocated in step 2.
  7. Restores the cursor.

9.5.5 Dimming and fading images

Dimming refers to adjusting the brightness of a single or combined image. Fading is relevant only to combined images and refers to the adjusting of the contribution of each image. A fade value of 0.5 implies that both images are being displayed equally. The user can change these values using the Image Controls Dialog which is managed by the fxa (UI) process.

The new fade and combo values are sent by the fxa, received by the IGC_Impl object and handled by the DisplayMgr object.

If the display panels are 24 bits deep, then the images in those panels will need to be re-rendered, following the recipe outlined in Section9.5.4.4. If the display panels are 8 bits deep, then the combined color table needs to be recalculated as described in the following section. A display can have a mixture of 8- and 24-bit display panels.

9.5.5.1 Calculating a combined color table

A color table object includes a method that combines itself with another color table object and then loads the resulting color table into the X colormap of a display panel's image window. This is accomplished by the following algorithm:

  1. Two subsets of 16 colors are computed from each color table. The color table is subdivided equally into 16 buckets. The most colorful color from each bucket is added to the subset. Three factors are considered in determining whether a color is more colorful:
    · the color's brightness (sum of all three red, green, and blue components);
    · the amount it differs from gray, white, or black;
    · the difference between this color and the color from the previous bucket.
  2. Each entry in the combined color table is a linear interpolation of one subset color with a color from the other subset. The interpolation is calculated using the image fade value, a float between 0 and 1 that is maintained by the DisplayMgr object. The scheme for building this color table is depicted in Figure9.2.
  3. Applies the image brightness level to every entry in the combined color table by multiplying the brightness with each color's red, green, and blue component.
Figure 9.2 Arrangement of a combined color table (entries 16 - 239 omitted)

9.6 Walk-throughs of Other IGC Operations

9.6.1 Zooming and roaming

Zooming is when the user specifies both a new display center and a zoom factor, while roaming is when the user specifies only a new display center. Although the user interfaces are quite different, the implementations of zooming and roaming are essentially the same.

9.6.1.1 Zooming user interface

The simplest user interface for zooming is positioning the cursor in the display panel where the display center should be, and clicking button 1 to zoom out or button 2 to zoom in. In four-panel mode, even though only one of the panels receives the button click event, all four panels will respond to the zoom. Here's what happens behind the scenes:

  1. The EventDispatcher object receives the Xlib event, and determines to which display panel object to send the event, by examining the window where the event occurred.
  2. The DisplayPanel object then tells the ViewMgr whether a zoom out or zoom in was selected, and also what the new display center should be.
  3. The ViewMgr then executes the zoom operation using the procedure described in Section 9.6.1.3.
A more useful interface is through the context sensitive pop-up menus. The user can pop up a cascading menu that presents a choice of zoom factors specified in terms of the width of the display in kilometers. The new display center is where the user pops up the menu. The advantage of this interface is that the user can go directly to the desired zoom factor. With the button click interface, it may take several display refreshes (which can be time consuming) before the desired zoom factor is obtained.

9.6.1.2 Roaming user interface

Roaming is initiated by the user by holding down button 2 at a certain position inside one of the display panel. As the user moves the cursor while holding the button, the picture (both the image and graphics/legends) follows the cursor, only inside the selected display panel. As the picture moves off the screen, the area where the picture used to be is drawn in black (see Figure9.3). Once the user releases the button, all the display panels are redrawn with the new display center. At this point, the roaming panel will have its black areas filled in with the new picture.

FIGURE 9.3 Example of roaming Panel A

Here's what happens behind the scenes:

  1. The DisplayPanel object who has the cursor gets the button press event from the EventDispatcher object.
  2. If the button is button 2, the display panel initiates a timer to expire in 0.5 seconds. If the button is still held down when the timer expires, then roaming begins, and the roaming start position is recorded.
  3. The DisplayPanel object receives motion notify events as the user moves the cursor.
  4. The window coordinate of where the upper left corner of the panel's pixmaps will now be placed is computed according to the new cursor position. The pixmaps (image and graphic) are recopied into the window at that new coordinate.
  5. Two rectangles, which are the difference between the new position and the old position of the pixmaps, are computed. Those two rectangles are drawn in the panel windows using the background color.
  6. Steps 3-5 are continued until the DisplayPanel object receives the button press event from the Event Dispatcher. The new display center, which is the (roaming end position - roaming start position) + the center of the display panel, is computed. The new display center is then passed to the ViewMgr object which then executes the roam operation using the procedure described in the next section.

9.6.1.3 Zooming and roaming implementation

  1. Tell the DisplayMgr object to change the cursor to its busy representation (wrist-watch).
  2. Convert the display center to 1:1 coordinate space by dividing it by the current zoom factor and adding it to the display origin (upper left corner position in 1:1 coordinate space).
  3. The ViewMgr maintains a list of zoom factors which are floating point values that are initialized from the fxa.config file. The ViewMgr also keeps track of the current zoom factor. If the user is zooming in, then select the next largest zoom factor. If the user is zooming out, select the next smallest zoom factor. If the user is roaming, then use the current zoom factor.
  4. Convert the panel's extent (width and height) in 1:1 coordinate space using the new zoom factor. If the display center is outside of the panel's extent, then move it inside.
  5. Calculate the display origin in 1:1 space, and save that coordinate, the display center, and the new zoom factor as the current view.
  6. Redraw the current frame, and all other frames marked as needing redrawing. Since the current view has changed, all tuples should be re-rendered, which means that each depictable will need to re-fill its tuple's drawable. See Section 9.5.1, "Displaying the current frame".
  7. Restore the cursor.

9.6.2 Toggling an overlay's visibility

The user is able to control whether a product is to be displayed, without having to unload or reload it. Basically, the IGC supports this by providing a mechanism for the user to select an overlay by clicking on its legend. The visibility flag inside the DepictSeq object associated with that overlay is then adjusted, and the display is refreshed. Here is a more detailed walk-through of this procedure:

  1. The user clicks on one of the displayed legends in the lower right corner of one of the display panels with mouse button 1.
  2. The EventDispatcher object receives the Xlib button release event, and determines to which DisplayPanel object to send the event, by examining the window where the event occurred.
  3. The DisplayPanel object then checks to see if the legends are being displayed. If not, then this event is interpreted as a zoom out and is handled as described in Section9.6.1.
  4. Obtains a list of the tuple objects that are loaded into the current frame. If only legends for background maps are being displayed, then all non-map tuples are removed from the list. Likewise, if product legends are being displayed, then all map tuples are removed from the list. Also, if a tuple is not being displayed in the display panel where the click took place, it is removed from the list.
  5. Uses the Y coordinate of the button release event to determine which legend is underneath the cursor. If the Y position is above the top-most legend, then this event is interpreted as a zoom out.
  6. Uses the X coordinate of the button release event to determine if the button click is to the left of the first character of the legend selected in the previous step. If it is to the left, then this event is interpreted as a zoom out. However, if it is to the right of the first character, then we found the legend under the button click. That legend has a DepictTuple object associated with it.
  7. The DepictSeq object, obtained from the selected tuple object, toggles its internal visibility flag.
  8. Redraws the current frame, and all other frames are marked as needing redrawing. If the selected overlay is turned off, then all tuples that have a pointer to the DepictSeq object associated with that overlay will be skipped during the copying of a tuple's drawable to a display panel. However, the legends of those tuples will still be displayed, but drawn in a gray color. For more details, see Section 9.5.1.

9.6.3 Sampling

Sampling is an IGC feature that displays text describing the meteorological values of the displayed products at the point where the cursor is. Thus, every time the cursor is moved while sampling is active, new values are displayed. Not every product currently supports sampling, although METAR plots and most images do. The text appears as close as possible to the cursor, and each product that supports sampling has a single line of text, drawn in the color of the product's legend. Here is how that feature is supported behind the scenes:

  1. The DisplayPanel object who has the cursor gets the button press event from the EventDispatcher object.
  2. If the button is button 1, the DisplayPanel object initiates a timer to expire in 0.5 seconds. If the button is still held down when the timer expires, then sampling is made active.
  3. When sampling becomes active, the FrameSeq object is instructed to disable background processing. The cursor is not as responsive while tuples are being rendered in the background.
  4. While sampling is active, the DisplayPanel object will do the following every time it receives a motion notify event from the X server (which is generated every time the user moves the cursor).
    · Tells the ViewMgr object to convert the display coordinate from the motion notify event to a lat/lon coordinate.
    · For every visible tuple in the current frame, instructs that tuple to call its depictable's "interrogate" method, passing the lat/lon coordinate. If that tuple is for a combo image, it may have several depictable objects to call. That method will return a TextString object. If that object does not contain a null string, then registers that string and the overlay color with every DisplayPanel object that is displaying this tuple.
    · Instructs every DisplayPanel object to erase the previous sample text. This is done by copying the area to be erased from the backing graphics pixmap to the panel's graphics window.
    · Instructs every DisplayPanel object to display all the registered sample text directly to the panel's graphics window. The upper left corner of the sample text is computed so that it appears directly below, and slightly the right of the cursor. However, if this position does not allow the entire sample text to be displayed in the panel, that position is adjusted to the left or above.
  5. When the user releases the mouse button, the DisplayPanel object with the cursor will receive the button release event from the EventDispatcher. If sampling is active, then it is made inactive, by setting an internal flag in the affected DisplayPanel object. Any sampling that was drawn to the panel's window is erased, and background processing is turned back on.

9.6.4 Stepping and looping

Stepping refers to specifying a new current frame and displaying that frame. If the frame does not need to be redrawn, then displaying that frame just involves having each display panel object copy the backing pixmaps corresponding to the new current frame into the panel windows. Stepping implies that the stimulus for changing the current frame comes from a user request. The IGC supports the following step commands:

A non-empty frame is defined as a frame that satisfies all of the following conditions:

Looping can be thought of as stepping, except the stimulus for changing the current frame comes from a timer expiration. In order for the user to effectively control looping, the IGC supports the following looping parameters.

9.6.4.1 Stepping walk-through

Here's what happens when the user selects one of the step control buttons of the control panel managed by the UI (fxa) process.

  1. The fxa sends an IPC message to an IGC process. The IGC_Impl object receives this message which contains a step command.
  2. If looping is on, then the LoopingMgr is told to turn looping off.
  3. The new current frame is calculated according to the step command described in the previous section.
  4. The new current frame is displayed. If the frame needs to be redrawn then the procedure described in Section 9.5.1, "Displaying the current frame" is followed. If the frame does not need to be redrawn, then each DisplayPanel object copies the backing pixmaps corresponding to the new current frame into its panel windows.

9.6.4.2 Looping walk-throughs

Here's what happens when the user enables looping by clicking on the loop state button of the control panel managed by the UI (fxa) process.

  1. The UI process sends an IPC message to an IGC process. The IGC_Impl object receives this message which contains a flag indicating that looping is to be turned on. This request is then passed on to the LoopingMgr object.
  2. The FrameSeq object is instructed to disable background processing since this is not really needed if looping is active.
  3. The EventDispatcher is told to activate a timer, and to call a method in the LoopingMgr object when the timer expires.
  4. When the timer expires, the following is done:
    · Determines the step command which is calculated from the looping direction and the current frame and passes the command to the FrameSeq object which executes steps 3 and 4 of the Stepping procedure (Section9.6.4.1).
    · Determines how long to display this current frame by considering the looping direction and the dwell times. The EventDispatcher object is then told to expire the next timer using this computed duration.
Here's what happens when the user disables looping by clicking off the loop state button of the control panel managed by the UI (fxa) process.

  1. The UI process sends an IPC message to an IGC process. The IGC_Impl object receives this message which contains a flag indicating that looping is to be turned off. This request is then passed on to the LoopingMgr object.
  2. The FrameSeq object is instructed to re-activate background processing.
  3. The EventDispatcher is told to deactivate the timer.

9.6.5 Swapping

Swapping is when the user selects one of the small IGC processes to display its loaded products in the large display area. That IGC process will now become the large IGC, responsive to the user interactions from the menu bar, control bar, volume browser, and other applications initiated by the UI process. Meanwhile, the IGC process that was displaying its products in the large display area will now use the display area that the small IGC was using.

Here is a walk-through of what happens to the IGC Process that is selected to be swapped into the large display.

  1. The user clicks mouse button 3 inside one of the display panels.
  2. The EventDispatcher object receives the Xlib button release event, and determines to which DisplayPanel object to send the event, by examining the window where the event occurred.
  3. The DisplayPanel object instructs the UI_WorkspaceManager object to send a message to the UI process requesting a swap.
  4. The IGC_Impl object will then receive an IPC message from the UI process indicating a new size type which the IGC_Impl object saves and the ID of a new X window to use for the IGC layout. This is sent to the DisplayMgr object.
  5. The DisplayMgr object destroys all of its DisplayPanel objects, and any X windows that were created for the four panel layout.
  6. If the panel layout is single panel, then a DisplayPanel object is created using the X window that was sent by the UI process.
  7. If the panel layout is four panel, then four new X windows are created. The X window passed in by the UI is the parent window. The origin and extent of these windows are computed so that they evenly subdivide the display area of the parent window. Since these windows will be displayed on top of the parent window, the parent window will not be visible, and will not be used by any DisplayPanel object. The depth (8 or 24 bits) of these new four windows will be the same as the parent window. The DisplayMgr will then create four DisplayPanel objects, each constructed with one of the four windows just created.
  8. Each created DisplayPanel object will determine whether its in 8- or 24-bit mode by querying the depth of the window that was passed in to the constructor. It will then create an additional window for graphics, if in 8-bit mode, and also create a backing pixmap for each active frame.
  9. Every DepictTuple object in the FrameSeq is forced to re-render by resetting its internal zoom and density information.
  10. The current frame is redrawn, and all other frames are marked as needing redrawing.
Here is what happens to the IGC_Process that will be swapped out of the large display and into one of the small displays.

  1. Receives a message from the UI process which asks the IGC_Impl to send a message back to the UI process acknowledging the receipt of the first message. This lets the UI process know that this IGC is not busy drawing or handling a user's request and is ready to swap.
  2. Steps 4 - 10 of the above procedure are then followed.
 
Table of Contents Next Chapter


This document is maintained by Joe Wakefield. Last updated 9 Jan 97.