Structured Graphics

Structured Graphics

Robbert Haarman

2010-12-11


Introduction

There are many graphics libraries out there, for various languages and platforms. Some provide only low-level access to the framebuffer, allowing the display to be controlled by standard byte or file operations. Others provide a set of drawing primitives to draw not just pixels, but also lines, circles, boxes, etc. Besides being more intuitive to the programmer, these primitives can sometimes be accelarated, giving primitive-based solutions an edge in both usability and performance. This design goes a step further, by composing the image of logical units that can be manipulated independently. This provides additional expressive power and potential for performance gains.


Goals

The goal of this design is to serve as a foundation for a GUI environment, as well as providing a graphics library on which graphical applications (such as games) can be built. The aim is to provide functions directly matching the needs of the application, providing ultimate power of expression to the programmer and all information that could aid optimization to the underlying implementation. At the same time, the library should not become overly bloated by providing functionality that would rarely be needed by any one application.


Modularity

To resolve the conflict between catering closely to any application's needs, and not burdening an application with functionality that it doesn't need or use, the library should be modularized wherever sensible. Functions that would often go together in that applications tend to use either none or many of them will be bundled together in a module. Only those modules that an application uses will have to be installed to enable that application to run.


Coordinates

Positions of structures are indicated by coordinates. Coordinates can be shared by multiple structures, meaning that if the coordinates change in one structure, they change in all structures that share them. This allows a simple operation to affect an arbitrary number of objects.

Coordinates are specified in pixels, which can have varying sizes on different underlying visuals. For example, displays typically have around 70 to 100 dots per inch, whereas printers can easily provide resolutions of over 600 DPI. Extensions can provide the programmer with the dimensions of the visual surface (e.g. display) in various units, and functions to convert between units.

make-coordinate x y => coordinate
coordinate-x coordinate => number
coordinate-y coordinate => number

Colors

It would be much easier to implement an efficient graphics library by only allowing one color, as any drawing operation could be optimized away. However, despite the merits of such an implementation, people seem reluctant to adopt it. Thus, the library is to provide colors. These are created as structures that encode both the format expected by the underlying implementation (e.g. the hardware), and the parameters were used to create the color. This is done so that, even if it wasn't possible to create the color exactly as specified, the specified values can still be retrieved.

Theoretically, the color model follows the coordinate model in that, if a color changes, everything drawn in that color is affected. However, this is probably not feasible to implement in many cases. Thus, it is likely that an implementation provides only immutable colors.

A programmer might wish to create colors using a variety of different colorspaces, such as RGB, HSV, etc. These could be provided in separate modules.

Another idea pertaining to colors is to group them in palettes, which can then be manipulated as a unit. E.g. All colors in a palette could be darkened, etc.

RGB Colors

make-rgb-color r g b => rgb-color
rgb-color-r rgb-color => number
rgb-color-g rgb-color => number
rgb-color-b rgb-color => number

Low-Level Operations

Because it is not possible (or even desirable) to provide every possibly required high-level function in the library, it is important that it provide access to low-level functionality that an application can use to implement functionality the library does not provide.

make-pixel coordinate => pixel
make-line start-coordinate end-coordinate => line
make-polyline (coordinates) => polyline
make-polygon (coordinates) => polygon
make-rectangle start-coordinate end-coordinate => rectangle
make-circle coordinate radius => circle
make-circle start-coordinate end-coordinate => circle

Canvases

A canvas is the virtual surface that operations are carried out on. The entire display is a canvas, and additional canvases can be created and manipulated individually. Canvases can be positioned on other canvases and their positions can be changed dynamically, allowing one simple function call to move the entire contents of one canvas, while the rest of the scene is untouched.

make-canvas start-coordinate end-coordinate => canvas
move-canvas canvas coordinate

Pixmaps

When a number of related structures are known to be in their final state (i.e. they will not move, change color, etc. anymore), they can be rendered on a pixmap. This will discard all structural information and retain only the visual presentation. The benefit of this is that pixmaps can be rendered very efficiently.

make-pixmap width height => pixmap
get-pixmap start-coordinate end-coordinate => pixmap
put-pixmap coordinate