Next: Queries, Previous: Built-in support, Up: Supported Image Formats [Contents][Index]
It is possible for application to add new formats to the library so
flimage_load()
and flimage_dump()
know how
to handle them. Basically, the application program tells the library
how to identify the image format, and the image dimension, and how to
read and write pixels.
The API for doing so is the following
typedef int (*FLIMAGE_Identify) (FILE *); typedef int (*FLIMAGE_Description) (FL_IMAGE *); typedef int (*FLIMAGE_Read_Pixels) (FL_IMAGE *); typedef int (*FLIMAGE_Write_Image) (FL_IMAGE *); int flimage_add_format(const char *formal_name, const char *short_name, const char *extension, int type, FLIMAGE_Identify identify, FLIMAGE_Description description, FLIMAGE_Read_Pixels read_pixels, FLIMAGE_Write_Image write_image);
where we have
formal_name
The formal name of the image format
short_name
An abbreviated name for the image format
extension
File extension, if this field is NULL
, short_name
will
be substituted
type
The image type. This field generally is one of the supported image
types (e.g., FL_IMAGE_RGB
), but it does not have to. For image
file formats that are capable of holding more than one type of images,
this field can be set to indicate this by ORing the supported types
together (e.g., FL_IMAGE_RGB|FL_IMAGE_GRAY
). However, when
description returns, the image type should be set to the actual type
in the file.
identify
This function should return 1 if the file pointed to by the file pointer passed in is the expected image format (by checking signature etc.). It should return a negative number if the file is not recognized. The decision if the file pointer should be rewound or not is between this function and the description function.
description
This function in general should set the image dimension and type
fields (and colormap length for color index images) if successful, so
the driver can allocate the necessary memory for read pixel. Of
course, if read_pixels
elects to allocate memory itself, the
description
function does not have to set any fields. However,
if reading should continue, the function should return 1 otherwise a
negative number.
The function should read from input file stream image->fpin
.
It is likely that some information obtained in this function needs to
be passed to the actual pixel reading routine. The easiest way is, of
course, to make these information static within the file, but if a GUI
system is in place, all the reading routines should try to be
reentrant. The method to avoid static variables is to use the
image->io_spec
field to keep these information. If this field
points to some dynamically allocated memory, you do not need to free
it after read_pixels
function finishes. However, if you free it
or this field points to static memory, you should set to this field to
NULL
when finished.
The following is a short example showing how this field may be utilized.
typedef struct { int bits_per_pixel; int other_stuff; } SPEC; static int description(FL_IMAGE *im) { SPEC *sp = fl_calloc(1, sizeof *sp); im->io_spec = sp; im->spec_size = sizeof *sp; sp->bits_per_pixel = read_from_file(im->fpin); return 0; } static int read_pixels(FL_IMAGE *im) { SPEC *sp = im->io_spec; int bits_per_pixel = sp->bits_per_pixel; read_file_based_on_bits_per_pixel(im->fpin); /* You don't have to free im->io_spec, but if you do remember to set it to NULL before returning */ return 0; }
read_pixels
This function reads the pixels from the file and fills one of the pixel matrix in the image structure depending on the type. If reading is successful, a non-negative number should be returned otherwise a negative number should be returned.
Upon entry, image->completed
is set to zero.
The function should not close the file.
write_image
This function takes an image structure and should write the image out
in a format it knows. Prior to calling this routine, the driver will
have already converted the image type to the type it wants. The
function should return 1 on success and a negative number otherwise.
If only reading of the image format is supported this parameter can be
set to NULL
.
The function should write to file stream image->fpout
.
By calling flimage_add_format()
the newly specified image
format is added to a "recognized image format" pool in the library.
When flimage_load()
is called the library, after
verifying that the file is readable, loops over each of the formats
and calls the identify
routine until a format is identified or
the pool exhausted. If the file is recognized as one of the supported
formats the description
routine is called to obtain the image
dimension and type. Upon its return the library allocates all memory
needed, then calls read_pixels
. If the image format pool is
exhausted before the file is recognized flimage_load()
fails.
On output, when flimage_dump()
is called, the requested
format name is used to look up the output routine from the image
format pool. Once an output routine for the requested format is found,
the library looks the image type the output is capable of writing. If
the current image type is not among the types supported by the format
the library converts image to the type needed prior to calling the
output routine write_image()
. So what
flimage_dump()
does is
int flimage_dump(FL_IMAGE *im, const char *filename, const char *formatName) { format = search_image_format_pool(formatName); if (!format) return -1; im->fpout = fopen(filename); if (im->pre_write) im->pre_write(im); convert image type if necessary(im); format->write_pixels(im); ... }
If the name of the image format supplied by
flimage_add_format()
is identical to one that is already
supported, the new routines replace those that are in the pool. This
way, the application can override the built-in supports.
For a non-trivial example of adding a new format, see file
flimage_jpeg.c. Another way of adding image formats is through
external filters that convert an unsupported format into one that is.
All you need to do is inform the library what external filter to use.
pbmplus
or netpbm
are excellent packages for this
purpose.
The library has two functions that deal with external filters
int flimage_description_via_filter(FL_IMAGE * im, char *const *cmds, const char *what, int verbose); int flimage_write_via_filter(FL_IMAGE *im, char *const *cmds, char *const formats[], int verbose);
where cmds
are a list of shell commands (filters) that convert
the format in question into one of the supported formats. Parameter
what
is for reporting purposes and parameter verbose
controls if some information and error messages should be printed.
This is mainly for debugging purposes.
Let us go through one example to show how this filter facility can be
used. In this example, we support SGI’s rgb format via the netpbm
package.
As with regular image format, we first define a function that identifies the image format:
static int IRIS_identify(FILE *fp) { char buf[2]; fread(buf, 1, 2, fp); return (buf[0] == '\001' && buf[1] == '\332') || (buf[0] == '\332' && buf[1] == '\001'); }
Then we need to define the filter(s) that can convert a RGB file into
one that’s supported. Here we use sgitopnm
, but you can use
diferent filters if available. Function
flimage_description_via_filter()
will try all the filters
specified until one of them succeeds. If none does an error code is
returned:
static int IRIS_description(FL_IMAGE *im) { static char *cmds[] = {"sgitopnm %s > %s", NULL /* sentinel, indicating end of list of filters */ }; return flimage_description_via_filter(im, cmds, "Reading RGB...", 0); }
All commands should be suitable format strings for function
sprintf()
and contain %s
twice. The first one will be
replaced by the input file name, the second by a filename which will
be supplied by the library to hold the converted image. The list
must be terminate with a NULL
element.
In the above example, sgitopnm %s > %s
specifies the external
command, sgitopnm
, and how it operates. Basically, the library
will do a sprintf(cmdbuf, cmd[i], irisfile, tmpfile)
and then
execute cmdbuf
.
There is really no need for a load function as the filter will have already invoked the correct load function when it returns. For the record of capability queries, a dummy load function is needed:
static int IRIS_load(FL_IMAGE * im) { fprintf(stderr, "We should never get here...\n"); return -1; }
Writing an image is similar:
static int IRIS_dump(FL_IMAGE *im) { static char *cmds[] = {"pnmtosgi %s > %s", NULL}; static char *cmds_rle[] = {"pnmtosgi -rle %s > %s", NULL}; static char *formats[] = {"ppm", "pgm", "pbm", NULL}; return flimage_write_via_filter(im, rle ? cmds_rle : cmds, formats, 0); }
Again, the external commands should accept two arguments. The first argument will be supplied by the library, a temporary file that holds the converted image in a format the filter understands, and the second argument will be the requested output filename.
For output, an additional argument is required. The additional
argument formats
specifies the image format accepted by the
external filter. In this case, this is the pnm format. It is important
that if the filter accepts more than one format, you should specify
the formats in decreasing generality, i.e., ppm, pgm, pbm.
With these functions in place, finally we’re ready to add iris support into the library
void add_iris(void) { flimage_add_format("SGI Iris", "iris", "rgb", FL_IMAGE_RGB|FL_IMAGE_GRAY|FL_IMAGE_MONO, IRIS_identify, IRIS_description, IRIS_load, IRIS_dump); }
After a call of add_iris()
you can now use
flimage_load()
and flimage_dump()
to read
and write SGI iris format just like any other format.
Next: Queries, Previous: Built-in support, Up: Supported Image Formats [Contents][Index]