Off-Screen Rendering with DirectGDI Tutorial

This tutorial shows how to create an image as a target for off-screen DirectGDI rendering, create a DirectGDI graphics context and use it to issue draw commands to the image and finally display the image in a Window Server window.

Target audience: Device creators.

Note: DirectGDI is deprecated in Symbian^3.

Introduction

In early versions of Symbian, off-screen rendering was always performed by using BitGDI. This had the disadvantage that the rendering could not be hardware accelerated. Later versions of Symbian introduced OpenGL ES (in v8.1) and OpenVG (in v9.4), which provide alternative mechanisms for off-screen rendering that can be hardware accelerated. However, these APIs are very different to the BitGDI API. Therefore porting legacy code from BitGDI to OpenGL ES or OpenVG in order to take advantage of graphics acceleration hardware is a considerable task.

DirectGDI can take advantage of graphics acceleration hardware if it is available and (unlike OpenGL ES and OpenVG) provides an API that is very similar to BitGDI. This means that porting legacy code to DirectGDI is much easier than porting it to OpenGL ES or OpenVG. In addition, you typically need to port legacy code only if it does off-screen rendering. Legacy code that does on-screen rendering using CWindowGc will automatically be hardware accelerated if the Window Server's render stage plug-in(s) are hardware accelerated.

This tutorial shows you how to perform off-screen rendering using DirectGDI.

Required background

This tutorial assumes a background knowledge of the following:

Procedure

1. Create the image to store the results

The first step is to create the image (RSgImage) that we will use to store the results of the off-screen rendering. For example:

// Create an image for off-screen DirectGDI rendering.
TSgImageInfo info;
info.iSizeInPixels = TSize(KWidth, KHeight); 
info.iPixelFormat = EUidPixelFormatARGB_8888_PRE;

// Usage is (1) to store results of DirectGDI rendering and 
// (2) display on screen through the Window Server.
info.iUsage = ESgUsageWindowGcSource | ESgUsageDirectGdiTarget; 

info.iShareable = ETrue; // Must be shared with the Window Server.
RSgImage image;
err = image.Create(info, NULL, 0); // Undefined initial contents.
User::LeaveIfError(err);

Notice that the Create() function takes a TSgImageInfo structure as an argument. This specifies the pixel format and image usage. There are two ways to choose the pixel format:

Notice that we specify the usage not only as a target for DirectGDI rendering but also as a source for the Window Server, because ultimately we want to display the results on the screen.

2. Draw using DirectGDI

Now we have created the image, we need to set the image as the target for DirectGDI rendering and then create a DirectGDI graphics context and use it to draw to the image. Here are the steps:

  1. Open the DirectGDI driver for use in this thread:

    err = CDirectGdiDriver::Open();
    User::LeaveIfError(err);
    CDirectGdiDriver* driver = CDirectGdiDriver::Static();
  2. Create the DirectGDI rendering target using the image that we created earlier:

    RDirectGdiImageTarget target(*driver);
    err = target.Create(image);
    User::LeaveIfError(err);
  3. Create a DirectGDI context and call Activate() to bind our off-screen rendering target with it:

    CDirectGdiContext* context = CDirectGdiContext::NewL(*driver);
    err = context->Activate(target);
    User::LeaveIfError(err);
  4. Use the DirectGDI context's draw commands to draw to our off-screen rendering target:

    context->SetBrushColor(KRgbWhite);
    context->SetDrawMode(DirectGdi::EDrawModeWriteAlpha);
    context->Clear();
    context->SetDrawMode(DirectGdi::EDrawModePEN);
    context->SetPenColor(KRgbBlue);
    context->SetPenStyle(DirectGdi::EDottedPen);
    context->DrawEllipse(KEllipseRect);
  5. Call CDirectGdiDriver::Finish() to ensure that all of the drawing commands have been processed:

    driver->Finish();

3. Display the results on the screen

The final stage is to display the image that we have used as our off-screen rendering target on the screen. We do this by drawing the image to a Window Server window, like this:

  1. Prepare the image for use as a source in a Window Server context:

    RWsSession& wsSession = GetMyWsSession();
    RWsDrawableSource source(wsSession);
    err = source.Create(image, screenNumber);
    User::LeaveIfError(err);
  2. Use the Window Server context to draw the image in a window:

    CWindowGc* windowContext = ActivateMyWindowContext();
    windowContext->DrawResource(TPoint(0, 0), source);