home *** CD-ROM | disk | FTP | other *** search
-
- Appendix G: Adding Other
- Image Formats to XV
-
-
- 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 write PostScript files, but it can't read them.
-
- The following instructions were written as I added PBM/PGM/PPM
- capability to the program, so they're likely to be fairly accurate.
- For example purposes, I'll be talking about the PBM/PGM/PPM code
- specifically. (See the file xvpbm.c for full details.)
-
-
- Section G.1: Writing Code for Reading a New File Format
-
- Note: Despite the wide variety of displays and file formats xv can
- deal with, internally it only manipulates 8-bit colormapped images.
- If you're loading an 8-bit colormapped image, such as a GIF image,
- no problem. If you're loading an 8-or-less-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. And if you're loading a 24 bit RGB file,
- you'll have to compress it down to 8 bits by calling Conv24to8().
-
- Make a copy of xvpm.c, calling it something appropriate. I'm
- adding PBM capabilities, so I think xvpbm.c is a fine file name.
-
- 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.
-
- 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 and the number of colors available on this
- display, like so:
-
- /*******************************************/
- int LoadPBM(fname,nc)
- char *fname; int nc;
- /*******************************************/
-
- 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.
- Your Load() routine is guaranteed that it will be reading from a real
- file, not a stream. This lets you use routines such as fseek(), and
- such.
-
- The number of colors argument is either going to be 2^n, where n is the
- number of bitplanes on your display, or 'ncols', if specified on
- the command line. In either case, this number will only come into
- play if you have to do a 24-to-8 bit conversion. More on that later.
-
- The Load() function returns '0' on success, non-zero on failure.
-
- This function is expected to load up the following global variables:
-
- byte *pic;
- this is a wide*high array of bytes, one byte per pixel,
- starting at the top-left corner, and proceeding in scan-line order.
- There is no padding of any sort at the end of a scan line. The Load()
- function is expected to malloc() the memory for this image.
-
- int pWIDE, pHIGH;
- these variables specify the size of the image that has been
- loaded, in pixels.
-
- byte r[256], g[256], b[256];
- the desired colormap. As specified above, 'pic' is an
- 8-bits per pixel image. A given pixel value in pic maps to an RGB
- color through these arrays. In each array, a value of 0 means
- 'off', and a value of 255 means 'fully on'. Note: the
- arrays do not have to be completely filled. Only RGB entries for
- pixels that actually exist in the 'pic' need to be set. For
- example, if the pic is known to be a B/W bitmap with pixel values of 0
- and 1, you'd only have to set entries '0' and '1' of
- the r,g,b arrays.
-
- char *formatStr;
- a short character string describing the format and size of the
- image. For example, "320x200 PBM".
-
- The function should also call 'SetISTR(ISTR_FORMAT, fmt, args)'
- to set the "Format:" string in the xv info window. It should
- call the function as soon as possible (i.e., once it knows the format
- of the picture, but before it has tried to load/parse all of the image
- data.) Note that the "Format:" string in the xv info window
- should be set to a somewhat more verbose version of formatStr. See
- the source code for examples.
-
- The Load() function should also call 'SetDirRButt(F_FORMAT, ...)' to
- set the default format (in the xv save window) to the format of the
- loaded file. This, of course, is only relevant if you will also be
- able write files in your new format. If you aren't planning to have a
- Write() function for this format, you won't have a listing for this
- format in the xv save window.
-
-
- Section G.1.1: Error Handling
-
- Non-fatal errors in your Load() routine should be handled by calling
- SetISTR(ISTR_WARNING, fmt, args...), and returning a non-zero value.
- The error string will be displayed in the xv controls and xv info
- windows.
-
- 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.
-
- 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.
-
-
- Section G.1.2: Loading 24-bit RGB Formats
-
- If (as in the case of PPM files) your file format has 24 bits of
- information per pixel, you'll have to get it down to 8 bits and a
- colormap for xv to make any use of it. Conveniently, a function
- 'Conv24to8(pic24, w, h, nc)' is provided, so don't worry
- about it.
-
- To use it, you'll have to load your picture into a pWIDE*pHIGH*3
- array of bytes. (You'll be expected to malloc() this array.) This
- array begins at the top left corner, and proceeds in scan-line order.
- The first byte of the array is the red component of pixel0, followed
- by the green component of pixel0, followed by the blue component of
- pixel0, followed by the red component of pixel1, etc... There is no
- padding of any kind.
-
- Once you've got this image loaded, call Conv24to8() with a pointer
- to the 24bit image, the width and height of the image, and the number
- of colors to 'shoot for' in the resulting 8-bit picture. This
- is the same parameter that was passed in to your Load() routine, so
- just pass it along.
-
- If successful, Conv24to8() will return '0'. It will have
- created and generated the pic array, and filled in the pWIDE and pHIGH
- variables, and the r[], g[], b[] arrays. You should now free() the
- memory associated with the 24-bit version of your image and leave your
- Load() function.
-
- Read the source in xvpbm.c for further info on writing the Load()
- routine.
-
- Once you have a working Load() routine, you'll want to hook it up
- to the xv source.
-
- Edit xv.h and add two function prototypes for any global functions
- you've written (presumably just LoadPBM() in this case). You'll
- need to add a full function prototype (with parameter types) in the
- #ifdef __STDC__ section (near the bottom), and a function reference
- (just the return type) in the #else /* non-ANSI */ section at the
- bottom.
-
- Edit xv.c:
-
- * Add a filetype #define near the top. Find the following section:
-
- /* file types that can be read */
- #define UNKNOWN 0
- #define GIF 1
- #define PM 2
-
- and add one more to the list, in this case: "#define PBM 3"
-
- Note: I only added one filetype to this list, despite the fact
- that I'm really adding three (or six, really) different file
- formats to the program (PBM, PGM, and PPM, in 'raw' and
- 'ascii' variations). This is because all of these file formats
- are related, and are handled by the same Load() function.
-
- * Now tell the openPic() routine about your Load() routine:
-
- find the following (in openPic()):
-
- filetype = UNKNOWN;
- if (strncmp(magicno,"GIF87a",6)==0 ||
- strncmp(magicno,"GIF89a",6)==0) filetype = GIF;
- else if (strncmp(magicno,"VIEW",4)==0 ||
- strncmp(magicno,"WEIV",4)==0) filetype = PM;
-
- Add another 'else' case that will set filetype if the
- file appears to be in your format:
-
- else if (magicno[0] == 'P' && magicno[1]>='1' &&
- magicno[1]<='6') filetype = PBM;
-
-
- And add another case to the switch statement (a few lines further down)
-
- switch (filetype) {
- case GIF: i = LoadGIF(filename,ncols); break;
- case PM: i = LoadPM(filename,ncols); break;
- }
-
- add:
-
- case PBM: i = LoadPBM(filename,ncols); break;
-
-
- That should do it. Consult the files xvpm.c or xvpbm.c for further
- information. Remember: do as I mean, not as I say.
-
-
- Section G.2: Adding Code for Writing a New File Format
-
- Note: Despite the wide variety of displays and file formats xv deals
- with, internally it only manipulates 8-bit colormapped images. As a
- result, writing out 24-bit RGB images is a horrible waste (unless your
- format does some clever file compression), and is to be avoided if
- your file format can handle colortable images.
-
- If you haven't already done so (if/when you created the Load()
- function): * Make a copy of xvpm.c, calling it something
- appropriate. I'm adding PBM capabilities, so I think xvpbm.c is a
- fine file name.
-
- * Edit the Makefile and/or the Imakefile so that your new module
- will be compiled. Add 'xvpbm.o' to the OBJS macro in the
- Makefile, and to the OBJS1 macro in the Imakefile.
-
- 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 writing the file. The function is called with a virtual
- plethora of arguments. At a minimum, you'll be given a FILE * to
- an already open-for-writing stream, a pointer to an 8-bits per pixel
- image, the width and height of that image, pointers to 256-entry red,
- green, and blue colormaps, the number of colors actually used in the
- colormaps, and the 'color style' from the xv save window.
-
- You may pass more parameters, since you're going to be adding the call
- to this function later on. For example, in my PBM code, I pass one
- more parameter, 'raw' (whether to save the file as 'raw' or 'ascii')
- to handle two very similar formats. (Rather than having to write
- WritePBMRaw() and WritePBMAscii() functions.)
-
- Your function definition should look something like this:
-
- /*******************************************/
- int WritePBM(fp,pic,w,h,rmap,gmap,bmap,
- numcols,colorstyle,raw)
- FILE *fp;
- byte *pic;
- int w,h;
- byte *rmap, *gmap, *bmap;
- int numcols, colorstyle, raw;
- /*******************************************/
-
- Write the function as you deem appropriate.
-
- Some Notes:
- * your function should return '0' if successful, non-zero if not
- * don't close 'fp'
- * pic is a w*h byte array, starting at top-left, and proceeding
- in normal scan-line order
- * colorstyle can (currently) take on three values:
- F_FULLCOLOR: This could mean either 24-bit RGB, or an 8-bit colormap
- or any other color format. r[pix],g[pix],b[pix] specify the color of
- pixel 'pix'.
-
- F_GREYSCALE: preferably 8 bits. Two caveats: you must use the
- colormap to determine what grey value to write. For all you know,
- pixel value '0' in pic could map to white, '1' could map to black, and
- '2' could map to a half-intensity grey. You cannot make the
- assumption that pixel values of '0' are black, and pixel values of
- '255' are white.
-
- The other note: unless the original picture was a greyscale,
- (which shouldn't be tested for), the colormap is going to have actual
- colors in it. You'll want to map RGB colors into greyscale values
- using 'the standard formula' (roughly .33R + .5G +.17B). The
- following code shows how to quickly write a raw greyscale image:
-
- if (colorstyle == F_GREYSCALE) {
- byte rgb[256];
- for (i=0; i<numcols; i++)
- rgb[i] = MONO(rmap[i],gmap[i],bmap[i]);
-
- for (i=0, p=pic; i<w*h; i++, p++)
- putc(rgb[*p],fp);
- }
-
- F_BWDITHER: The stippling algorithm will have already been performed
- by the time your function is called. pic will be an image consisting
- of the pixel values '1' (white) and '0' (white). pic will still be
- organized in the same way (i.e., one byte per pixel).
-
- Note: for F_FULLCOLOR or F_GREYSCALE images, you will be guaranteed
- that all pixel values in pic will be in the range [0 - numcols-1]
- (inclusive).
-
-
- That done, edit 'xv.h' and add a pair of function declarations for
- your new function (one full ANSI-style prototype, and one that just
- declares the return type). Copy the declarations for 'WritePM()'.
-
- Also find the block:
- #define F_GIF 0
- #define F_PM 1
- and add another line (or two, in this case)
- #define F_PBMRAW 2
- #define F_PBMASCII 3
-
- These numbers must be contiguous, as they are used as indices into the
- formatRB array.
-
- Edit 'xvdir.c'. This is the module that controls the xv save window.
-
- Add another format type to the 'formatRB' button list:
-
- In the function 'CreateDirW()', find the block that (starts like):
-
- formatRB = RBCreate(NULL,dirW,26,y,"GIF",infofg,infobg);
- RBCreate(formatRB,dirW,26,y+18,"PM",infofg,infobg);
-
- copy the last 'RBCreate' call in the list, add '18' to the 'y+**'
- argument, and stick in an appropriate format type name. In this case,
- I'm adding two formats (PBM raw and PBM ascii) so I'll add these two
- lines:
-
- RBCreate(formatRB, dirW, 26, y+36,
- "PBM (raw)", infofg, infobg);
- RBCreate(formatRB, dirW, 26, y+54,
- "PBM (ascii)", infofg, infobg);
-
- Note: The RBCreate() calls must be done in the same order as the
- F_GIF, F_PM, etc. macros were defined in xv.h.
-
-
- In the function DoSave(), find the following block:
- switch (fmt) {
- case F_GIF:
- rv = WriteGIF(fp,thepic,w, h, r, g, b,nc,col); break;
- case F_PM:
- rv = WritePM (fp,thepic,w, h, r, g, b,nc,col); break;
- }
-
- and add cases for your function(s), like so:
-
- case F_PBMRAW:
- rv = WritePBM(fp,thepic,w, h, r, g, b,nc,col,1); break;
- case F_PBMASCII:
- rv = WritePBM(fp,thepic,w, h, r, g, b,nc,col,0); break;
-
- That should do it!
-
-
- Section G.2.1: Writing Complex Formats
-
- If your format requires some additional information to specify how the
- file should be saved (such as the 'quality' setting in JPEG, or
- position/size parameters in PostScript), then your task is somewhat
- more difficult. You'll have to create some sort of pop-up dialog box
- to get the additional information that you want.
-
- This is not recommended for anyone who doesn't understand Xlib programming.
-
- The more adventurous types who wish to pursue this should take a look
- at the xvjpeg.c code, which implements an extremely simple pop-up
- dialog. A considerably more complicated dialog box is implemented in
- xvps.c. In addition to writing a module like these for your format,
- you'll also have to add the appropriate hooks to the DoSave() function
- (in xvdir.c) and the HandleEvent() function (in xvevent.c). 'grep PS
- *.c' will be helpful in finding places where the xvps.c module is
- called.
-
-