The Simple Raster Graphics Package is composed of a library of functions, and a header file ("srgp.h") that defines custom data types and constants, and which prototypes all SRGP routines. This paper is a complete but extremely terse description of the ANSI-C SRGP binding --- this is not a tutorial. If you are new to SRGP, you must read Chapter 2 of Computer Graphics - Principles and Practice (Foley, van Dam, Feiner and Hughes, Addison-Wesley, 1990).
The textbook spec did not discuss how SRGP would work in a windowing environment. This document describes how the windowing environment affects SRGP, and introduces a few new routines addressing window-specific problems.
The textbook's description of locator echo included "no echo" as an option available to the application. However, it was determined that an invisible cursor is frustrating to users of a multi-window system. The "no echo" option is thus honored only by the PC version of SRGP.
The textbook (page 33) claims that the colors black and white are 1 and 0, respectively, at all times. This is true for all devices having color tables, but it is sometimes false for monochrome systems that are designed for white-on-black display.
The textbook did not describe methods for loading bitmap and pixmap patterns into the respective pattern tables, for loading fonts into the font table, and for loading entries in the color table. See sections 3.1 and 3.3 of this document.
There are now "deluxe" versions of the measure records for each input device. These should be used when timestamps and modifier-key chords are needed.
The textbook specifies the size of the screen canvas cannot be changed by the application. That is no longer the case.
void SRGP_begin (char *name, int width, int height, int planes,
boolean enable_trace);
The fourth parameter is meaningful only on a display supporting color. It specifies how many planes of the color table should be reserved for SRGP's use; i.e., it places an upper bound on the number of colors that may be displayed simultaneously in the SRGP window. (The upper bound is 2 exponent p colors, where p is the number of planes.) The fourth parameter is ignored when the program is run on a bilevel display.
If the program is being run on a color display, and you send the special value "0" as the fourth parameter, SRGP will take over the entire color table, giving your application color support as rich as the hardware can offer. (After initializing SRGP, you can inquire the "canvas depth" to determine how many planes are available.) The disadvantage: it will be impossible for the user to simultaneously see the SRGP window's proper coloring and the other clients' windows' proper coloring. Thus, you should request "0" planes only when your application truly needs full control of the color table.
If you request more planes than available, all available planes are allocated, just as if you requested "0" planes. Inquiry is thus your only way of determining exactly how many planes are available.
void SRGP_tracing (boolean);
The initial status of tracing is set when the application calls SRGP_begin, but it may be changed at any time via a call to SRGP_tracing.
void SRGP_allowResize (boolean);
typedef int (*funcptr)(); void SRGP_registerResizeCallback (funcptr);
void SRGP_changeScreenCanvasSize (int newwidth, int newheight);
void SRGP_enableSynchronous (void);
void SRGP_end (void);
Each canvas has its own local coordinate system. The origin (0,0) for the local coordinate system is the lower-left corner of the canvas, with the X-coordinate increasing to the right, and the Y-coordinate increasing towards the top. The coordinates passed to all primitive-generation procedures are in terms of the local coordinate system of the currently-active canvas.
At any given time, one canvas is active: it is the canvas being modified. Associated with each canvas is a group of attributes which affect all drawing into that canvas. Modification of these attributes is only possible when the corresponding canvas is currently active. When a canvas is created, its attribute group is initialized to standard default values.
Each canvas is identified by a unique integer canvas index. When SRGP is enabled, one canvas already exists and is active: the screen canvas, having index 0, whose height and width are determined from the parameters to SRGP_begin. The screen canvas is the only canvas which is ever visible. No more than MAX_CANVAS_INDEX+1 canvases (including the screen) may be extant simultaneously.
Canvases may be manipulated by the following procedures:
typedef int canvasID; canvasID SRGP_createCanvas (int width, int height);
void SRGP_deleteCanvas (canvasID);
void SRGP_useCanvas (canvasID);
int SRGP_inquireCanvasDepth (void);The legal color indices are numbers between (inclusive) 0 and 2 exponent canvasdepth -1. The use of color indices beyond that range are clamped to 2 exponent canvasdepth -1. (Negative values are illegal and will cause an error.) All implementations support two colors that may be referenced using names instead of numbers: SRGP_WHITE and SRGP_BLACK.
On color nodes, SRGP_WHITE is 0 and SRGPBLACK is 1, and they are the only initialized entries in the LUT. One should note, however, that if the first two entries of the LUT are changed by the application, the names ( SRGP_WHITE and SRGPBLACK ) are no longer meaningful.
On monochrome displays, the two symbols SRGP_WHITE and SRGPBLACK are implementation-dependent constants. Moreover, the use of a color index greater than 1 is clamped to 1 on a monochrome display.
An application may load a contiguous portion of the LUT by creating three arrays (one for red, one for blue, one for green) of intensity values, each value being an unsigned 16-bit integer. Intensity value 0 represents that primary's not contributing at all to the actual color, and 2 exponent 16 - 1 (65,535) represents that primary contributing its full glory to the actual color. Note that this method for specifying colors is machine-independent; workstations supporting only C bits per intensity value will ignore all but the C most significant bits of each intensity value.
To load count entries of the LUT, starting with entry start, create the three intensity value arrays and then call:
typedef unsigned short ush; void SRGP_loadColorTable (int start, int count, ush *r, ush *g, ush *b);An easy way to store "common" colors is provided by SRGP. Common colors are those colors which have been given names (like "Purple", "MediumForestGreen", and "Orange") 11by the X11 implementation. A complete list of the supported colors is usually found in '/usr/lib/X11/rgb.txt' in the SRGP resource file. A complete list of the supported colors is provided below. SRGP supports only the setting of one LUT entry at a time when using common colors:
void SRGP_loadCommonColor (int entry, char *colorname)The supported colors are shown in their lower-case compressed forms below. Be aware that you can insert spaces, and use upper-case arbitrarily, in the string you hand to SRGP_loadCommonColor.
aliceblue greenyellow navyblue antiquewhite grey oldlace aquamarine honeydew olivedrab azure hotpink orange beige indianred orangered bisque ivory orchid black khaki palegoldenrod blanchedalmond lavender palegreen blue lavenderblush paleturquoise blueviolet lawngreen palevioletred brown lemonchiffon papayawhip burlywood lightblue peachpuff cadetblue lightcoral peru chartreuse lightcyan pink chocolate lightgoldenrod plum coral lightgoldenrodyellow powderblue cornflowerblue lightgray purple cornsilk lightgrey red cyan lightpink rosybrown darkgoldenrod lightsalmon royalblue darkgreen lightseagreen saddlebrown darkkhaki lightskyblue salmon darkolivegreen lightslateblue sandybrown darkorange lightslategray seagreen darkorchid lightslategrey seashell darksalmon lightsteelblue sienna darkseagreen lightyellow skyblue darkslateblue limegreen slateblue darkslategray linen slategray darkslategrey magenta slategrey darkturquoise maroon snow darkviolet mediumaquamarine springgreen deeppink mediumblue steelblue deepskyblue mediumorchid tan dimgray mediumpurple thistle dimgrey mediumseagreen tomato dodgerblue mediumslateblue turquoise firebrick mediumspringgreen violet floralwhite mediumturquoise violetred forestgreen mediumvioletred wheat gainsboro midnightblue white ghostwhite mintcream whitesmoke gold mistyrose yellow goldenrod moccasin yellowgreen gray navajowhite green navy
typedef struct {
int x, y;
} point;
typedef struct {
point bottom_left, top_right;
} rectangle;
Instances of these data types may be created using these routines:
point SRGP_defPoint (int x, int y); rectangle SRGP_defRectangle (int left_x, int bottom_y, int right_x, int top_y);
The bitmap pattern table is initialized in this way: Pattern 0 is all background, and pattern 1 is all foreground. Patterns 1 through 38 are the standard Macintosh patterns shown in Volume 1 of Chernicoff's Macintosh Revealed, and page I-474 of Inside Macintosh. Patterns 40 through 104 are greyscale patterns increasing gradually in intensity from all background (40) to all foreground (104). 11All other entries in the bitmap pattern table are undefined; use of an undefined pattern is a fatal error. All other entries in the bitmap pattern table have random patterns initially. To see an array of tiles showing the default bitmap pattern table, run the example program show_patterns.
Only entry 0 in the pixmap pattern table is defined, and it is simply an all-color-0 pattern.
SRGP provides two methods for changing entries in the pattern table:
To load a bitmap pattern from a file, open the file and pass the stream to the function:
int SRGP_loadBitmapPatternsFromFile (FILE *stream);The input may be composed of one or more pattern specifications ("specs"). Each spec must occupy exactly two lines and must match this format:
static char bitpat_Xy[] = 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??;where X is a non-negative integer specifying the index of the entry to be set, y is any arbitrary garbage between the integer and the left square-brace, and 0x?? is any arbitrary byte value represented in hexadecimal. This specification format is created automatically by X's bitmap editor bitmap.
As a convenience, any lines beginning with '#' are ignored, but these comment lines must not interrupt a single two-line spec sequence. Blank unused lines are not allowed anywhere in the file.
The closing of the input stream must be performed by the caller. This function returns 1 if any problems at all occurred; since its parser makes no attempt at error recovery, it is wise to check the return value.
To load a pixmap pattern from a file, use this routine whose functionality is similar to that of the one for bitmap patterns:
int SRGP_loadPixmapPatternsFromFile (FILE *stream);The input may be composed of one or more pattern specifications, each matching this format:
static int pixpat_Xy[] = ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?;In this specification, each ? represents a decimal integer color index.
Applications generating the patterns at runtime can use these routines to set one entry at a time:
void SRGP_loadBitmapPattern (int pattern_id, char *data); void SRGP_loadPixmapPattern (int pattern_id, int *data);
void SRGP_loadFont (int fontindex, char *name);
typedef enum {WRITE_REPLACE, WRITE_XOR, WRITE_OR, WRITE_AND} writeModeType;
void SRGP_setWriteMode (writeModeType);
void SRGP_setClipRectangle (rectangle);
void SRGP_setFont (int font_index);
void SRGP_setMarkerSize (int width_in_pixels);
typedef enum {MARKER_CIRCLE, MARKER_SQUARE, MARKER_X} markerStyleType;
void SRGP_setMarkerStyle (markerStyleType);
typedef enum {CONTINUOUS, DASHED, DOTTED, DOT_DASHED} lineStyleType;
void SRGP_setLineStyle (lineStyleType);
void SRGP_setLineWidth (int width_in_pixels);
void SRGP_setColor (int color_index);
void SRGP_setBackgroundColor (int color_index);
void SRGP_setPlaneMask (int bitmask);
typedef enum {
SOLID, BITMAP_PATTERN_OPAQUE, BITMAP_PATTERN_TRANSPARENT, PIXMAP_PATTERN}
drawStyle;
void SRGP_setFillStyle (drawStyle);
void SRGP_setPenStyle (drawStyle);
void SRGP_setFillBitmapPattern (int pattern_index); void SRGP_setFillPixmapPattern (int pattern_index); void SRGP_setPenBitmapPattern (int pattern_index); void SRGP_setPenPixmapPattern (int pattern_index);
typedef struct ... attribute_group; /* see srgppublic.h for details */ void SRGP_setAttributes (attribute_group*);
An ellipse is specified in terms of the rectangle within which it is inscribed. Polygons, rectangles, and ellipses may be generated as framed or filled. A filled primitive is all-interior - the frame is not displayed.
WARNING: because SRGP by default uses X's asynchronous mode, a call to an output primitive routine may not produce an image for an arbitrary length of time, or may not produce an image at all! This is because in asynchronous mode, X commands are buffered, being actually sent only when/if the buffer gets full or when/if your program attempts to perform any kind of graphical input. If your program often uses SRGP input devices, this should not be a problem; but, if your application is output-only or has sleeps or long pauses not related to waiting for input, you must explicitly flush the X buffer at appropriate times, using this function:
void SRGP_refresh (void);Alternatively, you can place X in synchronous mode using the routine described in section 1. This guarantees a buffer flush after each application-level call to SRGP output routines, but at great performance cost.
void SRGP_point (point); void SRGP_pointCoord (int x, int y);
void SRGP_marker (point) void SRGP_markerCoord (int x, int y)
void SRGP_line (point pt1, point pt2); void SRGP_lineCoord (int x1, int y1, int x2, int y2); void SRGP_rectangle (rectangle); void SRGP_rectanglePt (point lower_left, point upper_right); void SRGP_rectangleCoord (int left_x, int lower_y, int right_x, int upper_y);
void SRGP_polyPoint (int vert_count, point *vertices); void SRGP_polyMarker (int vert_count, point *vertices); void SRGP_polyLine (int vert_count, point *vertices); void SRGP_polygon (int vert_count, point *vertices); void SRGP_polyPointCoord (int vert_count, int *x_coords, int *y_coords); void SRGP_polyMarkerCoord (int vert_count, int *x_coords, int *y_coords); void SRGP_polyLineCoord (int vert_count, int *x_coords, int *y_coords); void SRGP_polygonCoord (int vert_count, int *x_coords, int *y_coords);
void SRGP_ellipse (rectangle bounds); void SRGP_ellipseArc (rectangle bounds, double startangle, double endangle);
void SRGP_fillPolygon (int vert_count, point *vertices); void SRGP_fillPolygonCoord (int vert_count, int *x_coords, int *y_coords); void SRGP_fillEllipse (rectangle); void SRGP_fillEllipseArc (rectangle bounds, double startangle, double endangle); void SRGP_fillRectangle (rectangle); void SRGP_fillRectanglePt (point lower_left, point upper_right); void SRGP_fillRectangleCoord (int left_x, int lower_y, int right_x, int upper_y);
void SRGP_text (point origin, char *str);
void SRGP_beep (void);
void SRGP_copyPixel (canvasID source_canvas, rectangle source_rect,
point dest_corner);
dest_corner describes the lower-left corner of the destination rectangle (lying inside the currently-active canvas) having the same size as source_rect.
Only the rectangular portion of source_rect which lies within the boundaries of the source canvas is copied. The placement operation is affected by the current clipping-rectangle and write-mode.
The measure of an input device is the value currently associated with the device.
The trigger of an an input device is the action which indicates a significant moment associated with the device.
The attributes of an input device are the parameters of the device which are under application-control, primarily the echo characteristics.
At any given time, each device is either active or inactive. The process of activation places a device into an active state; the process of deactivation places it into an inactive state. Zero or more devices may be simultaneously active.
typedef enum {NO_DEVICE, LOCATOR, KEYBOARD} inputDevice;
typedef enum {INACTIVE, SAMPLE, EVENT} inputMode;
void SRGP_setInputMode (inputDevice, inputMode);
When d 's mode is set to Inactive, the device is deactivated : trigger firings from the device are ignored and echoing is disabled.
The button-mask attribute of this device determines which of the buttons are of interest when the device is active in Event mode: only buttons specified in this mask can trigger an event.
typedef enum {UP, DOWN} buttonStatus;
typedef struct {
point position;
buttonStatus button_chord[3];
int button_of_last_transition;
} locator_measure;
The "deluxe" version of the locator measure includes
a chord giving the status of three primary modifier keys at the time of the
last button transition, and a timestamp structure. The modifier chord array is
indexed via
SHIFT, CONTROL, and META.
(Warning: the physical key to which the META modifier maps varies from system
to system; moreover, some window manager setups will swallow button presses
modified by these keys, and thus applications that rely on modifier keys lose
some portability.)
(The META key is labelled "option" on the Macintosh keyboards.)
The timestamp specifies the time at which the most recent
change
to the measure occurred - i.e., successive sampling of a
non-moving locator produces a "constant" timestamp.
typedef struct {
int seconds; /* duration since application launch */
int ticks; /* a tick is 1/60th second */
} srgp_timestamp;
typedef struct {
point position;
buttonStatus button_chord[3];
int button_of_last_transition;
buttonStatus modifier_chord[3]; /* status at last transition */
srgp_timestamp timestamp;
} deluxe_locator_measure;
The "deluxe" version of the measure includes the modifier chord, a timestamp, and a locator position:
typedef struct {
char *buffer; /* ptr to space allocated by application */
int buffer_length; /* set by application */
buttonStatus modifier_chord[3];
point position;
srgp_timestamp timestamp;
} deluxe_keyboard_measure;
The
processing-mode
attribute of this device determines which of the two
meanings is given to the measure of the device:
void SRGP_setLocatorButtonMask (int value);
SRGP_setLocatorEchoType (int value); /* CURSOR, RUBBER_LINE, or RUBBER_RECT */
void SRGP_loadCursorTable (int cursor_index, int shape); /* shape: any constant defined in*/
void SRGP_setLocatorEchoCursorShape (int cursor_index); void SRGP_setLocatorEchoRubberAnchor (point position);The keyboard's attributes are set via:
typedef enum EDIT, RAWkeyboardMode; void SRGP_setKeyboardProcessingMode (keyboardMode); void SRGP_setKeyboardEchoColor (int color_index); void SRGP_setKeyboardEchoFont (int font_index); void SRGP_setKeyboardEchoOrigin (point position);
void SRGP_setLocatorMeasure (point value); void SRGP_setKeyboardMeasure (char *value);
The SRGP_sampleKeyboard function copies the keyboard measure into the given character-array buffer of size bufferlength. If the current keyboard measure is longer than ( bufferlength - 1) bytes, it is truncated. Similarly, the keyboard-measure structure sent to SRGP_sampleDeluxeKeyboard must have pre-set values for its buffer and bufferlength fields.
void SRGP_sampleLocator (locator_measure *measure); void SRGP_sampleKeyboard (char *buffer, int buffer_length); void SRGP_sampleDeluxeLocator (deluxe_locator_measure *measure); void SRGP_sampleDeluxeKeyboard (deluxe_keyboard_measure *measure);
inputDevice SRGP_waitEvent (int maximum_wait_time);
The return value identifies the device causing the event. The special value NO_DEVICE is returned when the procedure exits due to timeout.
void SRGP_getLocator (locator_measure *measure); void SRGP_getKeyboard (char *measure, int buffer_length); void SRGP_getDeluxeLocator (deluxe_locator_measure *measure); void SRGP_getDeluxeKeyboard (deluxe_keyboard_measure *measure);
void SRGP_inquireAttributes (attribute_group *group);
canvasID SRGP_inquireActiveCanvas (void);
rectangle SRGP_inquireCanvasExtent (canvasID); void SRGP_inquireCanvasSize (canvasID, int *width, int *height);
int SRGP_inquireCanvasDepth (void);
void SRGP_inquireTextExtent (char *str, int *width, int *ascent, int *descent);
void SRGP_setMaxCanvasIndex (int i); void SRGP_setMaxPatternIndex (int i); void SRGP_setMaxCursorIndex (int i); void SRGP_setMaxFontIndex (int i); void SRGP_setMaxPointlistSize (int i);
The first feature is tracing. When enabled, each call to an SRGP routine (except a few input-related routines) produces a detailed message in a log file. A parameter to SRGP_begin() controls the initial state of tracing; calls to SRGP_tracing can be used to control the state during runtime. Early test runs of an application should always be performed with tracing enabled. For more details on tracing, see Section 2.
The second feature is parameter verification. All SRGP routines perform verification of all parameters (except those that are pointers to an array or structure) before they commence operation. All errors are fatal and produce a crash with a detailed error message. Only when a program is fully debugged should optimization efforts include disabling parameter verfication! You may permanently disable verification and tracing via:
void SRGP_disableDebugAids (void);If your application crashes due to an X server error or in the scope of an SRGP routine, there are two possibilities (assuming the X server is not at fault):
SRGP_beginWithDebug (name, w, h, p, TRUE); SRGP_enableSynchronous();About SRGP's diagnostics: There are two types of run-time errors. The first type is parameter-verification errors; the verification can be turned off as mentioned earlier. The second type occurs when a problem unrelated to bad input occurs, like running out of memory when attempting to allocate a canvas. All errors - of both types - are considered fatal by default, and cause a crash after displaying an informative message to the user. Some programmers might wish to make all errors be non-fatal, so program execution can continue (with suitable recovery algorithms, of course). The following routine can be used to choose between fatal and non-fatal error handling:
typedef enum {FATAL_ERRORS, NON_FATAL_ERRORS} errorHandlingMode;
void SRGP_setErrorHandlingMode (errorHandlingMode);
When an error is detected by SRGP while the mode is
NON_FATAL_ERRORS,
no message is issued to the user; rather, a
global variable is set to a positive integer that represents the error:
#include "srgp_errtypes.h" extern int SRGP_errorOccurred;The header file contains symbolic constants mapping the integers to error types. The global variable is never reset to 0 by SRGP; the application is responsible for examining and resetting it. Obviously, non-fatal mode should be used with great care, and only late in an application's development.
Keep the difference between synchronous and asynchronous modes in mind: if a brief application produces no output, it may be because the X command buffer is not being flushed. For more information, see section 4.4.
On color machines, if the SRGP application requests full color support (by passing 0 as the fourth parameter to SRGP_begin ), the windows of clients other than the SRGP application will not show their "true" colors whenever the cursor lies within the SRGP window; likewise, the SRGP window will show false colors whenever the cursor lies outside its extent. This can be disconcerting, and an application that does not need full use of the color table should instead reserve a subpart of the color table by sending a positive integer to SRGP_begin. (Note: if your application does not call SRGP_waitEvent often enough, SRGP will not be notified of the cursor's location, and thus it may not be able to restore true colors when the cursor enters the SRGP window.)
Note that color indices are integers (rather than unsigned longs, as in X). This means that in the rare event that one is using a machine with a 32-bit deep framebuffer but with only 16-bit integers, the full set of LUT entries will not be available.
Most machines support X's backing-store feature; on these machines, SRGP refreshes its window whenever any part that was previously covered is re-exposed, or whenever it is de-iconized. On other machines, the graphic information in the window is volatile and is lost whenever it is covered or iconized.