Features Help Download

Lomse Hacking Guide

3.5. Rendering

3.5.1. Rendering at glance

Rendering is the process of generating an image from the graphical model. The objects that form the GraphicModel, that is GmoBox and GmoShape objects, are responsible for rendering themselves by issuing drawing commands, such as draw line, draw path or draw glyph. These drawing commands are sent to a Drawer object.

The Drawer object is responsible for transforming the drawing commands into something understandable by the underlying rendering engine. Drawer is an abstract class and, therefore, specific drawer classes need to be implemented.

At high level, rendering is a simple operation. It is just traversing the graphical model tree and invoking the on_draw method on each GmoObj. The on_draw method will issue drawing commands that will be processed by a Drawer for generating an image.

Currently, the only Drawer implemented in Lomse is the ScreenDrawer class. It is oriented to rendering on a bitmap and it is based on the AGG rendering software (http://www.antigrain.com/). This class and the underlying rendering engine is described later in this chapter.

The Drawer class is equivalent to the typical Device Context (DC) that is part of the Graphics Device Interface (GDI) in the Microsoft Windows API. Similar drawing interfaces are present in other operating systems’ graphics libraries, such as Macintosh’s QuickDraw and GTK’s GDK/Xlib.

As Lomse aims to be platform independent, none of the above mentioned graphics libraries could be used. Instead it was decided to create an abstract interface class: Drawer. This solution allows you to implement your own drawer class by interfacing the graphical engine of your application specific platform and, thus, all drawing could be done natively without having to relay on copying bitmaps rendered by Lomse. Probably some trimming of the Drawer API would be required for this. I didn’t have time to think on this. Also, other approaches are possible. For instance, it would be possible to write a SvgDrawer class. It would transform drawing commands into SVG commands and to export rendered documents as an SVG file or as a SVG stream. In fact, the commands interface of Drawer class mimics SVG commands.

If a Lomse support platform dependent library is finally developed, direct drawing using each platform drawing interface could be developed. Send me an e-mail if you would like to work on this. Thank you.

3.5.2. Starting renderization

The GraphicModel class provides method draw_page that triggers the rendering of the desired page. It was decided to control rendering on a page-by-page basis instead of having a method to trigger the rendering of all the graphical model by two reasons:

  • It simplified having different View classes, with pages layouted in different ways: pages one after the other in vertical, pages one after the other in horizontal, an array of pages, etc.
  • It allows for performance improvements, by limiting rendering to those pages that are currently visible on the screen.

Rendering is started by the Interactor, when any of the methods redraw_bitmap() or force_redraw() are invoked. These methods ask the View to start redenderization (method View::redraw_bitmap). This method delegates in method draw_graphic_model() that does the real work. This method set up rendering options, prepares the Drawer, invokes method generate_paths() and ask the Drawer to render the generated paths:

void GraphicView::draw_graphic_model()
{
    m_options.background_color = Color(145, 156, 166);  //35, 52, 91); //127,127,127);
    m_options.page_border_flag = true;
    m_options.cast_shadow_flag = true;
    m_options.draw_anchors = m_libraryScope.draw_anchors();

    m_pDrawer->reset(*m_pRenderBuf, m_options.background_color);
    m_pDrawer->set_viewport(m_vxOrg, m_vyOrg);
    m_pDrawer->set_transform(m_transform);

    generate_paths();
    m_pDrawer->render();
}

Method generate_paths is specific for each View class, but any of them will finally create a loop for invoking GraphicMode method draw_page. For instance:

void VerticalBookView::generate_paths()
{
    GraphicModel* pGModel = get_graphic_model();
    UPoint origin(0.0f, 0.0f);

    m_pageBounds.clear();

    for (int i=0; i < pGModel->get_num_pages(); i++)
    {
        if (i > 0)
            origin.y += 1000;
        GmoBoxDocPage* pPage = pGModel->get_page(i);
        URect rect = pPage->get_bounds();
        UPoint bottomRight(origin.x+rect.width, origin.y+rect.height);
        m_pageBounds.push_back( URect(origin, bottomRight) );
        pGModel->draw_page(i, origin, m_pDrawer, m_options);
        origin.y += rect.height;
    }
}

3.5.2.1. The draw_page method

Method GraphicModel::draw_page is very simple. It just gets the box (object GmoBoxDocPage) for the requested page, and invokes its on_draw method. It is a recursive method. It draws the box border, the shapes contained in the box and, finally, invokes on_draw on all its descendant boxes:

void GmoBox::on_draw(Drawer* pDrawer, RenderOptions& opt)
{
    draw_border(pDrawer, opt);
    draw_shapes(pDrawer, opt);

    //draw contained boxes
    std::vector<GmoBox*>::iterator it;
    for (it=m_childBoxes.begin(); it != m_childBoxes.end(); ++it)
        (*it)->on_draw(pDrawer, opt);
}

But, what about the layers? It s necessary to take layers into account for renderization. GmoBoxDocPage maintains a collection with all the shapes in the page. The collection is ordered by layer and creation order. This collection is used for hit testing and selection. But for rendering the graphical model is traversed. This is an error, as the z-ordering decided by the user would not be taken into account at renderization. But as dccument edition is not yet implemented this error has not yet manifested. Need to fix this. See The draw_page method. [TODO5].

3.5.3. ScreenRenderer class

ScreenRenderer class behaves as a vertex source for the AGG rendering software.

(To be continued ...)

3.5.4. To do

[TODO5]It s necessary to take layers into account for renderization. GmoBoxDocPage maintains a collection with all the shapes in the page. The collection is ordered by layer and creation order. This collection is used for hit testing and selection. But for rendering the graphical model is traversed. This is an error, as the z-ordering decided by the user would not be taken into account at renderization. But as dccument edition is not yet implemented this error has not yet manifested. Need to fix this. See The draw_page method.