SDK – Device Plug-ins
Interface Rules
The following rules apply to all plug-ins implementing this interface:
- Device plug-ins need not be re-entrant. This interface is always called from a single thread as it requires a GUI.
- The interface currently only allows the plug-in to access one layer per call; this layer is chosen by the user during plug-in invocation
- The plug-in should allow the user to set any necessary parameters via an appropriate dialog box.
- The image buffer for this interface is prelocked, and can be accessed at any time
Interface Description
The file format plug-in interface is made up of three functions in addition to the general interface:
- dev_ReadImage - Used to read images from external devices
- dev_SendImage - Used to send images to external devices
Plug-ins are not required to implement the entire interface - you may implement dev_ReadImage, or dev_SendImage or both of them as you see fit.
Image Import via dev_ReadImage()
Chasys Photo looks for a function named dev_ReadImage() in the plug-in the user selects. If it finds this function, Chasys Photo calls it with a pointer to a dev_IMAGE_Rstructure.
struct dev_IMAGE_R
{
unsigned long width;
unsigned long height;
unsigned long pitch;
unsigned long* lp_pix;
//16
wchar_t* title_or_path;
HWND hwnd;
int (__cdecl* create)(dev_IMAGE_R* 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)(dev_IMAGE_R* self,char* type,int size);
//136
unsigned char reserved_1[256-136-(6*4)]; //do not touch
int (__cdecl* get_config)(dev_IMAGE_R* self,plg_CONFIGDATA config);
int (__cdecl* set_infostr)(dev_IMAGE_R* self,int type,const wchar_t* info);
int (__cdecl* set_progress)(dev_IMAGE_R* 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
};
dev_ReadImage() is expected to set the width and height members, then call the create() member function to request Chasys Photo to allocate the bitmap for each layer in the image.
create() 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 dev_ReadImage) 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 due to internal or host constraints, it should add the relevant PLUGIN_WARN_* flag to warnings to inform the host that the 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. Device plugins that must show dialogs should interpret this as an error and return 0.
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_ error code. 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 dev_SendImage()
Chasys Photo calls dev_SendImage() with all the members set; i.e. 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. Initially, layer_count will be set to the number of layers in the image.
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 dev_IMAGE_S
{
unsigned long width;
unsigned long height;
unsigned long pitch;
unsigned long* lp_pix;
//16
const wchar_t* title_or_path;
HWND hwnd;
int (__cdecl* select)(dev_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)(dev_IMAGE_S* self,char* type);
//136
unsigned char reserved_1[256-136-(6*4)]; //do not touch
int (__cdecl* get_config)(dev_IMAGE_S* self,plg_CONFIGDATA config);
int (__cdecl* set_infostr)(dev_IMAGE_S* self,int type,const wchar_t* info);
int (__cdecl* set_progress)(dev_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
};
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 sends the current frame, 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 frames have been sent. 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 sent (greater than 0) if it succeeds. If the image had more layers but the plug-in was unable to send them due to internal or host constraints, it should add the relevant PLUGIN_WARN_* flag to warnings to inform the host that the image may be incomplete. Failure is indicated by returning the relevant PLUGIN_ERR_* code (these codes are defined in plugin.h).
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.