about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--converter/other/sgitopnm.c88
-rw-r--r--doc/HISTORY14
2 files changed, 63 insertions, 39 deletions
diff --git a/converter/other/sgitopnm.c b/converter/other/sgitopnm.c
index 2adb76a8..008d5376 100644
--- a/converter/other/sgitopnm.c
+++ b/converter/other/sgitopnm.c
@@ -5,6 +5,11 @@
 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
 **
+** The definitive document describing the SGI image file format,
+** SGI Image File Format Version 1.00 is available from
+** ftp://ftp.sgi.com/graphics/grafica/sgiimage.html
+**
+**
 ** Permission to use, copy, modify, and distribute this software and its
 ** documentation for any purpose and without fee is hereby granted, provided
 ** that the above copyright notice appear in all copies and that both that
@@ -12,12 +17,9 @@
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
 **
-** 29Jan94: first version
-** 08Feb94: minor bugfix
-** 29Jul00: added -channel option (smar@reptiles.org)
-** 19Oct10: added checks for artihmetic overflows
-**          fixed problem with -channel on verbatim sgi images (afu)
 */
+
+
 #include <unistd.h>
 #include <limits.h>
 #include "pm_c_util.h"
@@ -28,12 +30,12 @@
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *  inputFileName;  /* '-' if stdin */
-    unsigned int  verbose;
+    const char * inputFileName;  /* '-' if stdin */
+    unsigned int verbose;
     unsigned int channelSpec;
     unsigned int channel;
 };
@@ -42,10 +44,10 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
+   and argv.  Return the information in the options as *cmdlineP.
 
    If command line is internally inconsistent (invalid options, etc.),
    issue error message to stderr and abort program.
@@ -53,7 +55,7 @@ parseCommandLine(int argc, const char ** argv,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -68,7 +70,7 @@ parseCommandLine(int argc, const char ** argv,
             &cmdlineP->channelSpec,            0);
     OPTENT3(0, "verbose",             OPT_FLAG,      NULL,
             &cmdlineP->verbose,       0);
-    OPTENT3(0, "noverbose",             OPT_FLAG,      NULL,
+    OPTENT3(0, "noverbose",           OPT_FLAG,      NULL,
             NULL,       0);  /* backward compatibility */
 
     opt.opt_table = option_def;
@@ -78,6 +80,8 @@ parseCommandLine(int argc, const char ** argv,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (argc-1 < 1)
         cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
@@ -165,9 +169,9 @@ getByteAsShort(FILE * const ifP) {
 
 
 static const char *
-compressionName(char const compr) {
+compressionName(unsigned char const storageCode) {
 
-    switch (compr) {
+    switch (storageCode) {
     case STORAGE_VERBATIM:
         return "none";
     case STORAGE_RLE:
@@ -214,7 +218,7 @@ readHeader(FILE *       const ifP,
     headP->pixmin    = getBigLong(ifP);
     headP->pixmax    = getBigLong(ifP);
     if (headP->pixmin >= headP->pixmax)
-        pm_error("Invalid sgi image header: pixmin larger than pixmax");   
+        pm_error("Invalid sgi image header: pixmin larger than pixmax");
     readBytes(ifP, 4, headP->dummy1);
     readBytes(ifP, 80, headP->name);
     headP->colormap  = getBigLong(ifP);
@@ -243,26 +247,32 @@ readHeader(FILE *       const ifP,
             headP->dimension = 2;
             break;
         case 2:
-            pm_error("don't know how to interpret 2-channel image");
+            if (!outChannelSpec)
+                pm_message("2-channel image, using only first channel.  "
+                           "Extract alpha channel with -channel=1");
             break;
         case 3:
             break;
         default:
             if (!outChannelSpec)
-                pm_message("%d-channel image, using only first 3 channels",
-                           headP->zsize);
+                pm_message("%u-channel image, using only first 3 channels  "
+                           "Extract %s with -channel=%c",
+                            headP->zsize,
+                            headP->zsize==4 ?
+                                "alpha channel" : "additional channels",
+                            headP->zsize==4 ? '3' : 'N');
             break;
         }
         break;
     default:
-        pm_error("illegal dimension value %d (only 1-3 allowed)",
+        pm_error("illegal dimension value %u (only 1-3 allowed)",
                  headP->dimension);
     }
 
     if (verbose) {
-        pm_message("raster size %dx%d, %d channels",
+        pm_message("raster size %ux%u, %u channels",
                    headP->xsize, headP->ysize, headP->zsize);
-        pm_message("compression: %d = %s",
+        pm_message("compression: 0x%02x = %s",
                    headP->storage, compressionName(headP->storage));
         headP->name[79] = '\0';  /* just to be safe */
         pm_message("Image name: '%s'", headP->name);
@@ -345,7 +355,7 @@ rleDecompress(ScanElem * const srcStart,
 static ScanLine *
 readChannels(FILE *       const ifP,
              Header *     const head,
-             TabEntry *   const table, 
+             TabEntry *   const table,
              bool         const outChannelSpec,
              unsigned int const outChannel) {
 
@@ -357,11 +367,14 @@ readChannels(FILE *       const ifP,
     if (outChannelSpec) {
         maxchannel = outChannel + 1;
         MALLOCARRAY_NOFAIL(image, head->ysize);
+    } else if (head->zsize <= 2) {
+        maxchannel = 1;
+        MALLOCARRAY_NOFAIL(image, head->ysize);
     } else {
-        maxchannel = MIN(3, head->zsize);
+        maxchannel = 3;
         MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel);
     }
-    if (table) 
+    if (table)
         MALLOCARRAY_NOFAIL(temp, WORSTCOMPR(head->xsize));
 
     for (channel = 0; channel < maxchannel; ++channel) {
@@ -376,17 +389,18 @@ readChannels(FILE *       const ifP,
                 MALLOCARRAY_NOFAIL(image[iindex], head->xsize);
 
             if (table) {
-                if (outChannelSpec && channel >= outChannel) {
-                    long const offset = table[sgiIndex].start;
+                if (!outChannelSpec || channel >= outChannel) {
+                    pm_filepos const offset = (pm_filepos)
+                        table[sgiIndex].start;
                     long const length = head->bpc == 2 ?
                         table[sgiIndex].length / 2 :
                         table[sgiIndex].length;
 
                     unsigned int i;
 
-                    /* doc says length is in bytes, we are reading words */
-                    if (fseek(ifP, offset, SEEK_SET) != 0)
-                        pm_error("seek error for offset %ld", offset);
+                    /* Note: (offset < currentPosition) can happen */
+
+                    pm_seek2(ifP, &offset, sizeof(offset));
 
                     for (i = 0; i < length; ++i)
                         if (head->bpc == 1)
@@ -404,7 +418,7 @@ readChannels(FILE *       const ifP,
                     else
                         p = getBigShort(ifP);
 
-                    if (channel == outChannel || !outChannelSpec)
+                    if (!outChannelSpec || outChannel == channel)
                         image[iindex][i] = p;
                 }
             }
@@ -430,7 +444,7 @@ imageToPnm(Header   *   const head,
     int row;
     int format;
 
-    if (head->zsize == 1 || outChannelSpec) {
+    if (head->zsize <= 2 || outChannelSpec) {
         pm_message("writing PGM image");
         format = PGM_TYPE;
     } else {
@@ -459,10 +473,10 @@ imageToPnm(Header   *   const head,
 
 
 
-int 
+int
 main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     TabEntry * table;
     ScanLine * image;
@@ -472,8 +486,8 @@ main(int argc, const char * argv[]) {
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
-    ifP = pm_openr(cmdline.inputFileName);    
+
+    ifP = pm_openr_seekable(cmdline.inputFileName);
 
     headP = readHeader(ifP, cmdline.channelSpec, cmdline.verbose);
 
@@ -491,14 +505,14 @@ main(int argc, const char * argv[]) {
         table = readTable(ifP, headP->ysize * headP->zsize);
     else
         table = NULL;
- 
+
     image = readChannels(ifP, headP, table,
                          cmdline.channelSpec, cmdline.channel);
 
     imageToPnm(headP, image, maxval, cmdline.channelSpec, cmdline.channel);
 
     pm_close(ifP);
- 
+
     return 0;
 }
 
diff --git a/doc/HISTORY b/doc/HISTORY
index 6a3fe42c..574b60e4 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,6 +6,12 @@ CHANGE HISTORY
 
 not yet  BJH  Release 10.67.00
 
+              sgitopnm: add ability to convert 2-channel SGI image.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
+              sgitopnm: add ability to work with non-seekable input (e.g. a
+              pipe).  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
               pamtotiff: add -output, use Standard Output normally (before, it
               had to be seekable.  Also, you could do an append operation to
               Standard Output; now you have to use -output for that).  Thanks
@@ -14,8 +20,9 @@ not yet  BJH  Release 10.67.00
               pamsharpness: put primary output on Standard Output instead of
               on Standard Error as a Netpbm message.
 
-              Windows build: include an icon in every executable.  The icon
-              was designed by Ron Vantreese (ait_frog-netpbm@yahoo.com).
+              sgitopnm: fix bug: no output if input is RLE compressed.  Broken
+              in Netpbm 10.53 (December 2010).  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
 
               jpegtopnm -dumpexif: fix incorrect display of resolution.
               Always broken.  (-dumpexif was new in Netpbm 9.18 September
@@ -29,6 +36,9 @@ not yet  BJH  Release 10.67.00
               Always broken.  (-dumpexif was new in Netpbm 9.18 September
               2001).
 
+              Windows build: include an icon in every executable.  The icon
+              was designed by Ron Vantreese (ait_frog-netpbm@yahoo.com).
+
               Build: Fix inconsistent use of upper and lower case Y and N in 
               make variables, causing static library not to get built.
               Introduced in 10.66.