This appendix is split up into two sections, one for reading a new file format, and the other for writing a new file format. Note that you do not necessarily have to read and write a new file format. For example, xv can read PCX files, but it doesn't write them. Likewise, xv traditionally could only write PostScript files, but couldn't read them. (And technically, it still doesn't.)
For the purposes of this example, I'll be talking about the PBM/PGM/PPM code specifically. (See the file xvpbm.c for full details.)
Note: Despite the wide variety of displays and file formats xv can deal with, internally it only manipulates 8-bit colormapped images or 24-bit RGB images. If you're loading an 8-bit colormapped image, such as a GIF image, no problem. If you're loading an 8-or-fewer-bits format that doesn't have a colormap (such as an 8-bit greyscale image, or a 1-bit B/W bitmap) your Load () routine will have to generate an appropriate colormap.
Make a copy of xvpbm.c , calling it something appropriate. For the rest of this appendix, mentally replace the string 'xvpbm.c' with the name of your new file.
Edit the Makefile and/or the Imakefile so that your new module will be compiled. In the Makefile , add "xvpbm.o" to the "OBJS = ..." macro definition. In the Imakefile , add "xvpbm.o" to the end of the "OBJS1 = ..." macro definition, and "xvpbm.c" to the end of the "SRCS1 = ..." macro definition.
Edit the new module.
You'll need to #include "xv.h" , of course.
The module should have one externally callable function that does the work of loading up the file. The function is called with two arguments, a filename to load, and a pointer to a PICINFO structure, like so:
/*******************************************/ int LoadPBM(fname, pinfo) char *fname; PICINFO *pinfo; /*******************************************/
The file name will be the complete file name (absolute, not relative to any directory). Note: if xv is reading from stdin, don't worry about it. stdin is always automatically copied to a temporary file. The same goes for pipes and compressed files. Your Load() routine is guaranteed that it will be reading from a real file that appears to be in your file format, not a stream. This lets you use routines such as fseek() , and such.
The pinfo argument is a pointer to a PICINFO structure. This structure is used to hold the complete set of information associated with the image that will be loaded. When your Load() routine is called, the fields in this structure will all be zeroed-out. It is your function's responsibility to load up the structure appropriately, and completely. The structure is defined as:
/* info structure filled in by the LoadXXX() image reading routines */ typedef struct {byte *pic; /* image data */ int w, h; /* size */ int type; /* PIC8 or PIC24 */ byte r[256],g[256],b[256]; /* colormap, if PIC8 */ int normw, normh; /* normal size of image. normally == w,h except when doing quick load for icons*/ int frmType; /* def. Format type to save in */ int colType; /* def. Color type to save in */ char fullInfo[128]; /* Format: field in info box */ char shrtInfo[128]; /* short format info */ char *comment; /* comment text */ int numpages; /* # of page files, if >1 */ char pagebname[64]; /* basename of page files */ } PICINFO;
The Load() function should return '1' on success, '0' on failure.
All other information is communicated using the PICINFO structure. The fields should be setup as follows:
Color PPM, raw format (12345 bytes)
Non-fatal errors in your Load() routine should be handled by calling the function SetISTR(ISTR_WARNING, "%s: %s", bname, err), and returning a zero value. Where bname is the 'simple' name of your file (which can be obtained using BaseName() function in xvmisc.c ), and err should be an appropriate error string.
Non-fatal errors are considered to be errors that only affect the success of loading this one image, and not the continued success of running xv. For instance, "can't open file", "premature EOF", "garbage in file", etc. are all non-fatal errors. On the other hand, not being able to allocate memory (unsuccessful returns from malloc() ) is considered a fatal error, as it means xv is likely to run out of memory in the near future anyhow.
Fatal errors should be handled by calling FatalError(error_string) . This function prints the string to stderr , and exits the program with an error code.
Warnings (such as 'truncated file') that may need to be noted can be handled by calling SetISTR() as shown above, but continuing to return '1' from the Load() routine, signifying success.
Also, if your load routine fails for any reason, it is your responsibility to free() any pointers allocated (such as the pic field and the comment field, and return NULL in these fields). Otherwise, there'll be memory leaks whenever an image load fails.
Once you have written a Load() routine, you'll want to hook it up to the xv source.
Edit xv.h and add a function prototype for any global functions you've written (presumably just LoadPBM() in this case). Follow the style used for the other Load*() function declarations.
Find the RFT_* definitions and tack one on the end for your format (e.g., RFT_PBM ). This is a list of values that ' ReadFileType() ' can return. We'll be working on that soon enough.
Edit xv.c :
The first thing you have to do is create a 'generic' icon for your file format. Copy one of the existing ones (such as ' bits/br_pbm.xbm ') to get the size and the general 'look' correct.
#include this icon at the top of the file.
Add an appropriately-named BF_* definition to the end of the list, and increase BF_MAX appropriately.
Have the icon pixmap created in the CreateBrowse() function, by doing something like this:
bfIcons[BF_PBM] = MakePix1(br->win, br_pbm_bits, br_pbm_width, br_pbm_height);
Hook your format into the scanFile() function. Find the following code:
switch (filetype) { case RFT_GIF: bf->ftype = BF_GIF; break; case RFT_PM: bf->ftype = BF_PM; break;
etc...
And add a case for your format. (To map RFT_* values into their corresponding BF_* values.)
Hook your format into the genIcon() function. Find the following code:
sprintf(str, "%dx%d ", pinfo.w, pinfo.h); switch (filetype) { case RFT_GIF: if (strstr(pinfo.shrtInfo, "GIF89")) strcat(str,"GIF89 file"); else strcat(str,"GIF87 file"); break; case RFT_PM: strcat(str,"PM file"); break
And add a case for your format. This generates an appropriate info string that gets put in the icon files maintained by the visual schnauzer (and displayed whenever you click on an icon in the schnauzer window).
That should do it. Consult the files xv.h, xv.c, xvbrowse.c, and xvpbm.c for any further specifics.