SDK – File Format Plug-ins
Interface Rules
The following rules apply to all plug-ins implementing this interface:
- All file format plug-ins must be thread-safe (re-entrant); a plug-in function may be called from several threads simultaneously, especially when running with GUIs blocked. It is also possible for a plug-in to indirectly call itself via the host. Thus, the use of global variables is highly discouraged, as is the use of __declspec(thread), which doesn’t work with dynamically loaded DLLs.
- The host may (and usually does) choose to cache (or 'hog') file format plug-ins by keeping them loaded in memory between calls. Your plug-in should therefore not make any assumptions about its life-span.
Interface Description
The file format plug-in interface is made up of five functions in addition to the general interface:
- flt_LoadImage - Used to load images from files
- flt_SaveImage - Used to save images to files
- flt_CheckLoad - Used in conjunction with flt_LoadImage
- flt_CheckSave - Used in conjunction with flt_SaveImage
- flt_TransformImage - For file formats that support generic lossless transformations (e.g. JPEG rotation)
Plug-ins are not required to implement the entire interface - you may implement flt_LoadImage alone, or implement flt_SaveImage and flt_CheckSave or all of them as you deem fit.
Image Import via flt_LoadImage() and flt_CheckLoad()
Chasys Photo looks for a function named flt_CheckLoad() in each plug-in it tries. This is the function that Chasys Photo calls to check if the plug-in can load files in the specified file format. If it doesn’t find this function, the plug-in is moved to the bottom of the priority list and will be tried last. If it is found, Chasys Photo calls it with a flt_CHECK_L:
struct flt_CHECK_L
{
wchar_t* extension[32];
//64
unsigned long hdr_size;
unsigned char hdr_data[32];
//100
unsigned char reserved_1[124-100]; //do not touch
//124
unsigned char level_image;
unsigned char level_layer;
unsigned char level_other;
unsigned char support_flags;
};
#define SUPPORT_FLAG_IS_BRIDGE 0x00000001
#define SUPPORT_FLAG_DEMOTE_ME 0x00000002
Chasys Photo may provide the file extension (e.g. “.jpg”), and/or up to the first 32 bytes of data from the file in extension and hdr_data respectively. If provided, the extension always begins with a dot. The plug-in should use this information to check if it supports that file. If it finds that it supports the file, it should set up the appropriate support data as outlined under support levels then return 1. If not, it should return 0.
If Chasys Photo chooses to use this plug-in, it looks for and calls flt_LoadImage() with a pointer to a flt_IMAGE_L structure. Embedded in the structure is the file name, which is stored as a unicode string (wchar_t*). The first order of business for the plug-in should be checking if the referenced file is in a format it recognizes (e.g. by checking the magic number/pattern in the file). If the file is not supported, the plug-in should immediately fail and return PLUGIN_ERR_NO_SUPPORT to the host. This should be done in the most timely manner possible, and the host may elect to rank plug-ins by how quickly they hand back control after encountering an unknown format.
struct flt_IMAGE_L
{
unsigned long width;
unsigned long height;
unsigned long pitch;
unsigned long* lp_pix;
//16
const wchar_t* file;
HWND hwnd;
int (__cdecl* create)(flt_IMAGE_L* self);
unsigned short host_flags;
unsigned char img_type;
unsigned char img_subtype;
//32
unsigned long layer_count;
short x_pos;
short y_pos;
wchar_t name[32];
unsigned short options;
unsigned short frame_delay;
unsigned char group_id;
unsigned char transparency;
unsigned char unused_1[18];
//128
unsigned char* meta_data;
int (__cdecl* meta_write)(flt_IMAGE_L* self,char* type,int size);
//136
unsigned char reserved_1[256-136-(6*4)]; //do not touch
int (__cdecl* get_config)(flt_IMAGE_L* self,plg_CONFIGDATA config);
int (__cdecl* set_infostr)(flt_IMAGE_L* self,int type,const wchar_t* info);
int (__cdecl* set_progress)(flt_IMAGE_L* self,int done,int total);
int host_response;
unsigned short warnings;
unsigned char reserved_2[2]; //do not touch
unsigned long timeout;
//256
pi_STATESTORE* pi_StateStore;
pi_METAMARKUP* pi_MetaMarkup;
pi_BASICQUERY* pi_BasicQuery;
pi_BASICIMAGE* pi_BasicImage;
pi_BASICCOLOR* pi_BasicColor;
pi_BASICUTILS* pi_BasicUtils;
//280
unsigned char reserved_x[1024-280]; //do not touch
};
flt_LoadImage() is expected to set the width, height and where applicable, x_pos, y_pos, options, transparency and name members, then call the create() member function to request Chasys Photo to allocate the bitmap for each layer in the image. This process is repeated until all layers have been loaded.
create() takes the flt_IMAGE_L object as a parameter and returns non-zero to indicate success. It also sets the width, height, pitch (in number of pixels, not bytes) and lp_pix. If the requested size is too large for the host to allocate and the argument flags contains PLUGIN_CREATE_RESIZE, it attempts to allocate a smaller bitmap of the same aspect ratio, sets width and height to the new values, then returns non-zero to indicate success. You can use pi_BasicImage::downsample_* to resize your image on the fly scanline-by-scanline. On failure, create() returns zero and sets host_response to the appropriate PLUGIN_ERR_* code. If the failure was as a result of requesting a bitmap size the is too big, host_response will be PLUGIN_ERR_BITMAP_BIG.
The plug-in proceeds to fill lp_pix as an array of 32 bit integers, each representing a pixel in BGRA format (blue LSB, alpha MSB), with alpha values of zero (0x00) for opaque and 255 (0xff) for transparent. If the image has additional layers, the plug-in sets width, height, etc, then calls create() again; this is repeated until all the layers have been loaded.
The plug-in returns (from flt_LoadImage) the number of layers loaded (greater than 0) to indicate success. If the image had more layers but the plug-in was unable to load them or some of their features due to internal or host constraints, the plug-in should add the relevant PLUGIN_WARN_* flag to warnings to inform the host that the loaded image may be incomplete. Failure is indicated by returning the relevant PLUGIN_ERR_* code (these codes are defined in plugin.h). Error details may be provided via set_infostr() if it is not zero by setting type to PLUGIN_INFOTYPE_ERROR and providing the error string.
Progress data may be shared with the host via set_progress() if it is not zero. It is up to the host to decide what to do with the progress information. A zero return from set_progress() indicates that the user wishes to cancel the operation. The plug-in should respond to this be immediately ceasing all operations and returning PLUGIN_ERR_USER_CANCEL. The host is expected to adapt to the progress update frequency of the plug-in and to minimize the time penalty associated with progress reporting.
The color-space is always sRGB. You are not required to perform any color matching except converting colors to sRGB if the file format uses another color-space. You can use pi_BasicColor to perform this and many other conversions.
The hwnd member being set to a valid handle (non-zero) is an indication to the plug-in that it is free to display a dialog box. DO NOT display a user interface of any sort if this member is zero.
If the plug-in generates metadata (a.k.a. layer attachments), it should pass the metadata entry by entry after calling create() by calling the meta_write() function with the size and type of data and optionally setting meta_data to the memory location of the data, e.g. calling meta_write(data,"MARK",256) will create an attachment of type MARK and write 256 bytes of data into it from meta_data. If meta_data is set to zero before calling meta_write() and a non-zero size is provided, Chasys Photo will create null metadata of the specified size and type and return the pointer in meta_data.
It is no longer necessary to set meta_data to zero before calling create(). To pass or request a metadata archive in a plug that knows how to handle archives, use the standard metadata functions with the archive four character code. The ability to provide or accept raw archives is an optional host feature that may not be available in all scenarios.
These are the flags for the img_type and img_subtype members (this list is not exhaustive; see plugin.h for complete list):
| PLUGIN_IMGTYPE_COMPOSITE | 0x0000 |
| PLUGIN_IMGTYPE_IMAGELIST | 0x0001 |
| PLUGIN_IMGTYPE_FRAMEANIM | 0x0002 |
| PLUGIN_IMGTYPE_OBJECTANIM | 0x0003 |
| PLUGIN_IMGSUBTYPE_NORMAL | 0x0000 |
| PLUGIN_IMGSUBTYPE_CLIPPED | 0x0001 |
| PLUGIN_IMGSUBTYPE_MIPMAP | 0x0001 |
| PLUGIN_IMGSUBTYPE_ONIONSKIN | 0x0001 |
The options member a combination of zero or more of the LAYER_OPTION_* flags below and exactly one of the LAYER_BLEND_* flags (this list is not exhaustive; see layerdefs.h for complete list):
| LAYER_OPTION_HIDDEN | /* Hidden layer */ | 0x00000100 |
| LAYER_OPTION_LOCKED | /* Locked layer */ | 0x00000200 |
| LAYER_OPTION_EXCLUDED | /* Excluded layer */ | 0x00000400 |
| LAYER_OPTION_PROTECTED | /* Alpha-protected layer */ | 0x00000800 |
| LAYER_OPTION_CLICKTHRU | /* Click-thru layer */ | 0x00001000 |
| LAYER_OPTION_FLOATING | /* Floating layer */ | 0x00002000 |
| LAYER_BLEND_NONE | /* Blend mode = None */ | 0x000000ff |
| LAYER_BLEND_USER | /* Blend mode = User Defined */ | 0x000000fe |
| LAYER_BLEND_NORM | /* Blend mode = Normal */ | 0x00000000 |
| LAYER_BLEND_ADD | /* Blend mode = Add */ | 0x00000001 |
| LAYER_BLEND_SUB | /* Blend mode = Subtract */ | 0x00000002 |
| LAYER_BLEND_MUL | /* Blend mode = Multiply */ | 0x00000003 |
| LAYER_BLEND_OFF | /* Blend mode = Offset */ | 0x00000004 |
| LAYER_BLEND_HUE | /* Blend mode = Hue */ | 0x00000005 |
| LAYER_BLEND_CLR | /* Blend mode = Chroma */ | 0x00000006 |
| LAYER_BLEND_LUM | /* Blend mode = Luma */ | 0x00000007 |
| LAYER_BLEND_SCRN | /* Blend mode = Screen */ | 0x00000008 |
| LAYER_BLEND_MASK | /* Blend mode = Mask */ | 0x00000009 |
| LAYER_BLEND_DIV | /* Blend mode = Divide */ | 0x0000000a |
| LAYER_BLEND_DIFF | /* Blend mode = Difference */ | 0x0000000b |
| LAYER_BLEND_MIN | /* Blend mode = Minimum */ | 0x0000000c |
| LAYER_BLEND_MAX | /* Blend mode = Maximum */ | 0x0000000d |
| LAYER_BLEND_SAT | /* Blend mode = Saturation */ | 0x0000000e |
| LAYER_BLEND_OVER | /* Blend mode = Overlay */ | 0x0000000f |
| LAYER_BLEND_DODG | /* Blend mode = Dodge */ | 0x00000010 |
| LAYER_BLEND_BURN | /* Blend mode = Burn */ | 0x00000011 |
| LAYER_BLEND_VAL | /* Blend mode = Value */ | 0x00000012 |
| LAYER_BLEND_LIT | /* Blend mode = Lightness */ | 0x00000013 |
| LAYER_BLEND_AND | /* Blend mode = Logical AND */ | 0x00000014 |
| LAYER_BLEND_OR | /* Blend mode = Logical OR */ | 0x00000015 |
| LAYER_BLEND_XOR | /* Blend mode = Logical XOR */ | 0x00000016 |
| LAYER_BLEND_LMSK | /* Blend mode = Luma Mask */ | 0x00000017 |
| LAYER_BLEND_BHND | /* Blend mode = Behind */ | 0x00000018 |
| LAYER_BLEND_OPNT | /* Blend mode = Overpaint */ | 0x00000019 |
The following may be used in combination for the host_flags member (this list is not exhaustive. See layerdefs.h for complete list):
| PLUGIN_HOSTFLAG_RESIZE | /* host may return resized image if necessary */ | 0x0001 |
| PLUGIN_HOSTFLAG_NO_SAVE | /* ask host to treat file format as read-only, i.e redirect save to another format */ | 0x0002 |
| PLUGIN_HOSTFLAG_MORE_TIME | /* ask host to defer time-out (e.g. plug-in was showing UI; set this immediately after the user closes the UI) */ | 0x0004 |
| PLUGIN_HOSTFLAG_WANT_THUMB | /* plug-in may return a thumbnail if the image cannot be retrieved quickly */ | 0x0100 |
| PLUGIN_HOSTFLAG_SKIP_META | /* plug-in may omit metadata if retrieval is difficult */ | 0x0200 |
| PLUGIN_HOSTFLAG_SKIP_ANIM | /* plug-in may omit animation because the host will not animate */ | 0x0400 |
The following may be used in combination for the warnings member (this list is not exhaustive; see layerdefs.h for complete list):
| PLUGIN_WARN_LAYER_COUNT | /* layers exceed allowed maximum */ | 0x0001 |
| PLUGIN_WARN_LAYER_TYPE | /* bad or unknown layer type found */ | 0x0002 |
| PLUGIN_WARN_BLEND_MODE | /* bad or unknown blend-mode found */ | 0x0004 |
| PLUGIN_WARN_IMAGE_DATA *** | /* bad or unknown image data found */ | 0x0008 |
| PLUGIN_WARN_ALPHA_DROP | /* transparency info removed */ | 0x0010 |
| PLUGIN_WARN_THUMB_ONLY | /* the operation timed out */ | 0x0020 |
| PLUGIN_WARN_NONSTANDARD | /* non-standard extension/feature used */ | 0x0080 |
| PLUGIN_WARN_TIMEOUT | /* the operation timed out */ | 0x0100 |
Data corruption should generally be treated as a permanent error that causes the loading process to fail with the appropriate PLUGIN_ERR_*. The PLUGIN_WARN_IMAGE_DATA option should only be used by experimental plug-ins that support file formats for which better support is known not to exist and for which some data corruption may be acceptable.
If the timeout member is non-zero, the plug-in has that many milliseconds to finish the operation. Chasys Photo may enforce this timeout by, for example, refusing to honour create() requests after the timeout period. In this case, Chasys Photo will add PLUGIN_WARN_TIMEOUT to warnings.
Chasys Photo may advise the plug-in that a thumbnail is okay if the full image will take long to retrieve by setting PLUGIN_HOSTFLAG_WANT_THUMB. If the plug-in elects to only provide a thumbnail, PLUGIN_WARN_THUMB_ONLY must be added to warnings.
Image Export via flt_SaveImage() and flt_CheckSave()
Chasys Photo looks for a function named flt_CheckSave() in each plug-in it tries. This is the function that Chasys Photo calls to check if the plug-in can save files in the specified file format. If this function is not found, plug-in will be rejected and Chasys Photo will not use it. If it is found, Chasys Photo calls it with a flt_CHECK_S:
struct flt_CHECK_S
{
wchar_t* extension[32];
//64
unsigned char reserved_1[124-64]; //do not touch
//124
unsigned char level_image;
unsigned char level_layer;
unsigned char level_other;
unsigned char support_flags;
};
Chasys Photo will provide the file extension (e.g. “.jpg”). The extension provided always begins with a dot. The plug-in should use this information to check if it supports that file. If it finds that it supports the file, it should set up the appropriate support data as outlined under support levels then return 1. If not, it should return 0.
If Chasys Photo chooses to use this plug-in, it looks for and calls flt_SaveImage() with a pointer to a flt_IMAGE_S structure, which contains all the information needed to save the image: width, height, pitch (in number of pixels, not bytes) and lp_pix are set to describe the bitmap, file to the save path, and hwnd to the parent window if GUIs are allowed. Initially, layer_count will be set to the number of layers in the image. If your plug-in does not support layers, or layer_count is greater than what your plug-in can handle, you should abort immediately by returning PLUGIN_ERR_NOT_FLAT. The host will then flatten the image for you and call flt_SaveImage() again with the flattened image and a layer_count of 1.
struct flt_IMAGE_S
{
unsigned long width;
unsigned long height;
unsigned long pitch;
unsigned long* lp_pix;
//16
const wchar_t* file;
HWND hwnd;
int (__cdecl* select)(flt_IMAGE_S* self,int layer_id);
unsigned short host_flags;
unsigned char img_type;
unsigned char img_subtype;
//32
unsigned long layer_count;
short x_pos;
short y_pos;
wchar_t name[32];
unsigned short options;
unsigned short frame_delay;
unsigned char group_id;
unsigned char transparency;
unsigned char unused_1[18];
//128
unsigned char* meta_data;
int (__cdecl* meta_read)(flt_IMAGE_S* self,char* type);
//136
unsigned char reserved_1[256-136-(6*4)]; //do not touch
int (__cdecl* get_config)(flt_IMAGE_S* self,plg_CONFIGDATA config);
int (__cdecl* set_infostr)(flt_IMAGE_S* self,int type,const wchar_t* info);
int (__cdecl* set_progress)(flt_IMAGE_S* self,int done,int total);
int host_response;
unsigned short warnings;
unsigned char reserved_2[2]; //do not touch
unsigned long timeout;
//256
pi_STATESTORE* pi_StateStore;
pi_METAMARKUP* pi_MetaMarkup;
pi_BASICQUERY* pi_BasicQuery;
pi_BASICIMAGE* pi_BasicImage;
pi_BASICCOLOR* pi_BasicColor;
pi_BASICUTILS* pi_BasicUtils;
//280
unsigned char reserved_x[1024-280]; //do not touch
};
The hwnd member being set to a valid handle (non-zero) is an indication to the plug-in that it is free to display a dialog box. DO NOT display a user interface of any sort if this member is zero.
As usual, lp_pix is an array of 32 bit integers, each representing a pixel in BGRA format (blue LSB, alpha MSB), with alpha values of zero (0x00) for opaque and 255 (0xff) for transparent. It should not be altered by the plug-in in any way. The plug-in, if capable of storing alpha data, should check all pixels to determine whether or not the alpha values are used (i.e. some are non-zero). If the plug-in is capable of handling layered images, it should check the transparency value as well.
If the image has metadata, the meta_data parameter will be a non-zero metadata archive handle. The host owns this memory handle; the plug-in should not free or change it. If the plug-in doesn’t know how to process a metadata archive, it can call the meta_read() function to load specific metadata elements into meta_data, e.g. calling meta_read(data,"MARK") will load the markup attachment if available. The function returns the size of the metadata or zero if the type is not found. Setting type to zero will load the entire metadata archive.
After the plug-in saves the current layer, it calls select() with the zero-based identifier of the next layer to retrieve it; the data for which is placed in the width, height, pitch and lp_pix members. This is repeated until all the layers have been saved. Please note that the select() member may be null if there’s only one layer. Some hosts allow specifying “-1” to return the merged image with limited functionality and without any metadata. If you use this extension, be sure to call select() again with a valid layer identifier before calling any other function. Calling select() with PLUGIN_SELECT_INFO_ONLY will select the layer without initializing image data; this may be used to collect info on layer dimensions before actual processing begins.
The img_type and options members have the same meanings are described above.
The plug-in should return the number of layers saved (greater than 0) if it succeeds. If the image has more layers than what the plug-in was able to save, or some of their features were dropped due to internal or host constraints, the plug-in should add the relevant PLUGIN_WARN_* flag to warnings to inform the host that the saved image may be incomplete. Failure is indicated by returning the relevant PLUGIN_ERR_* code (these codes are defined in plugin.h). Error details may be provided via set_infostr() if it is not zero by setting type to PLUGIN_INFOTYPE_ERROR and providing the error string.
Progress data may be shared with the host via set_progress() if it is not zero. It is up to the host to decide what to do with the progress information. A zero return from set_progress() indicates that the user wishes to cancel the operation. The plug-in should respond to this be immediately ceasing all operations and returning PLUGIN_ERR_USER_CANCEL. The host is expected to adapt to the progress update frequency of the plug-in and to minimize the time penalty associated with progress reporting.
Generic Transformation via flt_TransformImage()
Some lossy file formats, such as JPEG, support some generic transformations which are carried out losslessly without recompressing the image. This functionality may be exposed through the flt_TransformImage() call. It takes a pointer to a structure of type flt_IMAGE_X. Embedded in this structure is the unicode filename and transformation identifiers:
struct flt_IMAGE_X
{
const wchar_t* file;
HWND hwnd;
unsigned long xform_id;
unsigned long xform_id2;
//16
unsigned char reserved_1[256-16-(6*4)]; //do not touch
int (__cdecl* get_config)(flt_IMAGE_X* self,plg_CONFIGDATA config);
int (__cdecl* set_infostr)(flt_IMAGE_X* self,int type,const wchar_t* info);
int (__cdecl* set_progress)(flt_IMAGE_X* self,int done,int total);
int host_response;
unsigned short warnings;
unsigned char reserved_2[2]; //do not touch
unsigned long timeout;
//256
pi_STATESTORE* pi_StateStore;
pi_METAMARKUP* pi_MetaMarkup;
pi_BASICQUERY* pi_BasicQuery;
pi_BASICIMAGE* pi_BasicImage;
pi_BASICCOLOR* pi_BasicColor;
pi_BASICUTILS* pi_BasicUtils;
//280
unsigned char reserved_x[1024-280]; //do not touch
};
The file member contains the file to be transformed. One of the following constants must be specified for the xform_id parameter:
| PLG_FLT_XFORM_ROTATE_090 | 0x00000001 |
| PLG_FLT_XFORM_ROTATE_180 | 0x00000002 |
| PLG_FLT_XFORM_ROTATE_270 | 0x00000003 |
| PLG_FLT_XFORM_FLIP_H | 0x00000004 |
| PLG_FLT_XFORM_FLIP_V | 0x00000005 |
| PLG_FLT_XFORM_TRANSPOSE | 0x00000006 |
| PLG_FLT_XFORM_TRANSVERSE | 0x00000007 |
The xform_id2 parameter is currently not used.
The function should return a value greater than zero if the transformation was done. Failure is indicated by returning the relevant PLUGIN_ERR_* code (these codes are defined in plugin.h). Error details may be provided via set_infostr() if it is not zero by setting type to PLUGIN_INFOTYPE_ERROR and providing the error string.
Support Levels
Support Levels indicate how well a plug-in supports a specific file format, with 0 indicating no support (or "not sure") and 255 indicating full support including all semantics. The support level is calculated as shown below; the exact values used are at the discretion of the plug-in author. Plug-ins that do not directly load the image (and can therefore not guarantee a specific level of support) should always specify the SUPPORT_FLAG_IS_BRIDGE flag to allow plug-ins that are specifically designed for those file formats to take precedence.
The level_image member should be set to a value between 0 and 255 depending on the completeness of image data decoding implemented by the plug-in. If this value is zero, Chasys Photo will assume that the plug-in cannot guarantee any level of support and will use it only after everything else has failed.
| Complete support for the file format including all variations (animated, still, different formats, etc.) | 255 |
| Support for the most common variations of the file format | 128 - 254 |
| Support for some varieties of the file format | 1 - 127 |
| No support for the image semantics of the file format | 0 |
The level_layer member should be set to a value between 0 and 255 depending on the completeness of multi-layer/multi-frame decoding implemented by the plug-in. If the plug-in can only decode the first frame/layer, it should set this memeber to zero.
| Complete support for structures such as layers, frames, pages, mipmaps, etc. | 255 |
| Support for the most common structures | 128 - 254 |
| Support for some some structures | 1 - 127 |
| No support for structured images (i.e. flat images only with no layers/frames/pages) | 0 |
The level_other member should be set to a value between 0 and 255 depending on the completeness of metadata decoding implemented by the plug-in. Metadata may include thing such as EXIF data, vector data, etc.
| Complete support for other metadata/information such as EXIF, paths, vectors, etc. | 255 |
| Support for the most common metadata | 128 - 254 |
| Support for some metadata | 1 - 127 |
| No support for metadata | 0 |
The support_flags member should be set to the logical OR of the appropriate flags.
| A bridge plug-in, i.e. one that loads image by calling another module, such as decoding capabilities provided by the OS (e.g. OLE/LoadPicture) | SUPPORT_FLAG_IS_BRIDGE |
| Plug-in should be intentionally given a low priority (e.g. it employs an inefficient decoding mechanism) | SUPPORT_FLAG_DEMOTE_ME |
Sample Code
Here’s the full listing of the main file for the PNG plug-in included with Chasys Photo for your reference:
| View “flt_ORA.cpp” TAGS: Chasys Photo Source Code, OpenRaster File Format Plug-in |
You can also view other source code files here.