Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals  

XGL User Guide
version 1.0

Peter C. Mehlitz
Transvirtual Technologies Inc.
Contents
  1. Introduction
  2. Key Concepts
  3. Using XGL
  4. Extending and Building XGL

1 Introduction

XGL is a portable, extensible, autark 2D graphics library for framebuffer devices, which can fit in as less as 60kB. Its design follows the motto
"Keep it as simple as possible, but not simpler"

Most graphical user interface toolkits and windowing systems are based on separate 2D graphics libraries, encapsulating device driver functionality for basic operations like drawing lines, images and strings. Traditionally, these drivers have been focused on support for dedicated display processor chips (e.g. with 3D accelerations), and runtime (user) configured settings like color modes and (high) screen resolutions.

These design goals do not fit the majority of embedded devices, which usually have smaller displays, fixed display configurations, less memory, and the need to save power. As a consequence, most embedded systems use a simple framebuffer (i.e. memory mapped) display and do not have a dedicated display processor. On the other hand, many embedded systems utilize RISC CPUs which are suitable for accelerating certain graphics operations. Due to memory constraints and device specific input mechanisms there is also a increased need to scale the graphics support (e.g. touch-screen instead of pointing device / cursor).

XGL was specifically designed to meet the needs of resource constrained embedded systems like handsets and small PDAs. From a application developers point of view, it is mainly a library which provides rendering support for two dimensional graphics, i.e. a rasterizer which computes 2D pixels for certain shapes (lines, ellipses etc.), and maps these pixels into the 1D display memory (framebuffer). While core XGL can be used to directly build specialized graphics applications on top of it, the normal case is to utilize it for toolkit implementation. XGL is especially suitable to implement libraries like the Java MicroEdition ® user interface (the LCDUI).

The underlying implementation is what sets XGL apart. The library is rather a extensible set of heavily inlined modules than a closed binary in itself (e.g. a shared library). XGL does not make compromises - it trades display setting independent binary applications for size and speed (XGL can have less than 60kB of code). If the framebuffer format and/or library configuration is changed, applications have to be re-compiled. On the other hand, XGL provides a wealth of configuration options, including various font engines, color formats, CPU specific and coprocessor specific accelerations. The exported APIs shield the application from the subtleties of the actual configuration, not forcing the application to deal with explicit mode branches like #ifdefs etc.

XGL achieves its flexibility by means of layers and overlays. To decrease coupling between modules, XGL uses a internal layer structure.

XGL modules also have a well defined internal structure, enabling not only replacement, but also partial extension by means of more specialized, potentially cascaded overlay modules. For each module class (e.g. the 16bpp framebuffer access module fbac_16), there is a generic implementation. Overlay modules (like fbac_16_i386 and fbac_16_i386_mmx) only have to override their specific functions.

In general, module functions are inlined wherever appropriate (to avoid call overhead, and eliminate all non-required code generation), there is no distinction between headers and modules. A good example for this is a 16bpp bitmap fill operation, which usually requires a called function with a explicit loop
void fillBmp (bitmap bmp, color clr) {
  u16 *p, *pe = bmp->surface + (bmp->width * bmp->height);
  for ( p=bmp->surface; p<pe; p++ )
    p = clr;
}
As an example, on i386 machines this can be collapsed into two inlined machine instructions
REP
STOSW
which do not justify the overhead of a function call (at least in terms of code size).

The implementation of XGL deliberately uses ANSI C (with inline assembly extensions). Even though the required extensibility would normally justify the usage of C++, this would have negative effects on size and speed, since the extension is mainly a matter of replacing base module functions (i.e. completely erasing the base function code). In addition, XGL strives for utmost portability and the lowest possible runtime overhead.

In this sense, a XGL build is more like a snapshot of pre-configured functions, both linked from a library and inlined into the application. XGL is like a Lego® toolbox for 2D graphics drivers, and the most portable way to get graphics support for framebuffer devices.

2 Key Concepts

The most fundamental type of XGL is the color bitmap (xgl_cBitmap_t). Every drawing operation ultimately renders into a color bitmap, the screen itself (i.e. the framebuffer) is nothing else but a global, pre-initialized color bitmap object. The depth of color bitmaps always is the configured framebuffer depth, and has to be identical with the physical framebuffer setting (depending on the configured initialization module, XGL will take modest efforts to switch the framebuffer into the compiled-in format). Rendering into color bitmaps is done in a consistent way, regardless of if this directly goes into the framebuffer or a offscreen image (e.g. for double-buffering).

There are three specialized bitmap types. Monochrome bitmaps (xgl_mBitmap_t) have only one bit per pixel, nibble bitmaps (xgl_nBitmap_t) 4bpp, and byte bitmaps (xgl_bBitmap_t) 8bpp. These bitmaps are mainly used as masks or separate alpha channels, i.e. they either control which pixels of a color bitmap should be rendered, or are drawn with the current color (e.g. for fonts).

The construct logically sitting on top of the color bitmap is the graphics context (xgl_context_t). It is used to encapsulate state which applies to a number of related output operations, most notably rectangular clipping (region clipping can be added on top of core XGL), current font and color. Every graphics context is associated with a target color bitmap. Most graphics primitives (xgl_drawLine() etc.) take a context object parameter as the destination object.

The construct sitting underneath the color bitmap is the scanline cell (xgl_slc_t), which is used to map the two dimensional bitmap data into one dimensional framebuffer memory. A scanline cell is the smallest addressable framebuffer unit holding at least one pixel value. On displays with more than 8bpp depth, it simply maps to the appropriate built-in type (e.g. for 16 bit truecolor visuals, it is defined as a unsigned short). On 4bpp visuals (16 grayscale or VGA), it is to a byte holding two pixel values.

Mapping of 2D raster pixels into scanline cell addresses is done by a set of (usually inlined) functions residing in the framebuffer access module (fbac_x). Besides making the shape rasterizers independent of the actual framebuffer format, this serves as a fundamental acceleration mechanism: using scene coherence to reduce the number of full 2D-to-1D transformations

setPixel(bmp,x,y,clr) {
  *(bmp->surface + (y * bmp->width) + x) = clr;
}
and replace them by simple memory increment/decrement operations, thus saving multiplications and additions
xgl_slc_t *p = getSlc(bmp,x,y);
..
_xgl_setSlc(p, clr);  // expands to *p = clr
_xgl_incSlc(&p);      // expands to p++
A classical example of a shape algorithm suitable for scene coherence based reductions is the Bresenham line drawing algorithm.

Most XGL render operations use pixel value colors (xgl_color_t), i.e. the physical color format of the framebuffer (e.g. packed 565 RGB). However, there is a dedicated color mapping module (cmap) providing an API to convert 8888 ARGB values into pixel values, and to extract ARGB channel values from a given pixel value.

XGL fonts are internally stored as bitmapped fonts, using monochrome or byte bitmaps (for anti-aliased fonts) as the underlying construct. Instantiation of fonts is performed via a configured font engine. XGL comes with engine modules based on header files (i.e. font data in static memory), binary bitmapped font files (with a potentially mmapped glyph space), or external font libraries (e.g. the freetype library for TrueType fonts).

XGL fonts use a unicode encoding, i.e. describe consecutive unicode character subsets. Since a full unicode font would be usually way to big for a small device (requires >10MB of secondary storage), XGL fonts can be threaded to provide support for a configured locale. This builds on the assumption that most locales just need a small number of unicode subsets, e.g. a Japanese locale about 6000 cjk (kanji), 200 kana (katakana, hiragana), and 200 latin characters, thus significantly reducing the number of required glyphs.

Sprites are constructs used for movable, overlayed bitmaps (e.g. cursor shapes or animated symbols). Sprite objects consist of a color bitmap, a mask (to implement transparency), a current position (where they are rendered into the destination context), a visibility status, and a 'save under' color bitmap, holding the destination content obscured by a visible sprite. The sprite API (from module sprite) provides functions which ensure proper background save and restore operations. The main usage of sprites is to implement a global SW cursor.

The draw wrapper is a concept that does not have a corresponding data structure, but shows up in a common code pattern. Each operation that changes the content of a color bitmap (e.g. xgl_drawRect()) is wrapped by _xgl_beginDraw(), _xgl_endDraw() functions like

void xgl_drawXX (xgl_context_t* ctx, ){
  // do clipping and transformation, computing the bounds
  // (xLeft,yTop), (xRight,yBottom) of the area changed by
  // the subsequent rendering

  _xgl_beginDraw(ctx->bmp, xLeft, yTop, xRight, yBottom);
	 // do the rendering (change bitmap contents)
  _xgl_endDraw(ctx->bmp, xLeft, yTop, xRight, yBottom);
}

The two wrapper functions are defined in the draw module class, and can be used to do general update region handling, e.g.to implement a SW cursor, to manage a display processor instruction pipeline (acceleration), or to interface to a host windowing system (e.g. to display a simulated framebuffer on a Win32 or X system).

3 Using XGL

Using core XGL in applications is very simple, since most of the complexity is hidden inside its internal configuration. The inevitable ''hello world'' example looks like this

#include "xgl.h"

int main ( int argc, char* argv[] ){
  if ( !xgl_initialize() ) return 0;
  xgl_setColor( xgl_screenCtx, xgl_clrRed);
  xgl_drawString( xgl_screenCtx, "Hello XGL world", 100, 100);
  return 1;
}
The only XGL header that has to be included is xgl.h, which recursively includes all configured modules, i.e. provides the whole API definition.

To build it, we have to extend the include path to the module directory of XGL, and link against one of its build directories. For example, if XGL resides in /xgl, and the application should run in a 16bpp true-color mode under Linux on a i386 machine (build-i386-linux-t565), with a GNU tool chain (gcc) the build command could look like

gcc -I ~/xgl -I ~/xgl/build-i386-linux-t565 -o hello hello.c \
           -L ~/xgl/build-i386-linux-t565 -lxgl
(More complex application builds can be eased by means of a Makefile_xgl.frag file described in the next section).

Depending on the configured font engine, we might have to set a environment variable XGL_FONTPATH, to tell XGL where to find its fonts.

Even this small example shows the common usage pattern, which consists of the following steps

  1. initialize system (xgl_initialize(), termination will be done automatically)
  2. obtain a context object (the global, pre-initialized xgl_screenCtx in the above example)
  3. modify its state (xgl_setColor(), xgl_setFont(), xgl_setClip() etc.)
  4. call rendering functions for context (xgl_drawLine(), xgl_drawText(), xgl_fillRect() etc.)

XGL APIs can be divided into two function categories

The high level API is mainly provided by the following core XGL modules

cbmp
color bitmaps 
xgl_createCBitmap(), xgl_setCPixel(), xgl_cBlt(), xgl_cBltMask() etc.
mbmp
monochrome bitmaps
xgl_createMBitmap(), xgl_setMPixel(), xgl_mBlt() etc.
bbmp
byte bitmap
xgl_createBBitmap(), xgl_setBPixel(), xgl_bBlt() etc.
cmap
color maping 
xgl_getPixelValue(), xgl_getRgbValues() etc.
cntx
context manipulation 
xgl_createContext(), xgl_setColor(), xgl_setFont(), xgl_setClip() etc.
font
font handling 
xgl_getFont(), xgl_drawText(), xgl_getTextWidth() etc.
line
line drawing 
xgl_drawV/HLine(), xgl_drawLine() etc.
poly
polygon drawing 
xgl_drawPolygon(), xgl_fillPolygon() etc.
rect
rectangle drawing 
xgl_drawRect(), xgl_fillRect() etc.
oval
circle and ellipse drawing 
xgl_drawCircle/Ellipse(), xgl_fillCircle/Ellipse() etc.
spri
sprite handling 
xgl_createSprite(), xgl_hide/showSprite(), xgl_moveSprite() etc.
curs
cursor handling 
xgl_setCursor(), xgl_hide/showCursor(), xgl_moveCursor() etc.

The low level API is mainly used internally, but can be suitable for specialized applications like image manipulation. It is mainly located in

util
utility macros  
_xgl_swapXX(), _xgl_inInterval(), _xgl_clipXX() etc.
fbac
framebuffer access 
_xgl_getSlc(), _xgl_setSlc(), _xgl_incSlc(), _xgl_setSlcCols() etc.
draw
update rectangle management 
_xgl_beginDraw(), _xgl_endDraw() etc.

Performance critical applications which process pixels in scanline order are encouraged to use the fbac macros (i.e. xgl_slc_t) instead of the more expensive, high level xgl_set/getCPixel() functions.

Besides the 'xgl_' and '_xgl_' prefix rules, there are some other conventions used throughout the system

4 Extending and Building XGL

XGL consists of four different source types: base modules, overlay modules, configuration headers and the master header.

Extending XGL is done by writing new XGL base or overlay modules. Every XGL module is a '*.c' source file with a standard structure

#ifndef module_INCLUDED
#define module_INCLUDED

#include "xgl.h"
#include used_module
..
/* prototypes */
..
/* inlined exported function definitions */
#ifndef xgl_foo
static inline void xgl_foo () {..}
#endif
..
#ifdef COMPILE_base_module
/* global data definition */
..
/* module private data and functions */
..
/* non-inlined exported function definitions */
#ifndef xgl_bar
void xgl_bar () {..}
#endif
..
#endif COMPILE_base_module
#endif module_INCLUDED
Explicitly including used modules does not only increase readability, but is also required to avoid breaking the layer structure (by including higher level modules before the definition of a lower level module).

Overlay modules have the same structure, but just have to define functions which are replaced

#ifndef overlay_module_INCLUDED
..
#define xgl_bar xgl_bar
#include base_module
..
#ifdef COMPILE_base_module
void xgl_bar () {..}
..
#endif COMPILE_base_module
#endif overlay_module_INCLUDED
Note that overlay modules only include their base module, after #defining the function names which will be overridden. Make sure the overriden function name get #defined to itself, or the preprocessor will scramble the subsequent function definition.

All used modules are symbolically specified, e.g.

#include XGL_FBAC_MODULE
to enable configuration / selection of required modules by means of a configuration header config_xgl.h, which is in turn included by xgl.h, and contains two sections
/* basic type definitions */
typedef unsigned short u16;
..
typedef unsigned short xgl_slc_t;

/* module specifications */
#define XGL_FBAC_MODULE "fbac_16_i386"
..

The xgl.h master header has the following structure

#ifndef xgl_INCLUDED
#define xgl_INCLUDED

#include "config_xgl.h"

/* common XGL type definitions */
typedef struct {..} xgl_cBitmap_t;
..
/* common macros */
..
/* common global objects */
extern xgl_cBitmap_t xgl_screenBmp;
..
/* module API collector */
#ifndef XGL_MODULE
#include XGL_FBAC_MODULE
..
#endif

#endif xgl_INCLUDED

The following diagram shows how the different source types are combined to build XGL library instances and applications. The config_xgl.h header is 'injected' (from the build directory) into the master header xgl.h, which serves a two purpose role. For applications, it defines the whole API, collected from all configured modules. For the modules itself, it defines all common data types and macros, and the other modules themselves. Overlay modules recursively include their base modules, overriding just their specialized functions.

XGL uses a fixed directory structure to organize sources and binaries, concentrating module sources and the master header in the XGL root directory. The configuration header instances are kept in separate build sub-directories (e.g. build-i386-linux-t565), together with their corresponding Makefiles.

Test sources are kept in a separate root sub-directory, but are built in corresponding test directories inside of build directories. The font directory includes font files used by XGL (mainly the internal *.fnt format).

To add a new XGL configuration, we have to add a new build directory, and create a config_xgl.h, Makefile, and optionally a Makefile_xgl.frag in it. The config_xgl.h mainly has to define the modules to use, e.g.

..
#define XGL_FBAC_MODULE "fbac_16_i386.c"
.

The Makefile compiles the configured modules, with the build directory added to the include path, and source files looked up in the parent directory (e.g. by means of a VPATH setting). Using a GNU tool-chain, the Makefile could have the following structure

INCLUDES= -I. -I..

CFLAGS= -DXGL_MODULE

VPATH=..

HEADERS= xgl.h config_xgl.h

OBJECTS=\
  fbac_16_i386.o \
  ..

libxgl.a : $(OBJECTS) $(HEADERS)

fbac_16_i386.o : fbac_16_i386.c $(HEADERS)
    $(CC) $(INCLUDES) $(CFLAGS) -DCOMPILE_FBAC -c $< -o $@
Only the topmost override module is specified in the makefile (e.g. fbac_16_i386.c), since all its base modules (e.g. fbac_16.c) get automatically included by it. Note that without reverting to make - implementation specific behavior, we have to add explicit make-rules for all modules (i.e. object files) to set the -DCOMPILE_base_module switch, but this should be done to specify inter-module dependencies, anyway (modules get heavily inlined).

If the build is going to be used for applications outside the XGL source tree (not residing in tools or test), it is a good idea to create a Makefile_xgl.frag in the build directory, which should contain extensions of the usual (platform specific) build macros

CFLAGS += <compile options required by xgl build>

INCLUDES += -I$(XGL_ROOT)/$(XGL_BUILD) -I$(XGL_ROOT) \
              <include paths required by xgl build>

LIBS += -L$(XGL_ROOT)/$(XGL_BUILD) -lxgl \
              <libs required by xgl build>
This fragment would be included in the application Makefile after defining the XGL_ROOT and XGL_BUILD variables, for example
..
XGL_ROOT  = ~/projects/xgl
XGL_BUILD = build-i386-linux-t565
include $(XGL_ROOT)/$(XGL_BUILD)/Makefile_xgl.frag

 :
     $(CC) $(CFLAGS) $(INCLUDES)  $< $(LIBS)
..

The XGL build process is deliberately kept as simple as possible, and stays away from using sophisticated configuration and build tools (like autoconf etc.) to reduce build platform specific tool dependencies. The minimal requirement for a non-specialized build should be just a ANSI C compiler (e.g. gcc) and a make tool (e.g. GNU make).


The XOE Graphics Library, Copyright © 2002 Transvirtual Technologies Inc.