2023-10-24 08:37 AM
On the project where I'm working on, we have the requirement that bitmaps should be loaded dynamically.
Due to the restricted flash memory available, the L8_ARGB8888 + RLE compression (acceptable size/decompression values) format is proposed for storing the images. The images will be stored in the external (not memory-mapped) memory.
As even the uncompressed L8 format manipulation is not quite clear to me I will start with that.
Once an uncompressed L8 image is extracted from the elf file using the "arm-none-eabi-objcopy --dump-section" and that binary data is "seen" in the source code, how to load it in the dynamically created L8 bitmap?
For example, here is the intake from the map file for the "image_minus" image.
ExtFlashSection
0x000000002400086c 0x38a4 ./Application/User/generated/image_minus.o
0x000000002400086c image_minus_extra_data
0x00000000240008d0 image_minus
I'm guessing that
"image_minus" contains only pixel data (index in the palette table) and the "image_minus_extra_data" contains the palette itself and (hopefully) pallete size plus flags (compression / L8 format).
Am I on the right track?
The final question is how to do the same for L8 compressed image.
Do I need (if I even can ???) to decompress data manually to the location retrieved using the Bitmap::dynamicBitmapGetAddress or does the touchgfx do that in the background somehow?
Source/tutorials used:
Using Non-Memory Mapped Flash for Storing Images
https://support.touchgfx.com/docs/development/scenarios/using-non-memory-mapped-flash
Dynamic L8 image:
https://support.touchgfx.com/docs/development/ui-development/scenarios/creating-dynamic-l8-images
Dynamic Bitmaps:
https://support.touchgfx.com/docs/development/ui-development/touchgfx-engine-features/dynamic-bitmaps
2023-11-03 03:54 AM
Hello @kvrbek ,
You can let TouchGFX handle it. Provided you have implemented a custom version of the blockCopy function as described in your link about non-memory mapped memory, you can see an example of how to decompress images to the bitmap cache here:
I hope this helps
2023-11-06 02:33 PM
Hi Mohammad,
Thank you for the response.
Well, my problem is that any image information is not known in advance. Nothing about the image is stored locally and I cannot simply use the Id to process the image like in the example that You are suggesting.
Here is my current situation.
The images are stored on the filesystem (obviously not memory mapped). The filesystem media is the spi flash memory. Due to latency, files should be as small as possible.
The image magick and touchgfx are used to generate L8_ARGB8888 RLE images and the custom format is used for final image files. The format is <Header><PixelData><ExtraData>. The header is a simple structure containing image width, height, "pixel" data size, and "extra" data size.
Simplified code:
1) On the touchgfx View there is a finite number of elements/images that can show:
touchgfx::Image images[DYNAMIC_BITMAP_NUM];
touchgfx::BitmapId dynamicBitmap[DYNAMIC_BITMAP_NUM];
2) Once in the view setupScreen() cache is set.
Bitmap::setCache(...);
3) On-demand (event in the dedicated queue), from the view handleTickEvent() images are displayed
When an event arrives to show a certain image/file, the header of the file is retrieved and width/height is used to create the dynamic bitmap:
fread(&header, 1u, sizeof(header), file);
dynamicBitmap[bitmapIndex] = Bitmap::dynamicBitmapCreateL8(header.width, header.height, Bitmap::CLUT_FORMAT_L8_ARGB8888, 64);
After that, the pixels are copied to the "dynamicBitmap" space.
uint8_t *pixels = Bitmap::dynamicBitmapGetAddress(dynamicBitmap[bitmapIndex]);
fread(pixels, 1u, header.dataSize, file)
NOT immediately after the pixels (Like explained here: https://support.touchgfx.com/docs/development/ui-development/scenarios/creating-dynamic-l8-images#dynamic-l8-bitmap-example. Guess that procedure on the link is valid for the uncompressed images only) but on the position "pixel + pixel_number", the extra data is loaded:
uint8_t *extra = pixels + (header.width * header.height);
fread(extra, 1u, header.extraSize, file) != (long)header.extraSize);
Finally, the image is displayed:
remove(images[bitmapIndex]);
images[bitmapIndex].setBitmap(Bitmap(dynamicBitmap[bitmapIndex]));
images[bitmapIndex].setXY(x, y);
add(images[bitmapIndex]);
images[bitmapIndex].invalidate();
And that works - images are displayed. So the touchgfx recognizes format from "pixel" and "extra" data and performs decompression autonomously.
However, another issue pops up.
Most of the images that are displayed are around 120x120 pixels, and with that, everything works fine. But when the larger image is added to the view (720x60 pixels), that image is displayed correctly only if it is the last image added to the view. If any other image is added to the view after, regardless of the size, that large image changes color. The other images are not affected in any way.
Note that when this happens there is still enough free cache memory left and examining the images addresses on images adding (Bitmap::dynamicBitmapGetAddress(dynamicBitmap[bitmapIndex])) there is no overlapping memory between images.
Does anybody know how to avoid the described "phenomenon"?