uiimage (lxuiimage.h)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


Contents

Using UI Images

The UI image system has three tiers, Client Types, Image Groups and Images:

(1) UI Image Tiers
 - Client Types                  LXtID4, const char *, optionally ILxUIImageGrouperID for complex grouping
         - Image Groups          ILxUIImageGroupID
                 - Images        ILxUIImageID

Each client registers a Client Type. The Backdrop, GL Environment and Default Texture clients would register their own client types, each of which will have it's own list of images added and selected by the user.

Rather than directly listing the images in the list, they are instead arranged in groups. For simple clients like Backdrops and Default Textures, there is only one image per group. However, more complex clients such as GL Environments actually have six images per group, each representing a face of a cube. Since all of these images are required, they are manipulated as a single group entity. Groups therefore provide a username that represents the entire image group for UI purposes. To define these groups, an ILxUIImageGrouperID is used to get a group username and to resolve a single path into the entire group of paths.

Inside each group is the list of images. Again, Backdrops and Default Textures would have only one image per group, while GL Enviroments would have six.

ILxUIImageGrouper

The image grouper is used to associate multiple images into a single entity. A common case is the environement reflection images, which are really six different images that are manipulated as a single unit. This interface is used when registering a new category type with ILxUIImageService as described in the next section.

(2) SDK: LXu_UIIMAGEGROUPER, etc. defines
 #define LXu_UIIMAGEGROUPER              "B69F3583-F3C7-4059-BF8D-84CB61F34260"
 #define LXa_UIIMAGEGROUPER              "uiimagegrouper"

UserName() returns a human-readable group name from a single path. Multiple related paths should return the same group name (for example, the six images of the same GL Environment group). This group will appear in the menu in place of all the images within. Note that the user name returned will first be run through the config system to look for a match; if that fails, the raw username will be used. If the method fails or an empty string is returned, the path of the image will be used as a lookup, and finally returned as a last resort.

(3) SDK: UIImageGrouper::UserName
         LXxMETHOD(  LxResult,
 UserName) (
         LXtObjectID      self,
         const char      *path,
         char            *buffer,
         int              len );

Paths() finds all paths related to the input path, feeding them all into an ILxValueArray object (including the input path). Note that this should return ALL images that should be in the group, even if the images do not actually exist on disk. For example, the expected path of all six reflection directions above would be returned, even if some of the images do not exist, thus allowing the system to check for the existence of the entire group easily.

(4) SDK: UIImageGrouper::Paths
         LXxMETHOD(  LxResult,
 Paths) (
         LXtObjectID      self,
         const char      *path,
         ILxValueArrayID  array );

ILxUIImageService

This global service is used to manipulate the UI Image system, including walking the image list, registering new categories, requesting images, and so on.

(5) SDK: LXu_UIIMAGESERVICE, etc. defines
 #define LXu_UIIMAGESERVICE              "ECB8DE30-E0B1-4cda-8571-580ABEA1AC4D"
 #define LXa_UIIMAGESERVICE              "uiimageservice"
 
 #define LXe_UIIMAGE_INCOMPLETE_GROUP    LXxGOODCODE( LXeSYS_UIIMAGE,   1)       // Warning
 #define LXe_UIIMAGE_ALREADY_EXISTS      LXxFAILCODE( LXeSYS_UIIMAGE,   1)
 #define LXe_UIIMAGE_LOAD_FAILED         LXxFAILCODE( LXeSYS_UIIMAGE,   2)

Standard ScriptQuery method

(6) SDK: UIImageService::ScriptQuery
         LXxMETHOD(  LxResult,
 ScriptQuery) (
         LXtObjectID              self,
         void                   **ppvObj);

Registering Types

The UI Image system contains multiple categories of images intended for specific uses. For example, Backdrops, Default Textures and GL Environment Maps are all different categories. These are are primarily used to group the images in the popup.

This function is called to register a new category. An ID4 type is used for quick lookup, as well as an internal name that is used for config storage and username lookups.

The next argument is an optional ILxUIIMageGrouperID, as described above.

This fails with LXe_UIIMAGE_ALREADY_EXISTS if the type or name identify an existing client.

Clients are automatically removed on shutdown.

Clients can support some flags. If ALLOW_NONE is set, (none) is a valid selection. If COLOR_CORRECT is set, Adding images with GroupAdd() assumes that they are already color corrected; otherwise, they will be added as uncorrected images.

(7) SDK: LXfUIIMAGE_ALLOW_NONE, etc. defines

(8) SDK: UIImageService::RegisterType
         LXxMETHOD(  LxResult,
 RegisterType) (
         LXtObjectID              self,
         LXtID4                   type,
         const char              *name,
         ILxUIImageGrouperID      grouper,
         int                      flags);

These methods allow the list of registered types to be walked, returning their type ID and name. The list is not sorted in any particular order.

(9) SDK: UIImageService::TypeCount
         LXxMETHOD(  int,
 TypeCount) (
         LXtObjectID              self );

(10) SDK: UIImageService::TypeByIndex
         LXxMETHOD(  LxResult,
 TypeByIndex) (
         LXtObjectID              self,
         int                      i,
         LXtID4                  *type);

(11) SDK: UIImageService::TypeNameByIndex
         LXxMETHOD(  LxResult,
 TypeNameByIndex) (
         LXtObjectID               self,
         int                       i,
         const char              **name);

The username can also be looked up. If there is no user name, the internal name is returned.

(12) SDK: UIImageService::TypeUserNameByIndex
         LXxMETHOD(  LxResult,
 TypeUserNameByIndex) (
         LXtObjectID               self,
         int                       i,
         const char              **username);

This returns the LXfUIIMAGE_ flags associated with the type.

(13) SDK: UIImageService::TypeFlagsByIndex
         LXxMETHOD(  LxResult,
 TypeFlagsByIndex) (
         LXtObjectID              self,
         int                      i,
         int                     *flags);

Types can be looked up by their internal name or type ID. If the name is NULL, the type is checked, and the index of the type is returned. Note that indices are not guarenteed to be static.

(14) SDK: UIImageService::TypeLookup
         LXxMETHOD(  LxResult,
 TypeLookup) (
         LXtObjectID               self,
         const char               *name,
         LXtID4                    type,
         int                      *index);

Each type can have a "current" or "selected" image. This image will be selected with a checkmark in the user interface popup. Note that there can only be one selected image, even if multiple clients use the same type. In this case, the most recently selected client (say, the viewport the use most recently selected, in the case of the Backdrop Image type) should call this method to set the path to the selected image. The selected image path is not saved in the config, and must be explicitly set by the client.

As before, the ILxUIImageGroupID returned should NOT be freed or AddRef()'ed. Note that the selected image will never be removed from the image list, although the ILxImageIDs themselves may be purged by the cache system.

(15) SDK: UIImageService::TypeSelected
         LXxMETHOD(  LxResult,
 TypeSelected) (
         LXtObjectID               self,
         const char               *name,
         LXtID4                    type,
         void                    **ppvObj);

The selected image is set by it's path. NULL can be passed to clear the selected path.

(16) SDK: UIImageService::TypeSetSelected
         LXxMETHOD(  LxResult,
 TypeSetSelected) (
         LXtObjectID               self,
         const char               *name,
         LXtID4                    type,
         const char               *path);

Groups of Images of a Type

The list of groups of images in each category can be walked with these functions. Groups are referenced by their path or by an index into the type list. Note that this index is not guarenteed to be static. Images within groups are referenced by their path or by their index within the group. You'll save the path of the image in your config.

Given a type, an ILxUIImageGroupID can be obtained. These should be considered transient and not held onto by clients, but rather should be looked up as needed. If you really want to, you can probably hold onto the group ID and nothing bad will happen.

(17) SDK: UIImageService::GroupCount
         LXxMETHOD(  int,
 GroupCount) (
         LXtObjectID               self,
         LXtID4                    type );

(18) SDK: UIImageService::GroupByIndex
         LXxMETHOD(  int,
 GroupByIndex) (
         LXtObjectID               self,
         LXtID4                    type,
         int                       i,
         void                    **ppvObj );

A group can be also looked up by the path of one of it's images.

(19) SDK: UIImageService::GroupLookup
         LXxMETHOD(  LxResult,
 GroupLookup) (
         LXtObjectID               self,
         LXtID4                    type,
         const char               *path,
         void                    **ppvObj );

The ILxUIImageGroupID can be used to count the images in the group, get their paths and get specific images. These images are referenced by index, but can also be looked up by path.

(20) SDK: LXu_UIIMAGEGROUP, etc. defines
 #define LXu_UIIMAGEGROUP                "B1B2816C-B1FC-4b66-9A01-D4969D37ED3C"
 #define LXa_UIIMAGEGROUP                "uiimagegroup"

This method gets the username for the group.

(21) SDK: UIImageGroup::UserName
         LXxMETHOD(  LxResult,
 UserName) (
         LXtObjectID               self,
         const char              **username);

The type of the client this group belongs to can also be obtained.

(22) SDK: UIImageGroup::ClientType
         LXxMETHOD(  LxResult,
 ClientType) (
         LXtObjectID               self,
         LXtID4                   *type);

This returns the number of images within the group.

(23) SDK: UIImageGroup::ImageCount
         LXxMETHOD(  LxResult,
 ImageCount) (
         LXtObjectID               self);

(24) SDK: UIImageGroup::ImagePathByIndex
         LXxMETHOD(  LxResult,
 ImagePathByIndex) (
         LXtObjectID               self,
         int                       i,
         char                     *buffer,
         int                       len);

This returns some metris about the image.

(25) SDK: UIImageGroup::ImageMetricsByIndex
         LXxMETHOD(  LxResult,
 ImageMetricsByIndex) (
         LXtObjectID               self,
         int                       i,
         int                      *w,
         int                      *h,
         int                      *depth);

This function returns an image by index. Note that the ILxImageID is _NOT_ AddRef'ed, nor should you AddRef it yourself. The UI Image system will hold hold onto the image until no one is using it anymore, and will automatically purge it from memory when no longer needed.

(26) SDK: UIImageGroup::ImageByIndex
         LXxMETHOD(  LxResult,
 ImageByIndex) (
         LXtObjectID               self,
         int                       i,
         void                    **ppvObj);

A specific image in the group can be looked up by it's path. Again, the ILxImageID should NOT be AddRef'ed or released by the client; just use it and forget about it.

(27) SDK: UIImageGroup::ImageLookup
         LXxMETHOD(  LxResult,
 ImageLookup) (
         LXtObjectID               self,
         const char               *path,
         void                    **ppvObj);

This method can be used to see if all images in a group exist in memeory, or else on disk. The group name is one returned by the type's ILxUIImageGrouper::ToGroup() method.

(28) SDK: UIImageGroup::DoAllImagesExist
         LXxMETHOD(  LxResult,
 DoAllImagesExist) (
         LXtObjectID               self);

This returns the path of the first image that exists in memory or on disk in a group. This fails if none of the images can be found.

(29) SDK: UIImageGroup::FirstExistingImagePath
         LXxMETHOD(  LxResult,
 FirstExistingImagePath) (
         LXtObjectID               self,
         char                     *buffer,
         int                       len);

This is similar, but returns the index, or fails if none exist.

(30) SDK: UIImageGroup::FirstExistingImageIndex
         LXxMETHOD(  LxResult,
 FirstExistingImageIndex) (
         LXtObjectID               self,
         int                      *index);

This returns the time in seconds since January 1st, 1970 (or whatever the C time() function counts from) representing when an ILxImageID was last returned from one of this group's methods. The higher the number, the more recently it was used. ms can be used as a tie breaker if the last used times are the same. Either ms or lastUsed can be NULL if only one is desired.

(31) SDK: UIImageGroup::LastUsed
         LXxMETHOD(  LxResult,
 LastUsed) (
         LXtObjectID               self,
         time_t                   *lastUsed,
         int                      *ms);

Image Management

These functions allow images to be added and removed from the database. Note that adding an image to a type that supports groups, the remaining group images will automatically be found and added. If all of the images cannot be found, they are still and the the method succeeds with LXe_UIIMAGE_INCOMPLETE_GROUP. If ppvObj is non-NULL, it will be set to an ILxUIImageGroupID representing the new image group. Note that if a group containing this image already exists, the method will succeed and return the existing group instead.

The loadImages flag allows for more robust testing of images. Instead of just checking for file existence, it will try to actually load the images. If any image cannot be loaded, the method will fail with LXe_UIIMAGE_LOAD_FAILED.

(32) SDK: UIImageService::GroupAdd
         LXxMETHOD(  LxResult,
 GroupAdd) (
         LXtObjectID               self,
         LXtID4                    type,
         const char               *path,
         int                       loadImages,
         void                    **ppvObj);

This removes an image group from the database. Once removed, further lookups will fail.

(33) SDK: UIImageService::GroupRemove
         LXxMETHOD(  LxResult,
 GroupRemove) (
         LXtObjectID               self,
         LXtID4                    type,
         ILxUIImageGroupID         group);

This removes all images for that type from the database.

(34) SDK: UIImageService::GroupClearAll
         LXxMETHOD(  LxResult,
 GroupClearAll) (
         LXtObjectID               self,
         LXtID4                    type);

The user can also be asked to pick an image from a file requester. If ppvObj is non-NULL, it will be set to an ILxUIImageGroupID representing the new image group upon success.

(35) SDK: UIImageService::GroupLoad
         LXxMETHOD(  LxResult,
 GroupLoad) (
         LXtObjectID               self,
         LXtID4                    type,
         void                    **ppvObj);

Image Cache

To make image access easy and relatively memory efficient, the cache system is used. The above functions that get images do so by using the image cache, a thin layer on top of the cache system described in cache.qq This can be used for an image; you are not limited to using the functions above.

This single method is all you need to use the image cache. The image will be loaded from the path if neccessary, stored in the cache, and returned. If the image is already in the cache, it will simply be returned directly.

Do not hold on to the ILxImageID returned by this method, as this keeps the cache system from being able to intelligently purge unnecessary images. The client should not call XObjectRelease() on any images returned by this function.

Either a mini-thumbnail sitable for the face of a popup, a larger thumbnail at a size set in the prefs, or the main image at full res can be read.

Also, small (20 pixel tall) or larger (32 pixel tall) images can be requested. These are useful as icons on the face of buttons.

Finally, there are modifiers that can be applied to an image. CACHED

(36) SDK: LXiUIIMG_CACHED_MAIN, etc. defines
 // Base images
 #define LXiUIIMG_CACHED_MAIN            0
 #define LXiUIIMG_CACHED_THUMB           1
 #define LXiUIIMG_CACHED_FACE            2
 #define LXiUIIMG_CACHED_ICON_SMALL      3
 #define LXiUIIMG_CACHED_ICON_LARGE      4
 
 #define LXiUIIMG_CACHED_BASE_COUNT      5
 #define LXiUIIMG_CACHED_MOD_SCALAR      4               // Used with BASE_COUNT to figure out how many cache slots we need
 
 // Flags to modify behavior
 #define LXfUIIMG_CACHED_CORRECTED       0x10            // Color corrected (vs. uncorrected)
 #define LXfUIIMG_CACHED_SQUARE          0x20            // Square aspect (vs. original aspect)
 
 // Masks
 #define LXmUIIMG_CACHED_BASE            0x0F
 #define LXmUIIMG_CACHED_MOD             0xF0

(37) SDK: UIImageService::GetCachedImage
         LXxMETHOD(  LxResult,
 GetCachedImage) (
         LXtObjectID               self,
         const char               *path,
         int                       mode,
         void                    **ppvObj);

A specific cached image can be cleared. This is useful if the image has changed on disk and the new image should be loaded. This fails with LXe_NOTFOUND if no currently cached image matches this path.

Either the thumbnail, the main image or both can be purged.

(38) SDK: LXfUIIMG_FLUSH_MAIN, etc. defines

(39) SDK: UIImageService::FlushCachedImage
         LXxMETHOD(  LxResult,
 FlushCachedImage) (
         LXtObjectID               self,
         const char               *path,
         int                       mode);

It is often useful to embed the thumbnail image into a rich text string. Rich text only supports a-z, A-Z, 0-9 and _, which means that providing a path won't work. To simplify embedding cached images, this method can be used.

The ebmed string contains the appropriate \03 escape sequence, and can be directly drawn as rich text. The mode determines which cached image is returned, and is one of the LXiUIIMG_CACHED_ defines above.

(40) SDK: UIImageService::GetEmbedablePath
         LXxMETHOD(  LxResult,
 GetEmbedablePath) (
         LXtObjectID               self,
         const char               *path,
         int                       mode,
         const char              **embed);

Example Usage

The first step is to register your client. You may optionally provide a grouper if you're doing, say, GL Reflection images. If the last argument is 1, then a (none) option will appear in the popup.

(41) SDK: UI Image Example
 uiimage[0]->RegisterType( uiimage, LXxID4( 'T', 'E', 'S', 'T' ), "test", NULL, LXfUIIMAGE_ALLOW_NONE );

Once registered, you'll listen for events from the UI Image system to let you know when the user has selected a new image for your client.

(42) SDK: UI Image Example
 GlobalPortAttach( UIIMAGEs_PORTNAME, &something, MyUIImageEventHandler );

uiimage.select can be executed to open a popup that the user can interact with (NOTE: behavior may change in the future).

(43) SDK: UI Image Example
 if( LXx_OK( CmdSysExecuteNamedArgList( LXiCTAG_NULL, "uiimage.select", LXfCMD_EXEC_DEFAULT, "client", "test", NULL ) ) ) {
         ...
 }

Once the user has selected something, your event handler will be tripped. You can now read the path for one or all (in the case of a type using a grouper) of the images in the group. If you plan on using these images, be sure to make copies of them. The ILxUIImageGroupID is _NOT_ guarenteed to stick around. You need to make local copies of the paths (or AddRef() the object, although that's not really recommended). Note tha the group may be LXiUIIMG_NONE if the user selected the (none) option, assuming the client supports it.

(44) SDK: UI Image Example
         static void
 MyUIImageEventHandler (
         void                    *unused,
         WinPaneID                pane,
         int                      event,
         ILxUIImageGroupID        group)
 {
 
         /* If mulitple clients use this type, see if the client is topmost */
         if( isThisClientTopmost ) {
 
                 if( event == UIIMAGEv_GROUP_SELECTED ) {
                         char             buffer[ 512 ];
                         int              count, i;
 
                         if( group == LXiUIIMG_NONE ) {
                                 // (none) option selected
                                 ...
 
                         } else {
                                 count = group[0]->ImageCount( group );
                                 for( i=0; i < count; i++ ) {
                                         group[0]->ImagePathByIndex( group, i, buffer, 512 );
 
                                         // Copy the path
                                         ...
                                 }
                         }
                 }
         }
 }

Once you have the path, the best way to use it is though the GetCachedImage() method. Every time you need to use the image you should call this method. Do _NOT_ hold on to the ILxImageID, as that would interfere with the cache system.

(45) SDK: UI Image Example
 ILxImageID       image;
 
 if( LXx_FAIL( uiimage[0]->GetCachedImage( uiimage, path, (void **)&image ) ) ) {
         ...
 }

Alternatively, if you still have the ILxUIImageGroupID, you can use it's ImageByIndex() method.

(46) SDK: UI Image Example
 ILxImageID       image;
 
 if( LXx_FAIL( group[0]->ImageByIndex( uiimage, i, (void **)&image ) ) ) {
         ...
 }

You should save the paths to the images (or just one path that you can feed into your ILxImageGrouperID) in your client's config for future use. The ILxImageGroupID is not guarenteed to exist in the future, as new user selections may cause it to drop off of the recent list.

Multiple clients may use the same type. For example, each viewport may have it's own bakdrop image, but use the same Backdrop type. To ensure that the current image is checked or even in the popup menu, TypeSetSelected() should be called whenever the viewport selection changes.

Note that when this method is called, an event will be sent regarding the new selection. Since you are are already using this path, you may want to set a flag in your code to ignore the event. Or simply test to see if your current path and the one in the event are different.

(47) SDK: UI Image Example
 // Multiple clients use the same type, and the current client has changed;
 //  update the UI Image type selection via TypeSetSelected().
 
 // Ignore the UIIMAGEv_GROUP_SELECTED event that will be sent when we call
 //  TypeSetSelected().
 ignoreGroupSelEvent = 1;
 
 // Change the current image
 uiimage[0]->TypeSetSelected( uiimage, typeID, typeName, currentPath );
 
 // Reset the ignore flag
 ignoreGroupSelEvent = 1;

Now when the user trips a popup to select a UI Image, the current image will be in the list and chceked.