home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / xv221src / docs / xv.app < prev    next >
Encoding:
Text File  |  1992-01-14  |  14.8 KB  |  382 lines

  1.  
  2. Appendix G:    Adding Other
  3.     Image Formats to XV
  4.  
  5.  
  6. This appendix is split up into two sections, one for reading a new
  7. file format, and the other for writing a new file format.  Note that
  8. you do not necessarily have to read and write a new file format.  For
  9. example, xv can write PostScript files, but it can't read them.
  10.  
  11. The following instructions were written as I added PBM/PGM/PPM
  12. capability to the program, so they're likely to be fairly accurate.
  13. For example purposes, I'll be talking about the PBM/PGM/PPM code
  14. specifically.  (See the file xvpbm.c for full details.)
  15.  
  16.  
  17. Section G.1:  Writing Code for Reading a New File Format
  18.  
  19. Note: Despite the wide variety of displays and file formats xv can
  20. deal with, internally it only manipulates 8-bit colormapped images.
  21. If you're loading an 8-bit colormapped image, such as a GIF image,
  22. no problem.  If you're loading an 8-or-less-bits format that
  23. doesn't have a colormap (such as an 8-bit greyscale image, or a
  24. 1-bit B/W bitmap) your Load() routine will have to generate an
  25. appropriate colormap.  And if you're loading a 24 bit RGB file,
  26. you'll have to compress it down to 8 bits by calling Conv24to8().
  27.  
  28. Make a copy of xvpm.c, calling it something appropriate.  I'm
  29. adding PBM capabilities, so I think xvpbm.c is a fine file name.
  30.  
  31. Edit the Makefile and/or the Imakefile so that your new module will be
  32. compiled.  In the Makefile, add "xvpbm.o" to the "OBJS = ..." macro
  33. definition.  In the Imakefile, add "xvpbm.o" to the end of the "OBJS1
  34. = ..." macro definition.
  35.  
  36. Edit the new module.
  37.  
  38. You'll need to #include "xv.h", of course.
  39.  
  40. The module should have one externally callable function that does the
  41. work of loading up the file.  The function is called with two
  42. arguments, a filename and the number of colors available on this
  43. display, like so:
  44.  
  45. /*******************************************/
  46. int LoadPBM(fname,nc)
  47.     char *fname;   int nc;
  48. /*******************************************/
  49.  
  50. The file name will be the complete file name (absolute, not relative
  51. to any directory).  Note: if xv is reading from stdin, don't worry
  52. about it.  stdin is always automatically copied to a temporary file.
  53. Your Load() routine is guaranteed that it will be reading from a real
  54. file, not a stream.  This lets you use routines such as fseek(), and
  55. such.
  56.  
  57. The number of colors argument is either going to be 2^n, where n is the
  58. number of bitplanes on your display, or 'ncols', if specified on
  59. the command line.  In either case, this number will only come into
  60. play if you have to do a 24-to-8 bit conversion.  More on that later.
  61.  
  62. The Load() function returns '0' on success, non-zero on failure.
  63.  
  64. This function is expected to load up the following global variables:
  65.  
  66. byte *pic;
  67.     this is a wide*high array of bytes, one byte per pixel,
  68. starting at the top-left corner, and proceeding in scan-line order.
  69. There is no padding of any sort at the end of a scan line.  The Load()
  70. function is expected to malloc() the memory for this image.
  71.  
  72. int pWIDE, pHIGH;
  73.     these variables specify the size of the image that has been
  74. loaded, in pixels.
  75.  
  76. byte r[256], g[256], b[256];
  77.     the desired colormap.  As specified above, 'pic' is an
  78. 8-bits per pixel image.  A given pixel value in pic maps to an RGB
  79. color through these arrays.  In each array, a value of 0 means
  80. 'off', and a value of 255 means 'fully on'.  Note: the
  81. arrays do not have to be completely filled.  Only RGB entries for
  82. pixels that actually exist in the 'pic' need to be set.  For
  83. example, if the pic is known to be a B/W bitmap with pixel values of 0
  84. and 1, you'd only have to set entries '0' and '1' of
  85. the r,g,b arrays.
  86.  
  87. char *formatStr;
  88.     a short character string describing the format and size of the
  89. image.  For example, "320x200 PBM".
  90.  
  91. The function should also call 'SetISTR(ISTR_FORMAT, fmt, args)'
  92. to set the "Format:" string in the xv info window.  It should
  93. call the function as soon as possible (i.e., once it knows the format
  94. of the picture, but before it has tried to load/parse all of the image
  95. data.)  Note that the "Format:" string in the xv info window
  96. should be set to a somewhat more verbose version of formatStr.  See
  97. the source code for examples.
  98.  
  99. The Load() function should also call 'SetDirRButt(F_FORMAT, ...)' to
  100. set the default format (in the xv save window) to the format of the
  101. loaded file.  This, of course, is only relevant if you will also be
  102. able write files in your new format.  If you aren't planning to have a
  103. Write() function for this format, you won't have a listing for this
  104. format in the xv save window.
  105.  
  106.  
  107. Section G.1.1:  Error Handling
  108.  
  109. Non-fatal errors in your Load() routine should be handled by calling
  110. SetISTR(ISTR_WARNING, fmt, args...), and returning a non-zero value.
  111. The error string will be displayed in the xv controls and xv info
  112. windows.
  113.  
  114. Non-fatal errors are considered to be errors that only affect the
  115. success of loading this one image, and not the continued success of
  116. running xv.  For instance, "can't open file", "premature EOF",
  117. "garbage in file", etc. are all non-fatal errors.  On the other hand,
  118. not being able to allocate memory (unsuccessful returns from malloc())
  119. is considered a fatal error.
  120.  
  121. Fatal errors should be handled by calling 'FatalError(error_string)'.
  122. This function prints the string to stderr, and exits the program with
  123. an error code.
  124.  
  125.  
  126. Section G.1.2:  Loading 24-bit RGB Formats
  127.  
  128. If (as in the case of PPM files) your file format has 24 bits of
  129. information per pixel, you'll have to get it down to 8 bits and a
  130. colormap for xv to make any use of it.  Conveniently, a function
  131. 'Conv24to8(pic24, w, h, nc)' is provided, so don't worry
  132. about it.
  133.  
  134. To use it, you'll have to load your picture into a pWIDE*pHIGH*3
  135. array of bytes.  (You'll be expected to malloc() this array.)  This
  136. array begins at the top left corner, and proceeds in scan-line order.
  137. The first byte of the array is the red component of pixel0, followed
  138. by the green component of pixel0, followed by the blue component of
  139. pixel0, followed by the red component of pixel1, etc... There is no
  140. padding of any kind.
  141.  
  142. Once you've got this image loaded, call Conv24to8() with a pointer
  143. to the 24bit image, the width and height of the image, and the number
  144. of colors to 'shoot for' in the resulting 8-bit picture.  This
  145. is the same parameter that was passed in to your Load() routine, so
  146. just pass it along.
  147.  
  148. If successful, Conv24to8() will return '0'.  It will have
  149. created and generated the pic array, and filled in the pWIDE and pHIGH
  150. variables, and the r[], g[], b[] arrays.  You should now free() the
  151. memory associated with the 24-bit version of your image and leave your
  152. Load() function.
  153.  
  154. Read the source in xvpbm.c for further info on writing the Load()
  155. routine.
  156.  
  157. Once you have a working Load() routine, you'll want to hook it up
  158. to the xv source.
  159.  
  160. Edit xv.h and add two function prototypes for any global functions
  161. you've written (presumably just LoadPBM() in this case).  You'll
  162. need to add a full function prototype (with parameter types) in the
  163. #ifdef __STDC__ section (near the bottom), and a function reference
  164. (just the return type) in the #else /* non-ANSI */ section at the
  165. bottom.
  166.  
  167. Edit xv.c:
  168.  
  169. *    Add a filetype #define near the top.  Find the following section:
  170.  
  171.         /* file types that can be read */
  172.         #define UNKNOWN 0
  173.         #define GIF     1
  174.         #define PM      2
  175.  
  176.     and add one more to the list, in this case: "#define PBM 3"
  177.  
  178.     Note: I only added one filetype to this list, despite the fact
  179. that I'm really adding three (or six, really) different file
  180. formats to the program (PBM, PGM, and PPM, in 'raw' and
  181. 'ascii' variations).  This is because all of these file formats
  182. are related, and are handled by the same Load() function.
  183.  
  184. *    Now tell the openPic() routine about your Load() routine:
  185.  
  186.     find the following (in openPic()):
  187.  
  188.         filetype = UNKNOWN;
  189.         if (strncmp(magicno,"GIF87a",6)==0 ||
  190.             strncmp(magicno,"GIF89a",6)==0) filetype = GIF;
  191.         else if (strncmp(magicno,"VIEW",4)==0 ||
  192.                  strncmp(magicno,"WEIV",4)==0) filetype = PM;
  193.  
  194.     Add another 'else' case that will set filetype if the
  195. file appears to be in your format:
  196.  
  197.         else if (magicno[0] == 'P' && magicno[1]>='1' &&
  198.                  magicno[1]<='6') filetype = PBM;
  199.  
  200.  
  201.     And add another case to the switch statement (a few lines further down)
  202.  
  203.         switch (filetype) {
  204.         case GIF: i = LoadGIF(filename,ncols);  break;
  205.         case PM:  i = LoadPM(filename,ncols);   break;
  206.         }
  207.  
  208.     add:
  209.  
  210.         case PBM: i = LoadPBM(filename,ncols); break;
  211.  
  212.  
  213. That should do it.  Consult the files xvpm.c or xvpbm.c for further
  214. information.  Remember: do as I mean, not as I say.
  215.  
  216.  
  217. Section G.2:  Adding Code for Writing a New File Format
  218.  
  219. Note: Despite the wide variety of displays and file formats xv deals
  220. with, internally it only manipulates 8-bit colormapped images.  As a
  221. result, writing out 24-bit RGB images is a horrible waste (unless your
  222. format does some clever file compression), and is to be avoided if
  223. your file format can handle colortable images.
  224.  
  225. If you haven't already done so (if/when you created the Load()
  226. function): *    Make a copy of xvpm.c, calling it something
  227. appropriate.  I'm adding PBM capabilities, so I think xvpbm.c is a
  228. fine file name.
  229.  
  230. *    Edit the Makefile and/or the Imakefile so that your new module
  231. will be compiled.  Add 'xvpbm.o' to the OBJS macro in the
  232. Makefile, and to the OBJS1 macro in the Imakefile.
  233.  
  234. Edit the new module.
  235.  
  236. You'll need to #include "xv.h", of course.
  237.  
  238. The module should have one externally callable function that does the
  239. work of writing the file.  The function is called with a virtual
  240. plethora of arguments.  At a minimum, you'll be given a FILE * to
  241. an already open-for-writing stream, a pointer to an 8-bits per pixel
  242. image, the width and height of that image, pointers to 256-entry red,
  243. green, and blue colormaps, the number of colors actually used in the
  244. colormaps, and the 'color style' from the xv save window.
  245.  
  246. You may pass more parameters, since you're going to be adding the call
  247. to this function later on.  For example, in my PBM code, I pass one
  248. more parameter, 'raw' (whether to save the file as 'raw' or 'ascii')
  249. to handle two very similar formats.  (Rather than having to write
  250. WritePBMRaw() and WritePBMAscii() functions.)
  251.  
  252. Your function definition should look something like this:
  253.  
  254. /*******************************************/
  255. int WritePBM(fp,pic,w,h,rmap,gmap,bmap,
  256.              numcols,colorstyle,raw)
  257.     FILE *fp;
  258.     byte *pic;
  259.     int   w,h;
  260.     byte *rmap, *gmap, *bmap;
  261.     int   numcols, colorstyle, raw;
  262. /*******************************************/
  263.  
  264. Write the function as you deem appropriate.
  265.  
  266. Some Notes:
  267. *    your function should return '0' if successful, non-zero if not
  268. *    don't close 'fp'
  269. *    pic is a w*h byte array, starting at top-left, and proceeding
  270.     in normal scan-line order
  271. *    colorstyle can (currently) take on three values:
  272. F_FULLCOLOR: This could mean either 24-bit RGB, or an 8-bit colormap
  273. or any other color format.  r[pix],g[pix],b[pix] specify the color of
  274. pixel 'pix'.
  275.  
  276. F_GREYSCALE: preferably 8 bits.  Two caveats: you must use the
  277. colormap to determine what grey value to write.  For all you know,
  278. pixel value '0' in pic could map to white, '1' could map to black, and
  279. '2' could map to a half-intensity grey.  You cannot make the
  280. assumption that pixel values of '0' are black, and pixel values of
  281. '255' are white.
  282.  
  283.     The other note: unless the original picture was a greyscale,
  284. (which shouldn't be tested for), the colormap is going to have actual
  285. colors in it.  You'll want to map RGB colors into greyscale values
  286. using 'the standard formula' (roughly .33R + .5G +.17B).  The
  287. following code shows how to quickly write a raw greyscale image:
  288.  
  289.     if (colorstyle == F_GREYSCALE) {
  290.       byte rgb[256];
  291.       for (i=0; i<numcols; i++) 
  292.         rgb[i] = MONO(rmap[i],gmap[i],bmap[i]);
  293.  
  294.       for (i=0, p=pic; i<w*h; i++, p++)
  295.         putc(rgb[*p],fp);
  296.     }
  297.  
  298. F_BWDITHER: The stippling algorithm will have already been performed
  299. by the time your function is called.  pic will be an image consisting
  300. of the pixel values '1' (white) and '0' (white).  pic will still be
  301. organized in the same way (i.e., one byte per pixel).
  302.  
  303. Note: for F_FULLCOLOR or F_GREYSCALE images, you will be guaranteed
  304. that all pixel values in pic will be in the range [0 - numcols-1]
  305. (inclusive).
  306.  
  307.  
  308. That done, edit 'xv.h' and add a pair of function declarations for
  309. your new function (one full ANSI-style prototype, and one that just
  310. declares the return type).  Copy the declarations for 'WritePM()'.
  311.  
  312. Also find the block:
  313. #define F_GIF      0
  314. #define F_PM       1
  315. and add another line (or two, in this case)
  316. #define F_PBMRAW   2
  317. #define F_PBMASCII 3
  318.  
  319. These numbers must be contiguous, as they are used as indices into the
  320. formatRB array.
  321.  
  322. Edit 'xvdir.c'.  This is the module that controls the xv save window.
  323.  
  324. Add another format type to the 'formatRB' button list:
  325.  
  326. In the function 'CreateDirW()', find the block that (starts like):
  327.  
  328.     formatRB = RBCreate(NULL,dirW,26,y,"GIF",infofg,infobg);
  329.     RBCreate(formatRB,dirW,26,y+18,"PM",infofg,infobg);
  330.  
  331. copy the last 'RBCreate' call in the list, add '18' to the 'y+**'
  332. argument, and stick in an appropriate format type name.  In this case,
  333. I'm adding two formats (PBM raw and PBM ascii) so I'll add these two
  334. lines:
  335.  
  336.     RBCreate(formatRB, dirW, 26, y+36,
  337.              "PBM (raw)", infofg, infobg);
  338.     RBCreate(formatRB, dirW, 26, y+54, 
  339.              "PBM (ascii)", infofg, infobg);
  340.  
  341. Note: The RBCreate() calls must be done in the same order as the
  342. F_GIF, F_PM, etc. macros were defined in xv.h.
  343.  
  344.  
  345. In the function DoSave(), find the following block:
  346.     switch (fmt) {
  347.     case F_GIF:
  348.       rv = WriteGIF(fp,thepic,w, h, r, g, b,nc,col); break;
  349.     case F_PM:
  350.       rv = WritePM (fp,thepic,w, h, r, g, b,nc,col); break;
  351.     }
  352.  
  353. and add cases for your function(s), like so:
  354.  
  355.     case F_PBMRAW:
  356.       rv = WritePBM(fp,thepic,w, h, r, g, b,nc,col,1); break;
  357.     case F_PBMASCII:
  358.       rv = WritePBM(fp,thepic,w, h, r, g, b,nc,col,0); break;
  359.  
  360. That should do it!
  361.  
  362.  
  363. Section G.2.1:  Writing Complex Formats
  364.  
  365. If your format requires some additional information to specify how the
  366. file should be saved (such as the 'quality' setting in JPEG, or
  367. position/size parameters in PostScript), then your task is somewhat
  368. more difficult.  You'll have to create some sort of pop-up dialog box
  369. to get the additional information that you want.
  370.  
  371. This is not recommended for anyone who doesn't understand Xlib programming.
  372.  
  373. The more adventurous types who wish to pursue this should take a look
  374. at the xvjpeg.c code, which implements an extremely simple pop-up
  375. dialog.  A considerably more complicated dialog box is implemented in
  376. xvps.c.  In addition to writing a module like these for your format,
  377. you'll also have to add the appropriate hooks to the DoSave() function
  378. (in xvdir.c) and the HandleEvent() function (in xvevent.c).  'grep PS
  379. *.c' will be helpful in finding places where the xvps.c module is
  380. called.
  381.  
  382.