home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / netpbma.zip / pnm / sgitopnm.c < prev    next >
C/C++ Source or Header  |  1994-03-01  |  11KB  |  448 lines

  1. /* sgitopnm.c - read an SGI image and and produce a portable anymap
  2. **
  3. ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
  4. **
  5. ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
  6. ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
  7. **
  8. ** Permission to use, copy, modify, and distribute this software and its
  9. ** documentation for any purpose and without fee is hereby granted, provided
  10. ** that the above copyright notice appear in all copies and that both that
  11. ** copyright notice and this permission notice appear in supporting
  12. ** documentation.  This software is provided "as is" without express or
  13. ** implied warranty.
  14. **
  15. ** 29Jan94: first version
  16. ** 08Feb94: minor bugfix
  17. */
  18. #include "pnm.h"
  19. #include "sgi.h"
  20. #ifndef VMS
  21. #include <unistd.h>
  22. #endif
  23.  
  24. /*#define DEBUG*/
  25.  
  26. #ifndef SEEK_SET
  27. #define SEEK_SET    0
  28. #endif
  29.  
  30.  
  31. /* entry in RLE offset table */
  32. typedef struct {
  33.     long start;     /* offset in file */
  34.     long length;    /* length of compressed scanline */
  35. } TabEntry;
  36.  
  37. typedef short       ScanElem;
  38. typedef ScanElem *  ScanLine;
  39.  
  40. /* prototypes */
  41. static unsigned char get_byte ARGS(( FILE* f ));
  42. static long get_big_long ARGS((FILE *f));
  43. static short get_big_short ARGS((FILE *f));
  44. static short get_byte_as_short ARGS((FILE *f));
  45. static void readerr ARGS((FILE *f));
  46. static void * xmalloc ARGS((int bytes));
  47. #define MALLOC(n, type)     (type *)xmalloc((n) * sizeof(type))
  48. static char * compression_name ARGS((char compr));
  49. static void       read_bytes ARGS((FILE *ifp, int n, char *buf));
  50. static Header *   read_header ARGS((FILE *ifp));
  51. static TabEntry * read_table ARGS((FILE *ifp, int tablen));
  52. static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)) ));
  53. static void       image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval));
  54. static void       rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen));
  55.  
  56. #define WORSTCOMPR(x)   (2*(x) + 2)
  57.  
  58.  
  59. static short verbose = 0;
  60.  
  61.  
  62. int
  63. main(argc, argv)
  64.     int argc;
  65.     char *argv[];
  66. {
  67.     FILE *ifp;
  68.     int argn;
  69.     char *usage = "[-verbose] [sgifile]";
  70.     TabEntry *table = NULL;
  71.     ScanLine *image;
  72.     Header *head;
  73.     long maxval;
  74.  
  75.     pnm_init(&argc, argv);
  76.  
  77.     argn = 1;
  78.     while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
  79.         if( pm_keymatch(argv[argn], "-verbose", 2) )
  80.             verbose++;
  81.         else
  82.         if( pm_keymatch(argv[argn], "-noverbose", 4) )
  83.             verbose = 0;
  84.         else
  85.             pm_usage(usage);
  86.         ++argn;
  87.     }
  88.  
  89.     if( argn < argc ) {
  90.         ifp = pm_openr( argv[argn] );
  91.         argn++;
  92.     }
  93.     else
  94.         ifp = stdin;
  95.  
  96.     if( argn != argc )
  97.         pm_usage(usage);
  98.  
  99.     head = read_header(ifp);
  100.     maxval = head->pixmax - head->pixmin;
  101.     if( maxval > PNM_MAXMAXVAL )
  102.         pm_error("pixel values too large - try reconfiguring with PGM_BIGGRAYS\n    or without PPM_PACKCOLORS");
  103.  
  104.     if( head->storage != STORAGE_VERBATIM )
  105.         table = read_table(ifp, head->ysize * head->zsize);
  106.     if( head->bpc == 1 )
  107.         image = read_channels(ifp, head, table, get_byte_as_short);
  108.     else
  109.         image = read_channels(ifp, head, table, get_big_short);
  110.  
  111.     image_to_pnm(head, image, (xelval)maxval);
  112.     pm_close(ifp);
  113.  
  114.     exit(0);
  115. }
  116.  
  117.  
  118. static Header *
  119. read_header(ifp)
  120.     FILE *ifp;
  121. {
  122.     Header *head;
  123.  
  124.     head = MALLOC(1, Header);
  125.  
  126.     head->magic     = get_big_short(ifp);
  127.     head->storage   = get_byte(ifp);
  128.     head->bpc       = get_byte(ifp);
  129.     head->dimension = get_big_short(ifp);
  130.     head->xsize     = get_big_short(ifp);
  131.     head->ysize     = get_big_short(ifp);
  132.     head->zsize     = get_big_short(ifp);
  133.     head->pixmin    = get_big_long(ifp);
  134.     head->pixmax    = get_big_long(ifp);
  135.     read_bytes(ifp, 4, head->dummy1);
  136.     read_bytes(ifp, 80, head->name);
  137.     head->colormap  = get_big_long(ifp);
  138.     read_bytes(ifp, 404, head->dummy2);
  139.  
  140.     if( head->magic != SGI_MAGIC )
  141.         pm_error("bad magic number - not an SGI image");
  142.     if( head->storage < 0 || head->storage > 1 )
  143.         pm_error("unknown compression type");
  144.     if( head->bpc < 1 || head->bpc > 2 )
  145.         pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc );
  146.     if( head->colormap != CMAP_NORMAL )
  147.         pm_error("unsupported non-normal pixel data");
  148.  
  149.     /* adjust ysize/zsize to dimension, just to be sure */
  150.     switch( head->dimension ) {
  151.         case 1:
  152.             head->ysize = 1;
  153.             break;
  154.         case 2:
  155.             head->zsize = 1;
  156.             break;
  157.         case 3:
  158.             switch( head->zsize ) {
  159.                 case 1:
  160.                     head->dimension = 2;
  161.                     break;
  162.                 case 2:
  163.                     pm_error("don\'t know how to interpret 2-channel image");
  164.                     break;
  165.                 case 3:
  166.                     break;
  167.                 default:
  168.                     pm_message("%d-channel image, using only first 3 channels", head->zsize);
  169.                     head->zsize = 3;
  170.                     break;
  171.             }
  172.             break;
  173.         default:
  174.             pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension);
  175.     }
  176.  
  177.     if( verbose ) {
  178.         pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize);
  179.         pm_message("compression: %d = %s", head->storage, compression_name(head->storage));
  180.         head->name[79] = '\0';  /* just to be safe */
  181.         pm_message("Image name: \"%s\"", head->name);
  182. #ifdef DEBUG
  183.         pm_message("bpc: %d    dimension: %d    zsize: %d", head->bpc, head->dimension, head->zsize);
  184.         pm_message("pixmin: %ld    pixmax: %ld    colormap: %ld", head->pixmin, head->pixmax, head->colormap);
  185. #endif
  186.     }
  187.  
  188.     return head;
  189. }
  190.  
  191.  
  192. static TabEntry *
  193. read_table(ifp, tablen)
  194.     FILE *ifp;
  195.     int tablen;
  196. {
  197.     TabEntry *table;
  198.     int i;
  199.  
  200.     table = MALLOC(tablen, TabEntry);
  201.  
  202. #ifdef DEBUG
  203.     pm_message("reading offset table");
  204. #endif
  205.  
  206.     for( i = 0; i < tablen; i++ )
  207.         table[i].start = get_big_long(ifp);
  208.     for( i = 0; i < tablen; i++ )
  209.         table[i].length = get_big_long(ifp);
  210.  
  211.     return table;
  212. }
  213.  
  214.  
  215.  
  216. static ScanLine *
  217. read_channels(ifp, head, table, func)
  218.     FILE *ifp;
  219.     Header *head;
  220.     TabEntry *table;
  221.     short (*func) ARGS((FILE *));
  222. {
  223.     ScanLine *image;
  224.     ScanElem *temp;
  225.     int channel, maxchannel, row, sgi_index, i;
  226.     long offset, length;
  227.  
  228. #ifdef DEBUG
  229.     pm_message("reading channels");
  230. #endif
  231.  
  232.     maxchannel = head->zsize;
  233.     image = MALLOC(head->ysize * maxchannel, ScanLine);
  234.     if( table ) temp = MALLOC(WORSTCOMPR(head->xsize), ScanElem);
  235.  
  236.     for( channel = 0; channel < maxchannel;  channel++ ) {
  237. #ifdef DEBUG
  238.         pm_message("    channel %d", channel);
  239. #endif
  240.         for( row = 0; row < head->ysize; row++ ) {
  241.             sgi_index = channel * head->ysize + row;
  242.             image[sgi_index] = MALLOC(head->xsize, ScanElem);
  243.             if( table ) {
  244.                 offset = table[sgi_index].start;
  245.                 length = table[sgi_index].length;
  246.                 if( head->bpc == 2 )
  247.                     length /= 2;   /* doc says length is in bytes, we are reading words */
  248.                 if( fseek(ifp, offset, SEEK_SET) != 0 )
  249.                     pm_error("seek error for offset %ld", offset);
  250.  
  251.                 for( i = 0; i < length; i++ )
  252.                     temp[i] = (*func)(ifp);
  253.                 rle_decompress(temp, length, image[sgi_index], head->xsize);
  254.             }
  255.             else {
  256.                 for( i = 0; i < head->xsize; i++ )
  257.                     image[sgi_index][i] = (*func)(ifp);
  258.             }
  259.         }
  260.     }
  261.  
  262.     if( table ) free(temp);
  263.     return image;
  264. }
  265.  
  266.  
  267. static void
  268. image_to_pnm(head, image, maxval)
  269.     Header *head;
  270.     ScanLine *image;
  271.     xelval maxval;
  272. {
  273.     int col, row, format;
  274.     xel *pnmrow = pnm_allocrow(head->xsize);
  275.     int sub = head->pixmin;
  276.  
  277.     if( head->zsize == 1 ) {
  278.         pm_message("writing PGM image");
  279.         format = PGM_TYPE;
  280.     }
  281.     else {
  282.         pm_message("writing PPM image");
  283.         format = PPM_TYPE;
  284.     }
  285.  
  286.     pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0);
  287.     for( row = head->ysize-1; row >= 0; row-- ) {
  288.         for( col = 0; col < head->xsize; col++ ) {
  289.             if( format == PGM_TYPE )
  290.                 PNM_ASSIGN1(pnmrow[col], image[row][col] - sub);
  291.             else {
  292.                 pixval r, g, b;
  293.                 r = image[row][col] - sub;
  294.                 g = image[head->ysize + row][col] - sub;
  295.                 b = image[2* head->ysize + row][col] - sub;
  296.                 PPM_ASSIGN(pnmrow[col], r, g, b);
  297.             }
  298.         }
  299.         pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0);
  300.     }
  301.     pnm_freerow(pnmrow);
  302. }
  303.  
  304.  
  305. static void
  306. rle_decompress(src, srcleft, dest, destleft)
  307.     ScanElem *src;
  308.     int srcleft;
  309.     ScanElem *dest;
  310.     int destleft;
  311. {
  312.     int count;
  313.     unsigned char el;
  314.  
  315.     while( srcleft ) {
  316.         el = (unsigned char)(*src++ & 0xff);
  317.         --srcleft;
  318.         count = (int)(el & 0x7f);
  319.  
  320.         if( count == 0 )
  321.             return;
  322.         if( destleft < count )
  323.             pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
  324.         destleft -= count;
  325.         if( el & 0x80 ) {
  326.             if( srcleft < count )
  327.                 pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
  328.             srcleft -= count;
  329.             while( count-- )
  330.                 *dest++ = *src++;
  331.         }
  332.         else {
  333.             if( srcleft == 0 )
  334.                 pm_error("RLE error: not enough data for replicate run");
  335.             while( count-- )
  336.                 *dest++ = *src;
  337.             ++src;
  338.             --srcleft;
  339.         }
  340.     }
  341.     pm_error("RLE error: no terminating 0-byte");
  342. }
  343.  
  344.  
  345. /* basic I/O functions, taken from ilbmtoppm.c */
  346.  
  347. static short
  348. get_big_short(ifp)
  349.     FILE *ifp;
  350. {
  351.     short s;
  352.  
  353.     if( pm_readbigshort(ifp, &s) == -1 )
  354.         readerr(ifp);
  355.  
  356.     return s;
  357. }
  358.  
  359. static long
  360. get_big_long(ifp)
  361.     FILE *ifp;
  362. {
  363.     long l;
  364.  
  365.     if( pm_readbiglong(ifp, &l) == -1 )
  366.         readerr(ifp);
  367.  
  368.     return l;
  369. }
  370.  
  371. static unsigned char
  372. get_byte(ifp)
  373.     FILE* ifp;
  374. {
  375.     int i;
  376.  
  377.     i = getc(ifp);
  378.     if( i == EOF )
  379.         readerr(ifp);
  380.  
  381.     return (unsigned char) i;
  382. }
  383.  
  384.  
  385. static void
  386. readerr(f)
  387.     FILE *f;
  388. {
  389.     if( ferror(f) )
  390.         pm_error("read error");
  391.     else
  392.         pm_error("premature EOF");
  393. }
  394.  
  395.  
  396. static void
  397. read_bytes(ifp, n, buf)
  398.     FILE *ifp;
  399.     int n;
  400.     char *buf;
  401. {
  402.     int r;
  403.  
  404.     r = fread((void *)buf, 1, n, ifp);
  405.     if( r != n )
  406.         readerr(ifp);
  407. }
  408.  
  409.  
  410. static short
  411. get_byte_as_short(ifp)
  412.     FILE *ifp;
  413. {
  414.     return (short)get_byte(ifp);
  415. }
  416.  
  417.  
  418. static void *
  419. xmalloc(bytes)
  420.     int bytes;
  421. {
  422.     void *mem;
  423.  
  424.     if( bytes == 0 )
  425.         return NULL;
  426.  
  427.     mem = malloc(bytes);
  428.     if( mem == NULL )
  429.         pm_error("out of memory allocating %d bytes", bytes);
  430.     return mem;
  431. }
  432.  
  433.  
  434. static char *
  435. compression_name(compr)
  436.     char compr;
  437. {
  438.     switch( compr ) {
  439.         case STORAGE_VERBATIM:
  440.             return "none";
  441.         case STORAGE_RLE:
  442.             return "RLE";
  443.         default:
  444.             return "unknown";
  445.     }
  446. }
  447.  
  448.