Christoph Reichenbach
April 2nd, 2000
Up until version 0.3.0, FreeSCI used a graphics subsystem which used per-pixel operations on three 320x200 8 bit buffers. This concept, while being simple to implement for driver writers, proved to have several disadvantages:
Non-native memory layout: Using a fixed 8bpp visual buffer meant that, for each update, all graphics would have to be translated to the graphics driver's native format, unless it already was running in 8bpp.
No use of accellerated drawing functions: Many of the targetted graphics drivers supported hardware-accelerated drawing of rectangles or lines; this could not be taken advantage of, due to the per-pixel access
Scalability moved to the drivers: Each driver would have to take care of magnifying the resulting picture by itself (if it wanted to support it at all), since the base buffer was at a fixed size.
Manual graphics buffer access: This was in fact used in many places, making it hard to keep track of modifications, which, in turn, would have inhibited attempts to track modifications of the visual buffer. However, without those, either each drawing operation would have enforce an update, causing flickering in the general case, or the full screen would have to be re-drawn each time (which was what actually was done), resulting in major performance penalites, especially for remote displays.
Combined with some cases of code rot, these problems suggested a re-write of the complete graphics subsystem, and a more modular re-design in preparation for supporting later revisions of SCI (and, possibly, related engines such as AGI).
This documentation section will describe the architecture and functionality of the new graphics subsystem, which has been in operation since FreeSCI 0.3.1. I will start by giving a general overview of the various components involved and how they interact, and then give a more detailed description of each of those components in sequence.
In extension of the architecture used up until FreeSCI 0.3.0, the new graphics subsystem now uses a total of six buffers:
Map Name | # of buffers | scaled | bpp |
---|---|---|---|
visual | 3 | yes | determined by driver |
priority | 2 | yes | 8 |
control | 1 | no | 8 |
Of these, the visual and priority buffers have to be provided by the graphics driver, since they are relevant for display and may actually be present physically (since the priority map is nothing other than a Z buffer). The control map, a special buffer used by the interpreter to check whether moving objects hit obstacles on the screen or touch zones with special meanings, is only relevant for the interpreter and therefore handled one level above the graphics driver.
I will refer to the level above as the "operational layer". This layer handles all of the primitive graphical operations. It performs clipping, keeps track of modified regions, and emulates functions required but not supported natively by the graphics driver.
The operational layer is also responsible for the four pixmap operations, which draw background pictures, images, text, or mouse pointers. These pointers are only referred to by their respective ID numbers; they are retreived from the graphical resource manager. This graphical resource manager (GRM) is another separate subsystem- it retreives graphical resources in one of a set of standard formats, and translates them to the graphics driver's native format in one of several possible ways. It also receives hints from the operational layer to improve its caching strategy.
Finally, above the operational layer, another layer is situated: This widget layer provides abstract descriptions of things on the screen as objects,
so-called widgets. It provides the primary interface for the interpreter to interact with.
There are a number of standard data types defined in src/include/gfx_system.h which are used all over the place in the graphics
subsystem; therefore, they warrant some special attention in order to understand how it works.
This data type is nothing more than a tuple (x,y). It describes a coordinate on the screen; a one-line
way to generate a point_t is to use the function gfx_point(x,y).
This type describes a rectangular area on the screen, as a four-tuple (x,y,xlen, ylen), where the point (x, y) describes the upper left point of the rectangle, whereas xlen and ylen are the number of pixels the rectangle extends to the right on the x and downwards on the y axis, respectively. A rect_t can be generated in-line by the function gfx_rect(x,y,xl,yl).
A number of functions are available to operate on rect_ts. These functions are 'pure' in the functional sense, meaning that
they do not modify the original rectangle, but, rather, return a new one (of course, an optimizing compiler will make this a moot point from a performance
perspective).
This function is a predicate that returns non-zero iff rect_a describes the same rectangle as rect_b.
Returns a rectangle which equals rect translated (moved) by the (x, y)
tuple described by the point parameter (i.e. point is interpreted as a relative coordinate).
A predicate to determine whether all pixels contained in the area described by rect_a are also contained in the area described
by rect_b. Reflexive and transitive.
A predicate to test whether there exists a pixel in the area described by rect_a which is contained in the area described
by rect_b. Reflexive and symmetric.
This structure describes a single color in a pixmap. It consists of 8 bit r, g, b values to describe a color; when used in a pixmap, it is part of a palette of gfx_pixmap_color_ts where the entry at index i describes the color of the respective color index i inside the pixmap.
In palette mode, the global_index entry is used to store the color index entry of the global palette that correlates with the
pixmap index (or GFX_COLOR_INDEX_UNMAPPED if this value has not been determined yet).
gfx_color_t structures contain color information for all three color maps. They consist of a gfx_pixmap_color_t structure, visual, which describes the effects of the color on the visual map, an alpha entry to describe the color's transparency (0 means 'opaque', 255 means 'totally transparent', although graphics drivers may choose to slighly alter those meanings for performance considerations), priority and control values for the respective maps, and a mask to determine the maps affected.
This mask is a bitwise-OR of the constants GFX_MASK_VISUAL (meaning "draw to the visual map"), GFX_MASK_PRIORITY ("draw
to the priority map") and GFX_MASK_CONTROL (guess).
The FreeSCI only supports a small subset of all possible graphics modes; specifically, it only supports modes where the integer value of each pixel can be stored in 8, 16, 24, or 32 bits. Color index mode is supported, but non-indexed mode has additional requirements: Each color aspect of red, green, and blue must be represented by a consecutive sub-vector <vc, vc+1, ... ,vc+n-1> of the total color vector <v0, v1, ... ,vb-1>, where n and c are non-negative integers, and c+n ≤ b holds. With vb being the most significant bit of the total bit vector, we also require that for each m where 0 < m < n the bit vc+m should, if set, increase brightness about twice as much as setting vc+m-1 would. This allows us to represent each color aspect by means of an AND bitmask and an integer shift value.
This, along with a global palette and the scaling factors, is the core of the gfx_mode_t data. It also contains a shift values
and an AND bitmask for alpha values; if these values are set to non-zero by the graphics driver, alpha channel information will be written to the same
block of data the color values are written to when pixmaps are calculated. If they are not set, a separate 8bpp alpha data block will be added to the
pixmaps.
The gfx_pixmap_t structure is another fundamental element of the graphics subsystem. It describes a single pixmap, such as a background picture, a cel, a mouse pointer, or a single line of text. It contains up to two references to the graphical data it describes: One unscaled block of color-indexed data (index_data, and another block scaled and in the graphics driver's native format (data).
Each pixmap contains a local palette of colors_nr gfx_pixmap_color_t entries, called colors. This palette is allocated dynamically and may be NULL if no index_data block is present.
Also, a tuple (xoffset, yoffset) describes the pixmap's 'hot spot'. This is a relative offset into the unscaled data; it is used to describe the point which drawing operations will refer to. This means that pixmap draw operations on this pixmap will cause it to be drawn xoffset pixels (unscaled) to the left of the coordinate specified.
Next comes the unscaled pixmap data, called index_data, which occupies a size of index_xl * index_yl bytes. Each byte is either a reference into the palette, or GFX_COLOR_INDEX_TRANSPARENT (0xff), which means that it describes a transparent pixel, unless 256 colors are indeed present in the palette[1]
The pointer data, unless NULL, points to a block of data allocated to contain the translated graphical data in the graphics driver's native format. The number of bytes per pixel equals the bytespp property of the gfx_mode_t structure it was allocated for, whereas its horizontal and vertical extensions are stored in the xl and yl properties. Unless the graphics mode indicated that it supports an alpha channel itself, a separate alpha_map is also provided, at 8bpp.
Each pixmap also comes with a pixmap_internal block, which may be used by graphics drivers to store internal information (like pixmap repository handles).
Finally, each pixmap comes with a set of flags with the following meanings:
GFX_PIXMAP_FLAG_SCALED_INDEX: The pixmaps index data is already scaled; any algorithm for calculating data (and, possibly, alpha_map) therefore must not scale it again.
GFX_PIXMAP_FLAG_EXTERNAL_PALETTE: The palette supplied with the pixmap is stored externally, meaning that it must not be freed when the pixmap itself is freed
GFX_PIXMAP_FLAG_INSTALLED: The pixmap has been installed in the pixmap repository (used by the operational layer, although graphics drivers may choose to verify this if they don't trust that layer
GFX_PIXMAP_FLAG_PALETTE_ALLOCATED: (only relevant for color index mode) The pixmap's palette colors have been allocated in the internal palette listing and have been set appropriately in the palette
GFX_PIXMAP_FLAG_PALETTE_SET: (only relevant in color index mode) The pixmap's palette colors have been propagated to the graphics driver
GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE: (only relevant in color index mode) Instructs the pixmap freeing operations not to free the palette colors allocated by the pixmap. This is used in cases where the palette is stored externally.
src/include/gfx_tools.h defines many functions for creating pixmaps, allocating index data blocks, copying pixmap regions
etc.
These structures provide a bitmap lookup table intended for up to 256 entries. In practice, they are used to store font data. There is little surprising
about this structure, with the possible exception of the difference between the height and line_height variables: height describes
the actual character size, whereas line_height only describes how many pixels the text rendering functions should leave in between
text lines.
Every FreeSCI graphics driver provides an individual implementation for one specific target platform, such as the X Window System. In order to work correctly, it needs to implement the interface outlined in src/include/gfx_driver.h and list itself in src/include/gfx_drivers.h. Drivers have some freedom in determining which features they want to provide and which they want to have emulated. These features are determined by flags contained in its variable capabilities.
Graphics drivers must provide at least five buffers: Both priority buffers, and the three visual buffers. They are grouped in three sets labelled the Front Buffer (only one visual buffer), the Back Buffer, and the Static Buffer (both containing both a priority and a visual buffer). Most graphical operations operate on the back buffer, with their results being propagated to the front buffer by means of explicit buffer operations[2].
Driver implementations with limited or no hardware accelleration support, such as those operating on plain frame buffers, may use some shared functionality exported for their benefit. Those functions are listed in the appropriate function definitions below.
Unless specified differently, each function must return GFX_OK on success, GFX_ERROR on failure, and GFX_FATAL if and only if a fatal and unrecoverable error occured, such as the target display being closed by external means.
Functions that receive color parameters must respect those parameters' mask values for GFX_MAP_MASK.
For basic input and output, the GFXDEBUG(), GFXWARN() and GFXERROR() macros defined in src/include/gfx_system.h can be used. Also, there is another variable, debug_flags defined for drivers; while it cannot be changed during runtime (yet), it may be used in combination with the various GFX_DEBUG_ constants to selectively enable and disable debugging for certain parts of the driver during development.
For further debugging, the FreeSCI functions sciprintf() (a printf clone), MEMTEST() (tries to detect
heap corruption), and BREAKPOINT() (Sets a debugger breakpoint on Alpha, ia32 and SPARC) may be used.
None of the functions defined in here are optional. They are called during startup or shutdown and need not be considered performance critical.
This function is completely driver specific. Drivers may use it to allow external configuration of options not covered by the standard FreeSCI set of configuration options. It must be implemented to operate correctly both if init() has already been called and if it hasn't, although it may choose to ignore any options set afterwards.
Documentation of this function's options is up to the graphics driver's maintainer.
Initializes a graphics driver to a pre-determined mode, where xscale and yscale are the requested horizontal and vertical scaling factors (integers > 0), and bytespp is the number of bytes per pixel on the target display.
The function may set a higher resolution, provided that no matching resolution is available. The mode structure (stored locally to the driver structure) must be set by this function if it succeeds; for this, the function gfx_new_mode(), defined in src/include/gfx_tools.h, may be used.
GFX_OK must be returned iff the initialization succeeded; otherwise, GFX_ERROR must be reported,
unless the graphics target is not (or no longer) able to provide any of the supported modes (e.g. if a required external module was not found, or if
the driver detected during run-time that the target does not support any appropriate graphics mode).
This operation initializes the driver's default graphics mode. Determining this mode is up to the graphics driver; if its target platform has no means for determining an appropriate mode, it may choose to invoke init_specific() repeatedly with educated guesses. It must return one of GFX_OK or GFX_FATAL.
See the Section called init()> for details.
Deinitializes the graphics driver, frees all resources allocated by it and just generally performs clean-up. This function must succeed (so it does
not have a return value). It may use gfx_free_mode() (from src/include/gfx_tools.h) to free the
data allocated in the gfx_mode_t structure.
"Primitive drawing operations" here are operations that draw primitives. FreeSCI uses only two graphics primitives: Lines and solid boxes,
both of which are commonly provided by graphics libraries. Both operations draw to the back buffer; they also must respect the priority aspect of the
primary color used on them.
Draws a single line. The line parameter describes the starting point and a relative coordinates of the line to draw in the specified color, whereas line_mode specifies the line mode to use. This value may be GFX_LINE_MODE_FAST, which means that the line's thickness is roughly about the average of the horizontal and vertical scaling factors. The other two values need not be supported (they should fall back to GFX_LINE_MODE_FAST if they're used'):
GFX_LINE_MODE_FAST: Line thickness is averate of x and y scale factors
GFX_LINE_MODE_CORRECT: Lines are scaled separately for x and y and have correct widths there
GFX_LINE_MODE_THIN: Line has a width of 1
The other parameter, line_style, may be either of GFX_LINE_STYLE_NORMAL or GFX_LINE_STYLE_STIPPLED, although the latter is used iff the capability flag GFX_CAPABILITY_STIPPLED_LINES is set.
This function must return GFX_OK or GFX_FATAL.
By means of each widget's print method, its state can be written to the FreeSCI output stream. Output of the STATE is as follows:
STATE ::= VALIDITY "S"SERIAL ID [BOUNDS] FLAGS WIDGET-INFO VALIDITY ::= "v" /* widget is valid */ | "NoVis" /* Valid, but does not have a visual- internal error, unless it's a visual itself */ | "INVALID" /* Widget was invalidated */ | /* empty: Should never happen */ SERIAL ::= HEXNUMBER /* The widget's unique serial number */ ID ::= /* No ID */ | "#"HEXNUMBER /* ID assigned to the widget- typically an SCI heap address */ BOUNDS ::= (X-COORDINATE,Y-COORDINATE)(WIDTH,HEIGHT) /* Full extension of the graphics described by the widget */
The FLAGS are described by a sequence of characters; their meanings are listed below:
V: Widget is visible
O: Widget is completely opaque, i.e. fully covers all area in its bounds
C: Widget is a container
D: Widget is "dirty", i.e. will be redrawn at the next update
T: Widget has been tagged for clean-up
M: The widget's ID is not considered to be unique
I: Widget will not be freed if a snapshot is resored
The widget's ID will generally be considered to be unique within the container it is appended to, unless the Multi-ID flag ('M') is set. Functionally, this means that a widget w is appended to a list containing one or more widgets with an ID identical to its own, it overwrites the first widget with a matching ID, unless w itself has the M flag set.
The WIDGET-DESCRIPTION part of a widget starts with a string describing the widget's type; this
is followed by widget- specific information.
[1] This may cause a problem for SCI1 support, which explicitly
allows for 256 separate colors to be used alongside with transparency. Possible solutions include a separate transparency bitmap or increasing the number
of bits per index_data entry to 16bpp.
[2] These operations operate on partial buffer contents
and expect the back buffer's contents to be unmodified after the transfer. This is unlike the OpenGL back buffer concept.
Top
You can help keep The Sierra Help Pages and its affiliates alive by helping to defray some of the costs of hosting this site. If it has been of help to you, please consider contributing to help keep it online.Thank you.
The Sierra Help Pages | Sierra Game Help | Walkthroughs | Hints, Tips & Spoilers | Utilities | Links | SHP Forums | Search
© 2013 to present The Sierra Help Pages. All rights reserved. All Sierra games, artwork and music © Sierra.