Pre-rendered Layer-Screens
Introduction
This example application shows you how to use multiple Microchip Graphics Suite (MGS) Harmony Composer layers and canvas objects to create pre-rendered 'layer-screens.' Layer-screens are logical screens that the application can use to show different frames on the display with GFX Canvas.
Since these layer-screens are completely rendered during initialization and are persistent throughout the lifecycle of the application, these screen transitions are very quick and require minimal processing.
In addition, effects like sliding screen transitions can be applied using the GFX Canvas and the LCD overlays. This enables smooth screen transitions with no frame redraw and minimal system overhead.
Concept and Theory
Before proceeding, you must be familiar with the GFX Canvas. Refer to the "Using the Graphics Canvas" page for more information.
To support pre-rendered 'screens,' the application utilizes the ability of the MGS Harmony Composer and library to draw multiple layers to dedicated frame buffers using GFX Canvas.
Each layer represents a logical screen in the application, which we will refer to as 'layer-screen' in order to differentiate it from a regular screen that's used in a multi-screen MGS Harmony Composer design. Since each layer-screen uses a dedicated frame buffer, the size of memory available in the target device determines the maximum number of layer-screens that can be created. Figure 1 describes this configuration.
- At design time, each screen in the Graphical User Interface (GUI) is designed as a layer in MGS Harmony Composer.
- MGS Harmony Composer will be a single-screen project with multiple layers. Each layer represents a logical screen in the GUI, which we will refer to as a layer-screen.
- At run-time, each layer-screen in MGS Harmony Composer will be associated with a GFX Canvas object.
- At boot, the library will render each of these layer-screens drawn in a dedicated frame buffer in RAM.
- The application will use the GFX Canvas Application Programming Interface (APIs) to show or hide layer-screens by assigning a layer or overlay in the LCD controller and enabling or disabling those LCD layers.
- The LCD controller will display and composite the visible layer-screens on the display. For LCD controllers with multiple layers/overlays, layer screens can be overlaid on top of another to create effects like semi-transparent windows or dialog boxes.
This mechanism of showing or hiding layer-screens effectively simulates screen transitions in the GUI. Since the layer-screens are already rendered in dedicated frame buffers, these screen transitions do not require a redraw of the screen and are quick to complete.
Also, since the widgets in the layer-screens are stored by the MGS library as layers in a screen, the handle and objects for these widgets are persistent in memory. Widget states are preserved during layer-screen transitions.
Microchip Graphics Suite (MGS) Example Project
Refer to the project in GitHub for the example.
The application is configured as shown in Figure 2.
The example application has six layer-screens. Each layer screen is designed in MGS Harmony Composer as discrete layers.
At run-time, these layers are rendered to dedicated GFX Canvas objects with their associated frame buffers in Random-access Memory (RAM). The main screen provides menu buttons that will show different screens when pressed. These screen transitions are accomplished by calling the GFX Canvas APIs to hide, show, or move these canvas objects using the associated LCD layers or overlays.
For these specific applications, the main screen and screens 1-4 are shown using the LCD base layer. The popup screen is designed to slide over the main screen so that it uses the LCD overlay above the base layer.
MGS Simulator Output
Here's the MGS Simulator output. The simulator output is interactive, use your mouse to click the buttons and interact with the GUI.
At boot, the main menu screen is shown.
Pressing the buttons labeled 1-4 will show screens 1-4.
On each of these screens, pressing the buttons will toggle the button. Notice that the states of the button will persist between screen transitions (i.e., a pressed button remains pressed even after exiting the screen).
Pressing the back arrow button at the bottom left will return to the main screen.
On the main menu screen, pressing the Pop Up button will slide up a semi-transparent pop-up screen.
Pressing the back arrow will slide the pop-up screen down.
MPLAB® Code Configurator (MCC) Harmony Configuration
Here's how the project is configured in MPLAB® Code Configurator (MCC) Harmony.
The Graphics Canvas component is added to the project and connected between the MGS GFX library (Legato) and the MGS Simulator components.
The Graphics Canvas component is configured as shown in Figure 9. There are six canvas objects and each canvas object is associated with a layer-screen in the GUI design.
The GFX Canvas objects are configured with the same color mode, size, and buffer allocation.
MGS Harmony Composer Design
Project Settings
The MGS Harmony Composer project was created with the display settings set to WQVGA, and width and height are set to 480x272.
The project renderer color mode is set to RGBA8888 to allow layer-screens with alpha values. This enables complex effects like overlaying semi-transparent screens.
Screen Design
The MGS Composer design looks like the one shown in Figure 12. There's only one screen (Screen 0) in the design, and its tree contains six layers that each represent a layer-screen in the GUI.
Screen Event Callbacks
Note that the On Show and On Update screen event callbacks are enabled. These callbacks allow the application to initialize the screen-layers and show the main screen at boot. This will be covered in more detail in the the Application Code section of this user guide.
Button Widget Event Callbacks
Released event callbacks are enabled for buttons in the Main layer-screen. This allows the application to define a callback function that will be used to show the appropriate layer-screen using the GFX Canvas APIs.
Released event callbacks are enabled for the Back/Home buttons in the sub layer-screens. This allows the application to define a callback function that will be used to go back and show the Main screen.
Using Background Panel Widgets
Notice a pattern on how each layer-screen tree is configured.
A background panel the size of the layer is used as parent to all the widgets in the layer-screen.
This is important, as this parent panel widget is used as a container to enable or disable touch events on the layer-screen.
Using Editor Options to Manage the UI Design
Since MGS Composer layers are used to design layer-screens in the UI, the project can grow and become difficult to view and edit with multiple layer-screens stacked on top of each other in the screen designer.
To manage multiple layer-screens in the design, use the Hidden setting in the Editor Options to hide layers that you are not working on and show layers-screens that you need to design or configure. Selecting the layer-screen in the Screen Tree will show the layer editor options.
Application Code
The application screen code are located in the app_screen.c file.
Note that the OnShow and OnUpdate screen events were enabled.
The OnShow() event callback function is defined to initialize and hide all the layer-screens at boot.
{
/* Initialize all layer-screens */
APP_Screen_Initialize();
/* Hide all the layer-screens at boot */
APP_LayerScreenHideAll();
appScreenState = APP_SCREEN_INIT;
}
The APP_Screen_Initialize() function contains the GFX Canvas API calls used to set the LCD layer, size, and position of each layer-screen.
{
unsigned int i;
for (i = 0; i < MAX_SCREENS; i++)
{
/* Set the LCD layers for each layer-screen */
gfxcSetLayer(i, appLayerScreen[i].lcdLayer);
/* WQVGA size for all layer-screens */
gfxcSetWindowSize(i, DEFAULT_WIDTH, DEFAULT_HEIGHT);
/* 0,0 position for all layer-screens */
gfxcSetWindowPosition(i, 0, 0);
}
}
The OnShow() event callback function also calls APP_LayerScreenHideAll() to hide all the layer-screens at boot.
static void APP_LayerScreenHideAll(void)
{
unsigned int i;
for (i = 0; i < MAX_SCREENS; i++)
{
APP_LayerScreenHide(i);
}
}
It uses the APP_LayerScreenHide() helper function that contains the GFX Canvas API calls to hide a layer-screen.
static void APP_LayerScreenHide(uint32_t id)
{
/* Hide the canvas layer */
gfxcHideCanvas(id);
gfxcCanvasUpdate(id);
/* Disable the parent panel. This will disable events and allow events to
* pass through to the lower level layer-screens */
WIDGET_CLEAR_ENABLE_FLAG(*appLayerScreen[id].bkgdPanel);
}
A helper function for showing a layer-screen is also defined and used in the application.
static void APP_LayerScreenShow(uint32_t id)
{
/* Hide all layer-screens first */
APP_LayerScreenHideAll();
/* Show the layer-screen */
gfxcShowCanvas(id);
gfxcCanvasUpdate(id);
/* Enable the layer-screen panel for event processing */
WIDGET_SET_ENABLE_FLAG(*appLayerScreen[id].bkgdPanel);
}
Note the calls to macros for clearing and setting the widget-enabled flag.
* Enabled widgets get touch events.
* Disabled widgets (and its child widgets) do not get touch events,
* and allow events to go through it to lower level widgets.
*/
#define WIDGET_SET_ENABLE_FLAG(widget) (widget)->flags |= LE_WIDGET_ENABLED;
#define WIDGET_CLEAR_ENABLE_FLAG(widget) (widget)->flags &= ~LE_WIDGET_ENABLED;
The widget-enabled flag is used to enable or disable the parent background panel when a layer-screen is shown or hidden, respectively. This allows the application to indicate to the graphics library which layer-screens should receive and process touch events.
For the sliding pop-up screen, the GFX Canvas move effect is used to slide the pop-up layer-screen from off-screen to view. The function APP_ScreenShowPop() shows the series of GFX Canvas API calls used to accomplish this.
static void APP_ScreenShowPop(void)
{
/* Show the layer-screen */
gfxcSetWindowPosition(POPUP, 0, 272);
gfxcShowCanvas(POPUP);
gfxcCanvasUpdate(POPUP);
/* Start pop-up sliding from bottom to show layer-screen */
gfxcStartEffectMove(POPUP,
GFXC_FX_MOVE_DEC,
0,
272,
0,
0,
LAYER_FX_MOVE_DELTA);
/* Enable the pop-up panel so it can receive touch events */
WIDGET_SET_ENABLE_FLAG(*appLayerScreen[POPUP].bkgdPanel);
}
An equivalent function is defined to slide the pop-up layer-screen from view to off-screen.
{
/* Start pop-up sliding from top to hide layer-screen */
gfxcStartEffectMove(POPUP,
GFXC_FX_MOVE_DEC,
0,
0,
0,
272,
LAYER_FX_MOVE_DELTA);
/* Disable the parent panel. This will disable events */
WIDGET_CLEAR_ENABLE_FLAG(*appLayerScreen[POPUP].bkgdPanel);
}
The button event callback function does the appropriate helper function call to show the associated layer-screen.
{
/* Show Screen 1 layer-screen */
APP_LayerScreenShow(SCREEN_1);
}
The screen OnUpdate() function manages the application state machine. Here it waits for the GFX library to completely render all the layer-screens before showing the main menu screen by calling the leRenderer_IsIdle() API.
void Screen0_OnUpdate(void)
{
switch(appScreenState)
{
case APP_SCREEN_INIT:
{
/* Wait for the renderer to complete drawing all the layer-screens */
if (leRenderer_IsIdle() == true)
break;
/* Show the main menu screen */
APP_LayerScreenShow(MAIN_MENU);
appScreenState = APP_SCREEN_RUNNING;
break;
}
case APP_SCREEN_RUNNING:
{
break;
}
default:
break;
}
}
Refer to the "Microchip Graphics Suite (MGS) Graphics Canvas API Documentation" page for more details about the APIs used in this example project.
Design Guidelines and Recommendations
Use the following information to determine if pre-rendering with GFX Canvas will work for your UI design.
GUIs and the associated application code may need to be architected so that they can be efficiently implemented using GFX Canvas. Refer to the guidelines and recommendations for steps on how to optimize your GUI for pre-rendering.
- Determine if all the screens in your GUI design can be pre-rendered into RAM. This means that you must have enough RAM for the frame buffers needed to render all the screens at boot.
- If there is not enough RAM to render all the screens, consider grouping similar screens as sub-screens to a superset screen.
- The superset screen can be implemented as a layer-screen with multiple parent panels that contain the designs for the sub-screens. These panels can then be hidden or shown at run-time to show a specific sub-screen. Refer to the "How-To: Create Dialog Boxes" page for a guide on how to show/hide sub-screens.
- Since these panels are hidden or shown at run-time within a single layer-screen, the GFX library will need to update and re-draw the sections of the layer-screen for these sub-screens.
- Use the leRenderer_IsIdle() API to wait for the layer-screen to be updated with the new sub-screens before calling the GFX Canvas APIs to show the layer-screen.
- If a layer-screen needs to be updated (redrawn) by the GFX library, always use leRenderer_IsIdle() to make sure that the update is done before showing the layer-screen. This prevents screen tearing from being visible.
- For designs with a lot of screens, there will be a delay during boot while the GFX library is rendering the screens.
- To promptly show an active screen while pre-rendering, consider showing a splash screen. This splash screen can be implemented by having the contents of only one layer-screen as visible by default (the splash screen) and having the other layer-screens invisible. Use the visible state of the parent panel widget to set the visible state of a layer-screen.
- Once the splash layer-screen is rendered, use the GFX Canvas API to show the layer-screen on the display controller.
- While the splash layer-screen is visible, set the visible state of all the other layer-screens to visible so the GFX library will render them.
- Keep the splash layer-screen visible while the other layer screens are being rendered. Use the leRenderer_IsIdle() API to wait for rendering to complete.
- Once the GFX library is done rendering (leRenderer_IsIdle() returns LE_TRUE), use the GFX Canvas API to show the first screen on the LCD controller.
- The handles and objects for all the widgets in the layer-screens will be allocated in the GFX library's memory pool and will persist through the life cycle of the GUI application.
- Make sure there is enough memory to hold all this widget information. Refer to the "Optimizing the Memory Manager Settings" for a guide on how to properly size the GFX memory pools for your application.