Some applications, particularly in the field of multimedia, require the graphical user interface to share the screen with other video sources. Many hardware platforms targeted at such markets will offer dedicated hardware support for these use-cases by allowing the software to render to several independent hardware graphics layers. Those will then be composited to form the final video image which will be visible to the user.
If your application needs to display additional content from different sources integrated into the GUI, you can use the CGUILayerContainer-control. In general the topic multilayer-support is often integrated into the hardware via so-called Display-Controllers. They maintain and display various contents on the display with advanced possibilities, like alpha-blending or chroma-keying. The access to these Display-Controllers is abstracted via the DC-Wrapper in Guiliani. The CGUILayerContainer-control uses the DC-Wrapper (CDCWrap) as part of Guiliani to achieve multi-layer.
To define and manage different layers containing data from different sources in your application, use the CGUILayerContainer which uses the internal CDCWrap to mix and display the contents on the screen. Simply place container-controls, where you want to display the contents, as you do with "normal" CGUICompositeObjects.
When using CGUILayerContainer, it is also possible to mix user-content and Guiliani-content inside the same layer. The user-content will always be displayed in the background and Guiliani-control on top of it.
Note: Since different layers are on the same hierarchy-level for the Display-Controller, it is not advisable - though possible - to encapsulate CGUILayerContainer into one another. The displayed result might not always be the one expected. The drawing-order displayed in the GSE or the PC-Simulation might be different from the platform-specific drawing-order of layers via the display-controller.
To understand how Multi-Layer works you need to know how Guiliani sees each layer.
In general a layer (sometimes called surface or canvas) is a region on the screen where objects are rendered independently from the rest of the screen. This region does not necessarily start at the origin 0,0 and does not need to have the size of the whole screen. It can be moved or blended with other contents of the screen to receive a modern impression of the GUI. Also this region has its own part of memory which is used to render and display the contents.
Each layer is represented by several attributes, like position, size, alpha, pixel-format, etc. In addition to this attributes the layer contains one or more buffers for the data. This is especially necessary for user-generated content which should be displayed on the screen - seamlessly embedded into the rest of the GUI. To see all possible attributes refer to LayerConfiguration_t.
Note: size and pixel-format of the layer can't be changed during runtime, because they are linked to the memory allocated for the internal buffers. visibility, position, alpha and chroma-key and be changed without any restrictions.
Before you can use a layer in your application, you need to create a configuration for this layer and add it to the Display-Controller-wrapper. This should be done during start-up of the application. After that the Graphics-Wrapper should be informed to use the DC-Wrapper to access the memory for rendering and refreshing the contents of the screen.
Here is an architectural overview of the involved parts of Guiliani
By using the Display-Controller-wrapper Guiliani can access these layers and blit the contents into the respective buffers. If multiple buffers are used, they are automatically flipped after drawing is finished.
If all visible layers have been processed, the contents are updated on the screen using the plaform-specific implementation of the DC-Wrapper.
When using layers, Guiliani and the used Graphics-API have to be aware of these. The setup should be done where the platform-specific initialization is done. The needed steps are:
Here is a small example:
Note: when setting up the layer configuration, the order of the layers may be important. On some platforms the IDs which are used to access the different layers, also set the drawing order in which all layers are merged to the final screen. If it happens that content of a specific layer is overdrawn by content from another layer, they may have an incorrect drawing order as set by their ID.
In the application above there are three layers, two of which contain visual data generated by Guiliani and one layer containing user-generated content (e.g. Video-Data)
To update the user-generated content you will need to access the currently active render-buffer. If there is only one buffer used for the layer, this is always the same buffer and also points to the currently displayed buffer. After the buffer-contents are updated, Guiliani needs to know that there were changes in order to update the layer correctly. This is done by a call to InvalidateLayer()
Note: when accessing memory, it is vital not to write over the boundaries of the buffers. To get the size of the connected buffer of a layer, use the appropriate methods of CDCWrap to prevent access-violation when writing data.
Note: some platforms need a certain padding of memory for faster access. if the used platform needs padding the allocated buffer-sizes will differ from the visible size. the member m_uiPaddedWidth of LayerConfiguration_t should be adjusted accordingly to have proper access to the memory on the platform. m_uiWidth will contain the visible width of the layer.
because of this difference the width returned by GetLayerBufferSize will be the padded width which was used to allocate the memory. if no padding is needed m_uiPaddedWidth and m_uiWidth of LayerConfiguration_t will be the same.
Of course, if the layer is not used to display both user- and Guiliani-content, it can be manipulated directly using the DC-Wrapper via code in the user-application.
Note: When a layer is used by a CGUILayerContainer, the direct access to change position via the DC-Wrapper-API should not be used, since the CGUILayerContainer will move the layer to the correct position by itself. This might lead to unwanted artefacts during display.
Sometimes it may be necessary to adapt the behaviour of the DC-Wrapper to specific needs on a platform. In this case it is possible to derive a custom-made CDCWrap and implement platform-specific logic.
To use this DC-Wrapper simply change the class-name of the used DC-Wrapper inside the startup-code.
The following methods can be implemented to change the internal behaviour:
This method is called for each layer with its configuration which is added to the DC-Wrapper. Here the DC-Wrapper should set up platform-specific data and call platform-functions to create layers.
This is called if an attribute of a layer has been changed (position, alpha, etc.). Depending on the second parameter the DC-Wrapper can react to different changes. See LayerUpdate_t for details.
Will be called for each layer during the refresh. here the DC-Wrapper should call platform-functions to display the layer at the appropriate position with all applied attributes
This method is called at the of the refresh-cycle. This can be used to trigger a synchronization-mechanism with the screen.