ViewerWishes.tioga --- Stolfi May 24, 1984 5:21:58 pm PDT
Doug,
In a previous message you called for comments and suggestions about Viewers. Here are some of my complaints about the current interface, mostly from a client programmer's perspective. I addes some suggestions for the new version, but, but please don't take them too seriously; in those matters, I am only an interested layman.
Perhaps I have got irreparable brain damage for having been exposed to COBOL in my early infancy, but when I get an idea for a graphics program, it usually is in the form of classical, single-process read-compute-write loop. Of course, the "read" part now includes the acquisition and parsing of mouse and button commands, and the "write" part includes graphics commands. Whenever I tried to depart from this model, the program always got big, unreliable, hard to document, and hard to get used to.
I believe this simple model is good enough for most vanilla programs, those that take a few hours to write and are executed once every other leap year. I submit that, once Cedar ceases to be an end it itself and becomes a production tool, most programs written in it will belong to this category. In such programs, a good user interface is a secondary concern: if it is cheap to implement then fine, otherwise forget it.
Unfortunately, the "don't call us --- we'll call you" philosophy, that requires the client to do all its "writes" inside the Viewer's PaintProc, is incompatible with this model. The many ways of using Viewers one can find mentioned in the Cedar folklore are all unsafe and/or incomplete and/or hard to implement. The techniques I know of can be classified in three major classes, namely (1) put the entire program inside the PaintProc, (2) do only the painting inside the PaintProc, and (3) paint into a bitmap that is periodically bitblted onto the screen.
Solution (1) is generally bad, as it locks half the world for the duration of the program. In the "orthodox" solution (2), the programmer has to design and implement a suitable data structure (a "display file") that represents the current state of the image, and whose purpose is to satisfy the system-generated calls to the PaintProc. In my experience, this is not a trivial undertaking. When I implemented some Voronoi diagram algorithms in Cedar, a couple years ago, I found that implementing the display file took much more programming effort than coding the algorithm proper. Another problem with this approach is that the flow of control shuttles into and out of the PaintProc in a way that would make a BASIC programmer shudder of joy.
Solution (3) has less adverse impact on the structure of the program, but can be considered "simple" only by comparison with (2). To use it you have to import a dozen interfaces, fork two processes, synchronize them with the Window manager and the screen raster, and control their access to the bitmap via a monitor lock. If you also want to scale your image according to the window size, and be able to paint it also on the color monitor, that will probably require as much messy code as (2). This assuming, of course, that you have memory to spare for an extra color bitmap.
If I had to express just one wish for the new Viewers interface, I would ask that the PaintProc philosophy be abandoned. A programmer should not be forced to modify the structure of his/her program to account for the subtleties of window management. He/she should be able to write a safe and efficient program by a straightforward coding of the read-compute-write loop model (as if viewers were immutable after their creation), even if this leads to less than perfect repainting after window changes. Of course, the interface should not stand in the way of applications like Tioga, that do need a sophisticated repainting mechanism.
Below are two suggestions on how I think this might be accomplished
I. Bitmap Buffer Made Official. The first proposal is to implement the bitmap-buffer method (3) as the standard Viewers interface. ViewerOps.Create automatically allocates a bitmap buffer, and returns a context for it. Graphics commands write onto the buffer, and set a "bitmap changed" flag that is interrogated and reset by the screen bitbltting process. The dimensions of the bitmap may be either client-specified or equal to the initial size of the viewer.

When the viewer changes size on the screen, the bitmap is NOT reallocated; the old one continues to be displayed, clipped and/or padded to the new window size. The implementation may provide standard mouse commands for two-dimensional scrolling of the bitmap under the window.

There could be a client option telling the bitbltter to magnify or shrink the image in the buffer (by sampling) so as to fit the current window. This has to be done after each graphics command, however, and I guess we don't have enough computational power to do it.

The interface should allow the client to obtain the current size of the window on the screen, and to replace the bitmap buffer by a new, blank one of specified dimensions. This allows the client to repaint the whole image in a scale fitted to the current window size --- if and when it deems convenient to do so.

For added convenience, there could be an option (set, say, at viewer's creation time) that causes all graphics routines to raise a signal if the window size has changed since the previous graphics command. The client can use this signal to interrupt any painting or computation in progress, resize the bitmap, and repaint the current image from scratch . This would require some sort of a "display file", but it is not worse than (2), and may to allow more structured handling of these exceptions.

If the user moves a viewer to a device with different resolution, color encoding, or pixel format, the bitblting of the current bitmap onto the screen has to be either disabled, or performed in whatever crude way that is fast enough to do in real time, until the client notices the change and re-alocates the bitmap. Therefore, it seems better for the window manager to pass all "move to color display" requests to the client, rather than perform them right away.
II. Direct Screen Painting. The idea here is to let the client paint directly on the screen bitmap. As before, creating a viewer returns a context that stays around all the time. That context describes the current window on the screen. When the window moves, the image in the old window is bitblted to the new one, clipped and/or padded with background color as appropriate. The context is altered so as to point to the new window; its clipping box is set to that of the new window, but its user/device transformation is NOT changed. Each graphics command must momentarily acquire the screen lock to do its painting.

The positioning of the image in the new window is determined by the viewer's coordinate sustem. I suggest extending the current {top | bottom} choices to both axes independently, and add a "centering" option: {left | vert.centered | right}x{top | hor.centered | bottom}. (By the way, I find it confusing for the "top" coordinate system to have the y axis pointing down. I much prefer to have consistent directions, and deal with negative coordinates.)

The problem with this schema, of course, is that expanding a window will not make the hidden portion of the image visible --- only graphics commands executed after the move will paint into the new space. In particular, if the viewer is closed and re-opened, the old image is completely lost.

As in the previous proposal, we could consider magnifying or shrinking the old image by sampling (and scaling the user-to-device transform accordingly), instead of clipping it. This idea is now feasible, since the sampling has to be done only when the viewer moves, but the partial alleviation it gives may still not be worth the trouble.

Sampling and/or color conversion are necessary in any case if the viewer is moved to a device with different resolution, color encoding, or pixel format. The problem is less demanding here than in the Bitmap Buffer schema, since the mapping has to be done only once per move. Alternatively, we may channel all requests for device changes through the client, and let it worry about repainting the image in the new window.

As in the Bitmap Buffer schema, there will be tools for the client to obtain the current size of the window on the screen, change the user-to-device transformation of the context, and repaint the whole image in the desired scale --- if and when it deems convenient to do so. The option for the graphics routines to raise a signal upon detecting a window change could also be implemented.
Among these two proposals, my preferences as a layman are for Direct Painting: it seems faster, simpler, less storage-consuming, and more device-independent, and I believe these advantages more than compensate for its sloppy default repainting.
The question is whether Direct Painting is better than the current PaintProc schema. As for ease of programming, the answer is definitely "yes". What other criteria are there?
I suppose the PaintProc schema was introduced to guarantee that the "ideal" image be faithfully and promptly rendered, irrespective of device and window size changes. Unfortunately, considerations such as the size and complexity of the necesary display files, and the dangers of locking the world during long computations, make this schema impractical in its purest form. I guess it were these considerations that led to the introduction of the "whatChanged" parameter in the PaintProc. This addition to the basic model made incremental painting "kosher", thus leaving perfect and prompt repainting to the discretion of the programmer.
Therefore, from the point of view of encouraging good interactive behavior, Direct Painting and Bitmap Buffer are not terribly worse than the current PaintProc schema. They can be improved even more by making it easier for the client to detect window changes. A good idea would be for the window manager to put those events in a "user input queue", together with all TIP events and button clicks related to the viewer. The client will have to read this queue to get its input data and commands, and will be forced to take notice and dispose in some way of the window change events. (You mentioned something like this is already being planned for the new Viewers interface, didn't you?).
Given such tools, Direct Painting allows the programmer to respond to window changes as promptly and completely as in the Paintproc schema, with no greater effort. Indeed, the effort may be less, since Direct Painting allows the client to chose the timing of the repaints with greater flexibility and embed them in the rest of the program in a more natural way.
Well, that is more than enough for now. If it is old hat or pure junk, please forgive me for wasting your time.

jorge