Next: , Previous: , Up: Supported Image Formats   [Contents][Index]


37.5.2 Adding New Formats

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: , Previous: , Up: Supported Image Formats   [Contents][Index]