PrevNextUpHome SophiaFramework UNIVERSE 5.3

9.4. Rendering

9.4.1. Overview

In the responder system, the procedure to draw the responders is divided into the 6 parts as follows:

  1. Register the responder region as redrawing area with the SFYResponder::Invalidate function if you want to redraw the responder(In the standard responder class of SophiaFramework UNIVERSE, this operation is automatically performed internally).
  2. Boot up the renderer by calling the SFYResponder::Render function.
  3. The renderer sends the drawing event to only responders to be redrawn.
  4. The responder which receives the drawing event calls the drawing handler to redraw itself.
  5. The visible region of the responder which are not registered into redrawing area but influenced by redrawing of the parent or younger sister responders will be recoved by copying from the saved bitmap to restore the device bitmap.
  6. The redrawn area will be overridden and saved into the bitmap to restore the device bitmap.
[Important] Booting up the renderer

There are the following three cases in booting up the renderer:

In case of "1." and "2.", the SFYResponder::Render function is called automatically. But, in case of "3.", it is necessary to call the SFYResponder::Render function explicitly.

[Tip] Drawing the "invisible" responder

Even if the contents of the responder such as the text or the color are changed during an event loop, the drawing handler of the responder, which is in the "invisible" state or hidden by other responders, will not be booted up.

9.4.2. Redrawing Area

Rendering the responder is divided into registering the redrawing area and actual redrawing with the drawing handler.

For instance, if the text of the label control is changed with the USRResponder::SetText function in the code below, this label control must be redrawn since its content is updated. Here, only register the redrawing area into the renderer with the SFYResponder::Invalidate function. Redrawing the label itself is described in the drawing handler.

Example 9.26. Registering the redrawing area

// register the drawing handler
USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        ...

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));

        ...

    }
}

Void USRResponder::SetText(SFXAnsiStringConstRef param)
{
    SFCError error(SFERR_NO_ERROR);

    if (!param.equals(_text)) {
        if ((error = _text.Set(param)) == SFERR_NO_ERROR) {
            Invalidate(); // here not draw the label but register the redrawing area only
        }
    }

    return error;
}

// implement the drawing handler(actual redrawing is described in the drawing handler)
XANDLER_IMPLEMENT_VOIDRENDER(USRResponder, OnRenderRequest, invoker, reason, graphics)
{
    SFXRectangle local;

    // redrawing the label is described in the drawing handler
    local.Set(GetLocalBound());
    graphics->FillRectangle(local, SFXRGBColor(0xFF, 0xFF, 0xFF, 0x00));
    graphics->DrawSingleText(_text, local, SFXRGBColor(0x00, 0x00, 0x00, 0x00));

    return;
}
[Important] IMPORTANT

In the USRResponder::SetText function where redrawing is needed, only registering the redrawing area is described. Actual drawing is described in the OnRenderRequest drawing handler, which will be booted up after the SFYResponder::Render function is called.

[Tip] Hint on performance improvement

The SFYResponder::Invalidate function has the overload function with the rectangle argument.

If the subset rectangular region of the responder is specified as the redrawing area, since there is more possibility that it will be included in the union of responders placed foreground, the drawing performance may improve.

[Caution] Registering the redrawing area and calling the Render function

Registering the redrawing area is valid only when rendering is performed by calling the SFYResponder::Render function without no argument specified(i.e., "false" is specified in the argument).

At this time, only the responders whose redrawing areas are registered receive the drawing event.

[Tip] Redrawing the standard responders of SophiaFramework UNIVERSE

In case of the standard responder of SophiaFramework UNIVERSE, even if the visual element such as state, attribute, region, parent-child or sibling relationship, ID, type, reference, text, font, color and so on is changed by executing its function(mainly, the prefix is "Set"), the related responders will be automatically redrawn. Therefore, there is no need to register the redrawing area when the standard responder of SophiaFramework UNIVERSE is used.

9.4.3. Drawing Handler

The default drawing handler registered into SFYWidget is booted up first. This drawing handler will call the SFYWidget::HandleRenderRequest function after filling the background in the color set with the SFYWidget::SetBackgroundColor function if the transparency attribute is "false".

After this, the developer's own drawing handlers, if any, will be booted up in the registered order.

Example 9.27. Registering the drawing handler

// register drawing handler
USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        ...

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));

        ...

    }
}
[Tip] Tip

The drawing handler is registered with the SFYResponder::RegisterHandler function.

However, drawing the responder can be also described by overriding the SFYWidget::HandleRenderRequest function. This method is more useful since there is no need to register the handler.

Example 9.28. Implementing the drawing handler

// implement drawing handler
XANDLER_IMPLEMENT_VOIDRENDER(USRResponder, OnRenderRequest, invoker, reason, graphics)
{
    // draw "Hello World" in black at the center
    graphics->DrawSingleText("Hello World", 
                              GetLocalBound(), 
                              SFXRGBColor(0x00, 0x00, 0x00, 0x00));

    return;
}
[Tip] Tip

The drawing handler gets the SFXGraphics instance as the argument and draws the corresponding responder using the local region coordinate.

9.4.4. Rendering in the Event Loop

When the applet receives the event from the BREW environment, the SFYApplication::HandleEvent function below will be called automatically. The SFYApplication::HandleEvent function starts the event loop, and if the event is assumed to be handled at the end of the event loop, it will call the SFYResponder::Render function to boot up the renderer.

The renderer will send the drawing event only to the responders to be actually redrawn by calculating the state and the positional relation among the responders.

In each event loop, only the necessary drawing handlers are called. As a result, performance will not deteriorate even if it is the complex rendering.

Example 9.29. Implementation of the SFYApplication::HandleEvent function

// virtual function that will be called when the event dispatched from the BREW environment needs to be handled
/*protected virtual */Bool SFYApplication::HandleEvent(SFXEventConstRef event)
{
    // here describe the handling of the event dispatched from the BREW environment

    SFCError  error;
    Bool      result(false);

    // boot up the distributer to distribute the event using the tracer
    // first of all, the SFYDistributer instance bound with the root will receive the event
    // then, the event will be distributed to the responders in the responder tree according to the dispatching rules of the tracer
    // * _root is the root(SFZRoot)
    if ((error = _root->Distribute(event, &result)) == SFERR_NO_ERROR) {

        if (event.GetType() != SFEVT_APP_STOP && event.GetType() != SFEVT_APP_SUSPEND)) {  // if redrawing is necessary

            if (IsRenderable()) {  // if the screen can be redrawn (i.e., if no highest priority event handler is registered)

                 // boot up the renderer to redraw the responder tree below the root
                error = _root->Render();
            }
        }
    }
    if (error != SFERR_NO_ERROR) {

        // call HandleError() in case the fatal error such as insufficient memory during the initialization of the distributer or the renderer occurs
        if (HandleError(event, error)) {

            result = true;
        }
    }
    if ((event.GetType() == SFEVT_APP_SUSPEND) && IsRendererIntermissive()) {
        // if the back-up bitmap inside the renderer is released when an application suspends

        // terminate the renderer
        _renderer.Terminate();
    }
    return result;  // return "true" if the event has been handled, otherwise return "false"
}// SFYApplication::HandleEvent //

Reference: SFCApplication::HandleEvent | SFCApplication::IsRenderable | SFCApplication::HandleError | SFYApplication::IsRendererIntermissive | SFYResponder::Distribute | SFYResponder::Render | SFYRenderer::Initialize | SFYRenderer::Terminate | SFZRoot | Root | Tracer | Distributer | Renderer Drawing Event | Drawing Handler | Event Loop

[Note] Highest Priority Event Handler

For more details, see the description of the SFCApplication::RegisterBypass function.

Figure 9.23.  When redrawing is necessary


When redrawing is necessary

When the event is handled during the event loop, the SFEVT_APP_START / SFEVT_APP_RESUME event is received, or the highest priority event handler is unregistered, the renderer will be booted up automatically only if no highest priority event handler is registered.

When all handlers return "false" during the event loop, the SFEVT_APP_STOP / SFEVT_APP_SUSPEND event is received, the highest priority event handler is registered, or there is no region to be redrawn, the renderer will not be booted up. As a result, no responder will be redrawn.

Figure 9.24.  When redrawing is unnecessary


When redrawing is unnecessary

If the event is not handled during the event loop, the SFEVT_APP_STOP / SFEVT_APP_SUSPEND event is received, the highest priority event handler is registered, or there is no region to be redrawn, the renderer will not be booted up. As a result, no responder will be redrawn.

9.4.5. Rendering in the Callback

Different from redrawing in the event loop, the renderer will not be automatically booted up in the callback outside the event loop such as the network or the timer processing.

To redraw the responder on the device screen in the callback, call the SFYResponder::Render function explicitly after registering the redrawing areas to booted up the renderer.

Figure 9.25. Redrawing in the callback 1: in case the Render() function is not called


Redrawing in the callback 1: in case the Render() function is not called

Since callback is the processing outside the event loop, the renderer will not be automatically booted up. Therefore, even if you register the redrawing areas with the SFYResponder::Invalidate function, nothing will be redrawn on the device screen.

Figure 9.26. Redrawing in the callback 2: in case the Render() function is called


Redrawing in the callback 2: in case the Render() function is called
  1. Register the redrawing areas with the SFYResponder::Invalidate function.
  2. Call the SFYResponder::Render function explicitly.
  3. The renderer will be booted up, and the corresponding drawing handlers will be called.
  4. The screen will be actually redrawn.

Example 9.30. Redrawing in the callback

SFCError USRResponder::OpenConnection(Void)
{
    SFCError error(SFERR_NO_ERROR);

    _label->SetText("); 

    ...

    error = _http.Connect(XALLBACK_INTERNAL(OnConnect));

    ...

    return error;
}

// implement callback for completing the http connection
XALLBACK_IMPLEMENT_SFXHTTPCONNECTION(USRResponder, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        ...

    }
    _label->SetText("OnConnect was called."); // the Invalidate() function is called inside the SetText() function
    Render(); // redraw by calling the Render() function explicitly

    return;
}
[Note] High performance redrawing

To get the highest performance, you had better boot up the renderer only once per callback.

At the end of the callback, you should call the SFYResponder::Render function of as low as possible responder in the responder tree, which includes all possible responders to be redrawn as the child responder.

9.4.6. Full Screen Redrawing

When an applet starts / resumes or a highest priority event handler ends, the SFYApplication::HandleRender function will be called automatically.

The SFYApplication::HandleRender function redraws the full screen, i.e., the device screen and the responder tree below the root.

[Note] Highest Priority Event Handler

For more details, see the description of the SFCApplication::RegisterBypass function.

In the SFYApplication::HandleRender function, the SFYResponder::Render function will be called to boot up the renderer as in the code below.

If the saved bitmap to restore the device screen is available, the argument of the SFYResponder::Render function will become "true". Otherwise, "false".

[Note] Note

If "true" is specified in the argument of the SFYResponder::Render function, all visible responders in the device screen will be redrawn regardless of registering the redrawing area.

Example 9.31. Implementation of the SFYApplication::HandleRender function

// virtual function that will be called when the full screen needs to be redrawn
/*protected virtual */Bool SFYApplication::HandleRender(SFXEventConstRef event)
{
    // here describe full screen redrawing
    SFCError  error;
    Bool      result(false);

    if (SFCApplication::HandleRender(event)) { // redraw the device screen

        // redraw the responder tree below the root
        // *1. if possible, redraw using the bitmap to restore the device screen
        // *2. start up the renderer by calling "Render(true)" if the bitmap to restore the device screen is not available in a timing such as receiving the SFEVT_APP_START event
        // *3. in case of "Render(true)", all visible responders' drawing handlers will be called
        error = _root->Render((event.GetType() == SFEVT_APP_START) || (_root->Recover() != SFERR_NO_ERROR));

        if (error == SFERR_NO_ERROR) {

            result = true;
        }
        else {
            // call HandleError() when fatal error such as insufficient memory during initialization of renderer
            HandleError(event, error);
        }
    }
    return result;
}// SFYApplication::HandleRender //

Reference: SFCApplication::HandleRender | SFCApplication::HandleError | SFCApplication::RenderDeviceScreen | SFYResponder::Render | SFYResponder::Recover | SFZRoot | Root | Renderer

[Caution] About "_root->Render()" in the implementation of the SFYApplication::HandleRender function

If "true" is specified in the argument of the SFYResponder::Render function, the responder will be redrawn whether or not the redrawing area is registered.

In the implementation code of the SFYApplication::HandleRender function,

error = _root->Render((event.GetType() == SFEVT_APP_START) || (_root->Recover() != SFERR_NO_ERROR));

the above statement will redraw all visible responders in the device screen by force when an applet starts or no saved bitmap to restore the device screen is available.

If "false" is specified, the drawing handler of the responder which is not registered into the redrawing area will be not booted up. When there is intersection region between parent or younger sister responder and this one, the visible region of this responder will be recovered by copying from the saved bitmap to restore the device screen.