C
Managing Resources
This topic describes how Qt Quick Ultralite applications make use of image resources.
Font resources are not added through the resource system. For more information about font handling, see Text Rendering and Fonts.
See also topic on Image Caching.
Summary
To use resources in a Qt Quick Ultralite application, you must declare them using the QmlProject API.
The resource compiler (qulrcc tool) uses the resource declarations to prepare the contents for inclusion. It may perform optimizations on the raw resource data. For example, image optimization can result in lowering color depth, replacing with alphamap, removing outline transparent areas, and swizzling.
Adding files to the resource system
Use the ImageFiles.files QmlProject property to add resources to a Qt Quick Ultralite application.
ImageFiles { files: ["ui/spinner.png", "background.png"] }
The ImageFiles.files
property adds the files into to a virtual resource filesystem. Resources in the filesystem are referenced through resource URIs with the optional "qrc:" scheme.
qrc:/ |-- background.png |-- ui/ |-- spinner.png
In the above example, qrc:/ui/spinner.png
is a resource URI for the resource data based on the ui/spinner.png file from the source directory. It can be displayed by using its URI as source in an Image:
Image { source: "qrc:/ui/spinner.png" }
When using relative paths in resource URIs, the paths are relative to the root of the virtual resource filesystem. The scheme of the URI can be omited. The following URIs all refer to ui/spinner.png:
ui/spinner.png
/ui/spinner.png
qrc:ui/spinner.png
qrc:/ui/spinner.png
qrc:///ui/spinner.png
Resource properties
Optionally, you can set resource properties for a set of images listed using ImageFiles.files
, from the list of available properties in the QmlProject ImageFiles node.
ImageFiles { files: ["image.png"] MCU.resourceCompression: true }
Image resources support a number of resource properties that control how the image is stored on the device, or can trigger different optimizations. See QmlProject Manual for the full properties list.
In the following example, the large background.png is stored in a compressed format by setting ImageFiles.MCU.resourceCompression, while spinner.png is tagged with ImageFiles.MCU.resourceOptimizeForRotation to optimize it for runtime rotation:
ImageFiles { files: ["spinner.png"] MCU.resourceOptimizeForRotation: true } ImageFiles { files: ["background.png"] MCU.resourceCompression: true }
Image { source: "qrc:/background.png" Image { anchors.centerIn: parent source: "qrc:/spinner.png" transform: Rotation { NumberAnimation on angle { from: 0; to: 360; running: true; loops: Animation.Infinite } } } }
Lossless compressed image formats
Qt for MCUs supports Run-Length Encoding (RLE) compression formats as an alternative to ImageFiles.MCU.resourceCompression. It is useful if significant parts of an image have the same color. The RLE format options for the ImageFiles.MCU.resourceImagePixelFormat property are: RGB888RLE
, XRGB8888RLE
, and ARGB888RLE
.
The supported RLE formats vary depending on the platform. Therefore, use the AutomaticCompressedLossless
option to automatically choose the ideal format depending on the MCU.Config.platformOpaqueCompressedLosslessResourcePixelFormat and MCU.Config.platformAlphaCompressedLosslessResourcePixelFormat settings for the platform.
The benefit of using AutomaticCompressedLossless
is that the images can be decoded on the fly as they are being blended, without decompressing them into the image cache. This means, less volatile memory is needed compared to images that use ImageFiles.MCU.resourceCompression.
However, the average compression ratio is not as good, and transformed images are not supported. It could also have a negative impact on the blending performance, as it uses CPU fallback on most platforms. The CPU is usually slow at blending images that contain a lot of semi-transparent pixels or if the computed Item::opacity value is less than one. Refer to the Supported Features table for information about platforms that support hardware accelerated blending of RLE pixel formats.
The following table shows two example images along with their uncompressed and compressed (RLE or PNG) sizes. The first image compresses a lot better due to the contiguous areas that contain the same pixel value. Compressing such images into PNG using ImageFiles.MCU.resourceCompression produces even better compression ratios, but they must be decompressed onto the image cache before drawing on screen.
Image | ||
---|---|---|
Uncompressed | 82.9 kB | 82.9 kB |
RLE compression | 23.2 kB | 72.6 kB |
PNG compression | 6.7 kB | 27.5 kB |
Here are some useful heuristics for when to use PNG or RLE compression to better blending performance:
OnStartup
cache policy - On platforms that have sufficient flash and volatile memory for the application.- RLE compression - On platforms that support hardware accelerated blending without intermediate buffers. You could also use it on platforms without hardware acceleration, if the performance overhead is acceptable.
- PNG compression with ImageFiles.MCU.resourceCompression - On platforms that do not have sufficient memory required to store all the uncompressed image resources. If the image cache is big enough to fit all the visible images on screen at the same time, animation performance will be good as all the required images are in the volatile memory.
AutomaticCompressedLossless
or uncompressed images without caching to retain the images in flash memory. One of these two techniques is ideal on platforms with limited volatile memory, as PNG compression is not an option on such platforms.
Here's how to enable the use of lossless compression for a given image resource:
ImageFiles { files: "example.png" MCU.resourceImagePixelFormat: "AutomaticCompressedLossless" }
Resource placement in memory
By default a resource is placed in a reserved memory section named QulResourceData. Resources can be placed in a different storage section by setting the ImageFiles.MCU.resourceStorageSection property. The ImageFiles.MCU.resourceCachePolicy property controls when and how resources are loaded into volatile memory. When different kinds of volatile memory are available (for example RAM, VRAM or HyperRAMâ„¢), the ImageFiles.MCU.resourceRuntimeAllocationType property can be used to choose the volatile memory region to use for a given resource.
In the following example, three images are stored in a memory section named Arrows
.
ImageFiles { files: [ "images/arrow-0.png", "images/arrow-45.png", "images/arrow-90.png", ] MCU.resourceStorageSection: "Arrows" }
Arrows : { . = ALIGN(4); *(Arrows) } > QSPI_FLASH
The section name can be freely chosen as long as a section with the same name is defined in the linker script. The section start address needs to be aligned to the largest alignment requirement among resources within the same section.
Arrows : { . = ALIGN(4); *(Arrows) } > QSPI_FLASH
OTA resources update
If the user wants to do an over-the-air (OTA) update of some resources, these resources should be placed in a specific memory section (for example OTAResourceSection
) as shown above in Resource placement in memory.
Once the user has updated the images that are part of the OTA update on the host, it is possible to regenerate the binary content of the OTAResourceSection
for a given target application.
Such binary generation is performed by the resource compiler and can be invoked via CMake as follows:
cmake --build . --target <application>_resource_binaries
without the need of recompiling the whole application. The resource binaries are named qul_resources_<section_name>.bin
(for example qul_resources_OTAResourceSection.bin
) and can be found in the target application build folder.
Since some of the images might either be cached (OnDemand
) or preloaded at startup (OnStartup
) with ImageFiles.MCU.resourceCachePolicy, the Qt Quick Ultralite application must be restarted for the images part of the OTA update to be effectively used by the application.
Notes
- Apart from generating new binary data, Qt Quick Ultralite does not offer any direct mechanism for rewriting the data to Flash memory. An application specific solution will need to be implemented.
- The resource binary content is in Little-Endian format.
- Filenames need to match with the ones that have been used in the QML application originally. Otherwise the resource lookup will fail.
- To avoid overwriting other memory areas, the section part of an OTA update should be placed last in Flash.
- The flash memory must be erased and rewritten on a block-by-block basis. If the binary data does not start and end on the exact block boundaries, it might be necessary to read the old data for the memory blocks that will be erased. Ensure to take this into consideration if the flashing tool or library you are using does not handle it automatically.
Resource properties for sprite animation
Sprite animation involves some preprocessing at compile time. Resource properties give useful information for the process, enabling it to optimize the resources.
Directory as animation source
A sprite animation source can be a directory that has a series of image files, which AnimatedSpriteDirectory QML type can load and render. Tag such image files with ImageFiles.MCU.resourceAnimatedSprite in the .qmlproject
file, to enable identify their directory name at compile time.
ImageFiles { files: [ "loading/01.png", "loading/02.png", "loading/03.png", "loading/04.png" ] MCU.resourceAnimatedSprite: true }
This enables identifying the image files directory and using it as the sourcePath for AnimatedSpriteDirectory. The following example demonstrates how you can do it:
import QtQuickUltralite.Extras AnimatedSpriteDirectory { sourcePath: "loading" }
Resource optimization of sprite animation
Separate image files of a sprite animation offer a chance to optimize the resource storage after analyzing the series of images at compile time. The resource compiler (qulrcc tool) figures out common parts from the images, and reuses them to reduce memory footprint.
Specifying ImageFiles.MCU.resourceAnimatedSpriteFrameWidth and ImageFiles.MCU.resourceAnimatedSpriteFrameHeight for the image source of AnimatedSprite QML type offers all the benefit of separate frames.
In the Qt Quick Ultralite sprite_animations Example, the qt-image-sequence.png has 16 frames. If you set ImageFiles.MCU.resourceAnimatedSpriteFrameWidth and ImageFiles.MCU.resourceAnimatedSpriteFrameHeight for the image source, the resource compiler generates 16 frames internally.
frame #0 | frame #1 | frame #2 | frame #3 |
frame #4 | frame #5 | frame #6 | frame #7 |
frame #8 | frame #9 | frame #10 | frame #11 |
frame #12 | frame #13 | frame #14 | frame #15 |
This preprocess reduces runtime memory space for the image source because the application only loads each frame that has much smaller image size than the image source. And the resource compiler applies the same optimization by sharing common parts between the frame sequences.
Resources from external storage
You can use the MCU.resourceAsFile property to configure resources to be loaded from external storage.
ImageFiles { files: [ "2008.png" ] MCU.prefix: "logos" MCU.resourceAsFile: true MCU.resourceCompression: false }
When enabled, the image is processed but not included into the assets. Instead, it is created as a separate file to be manually copied into a file system on the device.
You have to use the file://
protocol to use these resources.
Image { source: "file://logos/2008.png" }
You can apply other asset properties as well, such as adding a prefix to the image paths or enabling RLE compression.
Note: The Filesystem platform API that enables access to resources on the local filesystem is in technology preview state. It might change in the future releases.
Loading assets from external storage has potential security threats
Warning: Loading assets from an external storage creates attack vectors against the device.
Qt Quick Ultralite does not validate the assets nor the file system these are loaded from. Manipulated data on the external storage can be used to compromise the device.
It is the responsibility of the user to make sure the data loaded from a file system is valid. This could be achieved by physically sealing off the access to it or cryptographically validating it during the boot phase.
Possible attack surfaces include
- The PNG and RLE decoders when the image data is invalid.
- The file system implementation when having invalid data blocks.
- Rendering and memory management with images that are too large or when their data does not match their declared size.
See also ImageFiles.MCU.resourceImagePixelFormat, MCU.Config.binaryAssetOptions, ImageFiles.MCU.prefix, and ImageFiles.MCU.resourceCompression.
Available under certain Qt licenses.
Find out more.