ClanLib Display - ClanLib Game SDK

#

## ClanLib Display

To do graphics with ClanLib, you need to use clanDisplay. This module acts as an abstraction for any graphics target available via ClanLib - currently the OpenGL and SDL targets. ClanDisplay allows you to do stuff in the following categories:

- Top level windows
- Handling input (see Input overview)
- Drawing simple graphics
- Loading images from common formats
- Animating images easily
- Font and text rendering
- Working with image data

ClanDisplay is an abstraction, working to allow ClanLib applications to do stuff in a general matter that can be applied to any display target supported by ClanLib. In order to use clanDisplay, one or more display targets must also be setup. For example, a GL-based ClanLib app must call both CL_SetupDisplay::init() and CL_SetupGL::init() when starting.

The SDL target does not support every feature currently offered by clanDisplay, and it most likely never will. The main point of the SDL target in ClanLib is to support older cards that come with no 3D accelerator. ClanDisplay has been written to try take advantage of as much of modern OpenGL hardware acceleration as possible - for example, all the blending functions offered by glBlendFunc. Implementing a software target for all this is would take forever, and most likely would end up being as slow as using OpenGL software targets such as Mesa. Instead, the clanSDL target aims at only supporting the things that SDL API itself supports; it's better for it to get some things fast, instead of everything slow.

Because clanDisplay is an abstraction, its API in itself will not directly offer you access to the objects the implementation is using. Instead, each of the display targets offer their own set of subclasses for clanDisplay. For more information about this, see the OpenGL overview.

In general, if you are just doing normal 2D graphics, you will not need to use the target specific interfaces.

### Top level windows

Main classes: CL_DisplayWindow, CL_DisplayWindowDescription.

Before you can do any graphics with ClanLib, you need to create a window for your application. CL_DisplayWindow creates such a top level window. There are two ways to create it; either use one of the convenience constructors on CL_DisplayWindow:

    	CL_DisplayWindow window("My ClanLib Window", 640, 480);

Or you can create it by setting up a CL_DisplayWindowDescription, and pass that to CL_DisplayWindow:

    	CL_DisplayWindowDescription desc;
    	desc.set_title("My ClanLib Window");
    	desc.set_size(CL_Size(640, 480));
    	CL_DisplayWindow window(desc);

If you need to set target specific parameters, you can create a CL_OpenGLWindowDescription or CL_SDLWindowDescription object instead.

The CL_DisplayWindow object mainly gives you access to four things: a graphic context (for rendering to the screen), an input context (for getting input from the user), pixel buffers for back and front buffers (for accessing the frame buffer) and top level window management (for setting window position, resizing the window, going fullscreen, etc):

    	CL_GraphicContext *gc = window.get_gc();
    	CL_InputContext *ic = window.get_ic();
    	CL_PixelBuffer buffer = window.get_back_buffer();
    	window.set_fullscreen();

### Drawing simple graphics

Main classes: CL_GraphicContext, CL_Surface

After setting up a display window and learning how to deal with input from it, the next logical step is to draw something in the window. All drawing in ClanLib is done on a graphic context, which CL_GraphicContext abstracts. Just like with the input context, CL_DisplayWindow also has a function get_gc() that returns the graphic context.

The graphic context itself has a set of functions that allow you to draw some common primitives (lines, boxes, gradients, etc.). These functions are fairly straightforward:

    	CL_DisplayWindow window("My ClanLib Window", 640, 480);
    	CL_GraphicContext *gc = window.get_gc();
    	gc->clear(CL_Color::black);
    	gc->draw_line(100, 100, 400, 400, CL_Color::red);
    	gc->draw_rect(CL_Rect(101,101,399,399), CL_Color::blue);

Sometimes it's practical to limit the drawing of items to a specific area of the screen. ClanLib always uses the screen boundary as a clipping rectangle, so you never have to worry about showing objects outside the screen - just call the draw-function and they are trivially rejected by the clipper.

Like many other clipping systems, the ClanLib clipping system uses a stack principle. It's not possible to create polygonal clipping areas this way. Instead, if you push a new clipping rectangle upon the clipping stack, the single clipping rectangle used to clip further graphics is defined as the area shared by the previous clipping rectangle and the newly pushed one (the intersection).

The previous clipping rectangle is stored and brought back to use when the clipping rectangle in action is popped off the clipping stack. However, it's also possible to set the clipping rectangle in absolute terms, disregarding any previous clipping rectangles.

Example of use:

    	gc->push_cliprect(CL_Rect(10,10,100,100));
    	gc->clear(CL_Color::black);
    	gc->fill_rect(CL_Rect(0, 0, 400, 400), CL_Color::white);
    	gc->pop_cliprect();

In this example, the clear() and fill_rect() calls are clipped to the 10,10,100,100 rectangle.

A translation stack is also available.

To draw images on a graphic context, we need to use the surface class. CL_Surface is the interface that stores images in a texture and perform drawing commands to render it. Using CL_Surface is rather simple and straightforward. Create the surface by specifying a source for the image to be loaded from, then call draw() to render it:

    	CL_Surface image("cow.png");
    	image.draw(10, 10, gc);

Note: It is important you create your surface objects after you have created the display window. Otherwise, mysterious crashes will result. This is a known bug, and might be fixed in a future version of ClanLib.

All the operations done by CL_GraphicContext are implemented by making calls to OpenGL (or SDL, if that's the display target). OpenGL uses a state machine model where states affect the result of primitive drawing commands - for example, the OpenGL command glBlendMode affects how textures with glBegin(GL_TRIANGLES) blend to the framebuffer. If an application needs to do its own lower level drawing using direct OpenGL calls, there has to be some kind of cooperation between ClanLib and the application about OpenGL states.

In other words, it is possible for a ClanLib application to interact directly with OpenGL, but you must warn ClanLib before and after doing so.

An application tells ClanLib it wants to do OpenGL operations by calling the begin_3d() method on the graphic context. This causes ClanLib to flush any pending drawing commands it has queued for rendering, restore the view matrix, and set other OpenGL states to their defaults. When the application is done doing its own drawing, it should restore the states to OpenGL defaults, then call end_3d() to tell ClanLib it can setup the OpenGL states for its own use again.

    	gc->begin_3d();
    	glMatrixMode(GL_PROJ_MATRIX);
    	glPushMatrix();
    	glProject(...);
    	glMatrixMode(GL_VIEW_MATRIX);
    	glPushMatrix();
    	gluLookAt(...);
    	glBegin(GL_POLYGONS);
    	glColor3f(1.0f, 1.0f, 0.0f);
    	for (int i=0; i<100; i++) glVertex3f(x[i],y[i],z[i]);
    	glEnd(GL_POLYGONS);
    	glPopMatrix();
    	glMatrixMode(GL_PROJ_MATRIX);
    	glPopMatrix();
    	gc->end_3d();

We are currently working on a system to control states in a more intelligent way in ClanLib. When this system is done, the begin_3d/end_3d system may be modified, but it should be fairly straightforward to port any code using the current model.

### Loading images from common formats

Main classes: CL_JPEGProvider, CL_PNGProvider, CL_PCXProvider, CL_TargaProvider.

As mentioned in the previous section, CL_Surface needs a source for its image data. Image data is offered through the CL_PixelBuffer interface. A pixel buffer is an image format description, plus the image data itself. You can feed CL_Surface with any image made available via a pixel buffer, but in most cases the built-in image providers will do. Currently those are the JPEG (.jpg), PNG (.png), PCX (.pcx) and Targa (.tga) providers.

The constructor on CL_Surface that take a filename uses the interface CL_ProviderFactory to find a suitable surface provider (pixel buffer) that can load the image. If you write your own image loader, and want ClanLib to be able to recognize images of the new type based on the filename extension, you must add a CL_ProviderType object to CL_ProviderFactory::types.

It is not possible to directly examine the image contained in a surface. CL_Surface does not keep a local copy of the image it uploads to the texture. CL_Surface offers a function called get_pixeldata() that can download the image from the texture, but this can result in a loss of quality depending on the graphics card. The original picture may be in 32 bit with 8 bits per channel for red, green, blue and alpha - but when stored on the card it may been stored in 16 bit, with 4 bits per channel.

If it is needed to examine the image used by a surface, the best way to do it is to first load image into a pixel buffer, analyze the image, and then pass on the pixel buffer to CL_Surface:

    CL_PixelBuffer *image = new CL_PNGProvider("dog.png");
    image->lock();
    examine_image(
    	image->get_format(),
    	image->get_width(),
    	image->get_height(),
    	image->get_pitch(),
    	image->get_data());
    image->unlock();
    CL_Surface surface(image, true);

Note: The built-in surface providers in ClanLib will not return the correct values for get_width(), get_height(), get_pitch() and get_format() until lock() have been called the first time. This will be fixed in a future version of ClanLib.

### Animating images easily

Main classes: CL_Sprite, CL_SpriteDescription, CL_SpritePacker.

As most people probably know, a sprite is a collection of 2D images (called frames), shown in sequence with a delay between each frame. Sprites are used for a lot of game objects, i.e. moving people, spaceships, chairs, powerups, missiles, animated mouse cursors, etc.

CL_Sprite does all this for you in a very easy, yet flexible way. The simplest sprites are just a collection of frames, all shown after each other, drawn on the screen somewhere.

You can also rotate sprites and make them translucent, you can tweak the animation speed for individual frames, set frame offsets, set drawing and rotation alignment, play frames in a backward looping pattern, a ping-pong loop, forward just-once, etc. Basically, CL_Sprite can do most things you'll ever need for basic 2d sprites.

ClanLib's sprites are rather advanced. Because of this it got its own overview. You can read it here.

### Font and text rendering

Main classes: CL_Font, CL_TextStyler, CL_GlyphBuffer.

Drawing text in ClanLib is simple, but creating a font is slightly more complex. There is support for drawing both system fonts and bitmap fonts, but the system font support should be considered experimental at this time. Creating a bitmap font requires some basic knowledge about sprites - CL_Font collects its bitmap glyphs from a sprite.

The easiest way to construct your first font is to take a look at one of the font image files from ClanLib's examples, and then read the font overview.

### Working with image data

Main classes: CL_PixelBuffer, CL_PixelFormat, CL_Palette.

Questions or comments, write to the ClanLib mailing list.