Graphics Facilities for the Icon Programming Language


© 1995, 1996, 1997 Gregg M. Townsend, Ralph E. Griswold, and Clinton L. Jeffery

Introduction

The Icon programming language [1] provides a large set of platform-independent facilities for graphical input and output. The implementation includes numerous functions and keywords specifically for graphics. These are augmented by additional library procedures that add higher-level capabilities.

This document describes the graphics facilities of Version 9.3 of Icon [2]. Previous experience with computer graphics is helpful.

The body of the text presents a survey Icon's graphics capabilities. Full descriptions of the functions, attributes, and other items appear in appendices. The Visual Interface Builder, VIB, is described in a separate document [3].

A book on Icon graphics is expected in 1998 [4].

A Simple Example

This small program illustrates several aspects of graphics programming in Icon:
   link graphics

   procedure main()
      WOpen("size=400,300") | stop("can't open window")
      repeat case Event() of {
         "q":      exit()
         &lpress:  DrawCircle(&x, &y, 5)
         &mpress:  DrawCircle(&x, &y, 10)
         &rpress:  DrawCircle(&x, &y, 15)
         }
   end
bubbles

The image above shows what the window might look like after several clicks of the mouse.

The link graphics declaration gives access to the standard set of graphics procedures in the Icon Program Library [5] that supplement the built-in repertoire.

The WOpen() call creates a window. The window is 400 pixels wide and 300 pixels high, with all other characteristics set by default.

The main loop repeatedly calls Event() to receive input. An event code of "q" is returned when the q key is pressed; when this happens, the program exits.

An event code matching &lpress, &mpress, or &rpress is returned when the left, middle, or right mouse button is pressed. In response, the program draws a circle at the mouse location, obtaining this location from the keywords &x and &y. Circles of radius 5, 10, or 15 pixels are drawn depending on which mouse button was pressed.

Fundamentals

A window in Icon is an object of type window. The value of the keyword &window is the subject window. By default, almost all of the graphics functions use the subject window, and a typical program makes no explicit mention of any window value.

When a window is given explicitly as the first argument to a graphics procedure, the procedure and the remaining arguments apply to that window. If the first argument of a graphics procedure is not a window, the procedure and its arguments apply to the subject window. A null first argument to a graphics procedure does not specify the subject window; it is illegal.

Graphics actions are accomplished by calling built-in functions and library procedures. The distinction usually is unimportant and is indicated only in Appendix A.

Many drawing functions accept extra sets of parameters to allow multiple drawing operations in a single call. These functions are indicated by the notation "......" in Appendix A.

Most output is drawn using the foreground color, which is set by calling Fg(). A few operations make use of the background color set by Bg().

Attributes

Window attributes describe and control various characteristics of a window. A full list of these attributes appears in Appendix C.

WAttrib() reads and writes attributes. For example, WAttrib("fg") returns the value of the fg attribute, which is the current foreground color. WAttrib("fg=brown", "linewidth=3") assigns values to the fg and linewidth attributes. WAttrib() ignores invalid names and values.

Some attributes can also be read or written using procedures such as Fg(), Bg(), and Font().

Coordinate System

Window locations and distances are measured in pixels. Angles are measured in radians.

The origin (0,0) is the pixel in the upper-left corner of the window. A left-handed coordinate system is used: x increases towards the right and y increases toward the bottom. As a consequence, angles are measured in a clockwise direction.

Rectangular areas are specified by four integers (x,y,w,h) giving the coordinates of a starting corner and the rectangle's width and height. w and/or h can be negative to extend the rectangle leftward or upward from (x,y).

The effective origin can be moved from (0,0) to (dx,dy) by setting the dx and dy attributes.

Output to the screen is limited to a clipping region that is set by calling Clip(x, y, w, h). Drawing outside the clipping region is not an error, but no pixels outside the region are changed. Clipping is disabled by calling Clip() with no arguments; this is the initial state of a new window.

Drawing Operations

DrawPoint(x, y) draws a single point.

DrawLine(x1, y1, x2, y2, ..., xn, yn) draws a line from (x1,y1) to (x2,y2). If more coordinates are given, (x2,y2) is then connected to (x3,y3), and so on.

DrawPolygon(x1, y1, x2, y2, ..., xn, yn) functions like DrawLine() with the addition that (xn,yn) is connected to (x1,y1) to form a closed path.

DrawSegment(x1, y1, x2, y2) draws a line from (x1,y1) to (x2,y2). Additional pairs of coordinates can be provided to draw additional, disconnected segments.

DrawCurve(x1, y1, x2, y2, ..., xn, yn) draws a smooth curve that passes though the given points. If the first and last points are identical, a smoothly closed curve is produced.

DrawRectangle(x, y, w, h) draws a rectangle having corners at (x,y) and (x+w,y+h).

DrawCircle(x, y, r, theta, alpha) draws a circle or circular arc of radius r centered at (x,y). theta specifies the starting angle, in radians, and alpha is the angle (positive or negative) subtended by the arc. If alpha is omitted, theta is immaterial and a full circle is drawn.

DrawArc(x, y, w, h, theta, alpha) draws an elliptical arc inscribed in the rectangle specified by (x,y,w,h). theta specifies the starting angle and alpha is the extent.

FillPolygon(), FillRectangle(), FillCircle(), and FillArc() are similar to their Draw counterparts, but they fill in the interior of a figure as well as its outline.

EraseArea(x, y, w, h) fills a rectangular area using the background color instead of the foreground color.

An Example

The following code starts by drawing some simple figures. It then moves the origin, draws the new coordinate axes, and sets a clipping region. Circles are drawn around the new origin to show the effect of clipping, then partially erased. The WDone() procedure called at the end waits for the q key to be pressed.
   WOpen("size=400,300") | stop("can't open window")
   DrawPolygon(40, 100, 70, 40, 100, 100)
   DrawCurve(140, 100, 170, 40, 200, 100)
   FillRectangle(240, 40, 40, 60)
   WAttrib("dx=120", "dy=200")
   DrawSegment(0, -1000, 0, 1000, 1000, 0, -1000, 0)
   Clip(-20, -50, 100, 100)
   every DrawCircle(0, 0, 20 to 100 by 5)
   EraseArea(30, 10, 40, 20)
   WDone()

Drawing Attributes

Functions that draw lines are affected by the linewidth and linestyle attributes. The linewidth attribute specifies the thickness of drawn lines. The linestyle attribute can be solid, dashed, or striped. A value of dashed causes lines to be drawn with regular gaps. A value of striped causes these gaps to be filled with the background color.

The fillstyle and pattern attributes, discussed later, affect all drawing and filling functions.

The drawop attribute specifies the way in which drawn pixels interact with existing pixels. Normally, with drawop=copy, new pixels simply replace existing pixels. When the drawing operation is reverse, new pixels combine with old in such a way as to turn foreground-colored pixels into the background color, and vice versa; the effect on old pixels of any other color is unpredictable.

Text

WWrite(), WWrites(), WRead(), and WReads() are analogous to write(), writes(), read(), and reads(), treating the window as a simple video terminal that scrolls when the bottom is reached.

Output appears at the cursor location, which is defined by row and col attributes or equivalently by x and y attributes. The cursor is moved by GotoRC(row, col) or GotoXY(x, y). The visibility of the cursor is controlled by the cursor attribute.

Characters read by WRead() or WReads() are echoed to the screen at the cursor location if the echo attribute is set. When a character is written or echoed using the cursor, the area behind the character is filled with the background color.

DrawString(x, y, s) outputs text string s without affecting the text cursor position or drawing the background. The first character appears at location (x,y). CenterString(), LeftString(), and RightString() output a string using different positionings.

TextWidth(s) returns the width of a string, measured in pixels.

Fonts

Font(s) sets the text font. An Icon font specification is a comma-separated string giving the family name, style characteristics, and size (a height measured in pixels). Examples are "Helvetica,bold,18" and "Times,bold,italic,14".

Font names and characteristics are system-dependent. However, four portable family names are always available:
   mono        a monospaced, sans-serif font
   typewriter  a monospaced, serif font
   sans        a proportionally spaced, sans-serif font
   serif       a proportionally spaced, serif font
The actual fonts vary from one system to another; some systems may not be able to supply fonts with all the correct attributes.

Font specifications are case-insensitive. As an alternative to an Icon font specification, a system-dependent font name can be supplied.

Information about a font can be obtained from the following attributes:
   fheight   font height
   fwidth    font width
   ascent    extent of the font above the baseline
   descent   extent of the font below the baseline
   leading   distance between baselines of successive text lines
Only the leading attribute can be altered.

An Example

The following example illustrates several aspects of text output. Note the use of newlines and leading spaces in strings in addition to more explicit positioning.
   WOpen("size=400,300") | stop("can't open window")
   Font("typewriter,bold,18")
   WWrite("line 1")
   WWrite("line 2")
   GotoRC(5, 3)
   WWrite("line 5")
   WWrite("line 6")
   WWrite("\n ", &lcase, &ascii)
   Font("Helvetica,18")
   WWrite("\n ", &lcase, &ascii)
   DrawSegment(110, 50, 130, 50, 120, 40, 120, 60)
   DrawString(120, 50, "drawn at (120,50)")
   WDone()

Colors

Fg(s) and Bg(s) set the foreground and background color respectively.

Colors are named by English phrases using a system loosely based on [5]. Examples are "brown", "yellowish green", and "moderate purple-gray". The syntax of a color name is



where choices enclosed in brackets are optional and hue can be one of black, gray, white, pink, violet, brown, red, orange, yellow, green, cyan, blue, purple, or magenta. A single hyphen or space separates each word from its neighbor. Color names are insensitive to case; purple and Purple are equivalent

Conventional English spelling is used. When adding ish to a hue ending in e, the e is dropped. For example, purple becomes purplish. The ish form of red is reddish.

When two hues are supplied and the first hue has no ish suffix, the resulting hue is halfway between the two named hues. When a hue with an ish suffix precedes a hue, the resulting hue is three-fourths of the way from the ish hue to the main hue. The default lightness is medium and the default saturation is vivid.

Colors can also be specified in terms of red, green, and blue brightness components. The decimal form uses three comma-separated values ranging from 0 to 65535, as in "12000,0,65535". The hexadecimal forms are "#rgb", "#rrggbb", "#rrrgggbbb", and "#rrrrggggbbbb", with the longer forms providing greater precision.

Color specifications not recognized by Icon are passed to the graphics system for interpretation, allowing the use of system-dependent names.

ColorValue(s) translates a color specification into decimal form.

Color Correction

Icon colors use a linear scale: the 50% values in "32767,32767,32767" specify a medium gray. Real graphics hardware is nonlinear. When the underlying graphics system does not correct for this, Icon applies its own gamma correction. The gamma attribute controls the amount of such correction. A value of 1.0 provides no correction; values between 2 and 3 are appropriate for most uncorrected monitors.

Mutable Colors

On systems with changeable color maps, Icon supports color map access through mutable colors.

NewColor(s) reserves a color map entry and returns a negative integer n, a mutable color representing that entry. If s is supplied, the entry is initialized to that color.

An integer returned by NewColor() can be used as a color specification. For example, Fg(n) makes a mutable color the foreground color.

Color(n, s) sets the color map entry of n to the color s. This changes the appearance of any pixels of color n already drawn as well as affecting those drawn subsequently.

FreeColor(n) frees a color map entry when no longer needed, and can be used with normal color specifications as well as mutable colors.

Color Palettes

Color palettes are fixed sets of colors (or grays) used for drawing or reading images. Icon's color palettes are described in Appendix F.

PaletteKey(palette, color) returns a character from the given palette representing an entry in the palette that is close to the given color.

PaletteColor(palette, s) returns the color represented by the single character s in the given palette, or fails if the character is not a member of the palette. The color is returned in the same form as produced by ColorValue().

PaletteChars(palette) returns a string containing the characters that are valid in the given palette. If fails if the palette name is invalid. PaletteGrays(palette) returns only the characters corresponding to shades of gray, ordered from black to white.

Drawing Images

DrawImage(x, y, spec) draws an arbitrarily complex figure in a rectangular area by giving a value to each pixel in the area. x and y specify the upper left corner of the area. spec is a string of the form "width,palette,data" where width gives the width of the area to be drawn, palette chooses the set of colors to be used, and data specifies the pixel values.

Each character of data corresponds to one pixel in the output image. Pixels are written a row at a time, left to right, top to bottom. The amount of data determines the height of the area drawn. The area is always rectangular; the length of the data must be an integral multiple of the width.

The data characters are interpreted in paint-by-number fashion according to the selected palette. Spaces and commas can be used as punctuation to aid readability. The characters ~ and \377 specify transparent pixels that do not overwrite the pixels on the canvas when the image is drawn. Punctuation and transparency characters lose their special meanings in palettes in which they represent colors.

An Example

The following example uses DrawImage() to draw spheres randomly. Transparent pixels are used for better appearance where the spheres overlap. The inset shows a magnified version of a single sphere.
   WOpen("size=400,300") | stop("can't open window")
   sphere := "16,g16,  ~~~~B98788AE~~~~_
      ~~D865554446A~~~ ~D856886544339~~ _
      E8579BA9643323A~ A569DECA7433215E_
      7569CDB86433211A 5579AA9643222108_
      4456776533221007 4444443332210007_
      4333333222100008 533322221100000A _
      822222111000003D D41111100000019~_
      ~A200000000018E~ ~~A4000000028E~~_
      ~~~D9532248B~~~~"
   every 1 to 100 do
      DrawImage(?380, ?280, sphere)
   WDone()

Bi-Level Images

DrawImage() accepts an alternative specification form "width,#data" for images composed of only the foreground and background colors. The data field is a series of hexadecimal digits specifying row values from top to bottom. Each row is specified by width/4 digits, with fractional values rounded up. An example of a 4-by-4 specification is "4,#9BD9".

The digits of each row are interpreted as a base-16 number. Each bit of this number corresponds to one pixel; a value of 0 selects the background color and a value of 1 selects the foreground color. The least significant bit corresponds to the left-most pixel.

If the data field is preceded by the character ~ instead of #, the image is written transparently: Bit values of 0 preserve existing pixels instead of writing the background color.

Patterns

The fillstyle attribute, normally solid, can be changed to cause the drawing operations to fill areas or draw lines using a pattern. If the fill style is textured, both the foreground and background colors are used. If the fill style is masked, pixels not set to the foreground color are left unchanged.

Pattern(spec) sets the pattern attribute, the pattern to be used when the fill style is not solid. spec is a bi-level image specification of the type used with DrawImage()or one of the following predefined names:

An Example

The following example draws three rectangles using patterns. The first two, using the horizontal pattern, differ in fill style. The third uses a custom pattern given as a bi-level specification.
   WOpen("size=400,300") | stop("can't open window")
   Fg("light gray")
   FillRectangle()
   WAttrib("fg=black", "fillstyle=masked")
   Pattern("horizontal")
   FillRectangle(40, 60, 80, 180)
   WAttrib("fillstyle=textured")
   FillRectangle(160, 60, 80, 180)
   Pattern("8,#FF7F3F1F0F070301")
   FillRectangle(280, 60, 80, 180)
   WDone()

Miscellaneous Operations

Alert() produces a beep or other signal to attract attention.

CopyArea(x1, y1, w, h, x2, y2) copies the rectangular area (x1,y1,w,h) to (x2,y2). Copying from one window to another is possible by explicitly specifying two window arguments: CopyArea(W1, W2, x1, y1, w, h, x2, y2).

Pixel(x, y, w, h) generates the colors of the pixels in a rectangle, left to right, top to bottom.

ReadImage(s, x, y, p) displays an image from the file named s at (x,y). If p is supplied, the image is displayed using only the colors of palette p. WriteImage(s, x, y, w, h) writes a rectangular area to the file named s. Icon supports GIF format on all platforms; additional formats are also available on some platforms.

WDefault(program, option) returns a custom value registered for the option named option of the program named program.

Events

User actions are passed to an Icon program as events. These events do not interrupt execution; they are placed in a queue for retrieval. Events are generated by pressing a key (except modifier keys like the shift key), by resizing the window, by pressing or releasing a button on the mouse, or by moving the mouse while a button is pressed. User resizing of a window is allowed only if the resize attribute is set to on.

Normal keyboard characters are encoded as one-character strings. Other keys such as the Home key produce integer codes; see Appendix D.

The other events produce integer codes corresponding to keywords:
   &lpress    left mouse press
   &ldrag     left mouse movement
   &lrelease  left mouse release
   &mpress    middle mouse press
   &mdrag     middle mouse movement
   &mrelease  middle mouse release
   &rpress    right mouse press
   &rdrag     right mouse movement
   &rrelease  right mouse release
   &resize    window resize
An event is accepted by calling Event(), which returns the code for the next unprocessed event. If no event is available, Event() waits for one to occur.

When Event() returns an event, it also sets the values of keywords that give further information about the event:
   &x, &y      mouse location in pixels
   &row, &col  mouse location in characters
   &control    succeeds if the Control key was pressed
   &meta       succeeds if the Meta key was pressed
   &shift      succeeds if the Shift key was pressed
   &interval   time in milliseconds since previous event
Enqueue(a, x, y, s, i) places event code a in the queue with x and y as the associated mouse coordinates. s is a string containing any of the characters c, m, or s to indicate modifier keys; i gives the interval.

The event queue is an Icon list; Appendix E describes its format. Pending() returns the event list for direct access from Icon. The expression *Pending() returns the size of the list and can be used to check whether an event is available.

Dialogs

Several library procedures display items in a window and then wait for input.

Notice(line1, line2, ...) displays one or more lines of text and then waits for the user to click an Okay button.

OpenDialog(caption, filename) and SaveDialog(caption, filename) each request a text string, normally a file name; they differ in their sets of accompanying buttons. The resulting file name is stored in the global variable dialog_value.

TextDialog() constructs a dialog containing arbitrary numbers of caption lines, text-entry fields, and buttons. SelectDialog() requests a choice from a list of items; ToggleDialog() displays a set of independently selectable buttons. ColorDialog() displays a color-selection window. See Appendix A for details.

The VIB program [3] supports interactive construction of dialog boxes with even more generality. It allows arbitrary placement of buttons and text within a dialog box and provides additional devices such as sliders and scroll bars.

Windows

WOpen() opens a window and returns a value of type window. If &window is null, WOpen() assigns the newly opened window to it. Attribute values can be given as arguments to WOpen() to configure the new window.

WFlush() flushes the window's output buffer, forcing the immediate display of any output that has been buffered. WSync() flushes the buffer and does not return until all pending output has been displayed. WDelay(i) flushes the buffer and then delays i milliseconds. WClose() closes a window.

The size and pos attributes (and other related attributes) can be used to resize or reposition a window. The resize attribute controls whether the user is allowed to resize the window. Raise() and Lower() adjust the window stacking order on the screen.

The canvas attribute specifies window visibility. The default value is normal. With canvas=maximal, the window fills the screen. With canvas=hidden, the window is not visible on the screen. With canvas=iconic, a minimized icon or label is displayed. The attributes iconlabel, iconimage, and iconpos can affect the appearance of the window while in this state.

Active() lets a program multiplex input from several windows. It checks all open windows and returns a window for which an event is available; it waits if there are no unprocessed events.

Graphics Contexts

A window is composed of two parts: a canvas, the visible or invisible area used for drawing, and a graphics context, a set of parameters that control drawing.

All the attributes of a window are associated with either its canvas or its graphics context. The tables in Appendix C list the attributes by category.

Clone() creates a new graphics context coupled with an existing canvas. The resulting window value shares the canvas with an existing window but has an independent set of graphics attributes. These attributes are initialized to the same values as in the original window, then modified by any arguments passed to Clone().

Couple(W1, W2) produces a window that couples the canvas of W1 with the graphics attributes of W2. This allows a set of graphics context attributes to be shared across multiple canvases.

Uncouple() discards a window value. If there are no other references to the window's canvas, it disappears from the screen.

An Example

This example illustrates the use of graphics contexts. First, several windows are created by cloning, each with different attributes. Note that the two clones of wide inherit its linewidth attribute. Then, the cloned windows are used to draw circles, showing the effects of the different attributes.
   WOpen("size=400,300") | stop("can't open window")
   dashed := Clone("linestyle=dashed")
   wide := Clone("linewidth=5")
   gray := Clone(wide, "fg=gray")
   patt := Clone(wide, "fillstyle=textured", "pattern=grains")

   DrawCircle(100, 150, 70)
   DrawCircle(dashed, 150, 150, 70)
   DrawCircle(wide, 200, 150, 70)
   DrawCircle(gray, 250, 150, 70)
   DrawCircle(patt, 300, 150, 70)
   WDone()

Further Information

Appendix A provides detailed documentation of all built-in graphics functions and all library procedures mentioned here. Additional library procedures are described in [4].

Some of the programs in the library can be run to help learn about color in Icon. The colrbook and colrpick programs allow interactive exploration of English and numeric color specifications, respectively. The palette program displays any of the predefined color palettes.

Many other library procedures make extensive use of the graphics facilities. Much can be learned by browsing through the source code.

Acknowledgments

Many people have assisted the development of Icon's graphics capabilities. Darren Merrill, Ken Walker, Nick Kline, and Jon Lipp contributed to the design. Darren Merrill, Sandy Miller, and Cheyenne Wills assisted with aspects of the implementation. Steve Wampler and Bob Alexander provided suggestions, bug reports, and program examples.

References

1. R. E. Griswold and M. T. Griswold, The Icon Programming Language, Peer-to-Peer Communications, Inc., San Jose, CA, third edition, 1996.

2. R. E. Griswold, C. L. Jeffery, and G. M. Townsend, Version 9.3 of the Icon Programming Language, The Univ. of Arizona Icon Project Document IPD278 (PDF), 1996.

3. G. M. Townsend and R. E. Griswold, Visual Interfaces for Icon Programs The Univ. of Arizona Icon Project Document IPD284 (PDF), 1998.

4. R. E. Griswold, C. L. Jeffery, and G. M. Townsend, Graphics Programming in Icon, Peer-to-Peer Communications, Inc., San Jose, CA, to appear.

4. R. E. Griswold, The Icon Program Library; Version 9.3, The Univ. of Arizona Icon Project Document IPD279 (PDF), 1996.

5. Berk T., Brownstein L., and Kaufman A., "A New Color-Naming System for Graphics Languages", IEEE Computer Graphics & Applications, May 1982, 37-44.


Back to Contents