Platform Integration Aspects: Extern Bitmap interface
At the runtime of the application, the content of existing bitmap resources can't be exchanged nor new resources can be added. This prevents the bitmap resources from being suitable to provide image contents determined dynamically at the runtime e.g. loaded from an SD memory card, received from a network adapter or rendered by a third-party application, like the map view in a GPS navigation system.
In order to be able to deal with such demanding application cases, the Mosaic framework provides the class Resources::ExternBitmap. Unlike bitmap resources, objects of this class are optimized to represent and manage image contents being obtained or rendered dynamically at the runtime of the GUI application - thus unknown at the compilation time.
This functionality depends on an interface you have to implement in your particular target system. Doing this you can precisely control how the dynamic image contents are found, loaded, decoded and eventually color converted. It's up to you whether the contents are loaded from an SD memory card, received from a network adapter or rendered dynamically by third-party application.
If you intend to display dynamically loaded image contents, this chapter will provide you with information how you implement the interface between the GUI application and your particular image provider. In turn, how the Extern Bitmap objects are used within the GUI application is described in the chapter Displaying dynamically loaded images.
Function EwLoadExternBitmap() for synchronous loading
In the simplest case of the synchronous loading of image contents the interface to integrate the GUI application with your particular, platform specific image provider consists of a single C function named EwLoadExternBitmap(). You will need to implement this function and ensure, that it is compiled and linked with your entire application. The function is declared as follows:
XBitmap* EwLoadExternBitmap( XString aName )
The function expects a single parameter aName. The value passed in this parameter does always correspond to the value specified in the property Name of the Extern Bitmap object causing the invocation of the function. The property Name and thus the parameter aName are used to identify the image content, the function EwLoadExternBitmap() should deliver.
Depending on your application case, aName could be understood as path to an image file existing on an SD memory card or as an URL to a network location from which the image should be received. It's up to you how you use the property Name and the parameter aName. Since both are declared as string (XString) you are flexible to pass any information you need to identify the desired image content.
For example, an application intended to show images from an SD memory card can use the parameter aName to address the corresponding file on the file system and to specify an optional flag indicating whether you want the image in its original size or as small thumbnail. Passing the value "/IMAGE123.JPG" in this parameter could be understood by your implementation of the EwLoadExternBitmap() function as provide the content of the image file IMAGE123. In turn the value "/IMAGE123.JPG (THUMB)" could be understood by the interface as provide the thumbnail content of the image file IMAGE123. Here the additional suffix (THUMB) could instruction your implementation to decode the file as a thumbnail.
Once the parameter aName is evaluated, your implementation of the function EwLoadExternBitmap() should create a new bitmap, fill it with the particular image content and return the bitmap. That's all. In case of an error, e.g. when the requested image content is not available, the function should return NULL. Similarly, if the parameter aName is an empty string, the function should also return NULL.
The following are the typical steps you have to take care of when implementing the function EwLoadExternBitmap():
★Step 1: Evaluate the parameter aName. If this parameter is composed of several operands (like the example above with the optional (THUMB) suffix), extract and process the operands.
★Step 2: Knowing the name of the desired image content determine its size in pixel (width and height). If the image content is not available (for example, the parameter aName addresses a non existing file), abort the operation and return NULL.
★Step 3: Create a new Embedded Wizard bitmap to accommodate the image content. The size of the bitmap should correspond to the size of the image determined in the preceding step. If the bitmap creation fails due to out-of-memory, abort the operation and return NULL.
★Step 4: Lock the entire bitmap for write operation. With this operation you will obtain a data structure with pointers to the memory where the bitmap pixel are stored. Having this information you can access and fill the bitmap with the image content.
★Step 5: Copy pixel-by-pixel the content of the image into the memory of the previously locked bitmap. If necessary you can apply during this step an additional color conversion or color reduction. For example, if the image content is stored originally with 32-bit per pixel RGBA8888 format but your target is running in RGBA4444 color format, you will need to convert the color values from RGBA8888 to RGBA4444.
IMPORTANT
Please note, Embedded Wizard supports bitmaps with various color formats. Which color formats are applicable in your particular case does depend on the version of your Platform Package. You should consider, that the color format affects the layout of how the pixel are stored and coded in the bitmap memory. For details explaining the different color formats see the chapter XBitmapLock.
★Step 6: Once you are finished with the copy operation, unlock the bitmap again.
★Step 7: Eventually release all resources used to find, load and decode the image.
★Step 8: Return the bitmap.
The following example demonstrates the typical implementation of the EwLoadExternBitmap() function. For the sake of simplicity, this example doesn't load any real image file. Instead it fills the bitmap with a solid color. The size of the bitmap and the color to fill it are determined by evaluating the parameter aName. If aName is "BLUE 100x50", the implementation creates a 100x50 pixel large bitmap and fills it with blue color. If the parameter is "SEMI-RED 200x100", a semi-transparent red bitmap with the corresponding size is created. All other aName values are considered as invalid and result in the function returning NULL. Furthermore, in this example we assume you target system (your Platform Package) is running in the color format RGBA8888:
/* Include necessary Embedded Wizard header files */ #include "ewrte.h" #include "ewgfx.h" #include "ewextpxl_RGBA8888.h" /* Include other header files */ #include <string.h> XBitmap* EwLoadExternBitmap( XString aName ) { char name[32]; int width; int height; unsigned int clr_red, clr_green, clr_blue, clr_alpha; XBitmap* bitmap; XPoint frameSize; XRect lockArea; XBitmapLock* lock; unsigned int* dest; int ofs; int x, y; /* STEP 1 */ /* Convert the 16-bit wide character string in 8-bit ANSI string */ EwStringToAnsi( aName, name, sizeof( name ), 0 ); /* STEP 1 and 2 */ /* The blue bitmap is requested? aName == "BLUE 100x50". Determine the size and the color to fill the bitmap. */ if ( !strcmp( name, "BLUE 100x50" )) { width = 100; height = 50; clr_red = 0; clr_green = 0; clr_blue = 255; clr_alpha = 255; } /* The red bitmap is requested? aName == "RED 200x100". Determine the size and the color to fill the bitmap. */ else if ( !strcmp( name, "SEMI-RED 200x100" )) { width = 200; height = 100; clr_red = 255; clr_green = 0; clr_blue = 0; clr_alpha = 128; } /* The requested image is not available */ else return NULL; /* STEP 3 */ /* Create a new bitmap with the previously determined size. Note, we create a NATIVE bitmap, which means the bitmap format supported natively by the target system. The format of a NATIVE bitmap depends on your Platform Package. In this example, the NATIVE bitmap has the format RGBA8888. */ frameSize.X = width; frameSize.Y = height; bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_NATIVE, frameSize, 0, 1 ); /* Enough memory to create the bitmap? */ if ( bitmap == NULL ) return NULL; /* STEP 4 */ /* Lock the entire bitmap for write operation */ lockArea.Point1.X = 0; lockArea.Point1.Y = 0; lockArea.Point2.X = width; lockArea.Point2.Y = height; lock = EwLockBitmap( bitmap, 0, lockArea, 0, 1 ); /* STEP 5 */ /* Get the pointer to the first pixel within the locked bitmap. In the RGBA8888 format every pixel is a 32-bit value (unsigned int). Additionally calculate the offset in pixel between the end of one row and the begin of the next row. */ dest = (unsigned int*)lock->Pixel1; ofs = ( lock->Pitch1Y / 4 ) - width; /* Iterate through the pixel within the locked bitmap area. Do this row-by-row and column-by-column. After one row is finished adjust the 'dest' pointer to refer to the next row. After one column is finished increment the 'dest' pointer only. */ for ( y = 0; y < height; y++, dest += ofs ) for ( x = 0; x < width; x++, dest++ ) { /* The color value you want to store in the pixel. Every variable is valid in range 0 .. 255 in case of RGBA8888 bitmap. */ unsigned int red = clr_red; unsigned int green = clr_green; unsigned int blue = clr_blue; unsigned int alpha = clr_alpha; /* In the color format RGBA8888, the color components 'red', 'green' and 'blue' can be premultiplied by 'alpha' component. Should this occur in your Platform Package? Please note, to optimize the implementation we divide the result of the multiplication by 256 (shift right 8) instead of by 255. To reduce calculation errors we have to increment the alpha value to also lie in range till 256. */ #ifdef EW_PREMULTIPLY_COLOR_CHANNELS red = (( red * ( alpha + 1 )) >> 8 ); green = (( green * ( alpha + 1 )) >> 8 ); blue = (( blue * ( alpha + 1 )) >> 8 ); #endif /* In the color format RGBA8888, the arrangement of the color components 'red', 'green', 'blue' and 'alpha' can be configured. Ensure, that all components are stored at right bit offsets within the 32-bit value of the pixel */ red <<= EW_COLOR_CHANNEL_BIT_OFFSET_RED; green <<= EW_COLOR_CHANNEL_BIT_OFFSET_GREEN; blue <<= EW_COLOR_CHANNEL_BIT_OFFSET_BLUE; alpha <<= EW_COLOR_CHANNEL_BIT_OFFSET_ALPHA; /* Now compose the color components to a single 32-bit value and store the result in the bitmap memory ... */ *dest = red | green | blue | alpha; } /* STEP 6 */ /* Don't forget to unlock the bitmap when you are done! */ EwUnlockBitmap( lock ); /* STEP 8 */ /* Return the bitmap */ return bitmap; }
Take in account the color format of the bitmap
In the code example from the section above we have simply assumed, that the bitmaps do store the pixel as 32-bit RGBA8888 values. This, however, is not always the case. When you intend to access and modify pixel data of a bitmap created with the format EW_PIXEL_FORMAT_NATIVE you should always consider, that the real color format of the bitmap does depend on the version of the Platform Package used in your target system.
Our Platform Packages are optimized to work with the color format supported natively by the graphics hardware of the particular target system (this is usually the color format of the frame-buffer). Accordingly, the version of the Platform Package implies the format in which all EW_PIXEL_FORMAT_NATIVE bitmaps are stored. In the table below you will find detailed documentation describing and demonstrating how you access and modify the pixel data depending on the version of your Platform Package:
Your Platform Package version |
The format of how EW_PIXEL_FORMAT_NATIVE bitmaps are stored |
---|---|
RGBA8888, RGB888 and RGB565 |
|
RGBA4444 |
|
LumA44 |
|
Index8 |
Besides the above described target specific EW_PIXEL_FORMAT_NATIVE color format, every Platform Package supports following two universal and one optional formats. If your application case it requires, you can adapt the implementation of the EwLoadExternBitmap() function to create and load bitmaps of those formats:
Format |
Description |
---|---|
EW_PIXEL_FORMAT_ALPHA8 |
Universal 8-bit per pixel alpha only format. As such it doesn't provide any color information. It is thus required to explicitly specify the desired color at the runtime as a solid color value or a linear color gradient (e.g. in the Image view). ALPHA8 bitmaps are ideal for all application cases, where monochrome contents should change the colors dynamically at the runtime. Please see XBitmapLock for information about how the pixel data of such bitmap are modified. |
EW_PIXEL_FORMAT_INDEX8 |
Universal 8-bit per pixel palette format. This format expects an additional color palette for translation between an 8-bit index and the native color value. The colors within the palette are determined by using the function EwModifyBitmapPalette(). Please see XBitmapLock for information about how the pixel data of such bitmap are modified. |
EW_PIXEL_FORMAT_RGB565 (optional) |
Universal 16-bit per pixel RGB format without any opacity information. Please see XBitmapLock for information about how the pixel data of such bitmap are modified. |
For example, if you need to implement the EwLoadExternBitmap() function to create and load an EW_PIXEL_FORMAT_ALPHA8 bitmap, adapt the first parameter in the invocation of the EwCreateBitmap() function. Please note, when modifying the bitmap, ALPHA8 bitmaps are stored with 8-bit per pixel as described in the chapter XBitmapLock:
/* Include necessary header files */ #include "ewrte.h" #include "ewgfx.h" XBitmap* EwLoadExternBitmap( XString aName ) { /* ALPHA8 bitmaps are stored with 8-bit per pixel (unsigned char). Accordingly adapt the declaration of the 'dest' variable. */ unsigned char* dest; [...] /* STEP 3 */ /* Create a new bitmap with the previously determined size */ bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_ALPHA8, frameSize, 0, 1 ); [...] /* STEP 5 */ /* Now get the pointer to the first pixel within the locked bitmap. In the ALPHA8 format every pixel is a 8-bit value (unsigned char). Additionally calculate the offset in pixel between the end of one row and the begin of the next row. */ dest = (unsigned char*)lock->Pixel1; ofs = lock->Pitch1Y - width; /* Iterate through the pixel within the locked bitmap area. Do this row-by-row and column-by-column. After one row is finished adjust the 'dest' pointer to refer to the next row. After one column is finished increment the 'dest' pointer only. */ for ( y = 0; y < height; y++, dest += ofs ) for ( x = 0; x < width; x++, dest++ ) { /* The Opacity value you want to store in the pixel. The values lies in the range 0 .. 255. */ unsigned char alpha = ... /* Store the alpha value in the bitmap memory ... */ *dest = alpha; } [...] }
Multi-frame and animated bitmaps
Similarly to the bitmap resource, every bitmap created dynamically by using the function EwCreateBitmap() can be configured as consisting of several individual images (so-called frames). Such bitmaps are called multi-frame bitmaps. When displaying a multi-frame bitmap, the number of the desired frame needs to be specified explicitly (e.g. in the Image view).
Multi-frame bitmaps are ideal wherever several versions of one and the same image are necessary (e.g. one for a pressed the other for a released button, etc.). If your application case it requires, you can implement the EwLoadExternBitmap() function to create and return a multi-frame bitmap. To achieve this, you specify the desired number of frames in the last parameter of the EwCreateBitmap() function. Please note, within a bitmap all frames will have equal size as determined by the second parameter (frameSize):
[...] XBitmap* EwLoadExternBitmap( XString aName ) { [...] /* Create a new bitmap with 6 frames. Every frame will have the size 'frameSize'. */ bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_NATIVE, frameSize, 0, 6 ); [...] }
When creating multi-frame bitmaps your implementation of the EwLoadExternBitmap() should also be adapted to load every frame. In particular, you will need to lock, load and unlock every frame individually. Loading all frames at once is not possible since the frames are managed as individual memory areas. In the simplest case you enhance the implementation by an additional for-loop to process the frames frame by frame:
[...] XBitmap* EwLoadExternBitmap( XString aName ) { int frameNo; [...] /* STEP 3 */ /* Create a multi-frame bitmap with 6 frames */ frameSize.X = width; frameSize.Y = height; bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_NATIVE, frameSize, 0, 6 ); /* The area to lock every bitmap frame. Since all frames have equal size, the area is equal too. */ lockArea.Point1.X = 0; lockArea.Point1.Y = 0; lockArea.Point2.X = width; lockArea.Point2.Y = height; /* Every frame of the bitmap is treated individually */ for ( frameNo = 0; frameNo < 6; frameNo++ ) { /* Lock the given frame of the bitmap for write operation */ lock = EwLockBitmap( bitmap, frameNo, lockArea, 0, 1 ); [...] /* Use the pointers returned in the 'lock' data structure to load the bitmap frame with content (exact as as demonstrated in the examples in the sections above). */ for ( y = 0; y < height; y++, dest += ofs ) for ( x = 0; x < width; x++, dest++ ) { [...] } /* Don't forget to unlock the bitmap frame */ EwUnlockBitmap( lock ); } [...] }
Additionally, multi-frame bitmaps can be configured as containing animated video sequences. When displaying such bitmap in a view (e.g. Image view), the view will play back the bitmap frames autonomously. For this purpose you will need to specify the delay in milliseconds between two consecutive frames. You use for this purpose the third parameter when creating the bitmap. Specifying a value less or equal 0 (zero) in this parameter will disable the animation for the affected bitmap:
[...] XBitmap* EwLoadExternBitmap( XString aName ) { [...] /* Create a new animated bitmap with 16 frames and ~30 FPS animation speed */ bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_NATIVE, frameSize, 1000 / 30, 16 ); [...] }
Using PNG, JPG, BMP, GIF, etc. image decoders
Embedded Wizard provides only the interface permitting you to integrate the GUI application with your particular software or hardware image decoder or even with any other content provider (e.g. GPS application generating map views). A dedicated implementation of a PNG, JPG, BMP, etc. decoder is as such not available. It's up to you to take care of the necessary libraries to access the image contents, to load, decode or even render them dynamically (like the GPS application). Once you have your preferred library working on your target system, follow the instructions in the sections above to fill Embedded Wizard bitmaps with the provided image contents.
For inspiration purpose, the following code demonstrates the possible integration with libJPEG as decoder permitting EwLoadExternBitmap() to load and decode image files in the JPG format stored on the local file system. The implementation is optimized for the color format RGBA8888:
#include "ewrte.h" #include "ewgfx.h" #include "ewextpxl_RGBA8888.h" #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include "libjpeg/jpeglib.h" /* This function has the job to create and fill a bitmap with content of an extern image file. aName identified extern file to load. If successful, an initialized bitmap is returned otherwise NULL. */ XBitmap* EwLoadExternBitmap( XString aName ) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE* infile; JSAMPARRAY buffer; XBitmap* bitmap = 0; XBitmapLock* lock = 0; int nameLen = EwGetStringLength( aName ); char* name = EwAlloc( nameLen + 1 ); XPoint frameSize; XRect lockArea; /* Out of memory? */ if ( !name ) return NULL; /* Convert the 16-bit wide char string in an 8-bit ANSI string */ EwStringToAnsi( aName, name, nameLen + 1, 0 ); /* Try to open the file */ if (( infile = fopen( name, "rb" )) == NULL ) { EwFree( name ); return NULL; } /* Allocate and initialize JPEG decompression object. We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error( &jerr ); /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress( &cinfo ); /* Specify data source (eg, a file) */ jpeg_stdio_src( &cinfo, infile ); /* Read file parameters with jpeg_read_header() */ jpeg_read_header( &cinfo, TRUE ); /* Start decompressor */ jpeg_start_decompress( &cinfo ); /* Create an Embedded Wizard bitmap to store the image pixel data. The size of the bitmap is known - we obtain it from the libJPEG decoder. */ frameSize.X = cinfo.output_width; frameSize.Y = cinfo.output_height; bitmap = EwCreateBitmap( EW_PIXEL_FORMAT_NATIVE, frameSize, 0, 1 ); /* Could create the image */ if ( bitmap ) { lockArea.Point1.X = 0; lockArea.Point1.Y = 0; lockArea.Point2.X = frameSize.X; lockArea.Point2.Y = frameSize.Y; /* Obtain access to the pixel memory of the just created bitmap. */ lock = EwLockBitmap( bitmap, 0, lockArea, 0, 1 ); /* Make a one-row-high sample array that will go away when done with image */ buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1 ); /* Repeat as long as there are any scan lines to process ... */ while ( cinfo.output_scanline < cinfo.output_height ) { unsigned int x; unsigned char* sample = buffer[0]; unsigned int* dest = (unsigned int*)((char*)lock->Pixel1 + cinfo.output_scanline * lock->Pitch1Y ); jpeg_read_scanlines( &cinfo, buffer, 1 ); /* Transfer the samples into the bitmap pixel row */ for ( x = 0; x < cinfo.output_width; x++ ) { /* Get the RGB value from the JPEG decoder and arrange the 'red', 'green', 'blue' channels at the right bit offset position */ unsigned int red = *sample++ << EW_COLOR_CHANNEL_BIT_OFFSET_RED; unsigned int green = *sample++ << EW_COLOR_CHANNEL_BIT_OFFSET_GREEN; unsigned int blue = *sample++ << EW_COLOR_CHANNEL_BIT_OFFSET_BLUE; /* Store the pixel data in the bitmap memory. Note, JPEG has no opacity information. All pixel are fully opaque. Therefore store 0xFF as alpha value in the pixel. */ *dest++ = red | green | blue | ( 0xFF << EW_COLOR_CHANNEL_BIT_OFFSET_ALPHA ); } } /* Don't forget to unlock the bitmap */ EwUnlockBitmap( lock ); } /* Step 7: Finish decompression */ jpeg_finish_decompress( &cinfo ); /* Step 8: Release JPEG decompression object */ jpeg_destroy_decompress( &cinfo ); /* After finish_decompress, we can close the input file. */ fclose( infile ); /* Release temporary memory */ EwFree( name ); /* all ok */ return bitmap; }
Extern Bitmap and screen rotation
With the attribute ScreenOrientation you have the possibility to configure your project to be prepared for devices with the LCD installed in an orientation different to the normal with the GUI coordinate origin 0,0 found at the LCD origin. If you intend to use Extern Bitmap in such configured device, please consider that the rotation affects how pixel are stored and accessed within a bitmap (surface).
The GUI application thinks always in logical screen coordinates, which are relative to the top-left corner of the GUI. In turn, internally, when rotation is used (attribute ScreenOrientation is not Normal), all bitmaps store the pixel corresponding to the specified rotation. This is also true for the storage of the framebuffer. In this manner, the bitmaps can be copied directly to the framebuffer without performing any rotation at the runtime. It is a significant optimization.
Embedded Wizard takes care of the right coordinate conversion wherever you access and display images. Similarly, all Bitmap resources are generated in the corresponding rotation format. In other words, usually you don't need to worry about the mechanisms. This however is not the case, when you intend to lock and access the bitmap by yourself.
In this case you have to convert between the logical GUI coordinates and the real surface coordinates. For example, with the 270 degree rotation (attribute ScreenOrientation is set Rotated_270) you have to calculate with exchanged bitmap width and height. Additionally, the X and Y coordinates need an adjustment.
The following is a code snippet demonstrating how the Graphics Engine converts internally between the logical GUI coordinates and the real surface coordinates to fill a rectangular area. You see there following code for the 270 degree rotation case:
#if EW_SURFACE_ROTATION == 270 FillRectangle( dstBitmap, Y, Width - X, Height, Width ); #endif
In turn, the following code demonstrates the same operation within a device configured with 0 degree rotation (attribute ScreenOrientation is set Normal):
#if EW_SURFACE_ROTATION == 0 FillRectangle( dstBitmap, X, Y, Width, Height ); #endif
As you see, in case of 270 degree rotation the resulting surface X coordinate is equal to the GUI logical Y coordinate while the surface Y coordinate is equal to the real width of the surface minus GUI logical X coordinate. The width of the area affected in the surface is equal to the height of the area seen in GUI logical coordinates. The height of the area affected in the surface is equal to the width of the area seen in GUI logical coordinates.
Thus when you lock and modify the bitmap memory as described in section Function EwLoadExternBitmap(), you should take care of the right coordinate conversion. If necessary, you can evaluate the ANSI-C macro EW_SURFACE_ROTATION and so support the various rotation cases within your code.
Reload the content of Extern Bitmap
Under normal circumstances, the Extern Bitmap object updates its content only when new value is assigned to its property Name. As long as the value of the property Name doesn't change, the Extern Bitmap object is assumed as containing the same image. Accordingly, the bitmap returned in the preceding EwLoadExternBitmap() doesn't change.
In special application cases, however, the image content represented by an Extern Bitmap object may change without the property Name being explicitly modified. Imagine an application intended to display an image received over the network. Now let's assume, the server providing the original image is updated and the image content changes. From the application point of view, the image is still identified by the same URL, so the value of the property Name remains unchanged. The content on the server, however, is modified.
In such case the application has to reload the image again obtaining so its newest version. This operation has to be triggered by the method Reload(). When you invoke the method, the Extern Bitmap object will call the EwLoadExternBitmap() function requesting so the new version of the affected image.
Accordingly, when the server notifies your application about the alternation of the image content, you have to feed the notification to the GUI application permitting it to trigger the Reload() method of the affected Extern Bitmap object. The simplest approach to feed such notifications is to use the System Event mechanisms and the corresponding System Event Handler.
Support asynchronous loading
Before version 13, the loading of external image contents occurred strictly in synchronous mode. All you had to implement for this purpose was the single above described EwLoadExternBitmap() function. The disadvantage of this approach was that each loading request was blocking. As long as the request was not finished, the GUI application was not able to react to user inputs and update screen contents.
Starting with version 13 the set of functions have been enhanced to allow asynchronous loading requests. If your application case requires such asynchronous loading mode, you will need to implement all following functions. The old EwLoadExternBitmap() function is then optional and needed only when your application also uses the synchronous mode:
Function |
Short description |
---|---|
XHandle EwStartExternBitmap( XString aName ) |
Initiates a new loading request. |
XBool EwProgressExternBitmap( XHandle aHandle ) |
Drives the loading request. |
XBitmap* EwFinishExternBitmap( XHandle aHandle ) |
Obtaines the image contant. |
void EwCancelExternBitmap( XHandle aHandle ) |
Cancels a request. |
XString EwGetExternBitmapInfo( XHandle aHandle ) |
Queries optionally associated information. |
The function EwStartExternBitmap() is invoked to kick-off the non-blocking bitmap loading operation. For example, the function could start a thread or task responsible for loading an image file. The desired image content is identified by the value provided in the parameter aName - analog to how this is done in the above described function EwLoadExternBitmap().
The function EwProgressExternBitmap() is invoked periodically to drive the loading operation and check its status. In this manner, the function can load and decode image contents in multiple, short steps. This is specially useful if your target system does not support parallel execution of threads or tasks. When implementing this function, please keep its execution time as short as possible to not block the GUI application.
With its return value, EwProgressExternBitmap() indicates the status of the loading operation. Your implementation should return true if the loading is still in progress and false if it is finished and the loaded bitmap can be obtained. Also, the function should return false if the loading operation is failed for whatever reason. If the extern bitmap loader uses threads or tasks to perform the loading operation, the function should limit to test the work status of the threads/tasks and return false when they are finished or failed with the loading operation.
The function EwFinishExternBitmap() is invoked when the bitmap loading is finished (indicated by the return value false of the above described function EwProgressExternBitmap()). The implementation of the function should return a Graphics Engine bitmap object containing the loaded image contents and release all involved resources. In case the loading is failed, the function should abort, release all involved resources and return NULL.
The function EwCancelExternBitmap() is invoked to cancel the loading request initiated by the corresponding EwStartExternBitmap() invocation. The implementation of this function should, for example, stop all threads or tasks driving the load operation and release all involved resources.
The function EwGetExternBitmapInfo() is invoked shortly before invoking EwFinishExternBitmap(). The function can return a string with information describing the loaded image content or an empty string if such information is not available.
To track asynchronous operations, the function EwStartExternBitmap() has to allocate and initialize a data structure identifying the operation and return a pointer to this structure (in the XHandle return value). Later, when one of the functions EwProgressExternBitmap(), EwFinishExternBitmap(), EwCancelExternBitmap() or EwGetExternBitmapInfo() is invoked, this value is passed in the function's parameter aHandle. The implementation of the function can thereupon access the data structure, check the progress of the loading operation, obtain the loaded bitmap or cancel the request. This permits multiple loading operations to be performed and managed simultaneously at the same time.
Both invocations, EwFinishExternBitmap() and EwCancelExternBitmap() finalize the operation and should release all involved resources, incl. the data structure allocated originally in the function EwStartExternBitmap() and referred by the parameter aHandle.
IMPORTANT
Before you start to implement the above described functions and use the asynchronous mode, we recommend to first implement (or at least study very well) the synchronous mode. It is very helpful to understand the basic concepts before you address the much more advanced asynchronous mode.
Following (pseudo) code demonstrates the basic structure of the interface. The exact implementation will vary depending on your application case, the used decoder, threads, etc. It is thus difficult to demonstrates a more concrete implementation of this interface:
/* Data structure representing the loading request */ typedef struct { /* The structure could already contain the bitmap which is then filled asynchronously with content. */ XBitmap* bitmap; /* ... other data fields referencing resources involved in the operation */ FILE* fileHandle; THREAD* thread; } Record; /* Kick-off the request */ XHandle EwStartExternBitmap( XString aName ) { /* Allocate a data structure to store state information associated to the request */ Record* r = EwAlloc( sizeof( Record )); /* Failed? Return 0 */ if ( !r ) return 0; /* For the moment the size of the bitmap is unknown */ r->bitmap = 0; /* Depending on your application case you could open files or start threads, which perform the loading, etc. When starting threads, you can pass this 'r' structure to the thread so it can work on it. */ r->fileHandle = fopen( ... ); r->thread = thread_start( ThreadFunc, r ); return r; } /* Cancel the request */ void EwCancelExternBitmap( XHandle aHandle ) { Record* r = (Record*)aHandle; /* If the bitmap was locked before, unlock it now */ if ( r->bitmap ) EwUnlockBitmap( r->bitmap, ... ); /* Release the evtl. already allocated bitmap */ if ( r->bitmap ) EwFreeBitmap( r->bitmap ); /* Stop evtl. threads and close files, etc. */ thread_stop_and_exit( r->thread ); fclose( r->fileHandle ); /* Finally free the data structure */ EwFree( r ); } /* Drive the decoding and check its state */ XBool EwProgressExternBitmap( XHandle aHandle ) { Record* r = (Record*)aHandle; /* Depending on where image contents are stored, you will eventually create and lock a bitmap. You do this only once when the size of the image is known. Then the decoder can write directly to the memory of the locked bitmap */ if ( SizeOfTheImageIsKnown( ... ) && !r->bitmap ) { r->bitmap = EwCreateBitma( ... ); EwLockBitmap( r->bitmap, ... ); } /* If the decoding is performed in a thread you will probably limit to test the status of the decoding. */ if ( IsThreadFinished( ... )) return false; /* If no threads are used, limit to decode the next fragment of the image and test the completeness */ if ( DecodeTheNextSlice( ... ) && DecodingIsFinished( ... )) return false; /* Failed? Return false */ if ( Failed( ... )) return false; /* Still in progress */ return true; } /* Receive the decoded image file and release all resources */ XBitmap* EwFinishExternBitmap( XHandle aHandle ) { Record* r = (Record*)aHandle; XBitmap* b = r->bitmap; /* If the bitmap was locked before, unlock it now */ EwUnlockBitmap( r->bitmap, ... ); /* Stop evtl. threads and close files, etc. */ thread_stop_and_exit( r->thread ); fclose( r->fileHandle ); /* Finally free the data structure */ EwFree( r ); return b; } /* Query associated information */ XString EwGetExternBitmapInfo( XHandle aHandle ) { Record* r = (Record*)aHandle; return EwNewStringAnsi( "Some info for the image" ); }
Extern Bitmap and Prototyping
Since the implementation of the above described interfaces is very particular to every application case and every target device, this interface is per default not available when you are running the application in the Prototyper or Composer window. This has as consequence that when you trigger an Extern Bitmap object to load the image content, the following runtime warning is reported during prototyping:
You have two possibilities how to deal with this situation:
★Option 1: You simply ignore the warning. As consequence, no extern bitmap contents are shown during the prototyping. The image views associated to the extern bitmap object remain empty. However, when you integrate the generated code in your target system, you will need to implement all needed functions (e.g. EwLoadExternBitmap()) as explained above, otherwise the C linker will report unresolved external symbol ... errors.
★Option 2: You enhance the prototyping environment by the missing interface implementation. Embedded Wizard supports for such application cases a technique called intrinsic modules. Technically seen, an intrinsic module is an ordinary Microsoft Windows DLL (dynamic loadable library). You develop such module by using the Microsoft Visual Studio. Once you are finished, you copy the module into your project folder. Afterwards when you start the prototyping environment, Embedded Wizard will load the module and provide its functionality for the tested application. How intrinsic modules are implemented is described in the chapter Implementing Prototyper intrinsics. This chapter also contains an example of an intrinsics module implementing the function EwLoadExternBitmap() to dynamically render QR-code image.
Extern Bitmap and WebGL (JavaScript) Platform Package
The WebGL Platform Package contains already an implementation of the above described interface. This implementation takes care of the loading of the requested image contents as PNG, JPG, etc. files from URLs passed in the parameter aName. The loading operation occurs asynchronously. As soon as the requested image contents are available, the Platform Package notifies automatically the affected Extern Bitmap object and updates all associated image views.
This has the consequence, that just after changing the property Name of the Extern Bitmap object, the old image content is discarded leaving the Extern Bitmap object temporarily without any content. In the meantime the affected Extern Bitmap object is considered as being empty.
The above described interfaces are similarly implementable in WebGL. For this purpose you will provide the functions in JavaScript data object during the initialization of the WebGL application see the generated *.HTML file for more details.