Beruflich Dokumente
Kultur Dokumente
level functionality such as scrolling, though it does (transformation matrix), color, pattern (for stippled
not provide some graphical capabilities that structured area fills), brush (for line drawing), and font. Each
graphics systems usually have, such as rotations and graphics state class implements operations for defin-
composite transformations. ing and modifying its attributes. For example, trans-
We have developed a C++ [12] library of graphical formers have translation, scaling, rotation, and matrix
objects that can be composed to form two-dimensional multiplication operations, and colors allow their com-
pictures. The library is a part of the InterViews graph- ponent intensities to be varied.
ical interface toolkit [8] and runs on top of the X win- A structured graphics package should be able to
dow system [10]. Our aim was to learn how inheri- transfer its graphical representations to and from disk.
tance and encapsulation could be used in the design of GKS uses “metafiles” for this purpose. The files
a structured graphics library. A base class graphic is PHIGS uses are called “archives.” Both packages pro-
defined from which all other structured graphics ob- vide procedures for saving and retrieving structures,
jects are derived. We show how a hierarchy of these for querying structures by name, and for deleting struc-
primitives can be composed to form more complex tures from the file.
graphics and how features such as hit detection and The approach used by these packages requires the
incremental screen update are incorporated into the programmer to save and retrieve structures explicitly.
model. We also compare this library to an earlier, The Graphic library uses persistent objects to auto-
non-object-oriented structured graphics library imple- matically manage the storage of graphics. The graphic
mented in Modula-2, relating experiences we had in class and graphics state classes are derived from a per-
using each library to implement a MacDraw-like draw- sistent class that provides transparent access to objects
ing editor. whether they are in memory or on disk. Persistent ob-
jects are faulted in from disk when they are first ref-
erenced, and “dirty” objects are written to disk when
2 Class Organization the client program exits.
Graphic
Graphic
Picture
Picture Label
Label Ellipse
Ellipse Point
Point Line
Line MultiLine
MultiLine Rect
Rect Instance
Instance
FillEllipse
FillEllipse Circle
Circle BSpline
BSpline Polygon
Polygon FillRect
FillRect
FillCircle
FillCircle ClosedBSpline
ClosedBSpline FillPolygon
FillPolygon
FillBSpline
FillBSpline
in Figure 2. PointObj and BoxObj are classes that do not have their own geometric information. Com-
serve as shorthand for specifying a point and a rect- position allows us to define how the composite’s state
angular region, respectively. Contains can be used information affects its components. The graphic base
to detect an exact hit on a graphic; Intersects can class implements a mechanism for combining, or con-
be used to detect a hit within a certain tolerance. catenating, graphics state information. The default
behavior for concatenation is described below. De-
rived classes redefine the concatenation operations as
3.2 Drawing Operations
needed.
Figure 3 lists the set of drawing and erasing opera-
Given two graphics states and , we can write
tions defined on graphics. InterViews defines canvas
their concatenation as , where is the re-
objects and the coord type. A canvas represents a re-
sultant graphics state. Concatenation associates but is
gion of the display in which to draw. Canvases are
not commutative; is considered “dominant.” re-
rectangular and may overlap. A coord is a integer
ceives attributes defined by . Attributes that does
coordinate.
not define are obtained from . An exception is the
The graphic base class implements each erasing op-
transformation matrix; ’s transformer is defined by
eration in terms of the corresponding drawing opera-
postmultiplying ’s transformer by ’s. thus dom-
tion. An erase operation first sets the foreground color
inates in that inherits ’s attributes over ’s, and
to the background color, then calls the drawing op-
’s coordinate system is defined by ’s transformation
eration, and finally resets the foreground color to its
with respect to ’s.
original value.
The operations taking a single parameter draw and A graphic might not define a particular attribute ei-
erase the graphic in its entirety. The coordinate param- ther because it is not meaningful for the graphic to
eters are used to specify a rectangular region. Bounded do so (a filled rectangle does not maintain a font,
Draw and Erase operations use the rectangular re- for instance) or because the value of the attribute has
gion as a hint to the graphic’s visibility. Graphics been set to nil explicitly. Defined attributes propa-
may perform optimizations based on this information. gate through successive concatenations without being
For example, because canvases do not permit drawing overridden or modified by undefined attributes. For
outside their boundaries, bounded draw and erase op- example, suppose graphics state defines a font but
erations can cull parts of the graphic that fall outside does not. Moreover, maintains a font but its value
the canvas.1 has been set to nil. Then will receive
DrawClipped and EraseClipped clip during ’s font attribute. If ’s transformer is nil but and
drawing or erasing. They are useful when drawing ’s are non-nil, then will receive a transformer that
must be strictly limited to a portion of the canvas. is the product of ’s and ’s. If ,
For example, DrawClipped is often used to redraw then will receive a transformer that is the product
portions of a graphic that had been obscured by an of ’s and ’s.
overlapping canvas. The semantics for concatenation as defined in the
base class are useful for describing how composite
graphics are drawn, but derived graphics can imple-
4 Composite Graphics ment their own concatenation mechanism. This cre-
Picture and instance are composite graphics. A pic- ates the potential for concatenation semantics that are
ture composes other graphics into a single object, more powerful than the default precedence relation-
while an instance is a reference to another graphic. ship. For example, the concatenation operation could
Both rely on a notion of graphics state concatenation be redefined so that concatenating two colors would
to define how they are drawn. yield a third that is the sum or difference of the two.
Two patterns could combine to form a pattern corre-
sponding to an overlay of the two. This behavior could
4.1 Graphics State Concatenation be used to define how to draw overlapping parts of a
Composite graphics are like other graphics in that they VLSI layout.
maintain their own graphics state information, but they The ability to redefine concatenation semantics
1 The demonstrates how inheritance lets the programmer ex-
bounded operation could obtain the rectangular region di-
rectly from the canvas. For generality, however, the region is spec- tend the graphics library easily. Flexibility is thus
ified explicitly. achieved without complicating or changing the library.
-5-
void Append(Graphic*);
void Prepend(Graphic*);
void Remove(Graphic*);
void InsertAfterCur(Graphic*);
void InsertBeforeCur(Graphic*);
void RemoveCur();
void SetCurrent(Graphic*);
Graphic* GetCurrent();
Graphic* First();
Graphic* Last();
Graphic* Next();
Graphic* Prev();
boolean IsEmpty();
boolean AtEnd()
Graphic* FirstGraphicContaining(PointObj&);
Graphic* FirstGraphicIntersecting(BoxObj&);
Graphic* FirstGraphicWithin(BoxObj&);
Graphic* LastGraphicContaining(PointObj&);
Graphic* LastGraphicIntersecting(BoxObj&);
Graphic* LastGraphicWithin(BoxObj&);
changes in the underlying graphical structure. Such damaged rectangle) or moves an object (producing one
techniques will be effective if the user makes small or two rectangles). Assuming that drawing editors rep-
changes most of the time, and experience with inter- resent a fair benchmark for interactive graphics appli-
active graphics editors shows this to be the case. cations, the two-rectangle limitation offers advantages
To support incremental update, the Graphic library in both performance and implementation simplicity.
includes a damage base class. A damage object is used
to keep the appearance of graphics consistent with their
representation. Damage objects try to minimize the 6 Experience
work required to redraw corrupted parts of a graphic.
The base class implements a simple incremental al- The design of the Graphic library was based on ex-
gorithm that is effective for many applications. The perience with an earlier structured graphics library we
algorithm can be replaced with a more sophisticated implemented in Modula-2. The Modula-2 design em-
one by deriving from the base class. phasized high drawing speed over low latency. It also
tried to handle incremental update completely auto-
matically; that is, it had no operation comparable to
5.1 Interface Incur. The extent of damage was inferred from
The interface to the damage class appears in Figure 6. the operations performed on each graphical object.
When a damage object is created it is passed a graphic Though the package attempted to provide an object-
(usually a picture) for which it is responsible. The oriented interface, the implementation language’s lack
Incur operation is called by the client program when- of inheritance resulted in a monolithic library that
ever the graphic is “damaged.” The graphic is incre- could not be extended easily.
mentally updated when Repair is called. Reset We have developed two versions of an object-
discards accumulated damage without updating the oriented drawing editor called idraw, shown in Fig-
graphic. Clients can determine whether any damage ures 7 and 8. The first version uses the Modula-2
has been incurred using the Incurred operation. graphic library, while the second version uses the C++
Graphic library. This gives us a good opportunity for
comparing the two libraries based on actual usage.
5.2 Implementation
The damage class implements a simple algorithm for 6.1 Graphics State Propagation versus
incremental update. Each damage object maintains
Concatenation
zero, one, or two non-overlapping rectangles. A dam-
age object must be notified whenever the graphic’s A difference between the Graphic and Modula-2 li-
appearance changes by calling the Incur operation braries is in the way they manage graphics state.
with either a region of the canvas or a graphic as a Modula-2 graphical elements propagate their graph-
parameter. If a graphic is supplied, its bounding box ics state to the leaves of the graphics hierarchy as part
determines the extent of the damaged region. of the modification operation. Graphic library objects
Incur either stores the new rectangle represent- defer the propagation until they are drawn, relying on
ing the damaged region or merges it with one or both the concatenation mechanism to do the job. The ratio-
of the rectangles it has stored. Merging replaces a nale behind propagation was to make drawing as fast
stored rectangle with the smallest rectangle circum- as possible. It was believed that on-the-fly concate-
scribing the rectangles being merged. Repair calls nation would slow drawing unnecessarily. Thus, as
DrawClipped on the graphic for each stored rectan- much work as possible was done before the drawing
gle. routine was called.
The number of rectangles maintained by damage We realized that propagation was a mistake as we
objects is limited to two because successive increases used the Modula-2 library to implement idraw. Prop-
in the number of rectangles bring diminishing returns. agating graphics state each time an operation is called
This is a result of the overhead associated with draw- precludes amortizing many changes over a few draws.
ing a graphic clipped; for complicated graphics this That is, if several state-modifying operations are made
involves significant computation. We found that the before the graphic is drawn, we can avoid traversing
limiting value of two yielded subjectively the quickest the structure if we defer propagation to draw time,
screen update on average in an object-oriented drawing when we must traverse it anyway.
editor based on the Graphic library. Typically the user Having made propagation an integral part of the
either transforms an object in place (producing a single Modula-2 library, there was no practical way for users
-8-
void Incur(Graphic*);
void Incur(BoxObj&);
void Repair();
void Reset();
boolean Incurred();
to modify the library to use concatenation. An object- way. The generality of the algorithm coupled with the
oriented design would have used inheritance to facil- lack of a way for the programmer to influence the re-
itate the modification of the library to use concatena- draw mechanism often rendered the facility useless;
tion. In comparison, it would be straightforward to de- the programmer would bypass the mechanism and re-
rive a new sort of picture and redefine its graphic state draw damaged objects explicitly.
modification operations to propagate attributes imme- To illustrate, consider the case where a drawing is
diately. restructured so that an object obscured by other objects
is brought to the top. A simple way to update the
screen is simply to draw the object; nothing else need
6.2 Incremental Update be redrawn. However, the incremental algorithm did
The Modula-2 graphics library implemented an auto- not consider this optimization, and Redraw proceeded
matic incremental update feature. The library kept to redraw all the obscured objects as well.
track of changes to objects by storing lists of rectan- The more serious problem arose because damaged
gles with each object. Newly-added rectangles were rectangle information was always accumulated, since
merged with any rectangles in the list they intersected. Redraw could be called at any time. This added over-
The list of rectangles was ultimately limited by the head to every appearance-modifying operation. The
object’s bounding box; when a rectangle in the list be- overhead remained even if the programmer decided
came large enough to subsume the bounding box, the to bypass automatic redraw and perform the update
incremental update mechanism was disabled and the manually. The addition of a Disable procedure that
object would be drawn in its entirety. turned off rectangle accumulation complicated the use
The Redraw procedure was used to initiate incre- of the package and presented problems of its own:
mental redraw of a graphical object. Redraw erased What should happen when automatic redraw is enabled
the regions defined by the rectangles in the object again? Should old damage information be eliminated?
and redrew the object clipped to each rectangle. Any How do we know the screen is still consistent?
nested objects would be redrawn recursively. The lesson we learned was that it is important not
This approach worked—the screen was never left in to exclude the programmer from the update process.
an inconsistent state following incremental redraw— Damage objects do not in any way interfere with the
but it did not always perform the update in an efficient normal operation of graphics. They incur no overhead
- 10 -
unless they are used, and they encapsulate the incre- comparisons are necessarily crude because of differ-
mental update algorithm, making it easy to enhance or ences in design criteria, in our experience level at the
replace. In contrast, the update mechanism pervaded start of each library’s implementation, and in the im-
the older library. Damage objects give programmers plementation languages themselves. Nevertheless, we
the option of performing tricks of their own when up- offer these comparisons to add insight into the relative
dating the screen without paying for mechanisms they merits of the Modula-2 and C++ implementations.
do not use. Table 1 shows the source code sizes for both li-
braries and both versions of idraw. The library code
6.3 Persistence is divided into five components: common code (that is,
code that implements the same functionality in both li-
We have mixed feelings about having used persistent braries), code for incremental update, code for storing
objects in the Graphic library. On one hand they are graphical objects on disk, code for hit detection, and
convenient because they free the programmer from comments. The idraw code is divided into common
worrying about storage. On the other hand, objects code, user interface code, and comments.
created by a program live in their own world analo- This partitioning lets us take into account differ-
gous to the address space in which they were created. ent capabilities and levels of commenting when com-
Thus, objects cannot communicate across program or paring code sizes. For example, the Graphic library
machine boundaries easily, nor is there provision for has a general persistent object facility, whereas the
moving objects from one world to another. Modula-2 library supports only manual read/write of
Persistent objects are useful for preserving the state graphical objects. Graphic subclasses implement fine-
of a program transparently across executions, but they grain hit detection, while the Modula-2 library can de-
are not suitable for communicating the state between tect hits only within an object’s bounding box. The
processes. We expect that a later version of the Modula-2 library uses a more complicated incremen-
Graphic library will incorporate a more conventional tal update mechanism and is commented more heav-
storage mechanism. ily than the Graphic library. Modula-2 idraw imple-
ments scroll bars, pull-down menus, and rubberbands
6.4 Cached Bounding Boxes explicitly, while InterViews provides this functionality
in the C++ version.
To improve performance, the more complex graph- From the information in Table 1, we conclude only
ics such as multilines, polygons, splines, and pic- that the C++ and Modula-2 code is comparable in size.
tures cache their bounding box once it is calculated. The amount of common code in the structured graphics
Caching can save substantial time, especially for large libraries is about the same, and the C++ version has
pictures, because the bounding box is needed when- proportionally more code to implement added func-
ever a graphic is drawn clipped or bounded. tionality. The Modula-2 idraw is somewhat smaller
The object-oriented approach makes it easy to add than the C++ version, taking into account that C++
this optimization to classes that can use it without pe- idraw relies on InterViews to implement its user in-
nalizing other classes. The graphic base class declares terface. However, C++ idraw provides more func-
operations for caching, invalidating, and retrieving a tionality, including arbitrary-level undo (versus single-
bounding box. These are null operations by default; level for Modula-2 idraw), more sophisticated text
derived classes can redefine them if they use caching. editing, and user customizability.
Thus, individual graphics can define their own caching A possible disadvantage of an object-oriented imple-
and invalidation criteria. Furthermore, since the base mentation is a runtime performance penality because
class does not allocate storage for the bounding box, of overhead such as method lookup. In the imple-
no overhead is incurred on subclasses that do not re- mentation of C++ we used, the overhead amounts to
quire caching. three or four extra memory references per virtual func-
tion call [13]. To see whether this overhead has a
6.5 Quantitative and Qualitative Com- significant impact on the performance of idraw, we
parisons measured how long it took each version of idraw
to do three different operations on two different draw-
This section presents quantitative and qualitative com- ings, car.6 (shown in Figure 7) and multidriver
parisons of the Modula-2 and C++ structured graphics (shown in Figure 8). These are representative of two
libraries and versions of idraw. Note that any direct common types of drawings: artistic drawings with
- 11 -
Modula-2 C++
structured common code 3600 3500
graphics incremental update 500 100
library hit detection 400 1500
persistence 600 1300
comments 700 300
total lines 5800 6700
idraw common code 13000 14000
user interface 2000 0
comments 1000 2000
total lines 16000 16000
many complex, overlapping polygons and splines, and significantly better structured, more understandable,
technical drawings consisting mainly of rectangles, and “cleaner” overall than their Modula-2 counter-
lines, and text with little or no overlap. We timed parts. One could argue that the lessons learned in the
the following operations: Modula-2 implementation efforts led to superior C++
versions. However, the versions of idraw were de-
1. In the “zoom #1” test, the drawing is zoomed veloped by two different people. In fact, the Modula-2
from half size to quarter size and back. The draw- version was its author’s second attempt at a drawing
ing is fully visible throughout the test. editor, while the C++ version was its author’s first at-
2. In “zoom #2,” the drawing is zoomed from half tempt. The object-oriented paradigm simply invites
size to full size and back. The drawing is clipped good program structuring through inheritance, encap-
when drawn at full size so that only half is visible. sulation, and late binding, all of which promote mod-
ularity and flexibility.
3. In “rotation,” the (top-level) object in the drawing
is rotated 90 .
7 Conclusion
Table 2 shows the average of ten trials for each test.
The C++ version outperforms the Modula-2 version in A striking aspect of graphics packages such as CORE,
every test. The difference in speed is greatest for the GKS, and PHIGS is their size and complexity. These
rotation test on car.6, but this difference is exagger- packages are intended as standards that provide ma-
ated because of a bug in the Modula-2 library’s incre- chine independence, extensive functionality, and gen-
mental update routine that caused redundant redraws erality, and they largely succeed in these respects.
of two subcomponents. In general, the Modula-2 li- However, all reflect their procedural implementation
brary is handicapped by the extra traversals associ- in their interface. Programmers cannot extend prim-
ated with graphic state propagation and incremental itives through inheritance to modify their semantics.
update. The results would be more comparable if the The result is a substantial complexity penalty for ev-
Modula-2 library were modified to use concatenation ery increase in flexibility.
and the simpler incremental update algorithm of the For example, some packages bind graphics state at-
damage class. tributes statically to graphical objects when the ob-
The last quantitative comparison involves the object jects are created. Others provide a simple form of
code sizes for each library and idraw version. These state inheritance by allowing graphics to reference
values are shown in Table 3. The C++ sizes are larger other graphics in a manner similar to instances in the
mainly because of the added functionality of both the Graphic library. These facilities are significantly less
Graphic library and C++ idraw, constructor, destruc- flexible than the graphics state concatenation mech-
tor, and inline code, and the overhead associated with anism, the semantics of which can be changed on a
virtual pointer tables. per-class basis. In an object-oriented package, gener-
From a qualitative standpoint, the Graphic library ality can be achieved through class inheritance instead
and the corresponding version of idraw are both of supporting a broad range of behaviors explicitly.
- 12 -
Table 2: Comparison of Modula-2 and C++ idraw drawing performance (in seconds)
Modula-2 C++
structured graphics library 40 110
idraw 130 280
Table 3: Comparison of Modula-2 and C++ object code sizes (in kilobytes)
Another advantage of the object-oriented approach Of more immediate interest is the introduction of
is the ability to treat graphical objects generically, re- version 2.0 of C++ [13] with multiple inheritance,
lying on the runtime system to determine the correct among other enhancements. Though single inheritance
method for a particular object. The virtual mech- is very useful, it often forces the programmer to derive
anism accomplishes this in C++. Thus, functionality from one of two equally attractive classes. This limits
such as hit detection can be implemented in a simple the applicability of predefined classes, often making
way without identifying objects with element point- it necessary to duplicate code. For example, there is
ers and labels. Furthermore, escape mechanisms for no way to derive a graphic that is both a circle and
exploiting special hardware facilities are unnecessary; a picture; one must derive from one or the other and
subclasses can be derived that reimplement key op- reimplement the functionality of the class that was ex-
erations such as Draw to take advantage of unique cluded.
capabilities.
The availability of multiple inheritance will un-
In our experience, structured graphics is useful for doubtedly change the class hierarchy shown in Fig-
applications that allow the user to manipulate graph- ure 1. Classes such as filled and open could be de-
ical objects interactively. Structured graphics is less fined to simplify the relationships between filled and
useful for implementing the appearance of the user in- non-filled graphics, which are currently derived as they
terface. It is unnecessary to define scroll bars, menus, are to maximize code sharing. Persistence could be
and buttons using structured graphics because they are implemented as a separate class from which to inherit.
simple to draw procedurally and their structure rarely Thus, non-persistent classes can avoid the small space
changes. Thus, structured graphics is not a replace- overhead caused by deriving graphic from a persistent
ment for immediate-mode graphics. class.