about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-30 03:59:47 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-30 03:59:47 +0000
commit25c660a745eedbff471fb636f97e7198a8b866f8 (patch)
treede434d3b286477762cdeff137225232cf773873b
parent38049c8b75e5e8abbacc5636562e57451f4ebb12 (diff)
downloadnetpbm-mirror-25c660a745eedbff471fb636f97e7198a8b866f8.tar.gz
netpbm-mirror-25c660a745eedbff471fb636f97e7198a8b866f8.tar.xz
netpbm-mirror-25c660a745eedbff471fb636f97e7198a8b866f8.zip
Release 10.49.00 - copied from trunk
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@1086 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rwxr-xr-xbuildtools/configure.pl48
-rwxr-xr-xbuildtools/makeman16
-rw-r--r--common.mk4
-rw-r--r--converter/other/fiasco/codec/coder.c1472
-rw-r--r--converter/other/fiasco/codec/decoder.c1801
-rw-r--r--converter/other/fiasco/codec/dfiasco.c2
-rw-r--r--converter/other/fiasco/codec/options.c2
-rw-r--r--converter/other/fiasco/fiasco.h15
-rw-r--r--converter/other/fiasco/fiascotopnm.c2
-rw-r--r--converter/other/fiasco/input/basis.c2
-rw-r--r--converter/other/fiasco/input/read.c2
-rw-r--r--converter/other/fiasco/lib/bit-io.c2
-rw-r--r--converter/other/fiasco/lib/image.c230
-rw-r--r--converter/other/fiasco/lib/image.h12
-rw-r--r--converter/other/fiasco/pnmtofiasco.c416
-rw-r--r--converter/other/fitstopnm.c167
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c47
-rw-r--r--converter/other/jpeg2000/libjasper/README4
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_cod.c5
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_dec.c4
-rw-r--r--converter/other/pamtopfm.c4
-rw-r--r--converter/other/pgmtopbm.c2
-rw-r--r--converter/other/pnmtopng.c45
-rw-r--r--converter/ppm/xpmtoppm.c211
-rw-r--r--doc/HISTORY45
-rw-r--r--doc/USERDOC6
-rw-r--r--editor/pamcut.c2
-rw-r--r--editor/pamdither.c320
-rw-r--r--editor/pamedge.c5
-rw-r--r--editor/pamflip.c92
-rw-r--r--editor/pammasksharpen.c10
-rw-r--r--editor/pamscale.c263
-rw-r--r--editor/pnmconvol.c3423
-rw-r--r--editor/pnmsmooth.c120
-rw-r--r--editor/specialty/Makefile1
-rw-r--r--editor/specialty/pnmmercator.c429
-rw-r--r--lib/Makefile8
-rw-r--r--lib/libpam.c7
-rw-r--r--lib/libpm.c2
-rw-r--r--lib/util/Makefile9
-rw-r--r--lib/util/io.c90
-rw-r--r--lib/util/io.h11
-rw-r--r--lib/util/shhopt.c88
-rw-r--r--lib/util/shhopt.h1
-rw-r--r--lib/util/token.c79
-rw-r--r--lib/util/token.h11
-rw-r--r--other/pambayer.c88
-rw-r--r--version.mk4
48 files changed, 5311 insertions, 4318 deletions
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index e82c229c..1219b437 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -1550,13 +1550,14 @@ sub testPngHdr($$) {
 
 
 
-sub printBadPngConfigLdflagsWarning($) {
-    my ($pngLdFlags) = @_;
+sub printBadPngConfigCFlagsWarning($) {
+    my ($pngCFlags) = @_;
 
     print << 'EOF';
-WARNING: 'libpng-config' in this environment (a program in your PATH)
-gives instructions that don't work for linking with the PNG library.
-Our test link failed.
+
+WARNING: 'libpng-config' in this environment (a program in your PATH) gives
+instructions that don't work for compiling for (not linking with) the PNG
+library.  Our test compile failed.
 
 This indicates Libpng is installed incorrectly on this system.  If so,
 your Netpbm build, which uses 'libpng-config', will fail.  But it
@@ -1568,13 +1569,29 @@ EOF
 
 
 
-sub printBadPngConfigCFlagsWarning($) {
-    my ($pngCFlags) = @_;
+sub printPngLinkWorksWithLzLm() {
 
     print << 'EOF';
+When we added "-lz -lm" to the linker flags, the link worked.  That means the
+fix for this may be to modify 'libpng-config' so that 'libpng-config
+--ldflags' includes "-lz -lm" in its output.  But the right fix may actually
+be to build libpng differently so that it specifies its dependency on those
+libraries, or to put those libraries in a different place, or to create
+missing symbolic links.
+
+EOF
+}
+
+
+
+sub printBadPngConfigLdflagsWarning($$) {
+    my ($pngLdFlags, $lzLmSuccess) = @_;
+
+    print << 'EOF';
+*****************************************************************************
 WARNING: 'libpng-config' in this environment (a program in your PATH)
-gives instructions that don't work for compiling for the PNG library.
-Our test compile failed.
+gives instructions that don't work for linking with the PNG library.
+Our test link failed.
 
 This indicates Libpng is installed incorrectly on this system.  If so,
 your Netpbm build, which uses 'libpng-config', will fail.  But it
@@ -1582,6 +1599,14 @@ might also just be our test that is broken.
 
 EOF
 
+    if ($lzLmSuccess) {
+        printPngLinkWorksWithLzLm();
+    }
+
+    print << 'EOF';
+*****************************************************************************
+
+EOF
 }
 
 
@@ -1607,7 +1632,10 @@ sub testLinkPnglib($$) {
                         \@cSourceCode, \my $success);
         
         if (!$success) {
-            printBadPngConfigLdflagsWarning($pngLdflags);
+            testCompileLink("$generalCflags $pngCflags $pngLdflags -lz -lm",
+                        \@cSourceCode, \my $lzLmSuccess);
+
+            printBadPngConfigLdflagsWarning($pngLdflags, $lzLmSuccess);
         }
     }
 }
diff --git a/buildtools/makeman b/buildtools/makeman
index 2e122779..100ad580 100755
--- a/buildtools/makeman
+++ b/buildtools/makeman
@@ -1,4 +1,4 @@
-#!/bin/env python
+#!/usr/bin/python
 #
 # makeman -- compile netpbm's stereotyped HTML to troff markup
 #
@@ -121,7 +121,10 @@ def makeman(name, file, indoc):
     indoc = re.sub("(?i)</SUP>", r"\\d", indoc)
     # Paragraph handling
     indoc = re.sub("(?i)\n*<P>\n*", r"\n.PP\n", indoc)
+    indoc = re.sub("(?i)<br */>", r"\n.PP\n", indoc)
     indoc = re.sub("(?i)</P>", "", indoc)
+    indoc = re.sub("(?i)<!--[^>]*-->", "", indoc)
+    indoc = re.sub("(?i)<meta[^>]*>", "", indoc)
     lines = indoc.split("\n")
     listdepth = 0
     for i in range(len(lines)):
@@ -138,13 +141,14 @@ def makeman(name, file, indoc):
     indoc = re.sub('(?i)<A[ \n]+HREF="mailto:[^>]+">([^<]+)</A>', r'\\fI\1\\fP', indoc)    
     # Format manual crossreferences
     def xrefmatch(match):
-        xrefto = match.group(1)
-        xrefsection = sectmap.get(xrefto, 1)
+        xrefto = match.group(2)
+        xrefurl = match.group(1)
+        xrefsection = sectmap.get(xrefurl, 1)
         if xrefsection == 0:
             return "\n.I " + xrefto
         else:
-            return "\n.BR %s (%d)" % (xrefto, xrefsection)
-    indoc = re.sub(r'(?i)\n* *(?:\\fB)?<A[ \n]+HREF="[^>]+.html">([^<]+)</A>(?:\\fP)?',
+            return "\n.BR %s (%d)\n" % (xrefto, xrefsection)
+    indoc = re.sub(r'(?i)\n* *(?:\\fB)?<A[ \n]+HREF="?([^>]+.html)"?>([^<]+)</A>(?:\\fP)?',
                    xrefmatch, indoc)
     # Format URLs
     def urlmatch(match):
@@ -159,7 +163,7 @@ def makeman(name, file, indoc):
     indoc = indoc.replace("&#174;", r"\*R")
     indoc = indoc.replace("&copy;", r"\(co")
     # Turn anchors into .UN tags
-    indoc = re.sub('(?i)<A NAME *= *"#?([a-zA-Z][a-zA-Z0-9.-]+)">(?:&nbsp;)*</A>\s*', ".UN \\1\n", indoc)
+    indoc = re.sub('(?i)<A NAME *= *"#?([a-zA-Z_][a-zA-Z_0-9.-]+)">(?:&nbsp;)*</A>\s*', ".UN \\1\n", indoc)
     # Strip off the index trailer
     trailer = re.compile('<HR */*>.*', re.DOTALL | re.IGNORECASE)
     indoc = re.sub(trailer, "", indoc)
diff --git a/common.mk b/common.mk
index ea7e5807..4b905ad4 100644
--- a/common.mk
+++ b/common.mk
@@ -131,8 +131,8 @@ IMPORTINC_LIB_HEADERS := \
   pm_gamma.h lum.h dithers.h
 
 IMPORTINC_LIB_UTIL_HEADERS := \
-  bitarith.h bitreverse.h filename.h intcode.h floatcode.h mallocvar.h\
-  nsleep.h nstring.h pm_c_util.h shhopt.h \
+  bitarith.h bitreverse.h filename.h intcode.h floatcode.h io.h mallocvar.h\
+  nsleep.h nstring.h pm_c_util.h shhopt.h token.h \
   wordaccess.h wordaccess_64_le.h wordaccess_gcc3_be.h wordaccess_generic.h \
   wordintclz.h
 
diff --git a/converter/other/fiasco/codec/coder.c b/converter/other/fiasco/codec/coder.c
index df878d87..927ebbda 100644
--- a/converter/other/fiasco/codec/coder.c
+++ b/converter/other/fiasco/codec/coder.c
@@ -1,8 +1,8 @@
 /*
- *  coder.c:		WFA coder toplevel functions
+ *  coder.c:        WFA coder toplevel functions
  *
- *  Written by:		Ullrich Hafner
- *		
+ *  Written by:     Ullrich Hafner
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -18,6 +18,7 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include "config.h"
+#include "pm_c_util.h"
 #include "pnm.h"
 
 #include <math.h>
@@ -53,328 +54,320 @@
 
 /*****************************************************************************
 
-				global variables
+                global variables
   
 *****************************************************************************/
 
-const real_t MAXCOSTS =	1e20;
+const real_t MAXCOSTS = 1e20;
 
 /*****************************************************************************
 
-				prototypes
+                private code
   
 *****************************************************************************/
 
-static coding_t *
-alloc_coder (char const * const *inputname, const c_options_t *options,
-	     wfa_info_t *wi);
-static void
-free_coder (coding_t *c);
 static char *
-get_input_image_name (char const * const *templptr, unsigned ith_image);
-static void
-video_coder (char const * const *image_template, bitfile_t *output,
-	     wfa_t *wfa, coding_t *c);
-static void 
-frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output);
-static void
-print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
-		  const range_t *range);
-static frame_type_e
-pattern2type (unsigned frame, const char *pattern);
-
-/*****************************************************************************
-
-				public code
-  
-*****************************************************************************/
-
-int
-fiasco_coder (char const * const *inputname, const char *outputname,
-	      float quality, const fiasco_c_options_t *options)
+get_input_image_name (char const * const *templptr, unsigned ith_image)
 /*
- *  FIASCO coder.
- *  Encode image or video frames given by the array of filenames `inputname'
- *  and write to the outputfile `outputname'.
- *  If 'inputname' = NULL or
- *     'inputname [0]' == NULL or
- *     'inputname [0]' == "-", read standard input.
- *  If 'outputname' == NULL or "-", write on standard output.
- *  'quality' defines the approximation quality and is 1 (worst) to 100 (best).
+ *  Construct the i-th image-name using templates.
+ *  If the template contains a '[' it must be of the form
+ *  "prefix[start-end{+,-}step]suffix"
+ *  where "{+,-}step" is optional.
+ *  Leading zeros of "start" are significant.
+ *
+ *  Example:
+ *   "image0[12-01-1].pgm" yields image012.pgm, image011.pgm, ..., image001.pgm
  *
  *  Return value:
- *	1 on success
- *	0 otherwise
+ *      ptr to name of image 'ith_image' or NULL if ith_image is out of range.
  */
 {
-   try
-   {
-      char const * const default_input [] = {"-", NULL};
-      fiasco_c_options_t *default_options = NULL;
-      const c_options_t  *cop;
-      char const * const *template;
-      
-      /*
-       *  Check parameters
-       */
-      if (!inputname || !inputname [0] || streq (inputname [0], "-"))
-	 template = default_input;
-      else
-	 template = inputname;
-      
-      if (quality <= 0)
-      {
-	 set_error (_("Compression quality has to be positive."));
-	 return 0;
-      }
-      else if (quality >= 100)
-      {
-	 warning (_("Quality typically is 1 (worst) to 100 (best).\n"
-		    "Be prepared for a long running time."));
-      }
-
-      if (options)
-      {
-	 cop = cast_c_options ((fiasco_c_options_t *) options);
-	 if (!cop)
-	    return 0;
-      }
-      else
-      {
-	 default_options = fiasco_c_options_new ();
-	 cop 		 = cast_c_options (default_options);
-      }
-
-   /*
-    *  Open output stream and initialize WFA
-    */
-      {
-	 bitfile_t *output = open_bitfile (outputname, "FIASCO_DATA",
-					   WRITE_ACCESS);
-	 if (!output)
-	 {
-	    set_error (_("Can't write outputfile `%s'.\n%s"),
-		       outputname ? outputname : "<stdout>",
-		       get_system_error ());
-	    if (default_options)
-	       fiasco_c_options_delete (default_options);
-	    return 0;
-	 }
-	 else
-	 {
-	    wfa_t    *wfa = alloc_wfa (YES);
-	    coding_t *c   = alloc_coder (template, cop, wfa->wfainfo);
-	 
-	    read_basis (cop->basis_name, wfa);
-	    append_basis_states (wfa->basis_states, wfa, c);
-	 
-	    c->price = 128 * 64 / quality;
-	 
-	    video_coder (template, output, wfa, c);
-	 
-	    close_bitfile (output);
-	    free_wfa (wfa);
-	    free_coder (c);
-	 
-	    if (default_options)
-	       fiasco_c_options_delete (default_options);
-	 }
-      }
-      return 1;
-   }
-   catch
-   {
-      return 0;
-   }
-}
+    while (*templptr)
+    {
+        const char *template = *templptr++;
+        char       *s;
+
+        if (!(s = strchr (template, '['))) /* no template, just a filename */
+        {
+            if (ith_image == 0)
+                return strdup (template);
+            else
+                ith_image--;
+        }
+        else              /* template parser */
+        {
+            unsigned  n_digits;        /* # of digits in image name no. */
+            char     *s2;
+            char     *suffix;      /* characters after template end */
+            char      prefix [MAXSTRLEN];  /* chars up to the template start */
+            unsigned  first;       /* first image number */
+            unsigned  last;        /* last image number */
+            int       image_num;       /* current image number */
+            int       increment = 1;
+            int       dummy;
+
+            strcpy (prefix, template);
+            prefix [s - template] = '\0';
+   
+            for (s2 = ++s, n_digits = 0; ISDIGIT (*s2); s2++, n_digits++)
+                ;
+            if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
+                error ("Input name template conversion failure.\n"
+                       "Check spelling of template.");
+            first = (unsigned) dummy;
+     
+            if (*s2++ != '-')
+                error ("Input name template conversion failure.\n"
+                       "Check spelling of template.");
+   
+            for (s = s2; ISDIGIT (*s2); s2++)
+                ;
+            if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
+                error ("Input name template conversion failure.\n"
+                       "Check spelling of template.");
+            last = (unsigned) dummy;
+     
+            if (*s2 == '+' || *s2 == '-') 
+            {
+                for (s = s2++; ISDIGIT (*s2); s2++)
+                    ;
+                if (sscanf (s, "%d", &increment) == 0)
+                    error ("Input name template conversion failure.\n"
+                           "Check spelling of template.");
+            }   
+            if (*s2 != ']')
+                error ("Input name template conversion failure.\n"
+                       "Check spelling of template.");
+            suffix = s2 + 1;
+   
+            image_num = first + increment * ith_image;
+            if (image_num < 0)
+                error ("Input name template conversion failure.\n"
+                       "Check spelling of template.");
+     
+            if ((increment >  0 && (unsigned) image_num > last) || 
+                (increment <= 0 && (unsigned) image_num < last))
+            {
+                /* TODO: check this */
+                ith_image -= (last - first) / increment + 1;
+            }
+            else
+            {
+                char formatstr [MAXSTRLEN];
+                    /* format string for image filename */
+                char image_name [MAXSTRLEN];
+                    /* image file name to be composed */
+        
+                strcpy (formatstr, "%s%0?d%s");
+                formatstr [4] = '0' + (char) n_digits;
+                sprintf (image_name, formatstr, prefix, image_num, suffix);
+                return strdup (image_name);
+            }
+        }
+    }
+    return NULL;
+}   
 
-/*****************************************************************************
 
-				private code
-  
-*****************************************************************************/
 
 static coding_t *
-alloc_coder (char const * const *inputname, const c_options_t *options,
-	     wfa_info_t *wi)
+alloc_coder (char const * const * const inputname,
+             const c_options_t *  const options,
+             wfa_info_t *         const wi,
+             unsigned int         const stdinwidth,
+             unsigned int         const stdinheight,
+             xelval               const stdinmaxval,
+             int                  const stdinformat)
 /*
  *  Coder structure constructor.
  *  Allocate memory for the FIASCO coder structure and
  *  fill in default values specified by 'options'.
  *
  *  Return value:
- *	pointer to the new coder structure or NULL on error
+ *  pointer to the new coder structure or NULL on error
  */
 {
-   coding_t *c = NULL;
+    coding_t * c;
+
+    c = NULL;  /* initial value */
    
    /*
     *  Check whether all specified image frames are readable and of same type
     */
-   {
-      char     *filename;
-      int   	width, w = 0, height, h = 0;
-      bool_t	color, c = NO;
-      unsigned 	n;
+    {
+        char     *filename;
+        int     width, w = 0, height, h = 0;
+        bool_t  color, c = NO;
+        unsigned    n;
       
-      for (n = 0; (filename = get_input_image_name (inputname, n)); n++)
-      {
-          FILE *file;
-          xelval maxval;
-          int format;
-          if (filename == NULL)
-              file = stdin;
-          else
-              file = pm_openr(filename);
-          pnm_readpnminit(file, &width, &height, &maxval, &format);
-          color = (PNM_FORMAT_TYPE(format) == PPM_FORMAT) ? TRUE: FALSE;
-
-          pm_close(file);
-	 if (n)
-	 {
-	    if (w != width || h != height || c != color)
-	    {
-	       set_error (_("Format of image frame `%s' doesn't match."),
-			  filename ? filename : "<stdin>");
-	       return NULL;
-	    }
-	 }
-	 else
-	 {
-	    w = width;
-	    h = height;
-	    c = color;
-	 }
-	 Free (filename);
-      }
-      wi->frames = n;
-      wi->width  = w;
-      wi->height = h;
-      wi->color  = c;
-   }
+        for (n = 0; (filename = get_input_image_name (inputname, n)); n++)
+        {
+            xelval maxval;
+            int format;
+            if (streq(filename, "-")) {
+                width  = stdinwidth;
+                height = stdinheight;
+                maxval = stdinmaxval;
+                format = stdinformat;
+            } else {
+                FILE *file;
+
+                file = pm_openr(filename);
+
+                pnm_readpnminit(file, &width, &height, &maxval, &format);
+
+                pm_close(file);
+            }
+            color = (PNM_FORMAT_TYPE(format) == PPM_FORMAT) ? TRUE: FALSE;
+                
+            if (n > 0)
+            {
+                if (w != width || h != height || c != color)
+                {
+                    set_error (_("Format of image frame `%s' doesn't match."),
+                               filename ? filename : "<stdin>");
+                    return NULL;
+                }
+            }
+            else
+            {
+                w = width;
+                h = height;
+                c = color;
+            }
+            Free (filename);
+        }
+        wi->frames = n;
+        wi->width  = w;
+        wi->height = h;
+        wi->color  = c;
+    }
 
-   /*
+    /*
     *  Levels ...
     */
-   {
-      unsigned lx, ly;
+    {
+        unsigned lx, ly;
       
-      lx = (unsigned) (log2 (wi->width - 1) + 1);
-      ly = (unsigned) (log2 (wi->height - 1) + 1);
+        lx = (unsigned) (log2 (wi->width - 1) + 1);
+        ly = (unsigned) (log2 (wi->height - 1) + 1);
       
-      wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
-   }
+        wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+    }
    
-   c = Calloc (1, sizeof (coding_t));
+    c = Calloc (1, sizeof (coding_t));
 
-   c->options 	      	   = *options;
-   c->options.lc_min_level = max (options->lc_min_level, 3);
-   c->options.lc_max_level = min (options->lc_max_level, wi->level - 1);
+    c->options             = *options;
+    c->options.lc_min_level = max (options->lc_min_level, 3);
+    c->options.lc_max_level = min (options->lc_max_level, wi->level - 1);
 
-   c->tiling = alloc_tiling (options->tiling_method,
-			     options->tiling_exponent, wi->level);
+    c->tiling = alloc_tiling (options->tiling_method,
+                              options->tiling_exponent, wi->level);
 
-   if (wi->frames > 1 && c->tiling->exponent > 0)
-   {
-      c->tiling->exponent = 0;
-      warning (_("Image tiling valid only with still image compression."));
-   }
+    if (wi->frames > 1 && c->tiling->exponent > 0)
+    {
+        c->tiling->exponent = 0;
+        warning (_("Image tiling valid only with still image compression."));
+    }
 
-   if (c->options.lc_max_level >= wi->level - c->tiling->exponent)
-   {
-      message ("'max_level' changed from %d to %d due to image tiling level.",
-	       c->options.lc_max_level, wi->level - c->tiling->exponent - 1);
-      c->options.lc_max_level = wi->level - c->tiling->exponent - 1;
-   }
+    if (c->options.lc_max_level >= wi->level - c->tiling->exponent)
+    {
+        message ("'max_level' changed from %d to %d "
+                 "due to image tiling level.",
+                 c->options.lc_max_level, wi->level - c->tiling->exponent - 1);
+        c->options.lc_max_level = wi->level - c->tiling->exponent - 1;
+    }
    
-   if (c->options.lc_min_level > c->options.lc_max_level)
-      c->options.lc_min_level = c->options.lc_max_level;
+    if (c->options.lc_min_level > c->options.lc_max_level)
+        c->options.lc_min_level = c->options.lc_max_level;
    
-   /*
-    *  p_min_level, p_max_level min and max level for ND/MC prediction
-    *  [p_min_level, p_max_level] must be a subset of [min_level, max_level] !
-    */
-   wi->p_min_level = max (options->p_min_level, c->options.lc_min_level);
-   wi->p_max_level = min (options->p_max_level, c->options.lc_max_level);
-   if (wi->p_min_level > wi->p_max_level)
-      wi->p_min_level = wi->p_max_level;
-
-   c->options.images_level = min (c->options.images_level,
-				  c->options.lc_max_level - 1);
+    /*
+     *  p_min_level, p_max_level min and max level for ND/MC prediction
+     *  [p_min_level, p_max_level] must be a subset of [min_level, max_level] !
+     */
+    wi->p_min_level = max (options->p_min_level, c->options.lc_min_level);
+    wi->p_max_level = min (options->p_max_level, c->options.lc_max_level);
+    if (wi->p_min_level > wi->p_max_level)
+        wi->p_min_level = wi->p_max_level;
+
+    c->options.images_level = min (c->options.images_level,
+                                   c->options.lc_max_level - 1);
    
-   c->products_level  = max (0, ((signed int) c->options.lc_max_level
-				 - (signed int) c->options.images_level - 1));
-   c->pixels 	      = Calloc (size_of_level (c->options.lc_max_level),
-				sizeof (real_t));
-   c->images_of_state = Calloc (MAXSTATES, sizeof (real_t *));
-   c->ip_images_state = Calloc (MAXSTATES, sizeof (real_t *));
-   c->ip_states_state = Calloc (MAXSTATES * MAXLEVEL, sizeof (real_t *));
+    c->products_level  = max (0, ((signed int) c->options.lc_max_level
+                                  - (signed int) c->options.images_level - 1));
+    c->pixels         = Calloc (size_of_level (c->options.lc_max_level),
+                                sizeof (real_t));
+    c->images_of_state = Calloc (MAXSTATES, sizeof (real_t *));
+    c->ip_images_state = Calloc (MAXSTATES, sizeof (real_t *));
+    c->ip_states_state = Calloc (MAXSTATES * MAXLEVEL, sizeof (real_t *));
    
-   debug_message ("Imageslevel :%d, Productslevel :%d",
-		  c->options.images_level, c->products_level);
-   debug_message ("Memory : (%d + %d + %d * 'states') * 'states' + %d",
-		  size_of_tree (c->options.images_level) * 4,
-		  size_of_tree (c->products_level) * 4,
-		  (c->options.lc_max_level - c->options.images_level),
-		  size_of_level (c->options.lc_max_level));
+    debug_message ("Imageslevel :%d, Productslevel :%d",
+                   c->options.images_level, c->products_level);
+    debug_message ("Memory : (%d + %d + %d * 'states') * 'states' + %d",
+                   size_of_tree (c->options.images_level) * 4,
+                   size_of_tree (c->products_level) * 4,
+                   (c->options.lc_max_level - c->options.images_level),
+                   size_of_level (c->options.lc_max_level));
    
-   /*
+    /*
     *  Domain pools ...
     */
-   c->domain_pool   = NULL;
-   c->d_domain_pool = NULL;
-
-   /*
-    *  Coefficients model ...
-    */
-   c->coeff   = NULL;
-   c->d_coeff = NULL;
-
-   /*
-    *  Max. number of states and edges
-    */
-   wi->max_states   	   = max (min (options->max_states, MAXSTATES), 1);
-   c->options.max_elements = max (min (options->max_elements, MAXEDGES), 1);
-
-   /*
-    *  Title and comment strings
-    */
-   wi->title   = strdup (options->title);
-   wi->comment = strdup (options->comment);
+    c->domain_pool   = NULL;
+    c->d_domain_pool = NULL;
+
+    /*
+     *  Coefficients model ...
+     */
+    c->coeff   = NULL;
+    c->d_coeff = NULL;
+
+    /*
+     *  Max. number of states and edges
+     */
+    wi->max_states         = max (min (options->max_states, MAXSTATES), 1);
+    c->options.max_elements = max (min (options->max_elements, MAXEDGES), 1);
+
+    /*
+     *  Title and comment strings
+     */
+    wi->title   = strdup (options->title);
+    wi->comment = strdup (options->comment);
    
-   /*
-    *  Reduced precision format
-    */
-   wi->rpf
-      = alloc_rpf (options->rpf_mantissa, options->rpf_range);
-   wi->dc_rpf
-      = alloc_rpf (options->dc_rpf_mantissa, options->dc_rpf_range);
-   wi->d_rpf
-      = alloc_rpf (options->d_rpf_mantissa, options->d_rpf_range);
-   wi->d_dc_rpf
-      = alloc_rpf (options->d_dc_rpf_mantissa, options->d_dc_rpf_range);
+    /*
+     *  Reduced precision format
+     */
+    wi->rpf
+        = alloc_rpf (options->rpf_mantissa, options->rpf_range);
+    wi->dc_rpf
+        = alloc_rpf (options->dc_rpf_mantissa, options->dc_rpf_range);
+    wi->d_rpf
+        = alloc_rpf (options->d_rpf_mantissa, options->d_rpf_range);
+    wi->d_dc_rpf
+        = alloc_rpf (options->d_dc_rpf_mantissa, options->d_dc_rpf_range);
    
-   /*
-    *  Color image options ...
-    */
-   wi->chroma_max_states = max (1, options->chroma_max_states);
+    /*
+     *  Color image options ...
+     */
+    wi->chroma_max_states = max (1, options->chroma_max_states);
 
-   /*
+    /*
     *  Set up motion compensation struct.
     *  p_min_level, p_max_level are also used for ND prediction
     */
-   wi->search_range   = options->search_range;
-   wi->fps 	      = options->fps;
-   wi->half_pixel     = options->half_pixel_prediction;
-   wi->cross_B_search = options->half_pixel_prediction;
-   wi->B_as_past_ref  = options->B_as_past_ref;
-   wi->smoothing      = options->smoothing;
+    wi->search_range   = options->search_range;
+    wi->fps            = options->fps;
+    wi->half_pixel     = options->half_pixel_prediction;
+    wi->cross_B_search = options->half_pixel_prediction;
+    wi->B_as_past_ref  = options->B_as_past_ref;
+    wi->smoothing      = options->smoothing;
    
-   c->mt = alloc_motion (wi);
+    c->mt = alloc_motion (wi);
 
-   return c;
+    return c;
 }
 
+
+
 static void
 free_coder (coding_t *c)
 /*
@@ -384,7 +377,7 @@ free_coder (coding_t *c)
  *  No return value.
  *
  *  Side effects:
- *	structure 'coder' is discarded.
+ *  structure 'coder' is discarded.
  */
 {
    free_tiling (c->tiling);
@@ -397,285 +390,8 @@ free_coder (coding_t *c)
    Free (c);
 }
 
-static char *
-get_input_image_name (char const * const *templptr, unsigned ith_image)
-/*
- *  Construct the i-th image-name using templates.
- *  If the template contains a '[' it must be of the form
- *  "prefix[start-end{+,-}step]suffix"
- *  where "{+,-}step" is optional.
- *  Leading zeros of "start" are significant.
- *
- *  Example:
- *   "image0[12-01-1].pgm" yields image012.pgm, image011.pgm, ..., image001.pgm
- *
- *  Return value:
- *      ptr to name of image 'ith_image' or NULL if ith_image is out of range.
- */
-{
-   while (*templptr)
-   {
-      const char *template = *templptr++;
-      char       *s;
-
-      if (!(s = strchr (template, '['))) /* no template, just a filename */
-      {
-	 if (ith_image == 0)
-	    return strdup (template);
-	 else
-	    ith_image--;
-      }
-      else				/* template parser */
-      {
-	 unsigned  n_digits;		/* # of digits in image name no. */
-	 char 	  *s2;
-	 char 	  *suffix;		/* characters after template end */
-	 char  	   prefix [MAXSTRLEN];	/* chars up to the template start */
-	 unsigned  first;		/* first image number */
-	 unsigned  last;		/* last image number */
-	 int  	   image_num;		/* current image number */
-	 int   	   increment = 1;
-	 int 	   dummy;
-
-	 strcpy (prefix, template);
-	 prefix [s - template] = '\0';
-   
-	 for (s2 = ++s, n_digits = 0; ISDIGIT (*s2); s2++, n_digits++)
-	    ;
-	 if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
-	    error ("Input name template conversion failure.\n"
-		   "Check spelling of template.");
-	 first = (unsigned) dummy;
-	 
-	 if (*s2++ != '-')
-	    error ("Input name template conversion failure.\n"
-		   "Check spelling of template.");
-   
-	 for (s = s2; ISDIGIT (*s2); s2++)
-	    ;
-	 if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
-	    error ("Input name template conversion failure.\n"
-		   "Check spelling of template.");
-	 last = (unsigned) dummy;
-	 
-	 if (*s2 == '+' || *s2 == '-') 
-	 {
-	    for (s = s2++; ISDIGIT (*s2); s2++)
-	       ;
-	    if (sscanf (s, "%d", &increment) == 0)
-	       error ("Input name template conversion failure.\n"
-		      "Check spelling of template.");
-	 } 	 
-	 if (*s2 != ']')
-	    error ("Input name template conversion failure.\n"
-		   "Check spelling of template.");
-	 suffix = s2 + 1;
-   
-	 image_num = first + increment * ith_image;
-	 if (image_num < 0)
-	    error ("Input name template conversion failure.\n"
-		   "Check spelling of template.");
-	 
-	 if ((increment >  0 && (unsigned) image_num > last) || 
-	     (increment <= 0 && (unsigned) image_num < last))
-	 {
-	    /* TODO: check this */
-	    ith_image -= (last - first) / increment + 1;
-	 }
-	 else
-	 {
-	    char formatstr [MAXSTRLEN]; /* format string for image filename */
-	    char image_name [MAXSTRLEN]; /* image file name to be composed */
-	    
-	    strcpy (formatstr, "%s%0?d%s");
-	    formatstr [4] = '0' + (char) n_digits;
-	    sprintf (image_name, formatstr, prefix, image_num, suffix);
-	    return strdup (image_name);
-	 }
-      }
-   }
-   return NULL;
-}	
-
-static void
-video_coder (char const * const *image_template, bitfile_t *output,
-	     wfa_t *wfa, coding_t *c)
-/*
- *  Toplevel function to encode a sequence of video frames specified
- *  by 'image_template'. The output is written to stream 'output'.
- *  Coding options are given by 'c'.
- *
- *  No return value.
- */
-{
-   unsigned  display;			/* picture number in display order */
-   int	     future_display;		/* number of future reference */
-   int	     frame;			/* current frame number */
-   char	    *image_name;		/* image name of current frame */
-   image_t  *reconst 	  = NULL; 	/* decoded reference image */
-   bool_t    future_frame = NO;		/* YES if last frame was in future */
-   
-   debug_message ("Generating %d WFA's ...", wfa->wfainfo->frames);
-
-   future_display = -1;
-   frame          = -1;
-   display        = 0;
-
-   while ((image_name = get_input_image_name (image_template, display)))
-   {
-      frame_type_e type;		/* current frame type: I, B, P */
-      
-      /*
-       *  Determine type of next frame.
-       *  Skip already coded frames (future reference!)
-       */
-      if (display == 0 && !c->options.reference_filename)
-	 type = I_FRAME;		/* Force first frame to be intra */
-      else
-	 type = pattern2type (display, c->options.pattern);
-      
-      if (type != I_FRAME && c->options.reference_filename)
-	 /* Load reference from disk */
-      {
-	 debug_message ("Reading reference frame `%s'.",
-			c->options.reference_filename);
-	 reconst 	 = read_image (c->options.reference_filename);
-	 c->options.reference_filename = NULL;
-      }
-      if ((int) display == future_display) /* Skip already coded future ref */
-      {				
-	 display++;
-	 continue;
-      }	  
-      else if (type == B_FRAME && (int) display > future_display) 
-      {
-	 unsigned i = display;
-	 /*
-	  *  Search for future reference
-	  */
-	 while (type == B_FRAME)
-	 {
-	    char *name;			/* image name of future frame */
-
-	    i++;
-	    name = get_input_image_name (image_template, i);
-	    
-	    if (!name)			/* Force last valid frame to be 'P' */
-	    {
-	       future_display = i - 1;
-	       type = P_FRAME;
-	    }
-	    else
-	    {
-	       future_display = i;    
-	       image_name     = name;
-	       type 	      = pattern2type (i, c->options.pattern);
-	    }
-	    frame = future_display;
-	 }
-      }
-      else
-      {
-	 frame = display;
-	 display++;
-      }
-
-      debug_message ("Coding \'%s\' [%c-frame].", image_name,
-		     type == I_FRAME ? 'I' : (type == P_FRAME ? 'P' : 'B'));
-       
-      /*
-       *  Depending on current frame type update past and future frames
-       *  which are needed as reference frames.
-       */
-      c->mt->frame_type = type;
-      if (type == I_FRAME)
-      {
-	 if (c->mt->past)		/* discard past frame */
-	    free_image (c->mt->past);
-	 c->mt->past = NULL;
-	 if (c->mt->future)		/* discard future frame */
-	    free_image (c->mt->future);
-	 c->mt->future = NULL;
-	 if (reconst)			/* discard current frame */
-	    free_image (reconst);
-	 reconst = NULL;
-      }
-      else if (type == P_FRAME)
-      {
-	 if (c->mt->past)		/* discard past frame */
-	    free_image (c->mt->past);
-	 c->mt->past = reconst;		/* past frame <- current frame */
-	 reconst    = NULL;
-	 if (c->mt->future)		/* discard future frame */
-	    free_image (c->mt->future);
-	 c->mt->future = NULL;
-      }
-      else				/* B_FRAME */
-      {
-	 if (future_frame)		/* last frame was future frame */
-	 {
-	    if (c->mt->future)		/* discard future frame */
-	       free_image (c->mt->future);
-	    c->mt->future = reconst;	/* future frame <- current frame */
-	    reconst      = NULL;
-	 }
-	 else
-	 {
-	    if (wfa->wfainfo->B_as_past_ref == YES)
-	    {
-	       if (c->mt->past)		/* discard past frame */
-		  free_image (c->mt->past);
-	       c->mt->past = reconst;	/* past frame <- current frame */
-	       reconst    = NULL;
-	    }
-	    else
-	    {
-	       if (reconst)		/* discard current frame */
-		  free_image (reconst);
-	       reconst = NULL;
-	    }
-	 }
-      }
-
-      /*
-       *  Start WFA coding of current frame
-       */
-      future_frame   = frame == future_display;
-      c->mt->number   = frame;
-      c->mt->original = read_image (image_name);
-      if (c->tiling->exponent && type == I_FRAME) 
-	 perform_tiling (c->mt->original, c->tiling);
-
-      frame_coder (wfa, c, output);
-
-      /*
-       *  Regenerate image:
-       *  1. Compute approximation of WFA ranges (real image bintree order)
-       *  2. Generate byte image in rasterscan order
-       *  3. Apply motion compensation
-       */
-      reconst = decode_image (wfa->wfainfo->width, wfa->wfainfo->height,
-			      FORMAT_4_4_4, NULL, wfa);
-
-      if (type != I_FRAME)
-	 restore_mc (0, reconst, c->mt->past, c->mt->future, wfa);
 
-      if (c->mt->original)
-	 free_image (c->mt->original);
-      c->mt->original = NULL;
-      
-      remove_states (wfa->basis_states, wfa); /* Clear WFA structure */
-   }
 
-   if (reconst)
-      free_image (reconst);
-   if (c->mt->future)
-      free_image (c->mt->future);
-   if (c->mt->past)
-      free_image (c->mt->past);
-   if (c->mt->original)
-      free_image (c->mt->original);
-}
 
 static frame_type_e
 pattern2type (unsigned frame, const char *pattern)
@@ -700,6 +416,72 @@ pattern2type (unsigned frame, const char *pattern)
     return retval;
 }
 
+
+
+static void
+print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
+          const range_t *range)
+{
+   unsigned max_level, min_level, state, label, lincomb;
+   
+   for (max_level = 0, min_level = MAXLEVEL, state = wfa->basis_states;
+    state < wfa->states; state++)
+   {
+      for (lincomb = 0, label = 0; label < MAXLABELS; label++)
+     lincomb += isrange(wfa->tree[state][label]) ? 1 : 0;
+     
+      if (lincomb)
+      {
+     max_level = max (max_level,
+              (unsigned) (wfa->level_of_state [state] - 1));
+     min_level = min (min_level,
+              (unsigned) (wfa->level_of_state [state] - 1));
+      }
+   }
+   debug_message ("Image partitioning: maximum level %d , minimum level %d",
+          max_level, min_level);
+   debug_message ("WFA contains %d states (%d basis states).",
+          wfa->states, wfa->basis_states);
+   debug_message ("Estimated error: %.2f (RMSE: %.2f, PSNR: %.2f dB).",
+          (double) range->err,
+          sqrt (range->err / image->width / image->height),
+          10 * log ( 255.0 * 255.0 /
+                 (range->err / image->width / image->height))
+          / log (10.0));
+   debug_message ("Estimated filesize: %.0f bits (%.0f bytes).",
+          (double) (range->tree_bits + range->matrix_bits
+                + range->weights_bits
+                + range->mv_tree_bits + range->mv_coord_bits
+                + range->nd_tree_bits + range->nd_weights_bits),
+          (double) (range->tree_bits + range->matrix_bits
+                + range->weights_bits + range->mv_tree_bits
+                + range->mv_coord_bits + range->nd_tree_bits
+                + range->nd_weights_bits) / 8);
+   if (c)
+      debug_message ("(%cT: %.0f, %cM: %.0f, %cW: %.0f, %cMC: %.0f, "
+             "%cMV: %.0f, %cNT: %.0f, %cNW: %.0f.)",
+             c, (double) range->tree_bits,
+             c, (double) range->matrix_bits,
+             c, (double) range->weights_bits,
+             c, (double) range->mv_tree_bits,
+             c, (double) range->mv_coord_bits,
+             c, (double) range->nd_tree_bits,
+             c, (double) range->nd_weights_bits);
+   else
+      debug_message ("(T: %.0f, M: %.0f, W: %.0f, MC: %.0f, MV: %.0f, "
+             "NT: %.0f, NW: %.0f.)",
+             (double) range->tree_bits,
+             (double) range->matrix_bits,
+             (double) range->weights_bits,
+             (double) range->mv_tree_bits,
+             (double) range->mv_coord_bits,
+             (double) range->nd_tree_bits,
+             (double) range->nd_weights_bits);
+   debug_message ("Total costs : %.2f", (double) costs);
+}
+
+
+
 static void 
 frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
 /*
@@ -712,9 +494,9 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
  */
 {
    unsigned state;
-   range_t  range;			/* first range == the entire image */
-   real_t   costs;			/* total costs (minimized quantity) */
-   unsigned bits;			/* number of bits written on disk */
+   range_t  range;          /* first range == the entire image */
+   real_t   costs;          /* total costs (minimized quantity) */
+   unsigned bits;           /* number of bits written on disk */
    clock_t  ptimer;
    
    prg_timer (&ptimer, START);
@@ -726,48 +508,48 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
 
    c->domain_pool
       = alloc_domain_pool (c->options.id_domain_pool,
-			   wfa->wfainfo->max_states,
-			   c->options.max_elements, wfa);
+               wfa->wfainfo->max_states,
+               c->options.max_elements, wfa);
    c->d_domain_pool
       = alloc_domain_pool ((c->options.prediction
-			    || c->mt->frame_type != I_FRAME)
-			   ? c->options.id_d_domain_pool : "constant",
-			   wfa->wfainfo->max_states,
-			   c->options.max_elements, wfa);
+                || c->mt->frame_type != I_FRAME)
+               ? c->options.id_d_domain_pool : "constant",
+               wfa->wfainfo->max_states,
+               c->options.max_elements, wfa);
 
    c->coeff   = alloc_coeff_model (c->options.id_rpf_model,
-				   wfa->wfainfo->rpf,
-				   wfa->wfainfo->dc_rpf,
-				   c->options.lc_min_level,
-				   c->options.lc_max_level);
+                   wfa->wfainfo->rpf,
+                   wfa->wfainfo->dc_rpf,
+                   c->options.lc_min_level,
+                   c->options.lc_max_level);
    c->d_coeff = alloc_coeff_model (c->options.id_d_rpf_model,
-				   wfa->wfainfo->d_rpf,
-				   wfa->wfainfo->d_dc_rpf,
-				   c->options.lc_min_level,
-				   c->options.lc_max_level);
+                   wfa->wfainfo->d_rpf,
+                   wfa->wfainfo->d_dc_rpf,
+                   c->options.lc_min_level,
+                   c->options.lc_max_level);
 
-   if (!c->mt->original->color)		/* grayscale image */
+   if (!c->mt->original->color)     /* grayscale image */
    {
       memset (&range, 0, sizeof (range_t));
       range.level = wfa->wfainfo->level;
 
       costs = subdivide (MAXCOSTS, GRAY, RANGE, &range, wfa, c,
-			 c->options.prediction || c->mt->frame_type != I_FRAME,
-			 NO);
+             c->options.prediction || c->mt->frame_type != I_FRAME,
+             NO);
       if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
-	 message ("");
+     message ("");
 
-      if (isrange (range.tree))		/* entire image is approx. by lc? */
-	 error ("No root state generated!");
+      if (isrange (range.tree))     /* entire image is approx. by lc? */
+     error ("No root state generated!");
       else
-	 wfa->root_state = range.tree;
+     wfa->root_state = range.tree;
 
       print_statistics ('\0', costs, wfa, c->mt->original, &range);
    }
    else
    {
       int     YCb_node = -1;
-      int     tree [3];			/* 3 root states of each color comp. */
+      int     tree [3];         /* 3 root states of each color comp. */
       color_e band;
       
       /*
@@ -781,62 +563,62 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
        */
       for (band = first_band (YES); band <= last_band (YES) ; band++)
       {
-	 debug_message ("Encoding color component %d", band);
-	 tree [band] = RANGE;
-	 if (band == Cb)
-	 {
-	    unsigned min_level;
-
-	    c->domain_pool->chroma (wfa->wfainfo->chroma_max_states, wfa,
-				    c->domain_pool->model);
-	    /*
-	     *  Don't use a finer partioning for the chrominancy bands than for
-	     *  the luminancy band.
-	     */
-	    for (min_level = MAXLEVEL, state = wfa->basis_states;
-		 state < wfa->states; state++)
-	    {
-	       unsigned lincomb, label;
-	       
-	       for (lincomb = 0, label = 0; label < MAXLABELS; label++)
-		  lincomb += isrange (wfa->tree [state][label]) ? 1 : 0;
-	       if (lincomb)
-		  min_level = min (min_level,
-				   (unsigned) (wfa->level_of_state [state]
-					       - 1));
-	    }
-	    c->options.lc_min_level = min_level;
-	    if (c->mt->frame_type != I_FRAME) /* subtract mc of luminance */
-	       subtract_mc (c->mt->original, c->mt->past, c->mt->future, wfa);
-	 }
-
-	 memset (&range, 0, sizeof (range_t));
-	 range.level = wfa->wfainfo->level;
-	 
-	 costs = subdivide (MAXCOSTS, band, tree [Y], &range, wfa, c,
-			    c->mt->frame_type != I_FRAME && band == Y, NO);
-	 if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
-	    message ("");
-	 {
-	    char colors [] = {'Y', 'B', 'R'};
-	    
-	    print_statistics (colors [band], costs, wfa,
-			      c->mt->original, &range);
-	 }
-	 
-	 if (isrange (range.tree))	/* whole image is approx. by a l.c. */
-	    error ("No root state generated for color component %d!", band);
-	 else
-	    tree[band] = range.tree;
-	 
-	 if (band == Cb)
-	 {
-	    wfa->tree [wfa->states][0] = tree[Y];
-	    wfa->tree [wfa->states][1] = tree[Cb];
-	    YCb_node = wfa->states;
-	    append_state (YES, compute_final_distribution (wfa->states, wfa),
-			  wfa->wfainfo->level + 1, wfa, c);
-	 }
+     debug_message ("Encoding color component %d", band);
+     tree [band] = RANGE;
+     if (band == Cb)
+     {
+        unsigned min_level;
+
+        c->domain_pool->chroma (wfa->wfainfo->chroma_max_states, wfa,
+                    c->domain_pool->model);
+        /*
+         *  Don't use a finer partioning for the chrominancy bands than for
+         *  the luminancy band.
+         */
+        for (min_level = MAXLEVEL, state = wfa->basis_states;
+         state < wfa->states; state++)
+        {
+           unsigned lincomb, label;
+           
+           for (lincomb = 0, label = 0; label < MAXLABELS; label++)
+          lincomb += isrange (wfa->tree [state][label]) ? 1 : 0;
+           if (lincomb)
+          min_level = min (min_level,
+                   (unsigned) (wfa->level_of_state [state]
+                           - 1));
+        }
+        c->options.lc_min_level = min_level;
+        if (c->mt->frame_type != I_FRAME) /* subtract mc of luminance */
+           subtract_mc (c->mt->original, c->mt->past, c->mt->future, wfa);
+     }
+
+     memset (&range, 0, sizeof (range_t));
+     range.level = wfa->wfainfo->level;
+     
+     costs = subdivide (MAXCOSTS, band, tree [Y], &range, wfa, c,
+                c->mt->frame_type != I_FRAME && band == Y, NO);
+     if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
+        message ("");
+     {
+        char colors [] = {'Y', 'B', 'R'};
+        
+        print_statistics (colors [band], costs, wfa,
+                  c->mt->original, &range);
+     }
+     
+     if (isrange (range.tree))  /* whole image is approx. by a l.c. */
+        error ("No root state generated for color component %d!", band);
+     else
+        tree[band] = range.tree;
+     
+     if (band == Cb)
+     {
+        wfa->tree [wfa->states][0] = tree[Y];
+        wfa->tree [wfa->states][1] = tree[Cb];
+        YCb_node = wfa->states;
+        append_state (YES, compute_final_distribution (wfa->states, wfa),
+              wfa->wfainfo->level + 1, wfa, c);
+     }
       }
       /*
        *  generate two virtual states (*) 
@@ -850,11 +632,11 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
       wfa->tree [wfa->states][0] = tree[Cr];
       wfa->tree [wfa->states][1] = RANGE;
       append_state (YES, compute_final_distribution (wfa->states, wfa),
-		    wfa->wfainfo->level + 1, wfa, c);
+            wfa->wfainfo->level + 1, wfa, c);
       wfa->tree[wfa->states][0] = YCb_node;
       wfa->tree[wfa->states][1] = wfa->states - 1;
       append_state (YES, compute_final_distribution (wfa->states, wfa),
-		    wfa->wfainfo->level + 2, wfa, c);
+            wfa->wfainfo->level + 2, wfa, c);
 
       wfa->root_state = wfa->states - 1;
    }
@@ -865,22 +647,22 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
       
       if (c->images_of_state [state])
       {
-	 Free (c->images_of_state [state]);
-	 c->images_of_state [state] = NULL;
+     Free (c->images_of_state [state]);
+     c->images_of_state [state] = NULL;
       }
       if (c->ip_images_state [state])
       {
-	 Free (c->ip_images_state [state]);
-	 c->ip_images_state [state] = NULL;
+     Free (c->ip_images_state [state]);
+     c->ip_images_state [state] = NULL;
       }
       for (level = c->options.images_level + 1;
-	   level <= c->options.lc_max_level;
-	   level++)
-	 if (c->ip_states_state [state][level])
-	 {
-	    Free (c->ip_states_state [state][level]);
-	    c->ip_states_state [state][level] = NULL;
-	 }
+       level <= c->options.lc_max_level;
+       level++)
+     if (c->ip_states_state [state][level])
+     {
+        Free (c->ip_states_state [state][level]);
+        c->ip_states_state [state][level] = NULL;
+     }
       
    }
    
@@ -889,11 +671,11 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
    
    bits = bits_processed (output) - bits;
    debug_message ("Total number of bits written: %d (%d bytes, %5.3f bpp)",
-		  bits, bits >> 3,
-		  bits / (double) (c->mt->original->height
-				   * c->mt->original->width));
+          bits, bits >> 3,
+          bits / (double) (c->mt->original->height
+                   * c->mt->original->width));
    debug_message ("Total encoding time (real): %d sec",
-		  prg_timer (&ptimer, STOP) / 1000);
+          prg_timer (&ptimer, STOP) / 1000);
 
    c->domain_pool->free (c->domain_pool);
    c->d_domain_pool->free (c->d_domain_pool);
@@ -902,64 +684,356 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
    c->d_coeff->free (c->d_coeff);
 }
 
+
+
 static void
-print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
-		  const range_t *range)
+video_coder(char const * const * const image_template,
+            bitfile_t *          const output,
+            wfa_t *              const wfa,
+            coding_t *           const c,
+            unsigned int         const stdinwidth,
+            unsigned int         const stdinheight,
+            unsigned int         const stdinmaxval,
+            unsigned int         const stdinformat)
+/*
+ *  Toplevel function to encode a sequence of video frames specified
+ *  by 'image_template'. The output is written to stream 'output'.
+ *  Coding options are given by 'c'.
+ *
+ *  No return value.
+ */
 {
-   unsigned max_level, min_level, state, label, lincomb;
+    unsigned  display;           /* picture number in display order */
+    int       future_display;        /* number of future reference */
+    int       frame;         /* current frame number */
+    char     *image_name;
+        /* image name of current frame.  File name or "-" for Standard Input */
+    image_t  *reconst      = NULL;   /* decoded reference image */
+    bool_t    future_frame = NO;     /* YES if last frame was in future */
    
-   for (max_level = 0, min_level = MAXLEVEL, state = wfa->basis_states;
-	state < wfa->states; state++)
-   {
-      for (lincomb = 0, label = 0; label < MAXLABELS; label++)
-	 lincomb += isrange(wfa->tree[state][label]) ? 1 : 0;
-	 
-      if (lincomb)
-      {
-	 max_level = max (max_level,
-			  (unsigned) (wfa->level_of_state [state] - 1));
-	 min_level = min (min_level,
-			  (unsigned) (wfa->level_of_state [state] - 1));
-      }
-   }
-   debug_message ("Image partitioning: maximum level %d , minimum level %d",
-		  max_level, min_level);
-   debug_message ("WFA contains %d states (%d basis states).",
-		  wfa->states, wfa->basis_states);
-   debug_message ("Estimated error: %.2f (RMSE: %.2f, PSNR: %.2f dB).",
-		  (double) range->err,
-		  sqrt (range->err / image->width / image->height),
-		  10 * log ( 255.0 * 255.0 /
-			     (range->err / image->width / image->height))
-		  / log (10.0));
-   debug_message ("Estimated filesize: %.0f bits (%.0f bytes).",
-		  (double) (range->tree_bits + range->matrix_bits
-			    + range->weights_bits
-			    + range->mv_tree_bits + range->mv_coord_bits
-			    + range->nd_tree_bits + range->nd_weights_bits),
-		  (double) (range->tree_bits + range->matrix_bits
-			    + range->weights_bits + range->mv_tree_bits
-			    + range->mv_coord_bits + range->nd_tree_bits
-			    + range->nd_weights_bits) / 8);
-   if (c)
-      debug_message ("(%cT: %.0f, %cM: %.0f, %cW: %.0f, %cMC: %.0f, "
-		     "%cMV: %.0f, %cNT: %.0f, %cNW: %.0f.)",
-		     c, (double) range->tree_bits,
-		     c, (double) range->matrix_bits,
-		     c, (double) range->weights_bits,
-		     c, (double) range->mv_tree_bits,
-		     c, (double) range->mv_coord_bits,
-		     c, (double) range->nd_tree_bits,
-		     c, (double) range->nd_weights_bits);
-   else
-      debug_message ("(T: %.0f, M: %.0f, W: %.0f, MC: %.0f, MV: %.0f, "
-		     "NT: %.0f, NW: %.0f.)",
-		     (double) range->tree_bits,
-		     (double) range->matrix_bits,
-		     (double) range->weights_bits,
-		     (double) range->mv_tree_bits,
-		     (double) range->mv_coord_bits,
-		     (double) range->nd_tree_bits,
-		     (double) range->nd_weights_bits);
-   debug_message ("Total costs : %.2f", (double) costs);
+    debug_message ("Generating %d WFA's ...", wfa->wfainfo->frames);
+
+    future_display = -1;
+    frame          = -1;
+    display        = 0;
+
+    while ((image_name = get_input_image_name (image_template, display)))
+    {
+        frame_type_e type;        /* current frame type: I, B, P */
+      
+        /*
+         *  Determine type of next frame.
+         *  Skip already coded frames (future reference!)
+         */
+        if (display == 0 && !c->options.reference_filename)
+            type = I_FRAME;        /* Force first frame to be intra */
+        else
+            type = pattern2type (display, c->options.pattern);
+      
+        if (type != I_FRAME && c->options.reference_filename)
+            /* Load reference from disk */
+        {
+            debug_message ("Reading reference frame `%s'.",
+                           c->options.reference_filename);
+            reconst     = read_image_file (c->options.reference_filename);
+            c->options.reference_filename = NULL;
+        }
+        if ((int) display == future_display)
+        {             
+            /* Skip already coded future ref */
+            display++;
+            continue;
+        }   
+        else if (type == B_FRAME && (int) display > future_display) 
+        {
+            unsigned i = display;
+            /*
+             *  Search for future reference
+             */
+            while (type == B_FRAME)
+            {
+                char *name;         /* image name of future frame */
+
+                i++;
+                name = get_input_image_name (image_template, i);
+        
+                if (!name)          /* Force last valid frame to be 'P' */
+                {
+                    future_display = i - 1;
+                    type = P_FRAME;
+                }
+                else
+                {
+                    future_display = i;    
+                    image_name     = name;
+                    type           = pattern2type (i, c->options.pattern);
+                }
+                frame = future_display;
+            }
+        }
+        else
+        {
+            frame = display;
+            display++;
+        }
+
+        debug_message ("Coding \'%s\' [%c-frame].", image_name,
+                       type == I_FRAME ? 'I' : (type == P_FRAME ? 'P' : 'B'));
+       
+        /*
+         *  Depending on current frame type update past and future frames
+         *  which are needed as reference frames.
+         */
+        c->mt->frame_type = type;
+        if (type == I_FRAME)
+        {
+            if (c->mt->past)       /* discard past frame */
+                free_image (c->mt->past);
+            c->mt->past = NULL;
+            if (c->mt->future)     /* discard future frame */
+                free_image (c->mt->future);
+            c->mt->future = NULL;
+            if (reconst)           /* discard current frame */
+                free_image (reconst);
+            reconst = NULL;
+        }
+        else if (type == P_FRAME)
+        {
+            if (c->mt->past)       /* discard past frame */
+                free_image (c->mt->past);
+            c->mt->past = reconst;     /* past frame <- current frame */
+            reconst    = NULL;
+            if (c->mt->future)     /* discard future frame */
+                free_image (c->mt->future);
+            c->mt->future = NULL;
+        }
+        else              /* B_FRAME */
+        {
+            if (future_frame)      /* last frame was future frame */
+            {
+                if (c->mt->future)      /* discard future frame */
+                    free_image (c->mt->future);
+                c->mt->future = reconst;    /* future frame <- current frame */
+                reconst      = NULL;
+            }
+            else
+            {
+                if (wfa->wfainfo->B_as_past_ref == YES)
+                {
+                    if (c->mt->past)     /* discard past frame */
+                        free_image (c->mt->past);
+                    c->mt->past = reconst;   /* past frame <- current frame */
+                    reconst    = NULL;
+                }
+                else
+                {
+                    if (reconst)     /* discard current frame */
+                        free_image (reconst);
+                    reconst = NULL;
+                }
+            }
+        }
+
+        /*
+         *  Start WFA coding of current frame
+         */
+        future_frame   = frame == future_display;
+        c->mt->number   = frame;
+
+        if (streq(image_name, "-"))
+            c->mt->original = read_image_stream(stdin,
+                                                stdinwidth, stdinheight,
+                                                stdinmaxval, stdinformat);
+        else 
+            c->mt->original = read_image_file(image_name);
+
+        if (c->tiling->exponent && type == I_FRAME) 
+            perform_tiling (c->mt->original, c->tiling);
+
+        frame_coder (wfa, c, output);
+
+        /*
+         *  Regenerate image:
+         *  1. Compute approximation of WFA ranges (real image bintree order)
+         *  2. Generate byte image in rasterscan order
+         *  3. Apply motion compensation
+         */
+        reconst = decode_image (wfa->wfainfo->width, wfa->wfainfo->height,
+                                FORMAT_4_4_4, NULL, wfa);
+
+        if (type != I_FRAME)
+            restore_mc (0, reconst, c->mt->past, c->mt->future, wfa);
+
+        if (c->mt->original)
+            free_image (c->mt->original);
+        c->mt->original = NULL;
+      
+        remove_states (wfa->basis_states, wfa); /* Clear WFA structure */
+    }
+
+    if (reconst)
+        free_image (reconst);
+    if (c->mt->future)
+        free_image (c->mt->future);
+    if (c->mt->past)
+        free_image (c->mt->past);
+    if (c->mt->original)
+        free_image (c->mt->original);
+}
+
+
+
+static void
+read_stdin_header(const char * const * const template,
+                  unsigned int * const widthP,
+                  unsigned int * const heightP,
+                  xelval *       const maxvalP,
+                  int *          const formatP)
+/* Read the PNM header from the Standard Input stream, if 'template' says
+   one of the images is to come from Standard Input.
+
+   Return the contents of that stream as *widthP, etc.
+*/
+{
+    unsigned int i;
+    bool endOfList;
+    bool stdinFound;
+
+    for (i = 0, stdinFound = FALSE, endOfList = FALSE; !endOfList; ++i) {
+        const char * const name = get_input_image_name(template, i);
+
+        if (!name)
+            endOfList = TRUE;
+        else {
+            if (streq(name, "-"))
+                stdinFound = TRUE;
+        }
+    }
+
+    if (stdinFound) {
+        int width, height;
+
+        pnm_readpnminit(stdin, &width, &height, maxvalP, formatP);
+
+        *widthP  = width;
+        *heightP = height;
+    }
 }
+
+
+
+/*****************************************************************************
+
+                public code
+  
+*****************************************************************************/
+
+int
+fiasco_coder (char const * const *inputname, const char *outputname,
+          float quality, const fiasco_c_options_t *options)
+/*
+ *  FIASCO coder.
+ *  Encode image or video frames given by the array of filenames `inputname'
+ *  and write to the outputfile `outputname'.
+ *  If 'inputname' = NULL or
+ *     'inputname [0]' == NULL or
+ *     'inputname [0]' == "-", read standard input.
+ *  If 'outputname' == NULL or "-", write on standard output.
+ *  'quality' defines the approximation quality and is 1 (worst) to 100 (best).
+ *
+ *  Return value:
+ *  1 on success
+ *  0 otherwise
+ */
+{
+    try
+        {
+            char const * const default_input [] = {"-", NULL};
+            fiasco_c_options_t *default_options = NULL;
+            const c_options_t  *cop;
+            char const * const *template;
+            unsigned int stdinheight, stdinwidth;
+            xelval stdinmaxval;
+            int stdinformat;
+      
+            /*
+             *  Check parameters
+             */
+            if (!inputname || !inputname [0] || streq (inputname [0], "-"))
+                template = default_input;
+            else
+                template = inputname;
+      
+            if (quality <= 0)
+            {
+                set_error (_("Compression quality has to be positive."));
+                return 0;
+            }
+            else if (quality >= 100)
+            {
+                warning (_("Quality typically is 1 (worst) to 100 (best).\n"
+                           "Be prepared for a long running time."));
+            }
+
+            if (options)
+            {
+                cop = cast_c_options ((fiasco_c_options_t *) options);
+                if (!cop)
+                    return 0;
+            }
+            else
+            {
+                default_options = fiasco_c_options_new ();
+                cop         = cast_c_options (default_options);
+            }
+
+            read_stdin_header(template, &stdinwidth, &stdinheight,
+                              &stdinmaxval, &stdinformat);
+
+            /*
+             *  Open output stream and initialize WFA
+             */
+            {
+                bitfile_t *output = open_bitfile (outputname, "FIASCO_DATA",
+                                                  WRITE_ACCESS);
+                if (!output)
+                {
+                    set_error (_("Can't write outputfile `%s'.\n%s"),
+                               outputname ? outputname : "<stdout>",
+                               get_system_error ());
+                    if (default_options)
+                        fiasco_c_options_delete (default_options);
+                    return 0;
+                }
+                else
+                {
+                    wfa_t    *wfa = alloc_wfa (YES);
+                    coding_t *c   = alloc_coder(template, cop, wfa->wfainfo,
+                                                stdinwidth, stdinheight,
+                                                stdinmaxval, stdinformat);
+     
+                    read_basis (cop->basis_name, wfa);
+                    append_basis_states (wfa->basis_states, wfa, c);
+     
+                    c->price = 128 * 64 / quality;
+     
+                    video_coder (template, output, wfa, c,
+                                 stdinwidth, stdinheight, stdinmaxval,
+                                 stdinformat);
+     
+                    close_bitfile (output);
+                    free_wfa (wfa);
+                    free_coder (c);
+     
+                    if (default_options)
+                        fiasco_c_options_delete (default_options);
+                }
+            }
+            return 1;
+        }
+    catch
+        {
+            return 0;
+        }
+}
+
diff --git a/converter/other/fiasco/codec/decoder.c b/converter/other/fiasco/codec/decoder.c
index c3982617..ab06dd2e 100644
--- a/converter/other/fiasco/codec/decoder.c
+++ b/converter/other/fiasco/codec/decoder.c
@@ -1,9 +1,9 @@
 /*
- *  decode.c:		Decoding of an image represented by a WFA
+ *  decode.c:       Decoding of an image represented by a WFA
  *
- *  Written by:		Ullrich Hafner
- *			Michael Unger
- *		
+ *  Written by:     Ullrich Hafner
+ *          Michael Unger
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -15,12 +15,13 @@
  *  $State: Exp $
  */
 
+#include "pm_config.h"
 #include "config.h"
 
 #if HAVE_STRING_H
-#	include <string.h>
+#   include <string.h>
 #else /* not HAVE_STRING_H */
-#	include <strings.h>
+#   include <strings.h>
 #endif /* not HAVE_STRING_H */
 
 #include "types.h"
@@ -37,33 +38,33 @@
 
 /*****************************************************************************
 
-				prototypes
+                prototypes
   
 *****************************************************************************/
 
 static void
 compute_state_images (unsigned frame_level, word_t **simg,
-		      const u_word_t *offset, const wfa_t *wfa);
+              const u_word_t *offset, const wfa_t *wfa);
 static void
 free_state_images (unsigned max_level, bool_t color, word_t **state_image,
-		   u_word_t *offset, const unsigned *root_state,
-		   unsigned range_state, format_e format, const wfa_t *wfa);
+           u_word_t *offset, const unsigned *root_state,
+           unsigned range_state, format_e format, const wfa_t *wfa);
 static void
 alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
-		    const unsigned *root_state, unsigned range_state,
-		    unsigned max_level, format_e format, const wfa_t *wfa);
+            const unsigned *root_state, unsigned range_state,
+            unsigned max_level, format_e format, const wfa_t *wfa);
 static void
 compute_actual_size (unsigned luminance_root,
-		     unsigned *width, unsigned *height, const wfa_t *wfa);
+             unsigned *width, unsigned *height, const wfa_t *wfa);
 static void
 enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
-	       wfa_t *wfa);
+           wfa_t *wfa);
 static word_t *
 duplicate_state_image (const word_t *domain, unsigned offset, unsigned level);
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
 
@@ -75,7 +76,7 @@ alloc_video (bool_t store_wfa)
  *  and future WFA if flag 'store_wfa' is TRUE.
  *
  *  Return value:
- *	pointer to the new video structure
+ *  pointer to the new video structure
  */
 {
    video_t *video = Calloc (1, sizeof (video_t));
@@ -84,7 +85,7 @@ alloc_video (bool_t store_wfa)
    video->display        = 0;
 
    video->future = video->sfuture = video->past
-		 = video->frame   = video->sframe = NULL;
+         = video->frame   = video->sframe = NULL;
 
    if (store_wfa)
    {
@@ -107,7 +108,7 @@ free_video (video_t *video)
  *  No return value.
  *
  *  Side effects:
- *	'video' struct is discarded.
+ *  'video' struct is discarded.
  */
 {
    if (video->past)
@@ -132,9 +133,9 @@ free_video (video_t *video)
 
 image_t *
 get_next_frame (bool_t store_wfa, int enlarge_factor,
-		int smoothing, const char *reference_frame,
-		format_e format, video_t *video, dectimer_t *timer,
-		wfa_t *orig_wfa, bitfile_t *input)
+        int smoothing, const char *reference_frame,
+        format_e format, video_t *video, dectimer_t *timer,
+        wfa_t *orig_wfa, bitfile_t *input)
 /*
  *  Get next frame of the WFA 'video' from stream 'input'.
  *  'orig_wfa' is the constant part of the WFA used by all frames.
@@ -148,265 +149,265 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
  *  If 'timer' is not NULL, then accumulate running time statistics. 
  *
  *  Return value:
- *	pointer to decoded frame
+ *  pointer to decoded frame
  *
  *  Side effects:
- *	'video' and 'timer' struct are modified.
+ *  'video' and 'timer' struct are modified.
  */
 {
-   image_t *frame 			  = NULL; /* current frame */
-   image_t *sframe 			  = NULL; /* current smoothed frame */
+   image_t *frame             = NULL; /* current frame */
+   image_t *sframe            = NULL; /* current smoothed frame */
    bool_t   current_frame_is_future_frame = NO;
 
-   if (video->future_display == video->display)	 
+   if (video->future_display == video->display)  
    {
       /*
        *  Future frame is already computed since it has been used
        *  as reference frame. So just return the stored frame.
        */
       if (video->frame) /* discard current frame */
-	 free_image (video->frame);
+     free_image (video->frame);
       video->frame  = video->future;
       video->future = NULL;
 
       if (video->sframe) /* discard current (smoothed) frame */
-	 free_image (video->sframe);
+     free_image (video->sframe);
       video->sframe  = video->sfuture;
       video->sfuture = NULL;
 
       if (store_wfa)
-	 copy_wfa (video->wfa, video->wfa_future);
+     copy_wfa (video->wfa, video->wfa_future);
 
       video->display++;
 
       if (!store_wfa)
-	 video->wfa = NULL;
+     video->wfa = NULL;
    }
    else
    {
-      do				/* compute next frame(s) */
+      do                /* compute next frame(s) */
       {
-	 unsigned      frame_number;	/* current frame number */
-	 clock_t       ptimer;
-	 unsigned int  stop_timer [3];
-	 wfa_t	      *tmp_wfa = NULL;
-	 
-	 if (!store_wfa)
-	    video->wfa = orig_wfa;
-	 else
-	 {
-	    tmp_wfa = alloc_wfa (NO);
-	    copy_wfa (tmp_wfa, video->wfa);
-	    copy_wfa (video->wfa, orig_wfa);
-	 }
+     unsigned      frame_number;    /* current frame number */
+     clock_t       ptimer;
+     unsigned int  stop_timer [3];
+     wfa_t        *tmp_wfa = NULL;
+     
+     if (!store_wfa)
+        video->wfa = orig_wfa;
+     else
+     {
+        tmp_wfa = alloc_wfa (NO);
+        copy_wfa (tmp_wfa, video->wfa);
+        copy_wfa (video->wfa, orig_wfa);
+     }
    
-	 /*
-	  *  First step: read WFA from disk
-	  */
-	 prg_timer (&ptimer, START);
-	 frame_number = read_next_wfa (video->wfa, input);
-	 stop_timer [0] = prg_timer (&ptimer, STOP);
-	 if (timer)
-	 {
-	    timer->input [video->wfa->frame_type] += stop_timer [0];
-	    timer->frames [video->wfa->frame_type]++;
-	 }
+     /*
+      *  First step: read WFA from disk
+      */
+     prg_timer (&ptimer, START);
+     frame_number = read_next_wfa (video->wfa, input);
+     stop_timer [0] = prg_timer (&ptimer, STOP);
+     if (timer)
+     {
+        timer->input [video->wfa->frame_type] += stop_timer [0];
+        timer->frames [video->wfa->frame_type]++;
+     }
       
-	 /*
-	  *  Read reference frame from disk if required
-	  *  (i.e., 1st frame is of type B or P)
-	  */
-	 if (video->display == 0 && video->wfa->frame_type != I_FRAME)
-	 {
-	    if (!reference_frame)
-	       error ("First frame is %c-frame but no "
-		      "reference frame is given.",
-		      video->wfa->frame_type == B_FRAME ? 'B' : 'P');
-
-	    video->frame  = read_image (reference_frame);
-	    video->sframe = NULL;
-	 }
+     /*
+      *  Read reference frame from disk if required
+      *  (i.e., 1st frame is of type B or P)
+      */
+     if (video->display == 0 && video->wfa->frame_type != I_FRAME)
+     {
+        if (!reference_frame)
+           error ("First frame is %c-frame but no "
+              "reference frame is given.",
+              video->wfa->frame_type == B_FRAME ? 'B' : 'P');
+
+        video->frame  = read_image_file (reference_frame);
+        video->sframe = NULL;
+     }
    
-	 /*
-	  *  Depending on current frame type update past and future frames
-	  */
-	 if (video->wfa->frame_type == I_FRAME)
-	 {
-	    if (video->past)		/* discard past frame */
-	       free_image (video->past);
-	    video->past = NULL;
-	    if (video->future)		/* discard future frame */
-	       free_image (video->future);
-	    video->future = NULL;
-	    if (video->sfuture)		/* discard (smoothed) future frame */
-	       free_image (video->sfuture);
-	    video->sfuture = NULL;
-	    if (video->frame)		/* discard current frame */
-	       free_image (video->frame);
-	    video->frame = NULL;
-	    if (video->sframe)		/* discard current (smoothed) frame */
-	       free_image (video->sframe);
-	    video->sframe = NULL;
-	 }
-	 else if (video->wfa->frame_type == P_FRAME)
-	 {
-	    if (video->past)		/* discard past frame */
-	       free_image (video->past);
-	    video->past = video->frame;	/* past <- current frame */
-	    video->frame = NULL;
-	    if (video->sframe)		/* discard current (smoothed) frame */
-	       free_image (video->sframe);
-	    video->sframe = NULL;
-	    if (store_wfa)
-	       copy_wfa (video->wfa_past, tmp_wfa);
-	    if (video->future)		/* discard future frame */
-	       free_image (video->future);
-	    video->future = NULL;
-	    if (video->sfuture)		/* discard (smoothed) future frame */
-	       free_image (video->sfuture);
-	    video->sfuture = NULL;
-	 }
-	 else				/* B_FRAME */
-	 {
-	    if (current_frame_is_future_frame)
-	    {
-	       if (video->future)	/* discard future frame */
-		  free_image (video->future);
-	       video->future = frame;	/* future <- current frame */
-	       if (video->sfuture)	/* discard (smoothed) future frame */
-		  free_image (video->sfuture);
-	       video->sfuture = sframe;	/* future <- current (smoothed) */
-	       if (store_wfa)
-		  copy_wfa (video->wfa_future, tmp_wfa);
-	       if (video->frame)	/* discard current frame */
-		  free_image (video->frame);
-	       video->frame = NULL;
-	       if (video->sframe)	/* discard current (smoothed) frame */
-		  free_image (video->sframe);
-	       video->sframe = NULL;
-	       frame  = NULL;
-	       sframe = NULL;
-	    }
-	    else
-	    {
-	       if (video->wfa->wfainfo->B_as_past_ref == YES)
-	       {
-		  if (video->past)	/* discard past frame */
-		     free_image (video->past);
-		  video->past  = video->frame; /* past <- current frame */
-		  video->frame = NULL;
-		  if (video->sframe)	/* discard current (smoothed) frame */
-		     free_image (video->sframe);
-		  video->sframe = NULL;
-		  if (store_wfa)
-		     copy_wfa (video->wfa_past, tmp_wfa);
-	       }
-	       else
-	       {
-		  if (video->frame)	/* discard current */
-		     free_image (video->frame);
-		  video->frame = NULL;
-		  if (video->sframe)	/* discard current (smoothed) frame */
-		     free_image (video->sframe);
-		  video->sframe = NULL;
-	       }
-	    }
-	 }
-	 if (tmp_wfa)
-	    free_wfa (tmp_wfa);
-	 
-	 current_frame_is_future_frame = NO;
-	 /*
-	  *  Second step: decode image
-	  *  Optionally enlarge image if specified by option 'enlarge_factor'.
-	  */
-	 {
-	    unsigned orig_width, orig_height;
-
-	    stop_timer [0] = stop_timer [1] = stop_timer [2] = 0;
-	 
-	    enlarge_image (enlarge_factor, format,
-			   (video->wfa->wfainfo->color
-			    && format == FORMAT_4_2_0)
-			   ? video->wfa->tree [video->wfa->tree [video->wfa->root_state][0]][0] : -1, video->wfa);
-
-	    if (enlarge_factor > 0)
-	    {
-	       orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
-	       orig_height = video->wfa->wfainfo->height << enlarge_factor; 
-	    }
-	    else
-	    { 
-	       orig_width  = video->wfa->wfainfo->width  >> - enlarge_factor;
-	       orig_height = video->wfa->wfainfo->height >> - enlarge_factor;
-	       if (orig_width & 1)
-		  orig_width++;
-	       if (orig_height & 1)
-		  orig_height++;
-	    }
-	 
-	    frame = decode_image (orig_width, orig_height, format,
-				  timer != NULL ? stop_timer : NULL,
-				  video->wfa);
-	    if (timer)
-	    {
-	       timer->preprocessing [video->wfa->frame_type] += stop_timer [0];
-	       timer->decoder [video->wfa->frame_type]       += stop_timer [1];
-	       timer->cleanup [video->wfa->frame_type]       += stop_timer [2];
-	    }
-	 }
-
-	 /*
-	  *  Third step: restore motion compensation
-	  */
-	 if (video->wfa->frame_type != I_FRAME)
-	 {
-	    prg_timer (&ptimer, START);
-	    restore_mc (enlarge_factor, frame, video->past, video->future,
-			video->wfa);
-	    stop_timer [0] = prg_timer (&ptimer, STOP);
-	    if (timer)
-	       timer->motion [video->wfa->frame_type] += stop_timer [0];
-	 }
-
-	 /*
-	  *  Fourth step: smooth image along partitioning borders
-	  */
-	 prg_timer (&ptimer, START);
-	 if (smoothing < 0)	/* smoothing not changed by user */
-	    smoothing = video->wfa->wfainfo->smoothing;
-	 if (smoothing > 0 && smoothing <= 100)
-	 {
-	    sframe = clone_image (frame);
-	    smooth_image (smoothing, video->wfa, sframe);
-	 }
-	 else
-	    sframe = NULL;
-	 
-	 stop_timer [0] = prg_timer (&ptimer, STOP);
-	 if (timer)
-	    timer->smooth [video->wfa->frame_type] += stop_timer [0];
-
-	 if (frame_number == video->display)
-	 {
-	    video->display++;
-	    video->frame  = frame;
-	    video->sframe = sframe;
-	    frame         = NULL;
-	    sframe        = NULL;
-	 }
-	 else if (frame_number > video->display)
-	 {
-	    video->future_display 	  = frame_number;
-	    current_frame_is_future_frame = YES;
-	 }
+     /*
+      *  Depending on current frame type update past and future frames
+      */
+     if (video->wfa->frame_type == I_FRAME)
+     {
+        if (video->past)        /* discard past frame */
+           free_image (video->past);
+        video->past = NULL;
+        if (video->future)      /* discard future frame */
+           free_image (video->future);
+        video->future = NULL;
+        if (video->sfuture)     /* discard (smoothed) future frame */
+           free_image (video->sfuture);
+        video->sfuture = NULL;
+        if (video->frame)       /* discard current frame */
+           free_image (video->frame);
+        video->frame = NULL;
+        if (video->sframe)      /* discard current (smoothed) frame */
+           free_image (video->sframe);
+        video->sframe = NULL;
+     }
+     else if (video->wfa->frame_type == P_FRAME)
+     {
+        if (video->past)        /* discard past frame */
+           free_image (video->past);
+        video->past = video->frame; /* past <- current frame */
+        video->frame = NULL;
+        if (video->sframe)      /* discard current (smoothed) frame */
+           free_image (video->sframe);
+        video->sframe = NULL;
+        if (store_wfa)
+           copy_wfa (video->wfa_past, tmp_wfa);
+        if (video->future)      /* discard future frame */
+           free_image (video->future);
+        video->future = NULL;
+        if (video->sfuture)     /* discard (smoothed) future frame */
+           free_image (video->sfuture);
+        video->sfuture = NULL;
+     }
+     else               /* B_FRAME */
+     {
+        if (current_frame_is_future_frame)
+        {
+           if (video->future)   /* discard future frame */
+          free_image (video->future);
+           video->future = frame;   /* future <- current frame */
+           if (video->sfuture)  /* discard (smoothed) future frame */
+          free_image (video->sfuture);
+           video->sfuture = sframe; /* future <- current (smoothed) */
+           if (store_wfa)
+          copy_wfa (video->wfa_future, tmp_wfa);
+           if (video->frame)    /* discard current frame */
+          free_image (video->frame);
+           video->frame = NULL;
+           if (video->sframe)   /* discard current (smoothed) frame */
+          free_image (video->sframe);
+           video->sframe = NULL;
+           frame  = NULL;
+           sframe = NULL;
+        }
+        else
+        {
+           if (video->wfa->wfainfo->B_as_past_ref == YES)
+           {
+          if (video->past)  /* discard past frame */
+             free_image (video->past);
+          video->past  = video->frame; /* past <- current frame */
+          video->frame = NULL;
+          if (video->sframe)    /* discard current (smoothed) frame */
+             free_image (video->sframe);
+          video->sframe = NULL;
+          if (store_wfa)
+             copy_wfa (video->wfa_past, tmp_wfa);
+           }
+           else
+           {
+          if (video->frame) /* discard current */
+             free_image (video->frame);
+          video->frame = NULL;
+          if (video->sframe)    /* discard current (smoothed) frame */
+             free_image (video->sframe);
+          video->sframe = NULL;
+           }
+        }
+     }
+     if (tmp_wfa)
+        free_wfa (tmp_wfa);
+     
+     current_frame_is_future_frame = NO;
+     /*
+      *  Second step: decode image
+      *  Optionally enlarge image if specified by option 'enlarge_factor'.
+      */
+     {
+        unsigned orig_width, orig_height;
+
+        stop_timer [0] = stop_timer [1] = stop_timer [2] = 0;
+     
+        enlarge_image (enlarge_factor, format,
+               (video->wfa->wfainfo->color
+                && format == FORMAT_4_2_0)
+               ? video->wfa->tree [video->wfa->tree [video->wfa->root_state][0]][0] : -1, video->wfa);
+
+        if (enlarge_factor > 0)
+        {
+           orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
+           orig_height = video->wfa->wfainfo->height << enlarge_factor; 
+        }
+        else
+        { 
+           orig_width  = video->wfa->wfainfo->width  >> - enlarge_factor;
+           orig_height = video->wfa->wfainfo->height >> - enlarge_factor;
+           if (orig_width & 1)
+          orig_width++;
+           if (orig_height & 1)
+          orig_height++;
+        }
+     
+        frame = decode_image (orig_width, orig_height, format,
+                  timer != NULL ? stop_timer : NULL,
+                  video->wfa);
+        if (timer)
+        {
+           timer->preprocessing [video->wfa->frame_type] += stop_timer [0];
+           timer->decoder [video->wfa->frame_type]       += stop_timer [1];
+           timer->cleanup [video->wfa->frame_type]       += stop_timer [2];
+        }
+     }
+
+     /*
+      *  Third step: restore motion compensation
+      */
+     if (video->wfa->frame_type != I_FRAME)
+     {
+        prg_timer (&ptimer, START);
+        restore_mc (enlarge_factor, frame, video->past, video->future,
+            video->wfa);
+        stop_timer [0] = prg_timer (&ptimer, STOP);
+        if (timer)
+           timer->motion [video->wfa->frame_type] += stop_timer [0];
+     }
+
+     /*
+      *  Fourth step: smooth image along partitioning borders
+      */
+     prg_timer (&ptimer, START);
+     if (smoothing < 0) /* smoothing not changed by user */
+        smoothing = video->wfa->wfainfo->smoothing;
+     if (smoothing > 0 && smoothing <= 100)
+     {
+        sframe = clone_image (frame);
+        smooth_image (smoothing, video->wfa, sframe);
+     }
+     else
+        sframe = NULL;
+     
+     stop_timer [0] = prg_timer (&ptimer, STOP);
+     if (timer)
+        timer->smooth [video->wfa->frame_type] += stop_timer [0];
+
+     if (frame_number == video->display)
+     {
+        video->display++;
+        video->frame  = frame;
+        video->sframe = sframe;
+        frame         = NULL;
+        sframe        = NULL;
+     }
+     else if (frame_number > video->display)
+     {
+        video->future_display     = frame_number;
+        current_frame_is_future_frame = YES;
+     }
       
-	 if (!store_wfa)
-	    remove_states (video->wfa->basis_states, video->wfa);
+     if (!store_wfa)
+        remove_states (video->wfa->basis_states, video->wfa);
       } while (!video->frame);
 
       if (!store_wfa)
-	 video->wfa = NULL;
+     video->wfa = NULL;
    }
    
    return video->sframe ? video->sframe : video->frame;
@@ -414,7 +415,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
 
 image_t *
 decode_image (unsigned orig_width, unsigned orig_height, format_e format,
-	      unsigned *dec_timer, const wfa_t *wfa)
+          unsigned *dec_timer, const wfa_t *wfa)
 /*
  *  Compute image which is represented by the given 'wfa'.
  *  'orig_width'x'orig_height' gives the resolution of the image at
@@ -422,20 +423,20 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
  *  If 'dec_timer' is given, accumulate running time statistics. 
  *  
  *  Return value:
- *	pointer to decoded image
+ *  pointer to decoded image
  *
  *  Side effects:
- *	'*dectimer' is changed if 'dectimer' != NULL.
+ *  '*dectimer' is changed if 'dectimer' != NULL.
  */
 {
-   unsigned   root_state [3];		/* root of bintree for each band */
-   unsigned   width, height;		/* computed image size */
-   image_t   *frame;			/* regenerated frame */
-   word_t   **images;			/* pointer to array of pointers
-					   to state images */
-   u_word_t  *offsets;			/* pointer to array of state image
-					   offsets */
-   unsigned   max_level;		/* max. level of state with approx. */
+   unsigned   root_state [3];       /* root of bintree for each band */
+   unsigned   width, height;        /* computed image size */
+   image_t   *frame;            /* regenerated frame */
+   word_t   **images;           /* pointer to array of pointers
+                       to state images */
+   u_word_t  *offsets;          /* pointer to array of state image
+                       offsets */
+   unsigned   max_level;        /* max. level of state with approx. */
    unsigned   state;
    clock_t    ptimer;
 
@@ -458,14 +459,14 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
     */
    for (max_level = 0, state = wfa->basis_states; state < wfa->states; state++)
       if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
-	 max_level = max (max_level, wfa->level_of_state [state]);
+     max_level = max (max_level, wfa->level_of_state [state]);
    
 
    /*
     *  Allocate frame buffer for decoded image
     */
    compute_actual_size (format == FORMAT_4_2_0 ? root_state [Y] : MAXSTATES,
-			&width, &height, wfa);
+            &width, &height, wfa);
    width  = max (width, orig_width);
    height = max (height, orig_height);
    frame = alloc_image (width, height, wfa->wfainfo->color, format);
@@ -480,7 +481,7 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
       wfa->level_of_state [wfa->tree[wfa->root_state][1]] = 128;
    }
    alloc_state_images (&images, &offsets, frame, root_state, 0, max_level, 
-		       format, wfa);
+               format, wfa);
 
    if (dec_timer)
       dec_timer [0] += prg_timer (&ptimer, STOP);
@@ -498,38 +499,38 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
     */
    prg_timer (&ptimer, START);
    free_state_images (max_level, frame->color, images, offsets, root_state, 0,
-		      format, wfa);
+              format, wfa);
    
    /*
     *  Crop decoded image if the image size differs.
     */
    if (orig_width != width || orig_height != height)
    {
-      frame->height = orig_height;	
-      frame->width  = orig_width;	
-      if (orig_width != width)		
+      frame->height = orig_height;  
+      frame->width  = orig_width;   
+      if (orig_width != width)      
       {
-	 color_e   band;		/* current color band */
-	 word_t	  *src, *dst;		/* source and destination pointers */
-	 unsigned  y;			/* current row */
-	 
-	 for (band  = first_band (frame->color);
-	      band <= last_band (frame->color); band++)
-	 {
-	    src = dst = frame->pixels [band];
-	    for (y = orig_height; y; y--)
-	    {
-	       memmove (dst, src, orig_width * sizeof (word_t));
-	       dst += orig_width;
-	       src += width;
-	    }
-	    if (format == FORMAT_4_2_0 && band == Y)
-	    {
-	       orig_width  >>= 1;
-	       orig_height >>= 1;
-	       width       >>= 1;
-	    }
-	 }
+     color_e   band;        /* current color band */
+     word_t   *src, *dst;       /* source and destination pointers */
+     unsigned  y;           /* current row */
+     
+     for (band  = first_band (frame->color);
+          band <= last_band (frame->color); band++)
+     {
+        src = dst = frame->pixels [band];
+        for (y = orig_height; y; y--)
+        {
+           memmove (dst, src, orig_width * sizeof (word_t));
+           dst += orig_width;
+           src += width;
+        }
+        if (format == FORMAT_4_2_0 && band == Y)
+        {
+           orig_width  >>= 1;
+           orig_height >>= 1;
+           width       >>= 1;
+        }
+     }
       }
    }
    if (dec_timer)
@@ -544,10 +545,10 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
  *  Decode 'state' image of 'wfa' at given 'level'.
  *
  *  Return value.
- *	pointer to decoded state image
+ *  pointer to decoded state image
  *
  *  Side effects:
- *	'wfa' states > 'state' are removed.  
+ *  'wfa' states > 'state' are removed.  
  */
 {
    word_t  *domains [2];
@@ -571,15 +572,15 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
     */
    {
       word_t   *src, *dst;
-      unsigned	y;
-	    
+      unsigned  y;
+        
       src = domains [0];
       dst = img->pixels [GRAY];
       for (y = img->height; y; y--)
       {
-	 memcpy (dst, src, width_of_level (level) * sizeof (word_t));
-	 src += width_of_level (level);
-	 dst += img->width;
+     memcpy (dst, src, width_of_level (level) * sizeof (word_t));
+     src += width_of_level (level);
+     dst += img->width;
       }
       Free (domains [0]);
    }
@@ -589,87 +590,87 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
 
 word_t *
 decode_range (unsigned range_state, unsigned range_label, unsigned range_level,
-	      word_t **domain, wfa_t *wfa)
+          word_t **domain, wfa_t *wfa)
 /*
  *  Compute 'wfa' image of range (identified by 'state' and 'label')
  *  at 'range_level (works as function decode_image()).
  *
  *  Return value:
- *	pointer to the pixels in SHORT format
+ *  pointer to the pixels in SHORT format
  *
  *  Side effects:
- *	if 'domain' != NULL then also the domain blocks
- *	of the corresponding range blocks are generated
+ *  if 'domain' != NULL then also the domain blocks
+ *  of the corresponding range blocks are generated
  *      and returned in domain[]
- *	'wfa->level_of_state []' is changed
+ *  'wfa->level_of_state []' is changed
  */
 {
-   unsigned   root_state [3];		/* dummy (for alloc_state_images) */
-   image_t   *state_image;		/* regenerated state image */
-   word_t   **images;			/* pointer to array of pointers
-					   to state images */
-   u_word_t  *offsets;			/* pointer to array of state image
-					   offsets */
+   unsigned   root_state [3];       /* dummy (for alloc_state_images) */
+   image_t   *state_image;      /* regenerated state image */
+   word_t   **images;           /* pointer to array of pointers
+                       to state images */
+   u_word_t  *offsets;          /* pointer to array of state image
+                       offsets */
    word_t    *range;
 
    enlarge_image (range_level - (wfa->level_of_state [range_state] - 1),
-		  FORMAT_4_4_4, -1, wfa);
+          FORMAT_4_4_4, -1, wfa);
    root_state [0] = range_state;
    state_image    = alloc_image (width_of_level (range_level + 1),
-				 height_of_level (range_level + 1),
-				 NO, FORMAT_4_4_4);
+                 height_of_level (range_level + 1),
+                 NO, FORMAT_4_4_4);
    alloc_state_images (&images, &offsets, state_image, NULL, range_state,
-		       range_level + 1, NO, wfa);
+               range_level + 1, NO, wfa);
    compute_state_images (range_level + 1, images, offsets, wfa);
 
    range = Calloc (size_of_level (range_level), sizeof (word_t));
 
-   if ((range_level & 1) == 0)		/* square image */
+   if ((range_level & 1) == 0)      /* square image */
    {
       memcpy (range,
-	      images [range_state + (range_level + 1) * wfa->states]
-	      + range_label * size_of_level (range_level),
-	      size_of_level (range_level) * sizeof (word_t));
+          images [range_state + (range_level + 1) * wfa->states]
+          + range_label * size_of_level (range_level),
+          size_of_level (range_level) * sizeof (word_t));
    }
-   else					/* rectangle */
+   else                 /* rectangle */
    {
       word_t   *src, *dst;
       unsigned  y;
       
       src = images [range_state + (range_level + 1) * wfa->states]
-	    + range_label * width_of_level (range_level);
+        + range_label * width_of_level (range_level);
       dst = range;
       for (y = height_of_level (range_level); y; y--)
       {
-	 memcpy (dst, src, width_of_level (range_level) * sizeof (word_t));
-	 dst += width_of_level (range_level);
-	 src += width_of_level (range_level + 1);
+     memcpy (dst, src, width_of_level (range_level) * sizeof (word_t));
+     dst += width_of_level (range_level);
+     src += width_of_level (range_level + 1);
       }
    }
 
-   if (domain != NULL)			/* copy domain images */
+   if (domain != NULL)          /* copy domain images */
    {
-      int      s;			/* domain state */
-      unsigned edge;			/* counter */
-		
+      int      s;           /* domain state */
+      unsigned edge;            /* counter */
+        
       if (ischild (s = wfa->tree [range_state][range_label]))
-	 *domain++ = duplicate_state_image (images [s + (range_level)
-						   * wfa->states],
-					    offsets [s + (range_level)
-						    * wfa->states],
-					    range_level);
+     *domain++ = duplicate_state_image (images [s + (range_level)
+                           * wfa->states],
+                        offsets [s + (range_level)
+                            * wfa->states],
+                        range_level);
       for (edge = 0; isedge (s = wfa->into[range_state][range_label][edge]);
-	   edge++)
-	 *domain++ = duplicate_state_image (images [s + (range_level)
-						   * wfa->states],
-					    offsets [s + (range_level)
-						    * wfa->states],
-					    range_level);
+       edge++)
+     *domain++ = duplicate_state_image (images [s + (range_level)
+                           * wfa->states],
+                        offsets [s + (range_level)
+                            * wfa->states],
+                        range_level);
       *domain = NULL;
    }
    
    free_state_images (range_level + 1, NO, images, offsets, NULL, range_state,
-		      NO, wfa);
+              NO, wfa);
    free_image (state_image);
    
    return range;
@@ -684,102 +685,102 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
  *  No return value.
  *
  *  Side effects:
- *	pixel values of the 'image' are modified with respect to 's'
+ *  pixel values of the 'image' are modified with respect to 's'
  */
 {
-   int	    is, inegs;			/* integer factors of s and 1 - s*/
-   unsigned state;			
+   int      is, inegs;          /* integer factors of s and 1 - s*/
+   unsigned state;          
    unsigned img_width  = image->width;
    unsigned img_height = image->height;
-   real_t   s 	       = 1.0 - sf / 200.0;
+   real_t   s          = 1.0 - sf / 200.0;
 
-   if (s < 0.5 || s >= 1)		/* value out of range */
+   if (s < 0.5 || s >= 1)       /* value out of range */
       return;
 
-   is 	 = s * 512 + .5;		/* integer representation of s */
-   inegs = (1 - s) * 512 + .5;		/* integer representation of 1 - s */
+   is    = s * 512 + .5;        /* integer representation of s */
+   inegs = (1 - s) * 512 + .5;      /* integer representation of 1 - s */
    
    for (state = wfa->basis_states;
-	state < (wfa->wfainfo->color
-		 ? wfa->tree [wfa->root_state][0]
-		 : wfa->states); state++)
+    state < (wfa->wfainfo->color
+         ? wfa->tree [wfa->root_state][0]
+         : wfa->states); state++)
    {
       word_t   *bptr   = image->pixels [Y]; /* pointer to right or
-					       lower line */
+                           lower line */
       unsigned  level  = wfa->level_of_state[state]; /* level of state image */
       unsigned  width  = width_of_level (level); /* size of state image */
       unsigned  height = height_of_level (level); /* size of state image */
       
       if (wfa->y [state][1] >= img_height || wfa->x [state][1] >= img_width)
-	 continue;			/* outside visible area */
-	 
-      if (level % 2)			/* horizontal smoothing */
+     continue;          /* outside visible area */
+     
+      if (level % 2)            /* horizontal smoothing */
       {
-	 unsigned  i;			/* line counter */
-	 word_t   *img1;		/* pointer to left or upper line */
-	 word_t   *img2;		/* pointer to right or lower line */
-
-	 img1 = bptr + (wfa->y [state][1] - 1) * img_width
-		+ wfa->x [state][1];
-	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
-	 
-	 for (i = min (width, img_width - wfa->x [state][1]); i;
-	      i--, img1++, img2++)
-	 {
-	    int tmp = *img1;
-	    
+     unsigned  i;           /* line counter */
+     word_t   *img1;        /* pointer to left or upper line */
+     word_t   *img2;        /* pointer to right or lower line */
+
+     img1 = bptr + (wfa->y [state][1] - 1) * img_width
+        + wfa->x [state][1];
+     img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+     
+     for (i = min (width, img_width - wfa->x [state][1]); i;
+          i--, img1++, img2++)
+     {
+        int tmp = *img1;
+        
 #ifdef HAVE_SIGNED_SHIFT
-	    *img1 = (((is * tmp) >> 10) << 1)
-		    + (((inegs * (int) *img2) >> 10) << 1);
-	    *img2 = (((is * (int) *img2) >> 10) << 1)
-		    + (((inegs * tmp) >> 10) << 1);
+        *img1 = (((is * tmp) >> 10) << 1)
+            + (((inegs * (int) *img2) >> 10) << 1);
+        *img2 = (((is * (int) *img2) >> 10) << 1)
+            + (((inegs * tmp) >> 10) << 1);
 #else /* not HAVE_SIGNED_SHIFT */
-	    *img1 = (((is * tmp) / 1024) * 2)
-		    + (((inegs * (int) *img2) / 1024) * 2);
-	    *img2 = (((is * (int) *img2) / 1024) * 2)
-		    + (((inegs * tmp) / 1024) *2);
+        *img1 = (((is * tmp) / 1024) * 2)
+            + (((inegs * (int) *img2) / 1024) * 2);
+        *img2 = (((is * (int) *img2) / 1024) * 2)
+            + (((inegs * tmp) / 1024) *2);
 #endif /* not HAVE_SIGNED_SHIFT */
-	 }
+     }
       }
-      else				/* vertical smoothing */
+      else              /* vertical smoothing */
       {
-	 unsigned  i;			/* line counter */
-	 word_t   *img1;		/* pointer to left or upper line */
-	 word_t   *img2;		/* pointer to right or lower line */
-
-	 img1 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1] - 1;
-	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
-	 
-	 for (i = min (height, img_height - wfa->y [state][1]); i;
-	      i--, img1 += img_width, img2 += img_width)
-	 {
-	    int tmp = *img1;
-	    
+     unsigned  i;           /* line counter */
+     word_t   *img1;        /* pointer to left or upper line */
+     word_t   *img2;        /* pointer to right or lower line */
+
+     img1 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1] - 1;
+     img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+     
+     for (i = min (height, img_height - wfa->y [state][1]); i;
+          i--, img1 += img_width, img2 += img_width)
+     {
+        int tmp = *img1;
+        
 #ifdef HAVE_SIGNED_SHIFT
-	    *img1 = (((is * tmp) >> 10) << 1)
-		    + (((inegs * (int) *img2) >> 10) << 1);
-	    *img2 = (((is * (int) *img2) >> 10) << 1)
-		    + (((inegs * tmp) >> 10) << 1);
+        *img1 = (((is * tmp) >> 10) << 1)
+            + (((inegs * (int) *img2) >> 10) << 1);
+        *img2 = (((is * (int) *img2) >> 10) << 1)
+            + (((inegs * tmp) >> 10) << 1);
 #else /* not HAVE_SIGNED_SHIFT */
-	    *img1 = (((is * tmp) / 1024) * 2)
-		    + (((inegs * (int) *img2) / 1024) * 2);
-	    *img2 = (((is * (int) *img2) / 1024) * 2)
-		    + (((inegs * tmp) / 1024) *2);
+        *img1 = (((is * tmp) / 1024) * 2)
+            + (((inegs * (int) *img2) / 1024) * 2);
+        *img2 = (((is * (int) *img2) / 1024) * 2)
+            + (((inegs * tmp) / 1024) *2);
 #endif /* not HAVE_SIGNED_SHIFT */
-	 }
+     }
       }
    }
 }
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
 static void
 enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
-	       wfa_t *wfa)
+           wfa_t *wfa)
 /*
  *  Enlarge or reduce size of state images by factor 2^'enlarge_factor'.
  *  Use 4:2:0 subsampling if specified by 'format', else use 4:4:4 format.
@@ -788,8 +789,8 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
  *  No return value.
  *
  *  Side effects:
- *	coordinates of ranges and motion blocks in the WFA structure 'wfa'
- *	are modified.
+ *  coordinates of ranges and motion blocks in the WFA structure 'wfa'
+ *  are modified.
  */
 {
    
@@ -799,53 +800,53 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
 
       if (enlarge_factor == 0)
       {
-	 state 		= y_root + 1;
-	 enlarge_factor = -1;
+     state      = y_root + 1;
+     enlarge_factor = -1;
       }
       else
-	 state = wfa->basis_states;
+     state = wfa->basis_states;
       
       for (; state < wfa->states; state++)
       {
-	 unsigned label, n;
-	 
-	 wfa->level_of_state [state]
-	    = max (wfa->level_of_state [state] + enlarge_factor * 2, 0);
-
-	 for (label = 0; label < MAXLABELS; label++)
-	    if (enlarge_factor > 0)
-	    {
-	       wfa->x [state][label] <<= enlarge_factor;
-	       wfa->y [state][label] <<= enlarge_factor;
-	       for (n = enlarge_factor; n; n--)
-	       {
-		  wfa->mv_tree [state][label].fx *= 2;
-		  wfa->mv_tree [state][label].fy *= 2;
-		  wfa->mv_tree [state][label].bx *= 2;
-		  wfa->mv_tree [state][label].by *= 2;
-	       }
-	    }
-	    else				/* enlarge_factor < 0 */
-	    {
-	       wfa->x [state][label] >>= - enlarge_factor;
-	       wfa->y [state][label] >>= - enlarge_factor;
-	       for (n = - enlarge_factor; n; n--)
-	       {
-		  wfa->mv_tree [state][label].fx /= 2;
-		  wfa->mv_tree [state][label].fy /= 2;
-		  wfa->mv_tree [state][label].bx /= 2;
-		  wfa->mv_tree [state][label].by /= 2;
-	       }
-	    }
-	 if (format == FORMAT_4_2_0 && state == y_root)
-	    enlarge_factor--;
+     unsigned label, n;
+     
+     wfa->level_of_state [state]
+        = max (wfa->level_of_state [state] + enlarge_factor * 2, 0);
+
+     for (label = 0; label < MAXLABELS; label++)
+        if (enlarge_factor > 0)
+        {
+           wfa->x [state][label] <<= enlarge_factor;
+           wfa->y [state][label] <<= enlarge_factor;
+           for (n = enlarge_factor; n; n--)
+           {
+          wfa->mv_tree [state][label].fx *= 2;
+          wfa->mv_tree [state][label].fy *= 2;
+          wfa->mv_tree [state][label].bx *= 2;
+          wfa->mv_tree [state][label].by *= 2;
+           }
+        }
+        else                /* enlarge_factor < 0 */
+        {
+           wfa->x [state][label] >>= - enlarge_factor;
+           wfa->y [state][label] >>= - enlarge_factor;
+           for (n = - enlarge_factor; n; n--)
+           {
+          wfa->mv_tree [state][label].fx /= 2;
+          wfa->mv_tree [state][label].fy /= 2;
+          wfa->mv_tree [state][label].bx /= 2;
+          wfa->mv_tree [state][label].by /= 2;
+           }
+        }
+     if (format == FORMAT_4_2_0 && state == y_root)
+        enlarge_factor--;
       }
    }
 }
 
 static void
 compute_actual_size (unsigned luminance_root,
-		     unsigned *width, unsigned *height, const wfa_t *wfa)
+             unsigned *width, unsigned *height, const wfa_t *wfa)
 /*
  *  Compute actual size of the frame represented by the given 'wfa'.
  *  (The reconstructed frame may get larger than the original due
@@ -853,24 +854,24 @@ compute_actual_size (unsigned luminance_root,
  *  If 'luminance_root' < MAXSTATES then the size of chroma ranges (4:2:0).
  *
  *  Return values:
- *	actual 'width' and 'height' of the decoded frame.
+ *  actual 'width' and 'height' of the decoded frame.
  */
 {
-   unsigned x = 0, y = 0;		/* maximum coordinates */
-   unsigned state;			/* counter */
+   unsigned x = 0, y = 0;       /* maximum coordinates */
+   unsigned state;          /* counter */
    
    for (state = wfa->basis_states; state < wfa->states; state++)
       if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
       {
-	 unsigned mult = state > luminance_root ? 2 : 1;
-	 
-	 x = max ((wfa->x [state][0]
-		   + width_of_level (wfa->level_of_state [state])) * mult, x);
-	 y = max ((wfa->y [state][0]
-		   + height_of_level (wfa->level_of_state [state])) * mult, y);
+     unsigned mult = state > luminance_root ? 2 : 1;
+     
+     x = max ((wfa->x [state][0]
+           + width_of_level (wfa->level_of_state [state])) * mult, x);
+     y = max ((wfa->y [state][0]
+           + height_of_level (wfa->level_of_state [state])) * mult, y);
       }
 
-   if (x & 1)				/* ensure that image size is even */
+   if (x & 1)               /* ensure that image size is even */
       x++;
    if (y & 1)
       y++;
@@ -880,8 +881,8 @@ compute_actual_size (unsigned luminance_root,
 
 static void
 alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
-		    const unsigned *root_state, unsigned range_state,
-		    unsigned max_level, format_e format, const wfa_t *wfa)
+            const unsigned *root_state, unsigned range_state,
+            unsigned max_level, format_e format, const wfa_t *wfa)
 /*
  *  Generate list of 'wfa' state images which have to be computed for
  *  each level to obtain the decoded 'frame'. 'root_state[]' denotes the
@@ -893,24 +894,24 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
  *  image of 'wfa->root_state'.
  *
  *  Return values:
- *	'*images'	Pointer to array of state image pointers
- *	'*offsets'	Pointer to array of state image offsets.
+ *  '*images'   Pointer to array of state image pointers
+ *  '*offsets'  Pointer to array of state image offsets.
  *
  *  Side effects:
- *	The arrays given above are filled with useful values.
+ *  The arrays given above are filled with useful values.
  */
 {
-   word_t   **simg;			/* ptr to list of state image ptr's */
-   u_word_t  *offs;			/* ptr to list of offsets */
-   unsigned   level;			/* counter */
+   word_t   **simg;         /* ptr to list of state image ptr's */
+   u_word_t  *offs;         /* ptr to list of offsets */
+   unsigned   level;            /* counter */
    
-   simg	= Calloc (wfa->states * (max_level + 1), sizeof (word_t *));
-   offs	= Calloc (wfa->states * (max_level + 1), sizeof (u_word_t));
+   simg = Calloc (wfa->states * (max_level + 1), sizeof (word_t *));
+   offs = Calloc (wfa->states * (max_level + 1), sizeof (u_word_t));
 
    /*
     *  Initialize buffers for those state images which are at 'max_level'.
     */
-   if (range_state > 0)			/* a range is given */
+   if (range_state > 0)         /* a range is given */
    {
       simg [range_state + max_level * wfa->states] = frame->pixels [GRAY];
       offs [range_state + max_level * wfa->states] = frame->width;
@@ -920,25 +921,25 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
       unsigned state;
 
       for (state = wfa->basis_states; state <= root_state [Y]; state++)
-	 if (wfa->level_of_state [state] == max_level)
-	 {
-	    simg [state + max_level * wfa->states]
-	       = (frame->pixels [Y] + wfa->y [state][0] * frame->width
-		  + wfa->x [state][0]);
-	    offs [state + max_level * wfa->states] = frame->width;
-	 }
+     if (wfa->level_of_state [state] == max_level)
+     {
+        simg [state + max_level * wfa->states]
+           = (frame->pixels [Y] + wfa->y [state][0] * frame->width
+          + wfa->x [state][0]);
+        offs [state + max_level * wfa->states] = frame->width;
+     }
       if (frame->color)
       {
-	 unsigned width = format == FORMAT_4_2_0 ?
-			  (frame->width >> 1) : frame->width;
-	 for (; state < wfa->states; state++)
-	    if (wfa->level_of_state [state] == max_level)
-	    {
-	       simg [state + max_level * wfa->states]
-		  = (frame->pixels [state > root_state [Cb] ? Cr : Cb]
-		     + wfa->y [state][0] * width + wfa->x [state][0]);
-	       offs [state + max_level * wfa->states] = width;
-	    }
+     unsigned width = format == FORMAT_4_2_0 ?
+              (frame->width >> 1) : frame->width;
+     for (; state < wfa->states; state++)
+        if (wfa->level_of_state [state] == max_level)
+        {
+           simg [state + max_level * wfa->states]
+          = (frame->pixels [state > root_state [Cb] ? Cr : Cb]
+             + wfa->y [state][0] * width + wfa->x [state][0]);
+           offs [state + max_level * wfa->states] = width;
+        }
       }
    }
    
@@ -954,63 +955,63 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
        *  Range approximation with child. 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (simg [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (ischild (child = wfa->tree[state][label]))
-	       {
-		  if (isedge (wfa->into[state][label][0]))
-		  {
-		     /*
-		      *  Allocate new image block.
-		      */
-		     simg [child + (level - 1) * wfa->states]
-			= Calloc (size_of_level (level - 1), sizeof (word_t));
-		     offs [child + (level - 1) * wfa->states]
-			= width_of_level (level - 1);
-		  }
-		  else
-		  {
-		     /*
-		      *  Use image block and offset of parent.
-		      */
-		     if (level & 1)	/* split vertically */
-		     {
-			simg [child + (level - 1) * wfa->states]
-			   = (simg [state + level * wfa->states]
-			      + label * (height_of_level (level - 1)
-					 * offs [state
-						+ level * wfa->states]));
-		     }
-		     else		/* split horizontally */
-		     {
-			simg [child + (level - 1) * wfa->states]
-			   = (simg [state + level * wfa->states]
-			      + label * width_of_level (level - 1));
-		     }
-		     offs [child + (level - 1) * wfa->states]
-			= offs [state + level * wfa->states];
-		  }
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (simg [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           if (ischild (child = wfa->tree[state][label]))
+           {
+          if (isedge (wfa->into[state][label][0]))
+          {
+             /*
+              *  Allocate new image block.
+              */
+             simg [child + (level - 1) * wfa->states]
+            = Calloc (size_of_level (level - 1), sizeof (word_t));
+             offs [child + (level - 1) * wfa->states]
+            = width_of_level (level - 1);
+          }
+          else
+          {
+             /*
+              *  Use image block and offset of parent.
+              */
+             if (level & 1) /* split vertically */
+             {
+            simg [child + (level - 1) * wfa->states]
+               = (simg [state + level * wfa->states]
+                  + label * (height_of_level (level - 1)
+                     * offs [state
+                        + level * wfa->states]));
+             }
+             else       /* split horizontally */
+             {
+            simg [child + (level - 1) * wfa->states]
+               = (simg [state + level * wfa->states]
+                  + label * width_of_level (level - 1));
+             }
+             offs [child + (level - 1) * wfa->states]
+            = offs [state + level * wfa->states];
+          }
+           }
       /*
        *  Range approximation with linear combination 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (simg [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-	       {
-		  if (domain > 0	/* don't allocate memory for state 0 */
-		      && !simg [domain + (level - 1) * wfa->states])
-		  {
-		     simg [domain + (level - 1) * wfa->states]
-			= Calloc (size_of_level (level - 1), sizeof (word_t));
-		     offs [domain + (level - 1) * wfa->states]
-			= width_of_level (level - 1);
-		  }
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (simg [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+            edge++)
+           {
+          if (domain > 0    /* don't allocate memory for state 0 */
+              && !simg [domain + (level - 1) * wfa->states])
+          {
+             simg [domain + (level - 1) * wfa->states]
+            = Calloc (size_of_level (level - 1), sizeof (word_t));
+             offs [domain + (level - 1) * wfa->states]
+            = width_of_level (level - 1);
+          }
+           }
       
    }
 
@@ -1020,8 +1021,8 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
 
 static void
 free_state_images (unsigned max_level, bool_t color, word_t **state_image,
-		   u_word_t *offset, const unsigned *root_state,
-		   unsigned range_state, format_e format, const wfa_t *wfa)
+           u_word_t *offset, const unsigned *root_state,
+           unsigned range_state, format_e format, const wfa_t *wfa)
 /*
  *  Free memory of state images.
  *  For more details refer to the inverse function 'alloc_state_images()'.
@@ -1029,10 +1030,10 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
  *  No return value.
  *
  *  Side effects:
- *	arrays 'state_image' and 'offset' are discarded.
+ *  arrays 'state_image' and 'offset' are discarded.
  */
 {
-   word_t   marker;			/* ptr is required as a marker */
+   word_t   marker;         /* ptr is required as a marker */
    unsigned level;
 
    if (range_state > 0)
@@ -1047,19 +1048,19 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
        *  Initialize state image array with states at 'max_level'
        */
       for (state = wfa->basis_states; state <= root_state [Y]; state++)
-	 if (wfa->level_of_state [state] == max_level)
-	    state_image [state + max_level * wfa->states] = &marker;
+     if (wfa->level_of_state [state] == max_level)
+        state_image [state + max_level * wfa->states] = &marker;
 
       if (color)
       {
-	 if (format == FORMAT_4_2_0)
-	    level = max_level - 2;
-	 else
-	    level = max_level;
+     if (format == FORMAT_4_2_0)
+        level = max_level - 2;
+     else
+        level = max_level;
       
-	 for (; state < wfa->states; state++)
-	    if (wfa->level_of_state [state] == level)
-	       state_image [state + level * wfa->states] = &marker;
+     for (; state < wfa->states; state++)
+        if (wfa->level_of_state [state] == level)
+           state_image [state + level * wfa->states] = &marker;
       }
    }
    
@@ -1071,37 +1072,37 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
        *  Range approximation with child. 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (state_image [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (ischild (child = wfa->tree[state][label]))
-	       {
-		  if (isedge (wfa->into[state][label][0])
-		      && (state_image [child + (level - 1) * wfa->states]
-			  != &marker))
-		     Free (state_image [child + (level - 1) * wfa->states]);
-		  state_image [child + (level - 1) * wfa->states] = &marker;
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (state_image [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           if (ischild (child = wfa->tree[state][label]))
+           {
+          if (isedge (wfa->into[state][label][0])
+              && (state_image [child + (level - 1) * wfa->states]
+              != &marker))
+             Free (state_image [child + (level - 1) * wfa->states]);
+          state_image [child + (level - 1) * wfa->states] = &marker;
+           }
       /*
        *  Range approximation with linear combination 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states);
-	   state++)
-	 if (state_image [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-		  if (domain > 0	
-		      && (state_image [domain + (level - 1) * wfa->states]
-			  != NULL)
-		      && (state_image [domain + (level - 1) * wfa->states]
-			  != &marker))
-		  {
-		     Free (state_image [domain + (level - 1) * wfa->states]);
-		     state_image [domain + (level - 1) * wfa->states]
-			= &marker;
-		  }
+                   range_state + 1 : wfa->states);
+       state++)
+     if (state_image [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+            edge++)
+          if (domain > 0    
+              && (state_image [domain + (level - 1) * wfa->states]
+              != NULL)
+              && (state_image [domain + (level - 1) * wfa->states]
+              != &marker))
+          {
+             Free (state_image [domain + (level - 1) * wfa->states]);
+             state_image [domain + (level - 1) * wfa->states]
+            = &marker;
+          }
    }
    Free (state_image);
    Free (offset);
@@ -1109,7 +1110,7 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
 
 static void
 compute_state_images (unsigned max_level, word_t **simg,
-		      const u_word_t *offset, const wfa_t *wfa)
+              const u_word_t *offset, const wfa_t *wfa)
 /*
  *  Compute all state images of the 'wfa' at level {1, ... , 'max_level'}
  *  which are marked in the array 'simg' (offsets of state images
@@ -1121,8 +1122,8 @@ compute_state_images (unsigned max_level, word_t **simg,
  *  No return value.
  *
  *  Side effects:
- *	state images (given by pointers in the array 'state_image')
- *	are computed.
+ *  state images (given by pointers in the array 'state_image')
+ *  are computed.
  */
 {
    unsigned level, state;
@@ -1132,8 +1133,8 @@ compute_state_images (unsigned max_level, word_t **simg,
     */
 
    for (state = 1; state < wfa->states; state++)
-      if (simg [state] != NULL)		/* compute image at level 0 */
-	 *simg [state] = (int) (wfa->final_distribution[state] * 8 + .5) * 2;
+      if (simg [state] != NULL)     /* compute image at level 0 */
+     *simg [state] = (int) (wfa->final_distribution[state] * 8 + .5) * 2;
 
    /*
     *  Compute images of states
@@ -1153,351 +1154,351 @@ compute_state_images (unsigned max_level, word_t **simg,
       unsigned height = height_of_level (level - 1);
       
       for (state = 1; state < wfa->states; state++)
-	 if (simg [state + level * wfa->states] != NULL)
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (isedge (wfa->into [state][label][0]))
-	       {
-		  unsigned  edge;
-		  int       domain;
-		  word_t   *range;	/* address of current range */
-		  bool_t    prediction_used; /* ND prediction found ? */
-
-		  /*
-		   *  Compute address of range image
-		   */
-		  if (level & 1)	/* split vertically */
-		  {
-		     range = simg [state + level * wfa->states]
-			     + label * (height_of_level (level - 1)
-					* offset [state
-						 + level * wfa->states]);
-		  }
-		  else			/* split horizontally */
-		  {
-		     range = simg [state + level * wfa->states]
-			     + label * width_of_level (level - 1);
-		  }
-
-		  /*
-		   *  Generate the state images by adding the corresponding 
-		   *  weighted state images:
-		   *  subimage [label] =
-		   *       weight_1 * image_1 + ... + weight_n * image_n
-		   */
-		  if (!ischild (domain = wfa->tree[state][label]))
-		     prediction_used = NO;
-		  else
-		  {
-		     unsigned  y;
-		     word_t   *src;
-		     word_t   *dst;
-		     unsigned  src_offset;
-		     unsigned  dst_offset;
-
-		     prediction_used = YES;
-		     /*
-		      *  Copy child image
-		      */
-		     src        = simg [domain + (level - 1) * wfa->states];
-		     src_offset = offset [domain + (level - 1) * wfa->states] ;
-		     dst        = range;
-		     dst_offset	= offset [state + level * wfa->states];
-		     for (y = height; y; y--)
-		     {
-			memcpy (dst, src, width * sizeof (word_t));
-			src += src_offset;
-			dst += dst_offset;
-		     }
-		  }
-
-		  if (!prediction_used
-		      && isedge (domain = wfa->into[state][label][0]))
-		  {
-		     /*
-		      *  If prediction is not used then the range is
-		      *  filled with the first domain. No addition is needed.
-		      */
-		     edge = 0;
-		     if (domain != 0)
-		     {
-			int	  weight;
-			word_t 	 *src;
-			unsigned  src_offset;
-
-			src        = simg [domain + ((level - 1)
-						     * wfa->states)];
-			src_offset = offset [domain + ((level - 1)
-						       * wfa->states)] - width;
-			weight     = wfa->int_weight [state][label][edge];
-			
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
+     if (simg [state + level * wfa->states] != NULL)
+        for (label = 0; label < MAXLABELS; label++)
+           if (isedge (wfa->into [state][label][0]))
+           {
+          unsigned  edge;
+          int       domain;
+          word_t   *range;  /* address of current range */
+          bool_t    prediction_used; /* ND prediction found ? */
+
+          /*
+           *  Compute address of range image
+           */
+          if (level & 1)    /* split vertically */
+          {
+             range = simg [state + level * wfa->states]
+                 + label * (height_of_level (level - 1)
+                    * offset [state
+                         + level * wfa->states]);
+          }
+          else          /* split horizontally */
+          {
+             range = simg [state + level * wfa->states]
+                 + label * width_of_level (level - 1);
+          }
+
+          /*
+           *  Generate the state images by adding the corresponding 
+           *  weighted state images:
+           *  subimage [label] =
+           *       weight_1 * image_1 + ... + weight_n * image_n
+           */
+          if (!ischild (domain = wfa->tree[state][label]))
+             prediction_used = NO;
+          else
+          {
+             unsigned  y;
+             word_t   *src;
+             word_t   *dst;
+             unsigned  src_offset;
+             unsigned  dst_offset;
+
+             prediction_used = YES;
+             /*
+              *  Copy child image
+              */
+             src        = simg [domain + (level - 1) * wfa->states];
+             src_offset = offset [domain + (level - 1) * wfa->states] ;
+             dst        = range;
+             dst_offset = offset [state + level * wfa->states];
+             for (y = height; y; y--)
+             {
+            memcpy (dst, src, width * sizeof (word_t));
+            src += src_offset;
+            dst += dst_offset;
+             }
+          }
+
+          if (!prediction_used
+              && isedge (domain = wfa->into[state][label][0]))
+          {
+             /*
+              *  If prediction is not used then the range is
+              *  filled with the first domain. No addition is needed.
+              */
+             edge = 0;
+             if (domain != 0)
+             {
+            int   weight;
+            word_t   *src;
+            unsigned  src_offset;
+
+            src        = simg [domain + ((level - 1)
+                             * wfa->states)];
+            src_offset = offset [domain + ((level - 1)
+                               * wfa->states)] - width;
+            weight     = wfa->int_weight [state][label][edge];
+            
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
 #ifdef HAVE_SIGNED_SHIFT
-			   *dst++ = ((weight * (int) *src++) >> 10) << 1;
-#else 					/* not HAVE_SIGNED_SHIFT */
-			   *dst++ = ((weight * (int) *src++) / 1024) * 2;
+               *dst++ = ((weight * (int) *src++) >> 10) << 1;
+#else                   /* not HAVE_SIGNED_SHIFT */
+               *dst++ = ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   if (height == 2) 
-			   {
-			      src += src_offset;
-			      dst += dst_offset;
+               if (height == 2) 
+               {
+                  src += src_offset;
+                  dst += dst_offset;
 #ifdef HAVE_SIGNED_SHIFT
-			      *dst++ = ((weight * (int) *src++) >> 10) << 1;
+                  *dst++ = ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			      *dst++ = ((weight * (int) *src++) / 1024) * 2;
+                  *dst++ = ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   }
-			}
-			else
-			{
-			   unsigned  y;
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   
-			   idst        = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) / 2;
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst; )
- 			      {
-				 int tmp; /* temp. value of adjacent pixels */
+               }
+            }
+            else
+            {
+               unsigned  y;
+               int      *idst;
+               unsigned  idst_offset;
+               
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) / 2;
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst; )
+                  {
+                 int tmp; /* temp. value of adjacent pixels */
 #ifdef HAVE_SIGNED_SHIFT
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
-				       | (((weight * (int) src [0]) >> 9)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) >> 10) << 17)
-				       | (((weight * (int) src [1]) >> 9)
-					  & 0xfffe);
-#	endif
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+                     | (((weight * (int) src [0]) >> 9)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) >> 10) << 17)
+                     | (((weight * (int) src [1]) >> 9)
+                        & 0xfffe);
+#   endif
 #else /* not HAVE_SIGNED_SHIFT */
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [0])/ 512)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [1]) / 512)
-					  & 0xfffe);
-#	endif /* not WORDS_BIGENDIAN */
-#endif
-				 src    +=  2;
-				 *idst++ = tmp & 0xfffefffe;
-			      }
-			      src  += src_offset;
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     else
-		     {
-			int weight = (int) (wfa->weight[state][label][edge]
-					    * wfa->final_distribution[0]
-					    * 8 + .5) * 2;
-			/*
-			 *  Range needs domain 0
-			 *  (the constant function f(x, y) = 1),
-			 *  hence a faster algorithm is used.
-			 */
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
-			   
-			   *dst++ = weight;
-			   if (height == 2)
-			   {
-			      dst += dst_offset;
-			      *dst++ = weight;
-			   }
-			}
-			else
-			{
-			   unsigned  x, y;
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   
-			   weight      = (weight * 65536) | (weight & 0xffff);
-			   idst	       = (int *) range;
-			   idst_offset = offset [state + level * wfa->states]
-					 / 2;
-			   for (x = width >> 1; x; x--)
-			      *idst++ = weight & 0xfffefffe;
-			   idst += (offset [state + level * wfa->states]
-				    - width) / 2;
-
-			   for (y = height - 1; y; y--)
-			   {
-			      memcpy (idst, idst - idst_offset,
-				      width * sizeof (word_t));
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     edge = 1;
-		  }
-		  else
-		     edge = 0;
-		  
-		  /*
-		   *  Add remaining weighted domain images to current range
-		   */
-		  for (; isedge (domain = wfa->into[state][label][edge]);
-		       edge++)
-		  {
-		     if (domain != 0)
-		     {
-			word_t 	 *src;
-			unsigned  src_offset;
-			int	  weight;
-
-			src        = simg [domain + (level - 1) * wfa->states];
-			src_offset = offset [domain + ((level - 1)
-						       * wfa->states)] - width;
-			weight     = wfa->int_weight [state][label][edge];
-			
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [0])/ 512)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [1]) / 512)
+                        & 0xfffe);
+#   endif
+#endif /* not HAVE_SIGNED_SHIFT */
+                 src    +=  2;
+                 *idst++ = tmp & 0xfffefffe;
+                  }
+                  src  += src_offset;
+                  idst += idst_offset;
+               }
+            }
+             }
+             else
+             {
+            int weight = (int) (wfa->weight[state][label][edge]
+                        * wfa->final_distribution[0]
+                        * 8 + .5) * 2;
+            /*
+             *  Range needs domain 0
+             *  (the constant function f(x, y) = 1),
+             *  hence a faster algorithm is used.
+             */
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
+               
+               *dst++ = weight;
+               if (height == 2)
+               {
+                  dst += dst_offset;
+                  *dst++ = weight;
+               }
+            }
+            else
+            {
+               unsigned  x, y;
+               int      *idst;
+               unsigned  idst_offset;
+               
+               weight      = (weight * 65536) | (weight & 0xffff);
+               idst        = (int *) range;
+               idst_offset = offset [state + level * wfa->states]
+                     / 2;
+               for (x = width >> 1; x; x--)
+                  *idst++ = weight & 0xfffefffe;
+               idst += (offset [state + level * wfa->states]
+                    - width) / 2;
+
+               for (y = height - 1; y; y--)
+               {
+                  memcpy (idst, idst - idst_offset,
+                      width * sizeof (word_t));
+                  idst += idst_offset;
+               }
+            }
+             }
+             edge = 1;
+          }
+          else
+             edge = 0;
+          
+          /*
+           *  Add remaining weighted domain images to current range
+           */
+          for (; isedge (domain = wfa->into[state][label][edge]);
+               edge++)
+          {
+             if (domain != 0)
+             {
+            word_t   *src;
+            unsigned  src_offset;
+            int   weight;
+
+            src        = simg [domain + (level - 1) * wfa->states];
+            src_offset = offset [domain + ((level - 1)
+                               * wfa->states)] - width;
+            weight     = wfa->int_weight [state][label][edge];
+            
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
 
 #ifdef HAVE_SIGNED_SHIFT
-			   *dst++ += ((weight * (int) *src++) >> 10) << 1;
+               *dst++ += ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			   *dst++ += ((weight * (int) *src++) / 1024) * 2;
+               *dst++ += ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   if (height == 2) 
-			   {
-			      src += src_offset;
-			      dst += dst_offset;
+               if (height == 2) 
+               {
+                  src += src_offset;
+                  dst += dst_offset;
 #ifdef HAVE_SIGNED_SHIFT
-			      *dst++ += ((weight * (int) *src++) >> 10) << 1;
+                  *dst++ += ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			      *dst++ += ((weight * (int) *src++) / 1024) * 2;
+                  *dst++ += ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   }
-			}
-			else
-			{
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   unsigned  y;
-			   
-			   idst        = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) / 2;
-			   
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst;)
- 			      {
-				 int tmp; /* temp. value of adjacent pixels */
+               }
+            }
+            else
+            {
+               int      *idst;
+               unsigned  idst_offset;
+               unsigned  y;
+               
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) / 2;
+               
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst;)
+                  {
+                 int tmp; /* temp. value of adjacent pixels */
 #ifdef HAVE_SIGNED_SHIFT
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
-				       | (((weight * (int) src [0]) >> 9)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int)src [0]) >> 10) << 17)
-				       | (((weight * (int)src [1]) >> 9)
-					  & 0xfffe);
-#	endif
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+                     | (((weight * (int) src [0]) >> 9)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int)src [0]) >> 10) << 17)
+                     | (((weight * (int)src [1]) >> 9)
+                        & 0xfffe);
+#   endif
 #else /* not HAVE_SIGNED_SHIFT */
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [0])/ 512)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [1])/ 512)
-					  & 0xfffe);
-#	endif /* not WORDS_BIGENDIAN */
-#endif
-				 src +=  2;
-				 *idst = (*idst + tmp) & 0xfffefffe;
-				 idst++;
-			      }
-			      src  += src_offset;
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     else
-		     {
-			int weight = (int) (wfa->weight[state][label][edge]
-					    * wfa->final_distribution[0]
-					    * 8 + .5) * 2;
-			/*
-			 *  Range needs domain 0
-			 *  (the constant function f(x, y) = 1),
-			 *  hence a faster algorithm is used.
-			 */
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
-			   
-			   *dst++ += weight;
-			   if (height == 2)
-			   {
-			      dst    += dst_offset;
-			      *dst++ += weight;
-			   }
-			}
-			else
-			{
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   unsigned  y;
-			   
-			   weight      = (weight * 65536) | (weight & 0xffff);
-			   idst	       = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) /2;
-			   
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst; )
-			      {
-				 *idst = (*idst + weight) & 0xfffefffe;
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [0])/ 512)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [1])/ 512)
+                        & 0xfffe);
+#   endif
+#endif /* not HAVE_SIGNED_SHIFT */
+                 src +=  2;
+                 *idst = (*idst + tmp) & 0xfffefffe;
+                 idst++;
+                  }
+                  src  += src_offset;
+                  idst += idst_offset;
+               }
+            }
+             }
+             else
+             {
+            int weight = (int) (wfa->weight[state][label][edge]
+                        * wfa->final_distribution[0]
+                        * 8 + .5) * 2;
+            /*
+             *  Range needs domain 0
+             *  (the constant function f(x, y) = 1),
+             *  hence a faster algorithm is used.
+             */
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
+               
+               *dst++ += weight;
+               if (height == 2)
+               {
+                  dst    += dst_offset;
+                  *dst++ += weight;
+               }
+            }
+            else
+            {
+               int      *idst;
+               unsigned  idst_offset;
+               unsigned  y;
+               
+               weight      = (weight * 65536) | (weight & 0xffff);
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) /2;
+               
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst; )
+                  {
+                 *idst = (*idst + weight) & 0xfffefffe;
                                  idst++;
-			      }
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		  }
-	       } 
+                  }
+                  idst += idst_offset;
+               }
+            }
+             }
+          }
+           } 
    }
 }
 
@@ -1509,24 +1510,24 @@ duplicate_state_image (const word_t *domain, unsigned offset, unsigned level)
  *  to the lock 'pixels'.
  *
  *  Return value:
- *	pointer to the new domain block
+ *  pointer to the new domain block
  */
 {
    word_t *dst, *pixels;
-   int	   y, n;
+   int     y, n;
 
    dst = pixels = Calloc (size_of_level (level), sizeof (word_t));
 
    if (domain)
       for (y = height_of_level (level); y; y--)
       {
-	 memcpy (dst, domain, width_of_level (level) * sizeof (word_t));
-	 dst    += width_of_level (level);
-	 domain += offset;
+     memcpy (dst, domain, width_of_level (level) * sizeof (word_t));
+     dst    += width_of_level (level);
+     domain += offset;
       }
-   else					/* state 0 */
+   else                 /* state 0 */
       for (n = size_of_level (level); n; n--)
-	 *dst++ = (int) (128 * 8 + .5) * 2;
+     *dst++ = (int) (128 * 8 + .5) * 2;
 
    return pixels;
 }
diff --git a/converter/other/fiasco/codec/dfiasco.c b/converter/other/fiasco/codec/dfiasco.c
index 1cdfc672..48c222aa 100644
--- a/converter/other/fiasco/codec/dfiasco.c
+++ b/converter/other/fiasco/codec/dfiasco.c
@@ -16,6 +16,8 @@
 
 #include <string.h>
 
+#include "nstring.h"
+
 #include "config.h"
 
 #include "types.h"
diff --git a/converter/other/fiasco/codec/options.c b/converter/other/fiasco/codec/options.c
index 77dbaf00..6018a16c 100644
--- a/converter/other/fiasco/codec/options.c
+++ b/converter/other/fiasco/codec/options.c
@@ -26,6 +26,8 @@
 
 #include <stdio.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
diff --git a/converter/other/fiasco/fiasco.h b/converter/other/fiasco/fiasco.h
index 235b1279..59367bb8 100644
--- a/converter/other/fiasco/fiasco.h
+++ b/converter/other/fiasco/fiasco.h
@@ -28,6 +28,8 @@
  *  $State: Exp $
  */
 
+#include "pnm.h"
+
 #undef __BEGIN_DECLS
 #undef __END_DECLS
 #ifdef __cplusplus
@@ -260,8 +262,17 @@ fiasco_decoder_get_comment (fiasco_decoder_t *decoder);
 			  image functions
 ****************************************************************************/
 
-/* Read FIASCO image (raw ppm or pgm format) */
-fiasco_image_t * fiasco_image_new (const char *filename);
+/* Create FIASCO image (from PNM image file) */
+fiasco_image_t *
+fiasco_image_new_file(const char * const filename);
+
+/* Create FIASCO image (from PNM image stream) */
+fiasco_image_t *
+fiasco_image_new_stream(FILE *       const ifP,
+                        unsigned int const width,
+                        unsigned int const height,
+                        xelval       const maxval,
+                        int          const format);
 
 /* Discard FIASCO image */
 void fiasco_image_delete (fiasco_image_t *image); 
diff --git a/converter/other/fiasco/fiascotopnm.c b/converter/other/fiasco/fiascotopnm.c
index 6d8b6f7f..43340b06 100644
--- a/converter/other/fiasco/fiascotopnm.c
+++ b/converter/other/fiasco/fiascotopnm.c
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <math.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 
diff --git a/converter/other/fiasco/input/basis.c b/converter/other/fiasco/input/basis.c
index cef075e6..4a748f61 100644
--- a/converter/other/fiasco/input/basis.c
+++ b/converter/other/fiasco/input/basis.c
@@ -16,6 +16,8 @@
 
 #include "config.h"
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
diff --git a/converter/other/fiasco/input/read.c b/converter/other/fiasco/input/read.c
index 26bae7e4..23f92070 100644
--- a/converter/other/fiasco/input/read.c
+++ b/converter/other/fiasco/input/read.c
@@ -23,6 +23,8 @@
 
 #include <string.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
diff --git a/converter/other/fiasco/lib/bit-io.c b/converter/other/fiasco/lib/bit-io.c
index 364a1c05..04017c31 100644
--- a/converter/other/fiasco/lib/bit-io.c
+++ b/converter/other/fiasco/lib/bit-io.c
@@ -24,6 +24,8 @@
 #   include <stdlib.h>
 #endif /* not STDC_HEADERS */
 
+#include "nstring.h"
+
 #include "macros.h"
 #include "types.h"
 #include "error.h"
diff --git a/converter/other/fiasco/lib/image.c b/converter/other/fiasco/lib/image.c
index 019ba03c..30fefdd2 100644
--- a/converter/other/fiasco/lib/image.c
+++ b/converter/other/fiasco/lib/image.c
@@ -1,8 +1,8 @@
 /*
- *  image.c:		Input and output of PNM images.
+ *  image.c:        Input and output of PNM images.
  *
- *  Written by:		Ullrich Hafner
- *		
+ *  Written by:     Ullrich Hafner
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -18,6 +18,8 @@
 
 #include <string.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -28,7 +30,7 @@
 
 /*****************************************************************************
 
-				prototypes
+                prototypes
   
 *****************************************************************************/
 
@@ -37,7 +39,7 @@ init_chroma_tables (void);
 
 /*****************************************************************************
 
-				local variables
+                local variables
   
 *****************************************************************************/
 static int *Cr_r_tab = NULL;
@@ -47,41 +49,80 @@ static int *Cb_b_tab = NULL;
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
 
+static fiasco_image_t *
+make_image_base(void)
+{
+    fiasco_image_t * const imageP = Calloc (1, sizeof (fiasco_image_t));
+
+    if (imageP == NULL)
+        pm_error("Failed to allocate memory for image object");
+    else {
+        imageP->delete     = fiasco_image_delete;
+        imageP->get_width  = fiasco_image_get_width;
+        imageP->get_height = fiasco_image_get_height;
+        imageP->is_color   = fiasco_image_is_color;
+    }
+    return imageP;
+}
+
+
+
 fiasco_image_t *
-fiasco_image_new (const char *filename)
+fiasco_image_new_file(const char * const filename)
 /*
  *  FIASCO image constructor.
  *  Allocate memory for the FIASCO image structure and
- *  load the specified image `filename'. The image has to be in
- *  raw pgm or ppm format.
+ *  load the image from PNM file `filename'.
  *
  *  Return value:
- *	pointer to the new image structure
- *	or NULL in case of an error
+ *  pointer to the new image structure
+ *  or NULL in case of an error
  */
 {
-   try
-   {
-      fiasco_image_t *image = Calloc (1, sizeof (fiasco_image_t));
+    fiasco_image_t * imageP;
 
-      image->private 	= read_image (filename);
-      image->delete  	= fiasco_image_delete;
-      image->get_width  = fiasco_image_get_width;
-      image->get_height = fiasco_image_get_height;
-      image->is_color  	= fiasco_image_is_color;
+    imageP = make_image_base();
 
-      return image;
-   }
-   catch
-   {
-      return NULL;
-   }
+    imageP->private = read_image_file(filename);
+
+    return imageP;
+}
+
+
+
+fiasco_image_t *
+fiasco_image_new_stream(FILE *       const ifP,
+                        unsigned int const width,
+                        unsigned int const height,
+                        xelval       const maxval,
+                        int          const format)
+/*
+ *  FIASCO image constructor.
+ *  Allocate memory for the FIASCO image structure and
+ *  load the image from the PNM stream in *ifP, which is positioned just
+ *  after the header.  'width', 'height', 'maxval', and 'format' are the
+ *  parameters of the image, i.e. the contents of that header.
+ *
+ *  Return value:
+ *  pointer to the new image structure
+ *  or NULL in case of an error
+ */
+{
+    fiasco_image_t * imageP;
+
+    imageP = make_image_base();
+
+    imageP->private = read_image_stream(ifP, width, height, maxval, format);
+
+    return imageP;
 }
 
+
+
 void
 fiasco_image_delete (fiasco_image_t *image)
 /*
@@ -91,7 +132,7 @@ fiasco_image_delete (fiasco_image_t *image)
  *  No return value.
  *
  *  Side effects:
- *	structure 'image' is discarded.
+ *  structure 'image' is discarded.
  */
 {
    image_t *this = cast_image (image);
@@ -149,7 +190,7 @@ cast_image (fiasco_image_t *image)
  *  Check whether `image' is a valid object of type image_t.
  *
  *  Return value:
- *	pointer to dfiasco_t struct on success
+ *  pointer to dfiasco_t struct on success
  *      NULL otherwise
  */
 {
@@ -158,8 +199,8 @@ cast_image (fiasco_image_t *image)
    {
       if (!streq (this->id, "IFIASCO"))
       {
-	 set_error (_("Parameter `image' doesn't match required type."));
-	 return NULL;
+     set_error (_("Parameter `image' doesn't match required type."));
+     return NULL;
       }
    }
    else
@@ -182,7 +223,7 @@ alloc_image (unsigned width, unsigned height, bool_t color, format_e format)
  *  are stored in 4:4:4 or 4:2:0 format.
  *
  *  Return value:
- *	pointer to the new image structure.
+ *  pointer to the new image structure.
  */
 {
    image_t *image;
@@ -193,21 +234,21 @@ alloc_image (unsigned width, unsigned height, bool_t color, format_e format)
    if (!color)
       format = FORMAT_4_4_4;
 
-   image         	  = Calloc (1, sizeof (image_t));
-   image->width  	  = width;
-   image->height 	  = height;
-   image->color  	  = color;
-   image->format 	  = format;
+   image              = Calloc (1, sizeof (image_t));
+   image->width       = width;
+   image->height      = height;
+   image->color       = color;
+   image->format      = format;
    image->reference_count = 1;
    
    strcpy (image->id, "IFIASCO");
 
    for (band = first_band (color); band <= last_band (color); band++)
       if (format == FORMAT_4_2_0 && band != Y)
-	 image->pixels [band] = Calloc ((width * height) >> 2,
-					sizeof (word_t));
+     image->pixels [band] = Calloc ((width * height) >> 2,
+                    sizeof (word_t));
       else
-	 image->pixels [band] = Calloc (width * height, sizeof (word_t));
+     image->pixels [band] = Calloc (width * height, sizeof (word_t));
    
    return image;
 }
@@ -219,23 +260,23 @@ clone_image (image_t *image)
  *  Construct new image by copying the given `image'.
  *
  *  Return value:
- *	pointer to the new image structure.
+ *  pointer to the new image structure.
  */
 {
    image_t *new = alloc_image (image->width, image->height, image->color,
-			       image->format);
+                   image->format);
    color_e band;
    
    for (band = first_band (new->color); band <= last_band (new->color); band++)
       if (new->format == FORMAT_4_2_0 && band != Y)
       {
-	 memcpy (new->pixels [band], image->pixels [band],
-		 ((new->width * new->height) >> 2) * sizeof (word_t));
+     memcpy (new->pixels [band], image->pixels [band],
+         ((new->width * new->height) >> 2) * sizeof (word_t));
       }
       else
       {
-	 memcpy (new->pixels [band], image->pixels [band],
-		 new->width * new->height * sizeof (word_t));
+     memcpy (new->pixels [band], image->pixels [band],
+         new->width * new->height * sizeof (word_t));
       }
 
    return new;
@@ -250,22 +291,22 @@ free_image (image_t *image)
  *  No return value.
  *
  *  Side effects:
- *	structure 'image' is discarded.
+ *  structure 'image' is discarded.
  */
 {
    if (image != NULL)
    {
       if (--image->reference_count)
-	 return;			/* image is still referenced */
+     return;            /* image is still referenced */
       else
       {
-	 color_e band;
+     color_e band;
 
-	 for (band  = first_band (image->color);
-	      band <= last_band (image->color); band++)
-	    if (image->pixels [band])
-	       Free (image->pixels [band]);
-	 Free (image);
+     for (band  = first_band (image->color);
+          band <= last_band (image->color); band++)
+        if (image->pixels [band])
+           Free (image->pixels [band]);
+     Free (image);
       }
    }
    else
@@ -327,27 +368,19 @@ read_image_data(image_t * const image, FILE *input, const bool_t color,
 
 
 image_t *
-read_image (const char *image_name)
+read_image_stream(FILE *       const ifP,
+                  unsigned int const width,
+                  unsigned int const height,
+                  xelval       const maxval,
+                  int          const format)
 /*
- *  Read image 'image_name'.
- *  
- *  Return value:
- *	pointer to the image structure.
+ * Read one PNM image from stream *ifP, which is positioned just after the
+ *  header.  'width', 'height', 'maxval', and 'format' are the parameters of
+ *  the image (i.e. the contents of that header).
  */
 {
-   FILE	    *input;			/* input stream */
-   image_t  *image;			/* pointer to new image structure */
-   int  width, height;		/* image size */
-   xelval   maxval;         /* Maxval of image */
-   int format;              /* Image's format code */
-   bool_t    color;			/* color image ? (YES/NO) */
-
-   if (image_name == NULL)
-       input = stdin;
-   else
-       input = pm_openr((char*)image_name);
-
-   pnm_readpnminit(input, &width, &height, &maxval, &format);
+   image_t  *image;         /* pointer to new image structure */
+   bool_t    color;         /* color image ? (YES/NO) */
 
    if (PNM_FORMAT_TYPE(format) == PPM_FORMAT)
        color = YES;
@@ -362,12 +395,40 @@ read_image (const char *image_name)
 
    image = alloc_image (width, height, color, FORMAT_4_4_4);
 
-   read_image_data(image, input, color, width, height, maxval, format);
+   read_image_data(image, ifP, color, width, height, maxval, format);
 
-   pm_close(input);
-   
    return image;
-}   
+}
+
+
+
+image_t *
+read_image_file(const char * const filename)
+/*
+ *  Read the PNM image from the file named 'filename'.
+ *
+ *  Return value:
+ *  pointer to the image structure.
+ */
+{
+    FILE * ifP;
+    int    width, height;    /* image dimensions */
+    xelval   maxval;         /* Maxval of image */
+    int format;              /* Image's format code */
+    image_t * imageP;        /* pointer to new image structure */
+
+    ifP = pm_openr(filename);
+
+    pnm_readpnminit(ifP, &width, &height, &maxval, &format);
+
+    imageP = read_image_stream(ifP, width, height, maxval, format);
+
+    pm_close(ifP);
+
+    return imageP;
+}
+
+
 
 void
 write_image (const char *image_name, const image_t *image)
@@ -377,12 +438,12 @@ write_image (const char *image_name, const image_t *image)
  *  No return value.
  */
 {
-   FILE	*output;			/* output stream */
+   FILE *output;            /* output stream */
    int format;
    int row;
    int i;     /* Cursor into image->pixel arrays */
    xel * xelrow;
-   unsigned *gray_clip;			/* clipping table */
+   unsigned *gray_clip;         /* clipping table */
 
    assert (image && image_name);
    
@@ -394,12 +455,12 @@ write_image (const char *image_name, const image_t *image)
    
    if (image_name == NULL)
        output = stdout;
-   else if (strcmp(image_name, "-") == 0)
+   else if (streq(image_name, "-"))
        output = stdout;
    else
        output = pm_openw((char*)image_name);
 
-   gray_clip  = init_clipping ();	/* mapping of int -> unsigned */
+   gray_clip  = init_clipping ();   /* mapping of int -> unsigned */
    if (!gray_clip)
       error (fiasco_get_error_message ());
    init_chroma_tables ();
@@ -445,21 +506,21 @@ same_image_type (const image_t *img1, const image_t *img2)
  *  Check whether the given images 'img1' and `img2' are of the same type.
  *
  *  Return value:
- *	YES	if images 'img1' and `img2' are of the same type
- *	NO	otherwise.
+ *  YES if images 'img1' and `img2' are of the same type
+ *  NO  otherwise.
  */
 {
    assert (img1 && img2);
    
    return ((img1->width == img2->width)
-	   && (img1->height == img2->height)
-	   && (img1->color == img2->color)
-	   && (img1->format == img2->format));
+       && (img1->height == img2->height)
+       && (img1->color == img2->color)
+       && (img1->format == img2->format));
 }
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
@@ -509,4 +570,3 @@ init_chroma_tables (void)
    Cb_g_tab += 256 + 128;
    Cb_b_tab += 256 + 128;
 }
-
diff --git a/converter/other/fiasco/lib/image.h b/converter/other/fiasco/lib/image.h
index 958049f6..00978526 100644
--- a/converter/other/fiasco/lib/image.h
+++ b/converter/other/fiasco/lib/image.h
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include "types.h"
 #include "fiasco.h"
+#include "pnm.h"
 
 typedef enum {FORMAT_4_4_4, FORMAT_4_2_0} format_e;
 
@@ -48,8 +49,17 @@ free_image (image_t *image);
 FILE *
 read_pnmheader (const char *image_name, unsigned *width, unsigned *height,
 		bool_t *color);
+
+image_t *
+read_image_stream(FILE *       const ifP,
+                  unsigned int const width,
+                  unsigned int const height,
+                  xelval       const maxval,
+                  int          const format);
+
 image_t *
-read_image (const char *image_name);
+read_image_file(const char * const filename);
+
 void
 write_image (const char *image_name, const image_t *image);
 bool_t
diff --git a/converter/other/fiasco/pnmtofiasco.c b/converter/other/fiasco/pnmtofiasco.c
index 2218256d..07b97d7e 100644
--- a/converter/other/fiasco/pnmtofiasco.c
+++ b/converter/other/fiasco/pnmtofiasco.c
@@ -1,8 +1,8 @@
 /*
- *  cwfa.c:		FIASCO coder
+ *  cwfa.c:     FIASCO coder
  *
- *  Written by:		Ullrich Hafner
- *		
+ *  Written by:     Ullrich Hafner
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -18,14 +18,14 @@
 #include "pnm.h"
 
 #if STDC_HEADERS
-#	include <stdlib.h>
-#	include <string.h>
+#   include <stdlib.h>
+#   include <string.h>
 #else /* not STDC_HEADERS */
-#	if HAVE_STRING_H
-#		include <string.h>
-#	else /* not HAVE_STRING_H */
-#		include <strings.h>
-#	endif /* not HAVE_STRING_H */
+#   if HAVE_STRING_H
+#       include <string.h>
+#   else /* not HAVE_STRING_H */
+#       include <strings.h>
+#   endif /* not HAVE_STRING_H */
 #endif /* not STDC_HEADERS */
 
 #include "types.h"
@@ -38,7 +38,7 @@
 
 /*****************************************************************************
 
-			     local variables
+                 local variables
   
 *****************************************************************************/
 
@@ -144,27 +144,27 @@ static param_t params [] =
 
 /*****************************************************************************
 
-				prototypes
+                prototypes
   
 *****************************************************************************/
 
 static void 
 checkargs (int argc, char **argv, char const ***image_template,
-	   char **wfa_name, float *quality, fiasco_c_options_t **options);
+       char **wfa_name, float *quality, fiasco_c_options_t **options);
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
  
 int 
 main (int argc, char **argv)
 {
-   char const 	      **image_template;	/* template for input image files */
-   char	       	       *wfa_name;	/* filename of output WFA */
-   float	      	quality;	/* approximation quality */
-   fiasco_c_options_t  *options;	/* additional coder options */
+   char const         **image_template; /* template for input image files */
+   char                *wfa_name;   /* filename of output WFA */
+   float            quality;    /* approximation quality */
+   fiasco_c_options_t  *options;    /* additional coder options */
    
    pnm_init(&argc, argv);
    
@@ -184,228 +184,228 @@ main (int argc, char **argv)
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
 static void 
 checkargs (int argc, char **argv, char const ***image_template,
-	   char **wfa_name, float *quality, fiasco_c_options_t **options)
+           char **wfa_name, float *quality, fiasco_c_options_t **options)
 /*
  *  Check validness of command line parameters and of the parameter files.
  *
  *  Return value:
- *	1 on success
- *	0 otherwise
+ *  1 on success
+ *  0 otherwise
  *  
  *
  *  Side effects:
- *	'image_template', 'wfa_name', 'quality' and 'options' are set.
+ *  'image_template', 'wfa_name', 'quality' and 'options' are set.
  */
 {
-   int	 optind;			/* last processed commandline param */
-   char	*image_name;			/* filename given by option '-i' */
-   int	 i;				/* counter */
+    int   optind;            /* last processed commandline param */
+    char *image_name;            /* filename given by option '--input_name' */
+    int   i;             /* counter */
    
-   optind = parseargs (params, argc, argv,
-		       "Compress raw PPM/PGM image FILEs to a FIASCO file.",
-		       "With no image FILE, or if FILE is -, "
-		       "read standard input.\n"
-		       "FILE must be either a filename"
-		       " or an image template of the form:\n"
-		       "`prefix[start-end{+,-}step]suffix'\n"
-		       "e.g., img0[12-01-1].pgm is substituted by"
-		       " img012.pgm ... img001.pgm\n\n"
-		       "Environment:\n"
-		       "FIASCO_DATA   Search and save path for FIASCO files. "
-		       "Default: ./\n"
-		       "FIASCO_IMAGES Search path for image files. "
-		       "Default: ./", " [FILE]...",
-		       FIASCO_SHARE, "system.fiascorc", ".fiascorc");
+    optind = parseargs (params, argc, argv,
+                        "Compress raw PPM/PGM image FILEs to a FIASCO file.",
+                        "With no image FILE, or if FILE is -, "
+                        "read standard input.\n"
+                        "FILE must be either a filename"
+                        " or an image template of the form:\n"
+                        "`prefix[start-end{+,-}step]suffix'\n"
+                        "e.g., img0[12-01-1].pgm is substituted by"
+                        " img012.pgm ... img001.pgm\n\n"
+                        "Environment:\n"
+                        "FIASCO_DATA   Search and save path for FIASCO files. "
+                        "Default: ./\n"
+                        "FIASCO_IMAGES Search path for image files. "
+                        "Default: ./", " [FILE]...",
+                        FIASCO_SHARE, "system.fiascorc", ".fiascorc");
 
-   /*
-    *  Default options ...
-    */
-   image_name = (char *) parameter_value (params, "image-name"); 
-   *wfa_name  = (char *) parameter_value (params, "output-name");
-   for (;;)
-   {
-      *quality = * (float *) parameter_value (params, "quality");
-      if (*quality > 100)
-	 fprintf (stderr, "Typical range of quality: (0,100].\n"
-		  "Expect some trouble on slow machines.\n");
-      if (*quality > 0)
-	 break;
-      ask_and_set (params, "quality",
-		   "Please enter coding quality 'q' ('q' > 0): ");
-   }
+    /*
+     *  Default options ...
+     */
+    image_name = (char *) parameter_value (params, "image-name"); 
+    *wfa_name  = (char *) parameter_value (params, "output-name");
+    for (;;)
+    {
+        *quality = * (float *) parameter_value (params, "quality");
+        if (*quality > 100)
+            fprintf (stderr, "Typical range of quality: (0,100].\n"
+                     "Expect some trouble on slow machines.\n");
+        if (*quality > 0)
+            break;
+        ask_and_set (params, "quality",
+                     "Please enter coding quality 'q' ('q' > 0): ");
+    }
    
-   if (optind < argc)			/* Additional command line param */
-   {
-      if (image_name)
-	 error ("Multiple image_template arguments."
-		"\nOption -i %s already specified!", image_name);
+    if (optind < argc)           /* Additional command line param */
+    {
+        if (image_name)
+            error ("Multiple image_template arguments."
+                   "\nOption --input-name %s already specified!", image_name);
 
-      *image_template = calloc (argc - optind + 1, sizeof (char *));
-      if (!*image_template)
-	 error ("Out of memory.");
-      for (i = 0; optind < argc; i++, optind++)
-	 (*image_template) [i] = argv [optind];
-      (*image_template) [i] = NULL;
-   }
-   else					/* option -i image_name */
-   {
-      *image_template = calloc (2, sizeof (char *));
-      if (!*image_template)
-	 error ("Out of memory.");
-      (*image_template) [0] = image_name;
-      (*image_template) [1] = NULL;
-   }
-   /*
-    *  Additional options ... (have to be set with the fiasco_set_... methods)
-    */
-   {
-      *options = fiasco_c_options_new ();
+        *image_template = calloc (argc - optind + 1, sizeof (char *));
+        if (!*image_template)
+            error ("Out of memory.");
+        for (i = 0; optind < argc; i++, optind++)
+            (*image_template) [i] = argv [optind];
+        (*image_template) [i] = NULL;
+    }
+    else                 /* option -i image_name */
+    {
+        *image_template = calloc (2, sizeof (char *));
+        if (!*image_template)
+            error ("Out of memory.");
+        (*image_template) [0] = image_name;
+        (*image_template) [1] = NULL;
+    }
+    /*
+     *  Additional options ... (have to be set with the fiasco_set_... methods)
+     */
+    {
+        *options = fiasco_c_options_new ();
       
-      {
-	 char *pattern = (char *) parameter_value (params, "pattern");
+        {
+            char *pattern = (char *) parameter_value (params, "pattern");
 
-	 if (!fiasco_c_options_set_frame_pattern (*options, pattern))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_frame_pattern (*options, pattern))
+                error (fiasco_get_error_message ());
+        }
 
-      {
-	 char *basis = (char *) parameter_value (params, "basis-name");
-	 
-	 if (!fiasco_c_options_set_basisfile (*options, basis))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            char *basis = (char *) parameter_value (params, "basis-name");
+     
+            if (!fiasco_c_options_set_basisfile (*options, basis))
+                error (fiasco_get_error_message ());
+        }
 
-      {
-	 int   n = * (int *) parameter_value (params, "chroma-dictionary");
-	 float q = * (float *) parameter_value (params, "chroma-qfactor");
+        {
+            int   n = * (int *) parameter_value (params, "chroma-dictionary");
+            float q = * (float *) parameter_value (params, "chroma-qfactor");
       
-	 if (!fiasco_c_options_set_chroma_quality (*options, q, max (0, n)))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_chroma_quality (*options, q, max (0, n)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 int n = *((int *) parameter_value (params, "smooth"));
-	 
-	 if (!fiasco_c_options_set_smoothing (*options, max (0, n)))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            int n = *((int *) parameter_value (params, "smooth"));
+     
+            if (!fiasco_c_options_set_smoothing (*options, max (0, n)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-          int n = * (int *) parameter_value (params, "progress-meter");
-          fiasco_progress_e type = (n < 0) ? 
-              FIASCO_PROGRESS_NONE : (fiasco_progress_e) n;
+        {
+            int n = * (int *) parameter_value (params, "progress-meter");
+            fiasco_progress_e type = (n < 0) ? 
+                FIASCO_PROGRESS_NONE : (fiasco_progress_e) n;
       
-          if (!fiasco_c_options_set_progress_meter (*options, type))
-              error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_progress_meter (*options, type))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 char *t = (char *) parameter_value (params, "title");
-	 
-	 if (strlen (t) > 0 && !fiasco_c_options_set_title (*options, t))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            char *t = (char *) parameter_value (params, "title");
+     
+            if (strlen (t) > 0 && !fiasco_c_options_set_title (*options, t))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 char *c = (char *) parameter_value (params, "comment");
+        {
+            char *c = (char *) parameter_value (params, "comment");
 
-	 if (strlen (c) > 0 && !fiasco_c_options_set_comment (*options, c))
-	    error (fiasco_get_error_message ());
-      }
+            if (strlen (c) > 0 && !fiasco_c_options_set_comment (*options, c))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 fiasco_tiling_e method = FIASCO_TILING_VARIANCE_DSC;
-	 int   e  = * (int *) parameter_value (params, "tiling-exponent");
-	 char *m  = (char *) parameter_value (params, "tiling-method");
+        {
+            fiasco_tiling_e method = FIASCO_TILING_VARIANCE_DSC;
+            int   e  = * (int *) parameter_value (params, "tiling-exponent");
+            char *m  = (char *) parameter_value (params, "tiling-method");
 
-	 if (strcaseeq (m, "desc-variance"))
-	    method = FIASCO_TILING_VARIANCE_DSC;
-	 else if (strcaseeq (m, "asc-variance"))
-	    method = FIASCO_TILING_VARIANCE_ASC;
-	 else if (strcaseeq (m, "asc-spiral"))
-	    method = FIASCO_TILING_SPIRAL_ASC;
-	 else if (strcaseeq (m, "dsc-spiral"))
-	    method = FIASCO_TILING_SPIRAL_DSC;
-	 else
-	    error (_("Invalid tiling method `%s' specified."), m);
+            if (strcaseeq (m, "desc-variance"))
+                method = FIASCO_TILING_VARIANCE_DSC;
+            else if (strcaseeq (m, "asc-variance"))
+                method = FIASCO_TILING_VARIANCE_ASC;
+            else if (strcaseeq (m, "asc-spiral"))
+                method = FIASCO_TILING_SPIRAL_ASC;
+            else if (strcaseeq (m, "dsc-spiral"))
+                method = FIASCO_TILING_SPIRAL_DSC;
+            else
+                error (_("Invalid tiling method `%s' specified."), m);
 
-	 if (!fiasco_c_options_set_tiling (*options, method, max (0, e)))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_tiling (*options, method, max (0, e)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 int M/*  = * (int *) parameter_value (params, "max-level") */;
-	 int m/*  = * (int *) parameter_value (params, "min-level") */;
-	 int N/*  = * (int *) parameter_value (params, "max-elements") */;
-	 int D = * (int *) parameter_value (params, "dictionary-size");
-	 int o = * (int *) parameter_value (params, "optimize");
+        {
+            int M/*  = * (int *) parameter_value (params, "max-level") */;
+            int m/*  = * (int *) parameter_value (params, "min-level") */;
+            int N/*  = * (int *) parameter_value (params, "max-elements") */;
+            int D = * (int *) parameter_value (params, "dictionary-size");
+            int o = * (int *) parameter_value (params, "optimize");
 
-	 if (o <= 0)
-	 {
-	    o = 0;
-	    M = 10;
-	    m = 6;
-	    N = 3;
-	 }
-	 else
-	 {
-	    o -= 1;
-	    M = 12;
-	    m = 4;
-	    N = 5;
-	 }
-	 
-	 if (!fiasco_c_options_set_optimizations (*options, m, M, N,
-						  max (0, D), o))
-	    error (fiasco_get_error_message ());
-      }
-      {
-	 int M = * (int *) parameter_value (params, "max-level");
-	 int m = * (int *) parameter_value (params, "min-level");
-	 int p = * (int *) parameter_value (params, "prediction");
-	 
-	 if (!fiasco_c_options_set_prediction (*options,
-					       p, max (0, m), max (0, M)))
-	    error (fiasco_get_error_message ());
-      }
-      {
-	 float r    = * (float *) parameter_value (params, "rpf-range");
-	 float dc_r = * (float *) parameter_value (params, "dc-rpf-range");
-	 int   m    = * (int *)   parameter_value (params, "rpf-mantissa");
-	 int   dc_m = * (int *)   parameter_value (params, "dc-rpf-mantissa");
-	 fiasco_rpf_range_e range, dc_range;
-	 
-	 if (r < 1)
-	    range = FIASCO_RPF_RANGE_0_75;
-	 else if (r < 1.5)
-	    range = FIASCO_RPF_RANGE_1_00;
-	 else if (r < 2.0)
-	    range = FIASCO_RPF_RANGE_1_50;
-	 else
-	    range = FIASCO_RPF_RANGE_2_00;
-	    
-	 if (dc_r < 1)
-	    dc_range = FIASCO_RPF_RANGE_0_75;
-	 else if (dc_r < 1.5)
-	    dc_range = FIASCO_RPF_RANGE_1_00;
-	 else if (dc_r < 2.0)
-	    dc_range = FIASCO_RPF_RANGE_1_50;
-	 else
-	    dc_range = FIASCO_RPF_RANGE_2_00;
-	    
-	 if (!fiasco_c_options_set_quantization (*options,
-						 max (0, m), range,
-						 max (0, dc_m), dc_range))
-	    error (fiasco_get_error_message ());
-      }
+            if (o <= 0)
+            {
+                o = 0;
+                M = 10;
+                m = 6;
+                N = 3;
+            }
+            else
+            {
+                o -= 1;
+                M = 12;
+                m = 4;
+                N = 5;
+            }
+     
+            if (!fiasco_c_options_set_optimizations (*options, m, M, N,
+                                                     max (0, D), o))
+                error (fiasco_get_error_message ());
+        }
+        {
+            int M = * (int *) parameter_value (params, "max-level");
+            int m = * (int *) parameter_value (params, "min-level");
+            int p = * (int *) parameter_value (params, "prediction");
+     
+            if (!fiasco_c_options_set_prediction (*options,
+                                                  p, max (0, m), max (0, M)))
+                error (fiasco_get_error_message ());
+        }
+        {
+            float r    = * (float *) parameter_value (params, "rpf-range");
+            float dc_r = * (float *) parameter_value (params, "dc-rpf-range");
+            int   m    = * (int *)   parameter_value (params, "rpf-mantissa");
+            int   dc_m = * (int *)   parameter_value (params, "dc-rpf-mantissa");
+            fiasco_rpf_range_e range, dc_range;
+     
+            if (r < 1)
+                range = FIASCO_RPF_RANGE_0_75;
+            else if (r < 1.5)
+                range = FIASCO_RPF_RANGE_1_00;
+            else if (r < 2.0)
+                range = FIASCO_RPF_RANGE_1_50;
+            else
+                range = FIASCO_RPF_RANGE_2_00;
+        
+            if (dc_r < 1)
+                dc_range = FIASCO_RPF_RANGE_0_75;
+            else if (dc_r < 1.5)
+                dc_range = FIASCO_RPF_RANGE_1_00;
+            else if (dc_r < 2.0)
+                dc_range = FIASCO_RPF_RANGE_1_50;
+            else
+                dc_range = FIASCO_RPF_RANGE_2_00;
+        
+            if (!fiasco_c_options_set_quantization (*options,
+                                                    max (0, m), range,
+                                                    max (0, dc_m), dc_range))
+                error (fiasco_get_error_message ());
+        }
 
-      if (fiasco_get_verbosity () == FIASCO_ULTIMATE_VERBOSITY)
-	 write_parameters (params, stderr);
-   }
-}	
+        if (fiasco_get_verbosity () == FIASCO_ULTIMATE_VERBOSITY)
+            write_parameters (params, stderr);
+    }
+}   
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
index 0d8753e5..92ecfb78 100644
--- a/converter/other/fitstopnm.c
+++ b/converter/other/fitstopnm.c
@@ -34,10 +34,16 @@
   The official specification of FITS format (which is for more than
   just visual images) is at
   ftp://legacy.gsfc.nasa.gov/fits_info/fits_office/fits_standard.pdf
+
+  An example FITS file is at
+
+    http://fits.gsfc.nasa.gov/nrao_data/tests/incunabula/mndrll-8.fits
+
 */
 
 #include <string.h>
 #include <float.h>
+#include <assert.h>
 
 #include "pm_config.h"
 #include "pm_c_util.h"
@@ -145,7 +151,10 @@ parseCommandLine(int argc, char ** argv,
 
 struct FITS_Header {
   int simple;       /* basic format or not */
-  int bitpix;       /* number of bits per pixel */
+  int bitpix;
+      /* number of bits per pixel, positive for integer, negative 
+         for floating point
+      */
   int naxis;        /* number of axes */
   int naxis1;       /* number of points on axis 1 */
   int naxis2;       /* number of points on axis 2 */
@@ -157,6 +166,16 @@ struct FITS_Header {
 };
 
 
+typedef enum {
+    VF_CHAR, VF_SHORT, VF_LONG, VF_FLOAT, VF_DOUBLE
+} valFmt;
+
+struct fitsRasterInfo {
+    valFmt valFmt;
+    double bzer;
+    double bscale;
+};
+
 /* This code deals properly with integers, no matter what the byte order
    or integer size of the host machine.  We handle sign extension manually to
    prevent problems with signed/unsigned characters.  We read floating point
@@ -276,34 +295,56 @@ readFitsDouble(FILE *   const ifP,
 
 
 
+static valFmt
+valFmtFromBitpix(int const bitpix) {
+/*----------------------------------------------------------------------------
+   Return the format of a "value" in the FITS file, given the value
+   of the BITPIX header in the FITS file.
+
+   BITPIX has a stupid format wherein it is fundamentally the number
+   of bits per value, but its sign indicates whether it is integer
+   or floating point.
+-----------------------------------------------------------------------------*/
+    switch (bitpix) {
+    case  +8: return VF_CHAR;
+    case +16: return VF_SHORT;
+    case +32: return VF_LONG;
+    case -32: return VF_FLOAT;
+    case -64: return VF_DOUBLE;
+    default:
+        /* Every possibility is covered above. */
+        assert(false);
+        return 0;  /* quiet compiler warning */
+    }
+}
+
+
+
 static void
 readVal(FILE *   const ifP,
-        int      const bitpix,
+        valFmt   const fmt,
         double * const vP) {
 
-    switch (bitpix) {
-    case 8:
+    switch (fmt) {
+    case VF_CHAR:
         readFitsChar(ifP, vP);
         break;
 
-    case 16:
+    case VF_SHORT:
         readFitsShort(ifP, vP);
         break;
       
-    case 32:
+    case VF_LONG:
         readFitsLong(ifP, vP);
         break;
       
-    case -32:
+    case VF_FLOAT:
         readFitsFloat(ifP, vP);
         break;
       
-    case -64:
+    case VF_DOUBLE:
         readFitsDouble(ifP, vP);
         break;
-      
-    default:
-        pm_error("Strange bitpix value %d in readVal()", bitpix);
     }
 }
 
@@ -419,7 +460,7 @@ scanImageForMinMax(FILE *       const ifP,
                    unsigned int const images,
                    int          const cols,
                    int          const rows,
-                   unsigned int const bitpix,
+                   valFmt       const valFmt,
                    double       const bscale,
                    double       const bzer,
                    unsigned int const imagenum,
@@ -436,14 +477,12 @@ scanImageForMinMax(FILE *       const ifP,
 
     pm_message("Scanning file for scaling parameters");
 
-    switch (bitpix) {
-    case   8: fmaxval = 255.0;        break;
-    case  16: fmaxval = 65535.0;      break;
-    case  32: fmaxval = 4294967295.0; break;
-    case -32: fmaxval = FLT_MAX;      break;
-    case -64: fmaxval = DBL_MAX;      break;
-    default:
-        pm_error("unusual bits per pixel (%u), can't read", bitpix);
+    switch (valFmt) {
+    case VF_CHAR:   fmaxval = 255.0;        break;
+    case VF_SHORT:  fmaxval = 65535.0;      break;
+    case VF_LONG:   fmaxval = 4294967295.0; break;
+    case VF_FLOAT:  fmaxval = FLT_MAX;      break;
+    case VF_DOUBLE: fmaxval = DBL_MAX;      break;
     }
 
     dmax = -fmaxval;
@@ -454,7 +493,7 @@ scanImageForMinMax(FILE *       const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, bitpix, &val);
+                readVal(ifP, valFmt, &val);
                 if (image == imagenum || multiplane ) {
                     dmax = MAX(dmax, val);
                     dmin = MIN(dmin, val);
@@ -509,7 +548,8 @@ computeMinMax(FILE *             const ifP,
 
     if (datamin == -DBL_MAX || datamax == DBL_MAX) {
         double scannedDatamin, scannedDatamax;
-        scanImageForMinMax(ifP, images, cols, rows, h.bitpix, h.bscale, h.bzer,
+        scanImageForMinMax(ifP, images, cols, rows,
+                           valFmtFromBitpix(h.bitpix), h.bscale, h.bzer,
                            imagenum, multiplane,
                            &scannedDatamin, &scannedDatamax);
 
@@ -526,7 +566,7 @@ computeMinMax(FILE *             const ifP,
 
 static xelval
 determineMaxval(struct cmdlineInfo const cmdline,
-                struct FITS_Header const fitsHeader,
+                valFmt             const valFmt,
                 double             const datamax,
                 double             const datamin) {
 
@@ -535,7 +575,7 @@ determineMaxval(struct cmdlineInfo const cmdline,
     if (cmdline.omaxvalSpec)
         retval = cmdline.omaxval;
     else {
-        if (fitsHeader.bitpix < 0) {
+        if (valFmt == VF_FLOAT || valFmt == VF_DOUBLE) {
             /* samples are floating point, which means the resolution
                could be anything.  So we just pick a convenient maxval
                of 255.  Before Netpbm 10.20 (January 2004), we did
@@ -561,16 +601,16 @@ determineMaxval(struct cmdlineInfo const cmdline,
 
 
 static void
-convertPgmRaster(FILE *             const ifP,
-                 unsigned int       const cols,
-                 unsigned int       const rows,
-                 xelval             const maxval,
-                 unsigned int       const desiredImage,
-                 unsigned int       const imageCount,
-                 struct FITS_Header const fitsHdr,
-                 double             const scale,
-                 double             const datamin,
-                 xel **             const xels) {
+convertPgmRaster(FILE *                const ifP,
+                 unsigned int          const cols,
+                 unsigned int          const rows,
+                 xelval                const maxval,
+                 unsigned int          const desiredImage,
+                 unsigned int          const imageCount,
+                 struct fitsRasterInfo const rasterInfo,
+                 double                const scale,
+                 double                const datamin,
+                 xel **                const xels) {
         
     /* Note: the FITS specification does not give the association between
        file position and image position (i.e. is the first pixel in the
@@ -593,10 +633,10 @@ convertPgmRaster(FILE *             const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, fitsHdr.bitpix, &val);
+                readVal(ifP, rasterInfo.valFmt, &val);
                 {
                     double const t = scale *
-                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                        (val * rasterInfo.bscale + rasterInfo.bzer - datamin);
                     xelval const tx = MAX(0, MIN(t, maxval));
                     if (image == desiredImage)
                         PNM_ASSIGN1(xels[row][col], tx);
@@ -609,14 +649,14 @@ convertPgmRaster(FILE *             const ifP,
 
 
 static void
-convertPpmRaster(FILE *             const ifP,
-                 unsigned int       const cols,
-                 unsigned int       const rows,
-                 xelval             const maxval,
-                 struct FITS_Header const fitsHdr,
-                 double             const scale,
-                 double             const datamin,
-                 xel **             const xels) {
+convertPpmRaster(FILE *                const ifP,
+                 unsigned int          const cols,
+                 unsigned int          const rows,
+                 xelval                const maxval,
+                 struct fitsRasterInfo const rasterInfo,
+                 double                const scale,
+                 double                const datamin,
+                 xel **                const xels) {
 /*----------------------------------------------------------------------------
    Read the FITS raster from file *ifP into xels[][].  Image dimensions
    are 'cols' by 'rows'.  The FITS raster is 3 planes composing one
@@ -634,10 +674,10 @@ convertPpmRaster(FILE *             const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, fitsHdr.bitpix, &val);
+                readVal(ifP, rasterInfo.valFmt, &val);
                 {
                     double const t = scale *
-                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                        (val * rasterInfo.bscale + rasterInfo.bzer - datamin);
                     xelval const sample = MAX(0, MIN(t, maxval));
 
                     switch (plane) {
@@ -654,17 +694,17 @@ convertPpmRaster(FILE *             const ifP,
 
 
 static void
-convertRaster(FILE *             const ifP,
-              unsigned int       const cols,
-              unsigned int       const rows,
-              xelval             const maxval,
-              bool               const forceplain,
-              bool               const multiplane,
-              unsigned int       const desiredImage,
-              unsigned int       const imageCount,
-              struct FITS_Header const fitsHdr,
-              double             const scale,
-              double             const datamin) {
+convertRaster(FILE *                const ifP,
+              unsigned int          const cols,
+              unsigned int          const rows,
+              xelval                const maxval,
+              bool                  const forceplain,
+              bool                  const multiplane,
+              unsigned int          const desiredImage,
+              unsigned int          const imageCount,
+              struct fitsRasterInfo const rasterInfo,
+              double                const scale,
+              double                const datamin) {
 
     xel ** xels;
     int format;
@@ -673,12 +713,12 @@ convertRaster(FILE *             const ifP,
 
     if (multiplane) {
         format = PPM_FORMAT;
-        convertPpmRaster(ifP, cols, rows, maxval, fitsHdr, scale, datamin,
+        convertPpmRaster(ifP, cols, rows, maxval, rasterInfo, scale, datamin,
                          xels);
     } else {
         format = PGM_FORMAT;
         convertPgmRaster(ifP, cols, rows, maxval,
-                         desiredImage, imageCount, fitsHdr, scale, datamin,
+                         desiredImage, imageCount, rasterInfo, scale, datamin,
                          xels);
     }
     pnm_writepnm(stdout, xels, cols, rows, maxval, format, forceplain);
@@ -697,6 +737,7 @@ main(int argc, char * argv[]) {
     double scale;
     double datamin, datamax;
     struct FITS_Header fitsHeader;
+    struct fitsRasterInfo rasterInfo;
 
     unsigned int imageCount;
     unsigned int desiredImage;
@@ -725,6 +766,10 @@ main(int argc, char * argv[]) {
     cols = fitsHeader.naxis1;
     rows = fitsHeader.naxis2;
 
+    rasterInfo.bscale = fitsHeader.bscale;
+    rasterInfo.bzer   = fitsHeader.bzer;
+    rasterInfo.valFmt = valFmtFromBitpix(fitsHeader.bitpix);
+
     interpretPlanes(fitsHeader, cmdline.image, cmdline.verbose,
                     &imageCount, &multiplane, &desiredImage);
 
@@ -734,7 +779,7 @@ main(int argc, char * argv[]) {
                   cmdline.min, cmdline.max,
                   &datamin, &datamax);
 
-    maxval = determineMaxval(cmdline, fitsHeader, datamax, datamin);
+    maxval = determineMaxval(cmdline, rasterInfo.valFmt, datamax, datamin);
 
     if (datamax - datamin == 0)
         scale = 1.0;
@@ -746,7 +791,7 @@ main(int argc, char * argv[]) {
     else
         convertRaster(ifP, cols, rows, maxval, cmdline.noraw,
                       multiplane, desiredImage, imageCount,
-                      fitsHeader, scale, datamin);
+                      rasterInfo, scale, datamin);
 
     pm_close(ifP);
     pm_close(stdout);
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index e6db7658..d3768a0e 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -41,7 +41,7 @@ parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
-   *cmdline_p structure are actually in the supplied argv array.  And
+   *cmdlineP structure are actually in the supplied argv array.  And
    sometimes, one of these strings is actually just a suffix of an entry
    in argv!
 -----------------------------------------------------------------------------*/
@@ -82,36 +82,57 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-readJpc(const char *   const inputFilename, 
+validateJ2k(jas_stream_t * const instreamP) {
+/*----------------------------------------------------------------------------
+   Abort program with error message if *instreamP is not a JPEG-2000 code
+   stream (JPC) or image file (JP2).
+-----------------------------------------------------------------------------*/
+    assert(jas_image_lookupfmtbyname("jpc"));
+    assert(jas_image_lookupfmtbyname("jp2"));
+
+    if (jas_image_lookupfmtbyname("jpc")->ops.validate(instreamP) != 0 &&
+        jas_image_lookupfmtbyname("jp2")->ops.validate(instreamP) != 0) {
+
+        pm_error("Input is not JPEG-2000 image file (JP2) "
+                 "or code stream (JPC).  "
+                 "(the first few bytes of the file are not the required "
+                 "signature)");
+    }
+}
+
+        
+
+
+static void
+readJ2k(const char *   const inputFilename, 
         jas_image_t ** const jasperPP) {
 
     jas_image_t * jasperP;
-    jas_stream_t *instream;
+    jas_stream_t * instreamP;
     const char * options;
 
     if ( strcmp(inputFilename, "-") == 0) {
         /* The input image is to be read from standard input. */
-        instream = jas_stream_fdopen(fileno(stdin), "rb");
-        if (instream == NULL)
+        instreamP = jas_stream_fdopen(fileno(stdin), "rb");
+        if (instreamP == NULL)
             pm_error("error: cannot reopen standard input");
     } else {
-        instream = jas_stream_fopen(inputFilename, "rb");
-        if (instream == NULL )
+        instreamP = jas_stream_fopen(inputFilename, "rb");
+        if (instreamP == NULL )
             pm_error("cannot open input image file '%s'", inputFilename);
     } 
 
-    if (jas_image_getfmt(instream) != jas_image_strtofmt((char*)"jpc"))
-        pm_error("Input is not JPEG-2000 code stream");
+    validateJ2k(instreamP);
 
     options = "";
 
-    jasperP = jas_image_decode(instream, jas_image_strtofmt((char*)"jpc"), 
+    jasperP = jas_image_decode(instreamP, jas_image_getfmt(instreamP),
                                (char*)options);
     if (jasperP == NULL)
         pm_error("Unable to interpret JPEG-2000 input.  "
                  "The Jasper library jas_image_decode() subroutine failed.");
 
-	jas_stream_close(instream);
+	jas_stream_close(instreamP);
 
     *jasperPP = jasperP;
 }
@@ -189,7 +210,7 @@ static void
 validateComponentsAlike(jas_image_t * const jasperP) {
 /*----------------------------------------------------------------------------
    JPC allows each component to have its own width and height.  But
-   PAM requires all planes to the same shape.  So we validate now that
+   PAM requires all planes to have the same shape.  So we validate now that
    all the channels are the same, and abort the program if not.
 -----------------------------------------------------------------------------*/
     int cmptNo;
@@ -479,7 +500,7 @@ main(int argc, char **argv)
     
     jas_setdbglevel(cmdline.debuglevel);
     
-    readJpc(cmdline.inputFilename, &jasperP);
+    readJ2k(cmdline.inputFilename, &jasperP);
 
     outpam.file = stdout;
     outpam.size = sizeof(outpam);
diff --git a/converter/other/jpeg2000/libjasper/README b/converter/other/jpeg2000/libjasper/README
index ad3e019b..b0512fe8 100644
--- a/converter/other/jpeg2000/libjasper/README
+++ b/converter/other/jpeg2000/libjasper/README
@@ -6,10 +6,12 @@ The adaptation was done by Bryan Henderson on 2002.10.26.
 
 The adaptation involved:
 
-  - remove stuff for formats other than PNM.
+  - Remove stuff for formats other than JPEG-2000.
 
   - Replace build stuff (Jasper uses GNU Autoconf/Automake/Libtool).
 
+  - Make JP2 decoder not dump the box to stderr unless debug is turned on.
+
 See <http://www.ece.uvic.ca/~mdadams/jasper/>.
 
 
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
index cb70fd24..f8fc013b 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
@@ -337,8 +337,9 @@ jp2_box_t *jp2_box_get(jas_stream_t *in)
 		jas_stream_close(tmpstream);
 	}
 
-	jp2_box_dump(box, stderr);
-
+	if (jas_getdbglevel() > 0) {
+        jp2_box_dump(box, stderr);
+    }
 	return box;
 	abort();
 
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
index 3cce9278..aaebf411 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
@@ -340,7 +340,8 @@ jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
 		iccp = dec->colr->data.colr.iccp;
 		cs = (iccp[16] << 24) | (iccp[17] << 16) | (iccp[18] << 8) |
 		  iccp[19];
-		jas_eprintf("ICC Profile CS %08x\n", cs);
+        if (jas_getdbglevel() > 1)
+            jas_eprintf("ICC Profile CS %08x\n", cs);
 		jas_image_setcolorspace(dec->image, fromiccpcs(cs));
 		break;
 	}
@@ -454,7 +455,6 @@ jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
 		jas_eprintf("error: no components\n");
 		goto error;
 	}
-fprintf(stderr, "no of components is %d\n", jas_image_numcmpts(dec->image));
 
 	/* Prevent the image from being destroyed later. */
 	image = dec->image;
diff --git a/converter/other/pamtopfm.c b/converter/other/pamtopfm.c
index 129b8eee..882f75c3 100644
--- a/converter/other/pamtopfm.c
+++ b/converter/other/pamtopfm.c
@@ -150,12 +150,12 @@ floatToPfmSample(float       const input,
    Type converter
 -----------------------------------------------------------------------------*/
     if (machineEndianness == pfmEndianness) {
-        *(float *)outputP->bytes = input;
+        MEMSCPY(&outputP->bytes, &input);
     } else {
         unsigned char reversed[sizeof(pfmSample)];
         unsigned int i, j;
 
-        *(float *)reversed = input;
+        MEMSCPY(&reversed, &input);
         
         for (i = 0, j = sizeof(pfmSample)-1; 
              i < sizeof(pfmSample); 
diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c
index f828b716..dfc6c865 100644
--- a/converter/other/pgmtopbm.c
+++ b/converter/other/pgmtopbm.c
@@ -585,6 +585,7 @@ createDither8Converter(unsigned int const cols,
     converter.convertRow = &dither8ConvertRow;
     converter.destroy = dither8Destroy;
     converter.stateP = stateP;
+    converter.maxval = maxval;
 
     /* Scale dither matrix. */
     for (row = 0; row < 16; ++row) {
@@ -660,6 +661,7 @@ createClusterConverter(unsigned int const radius,
     unsigned int row;
 
     converter.cols = cols;
+    converter.maxval = maxval;
     converter.convertRow = &clusterConvertRow;
     converter.destroy = &clusterDestroy;
 
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 6cef96ad..200cca44 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -493,9 +493,10 @@ parseCommandLine(int argc, char ** argv,
 
 
 static png_color_16
-xelToPngColor_16(xel const input, 
+xelToPngColor_16(xel    const input, 
                  xelval const maxval, 
                  xelval const pngMaxval) {
+
     png_color_16 retval;
 
     xel scaled;
@@ -1926,6 +1927,10 @@ computeColorMap(FILE *         const ifP,
   palette_pnm[] and trans_pnm[], allocated by Caller, with sizes
   *paletteSizeP and *transSizeP.
 
+  'pfP' is a handle to the file that the user requested be used for the
+  palette (it's a Netpbm image whose colors are the colors of the palette).
+  'pfP' is null if the user did not request a particular palette.
+
   'background' means the image is to have a background color, and that
   color is 'backcolor'.  'backcolor' is meaningless when 'background'
   is false.
@@ -2559,11 +2564,11 @@ doSbitChunk(png_info * const pngInfoP,
 
 static void 
 convertpnm(struct cmdlineInfo const cmdline,
-           FILE *             const ifp,
-           FILE *             const ofp,
-           FILE *             const afp,
-           FILE *             const pfp,
-           FILE *             const tfp,
+           FILE *             const ifP,
+           FILE *             const ofP,
+           FILE *             const afP,
+           FILE *             const pfP,
+           FILE *             const tfP,
            int *              const errorLevelP
     ) {
 /*----------------------------------------------------------------------------
@@ -2654,8 +2659,8 @@ convertpnm(struct cmdlineInfo const cmdline,
 
   pngx_create(&pngxP, PNGX_WRITE, &jmpbuf);
 
-  pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-  pm_tell2(ifp, &rasterPos, sizeof(rasterPos));
+  pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
+  pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
   pnm_type = PNM_FORMAT_TYPE(format);
 
   xelrow = pnm_allocrow(cols);
@@ -2669,8 +2674,8 @@ convertpnm(struct cmdlineInfo const cmdline,
       pm_message ("reading a PPM file (maxval=%d)", maxval);
   }
 
-  determineTransparency(cmdline, ifp, rasterPos, cols, rows, maxval, format,
-                        afp,
+  determineTransparency(cmdline, ifP, rasterPos, cols, rows, maxval, format,
+                        afP,
                         &alpha, &transparent, &transcolor, &transexact,
                         &alpha_mask, &alpha_maxval);
 
@@ -2683,10 +2688,10 @@ convertpnm(struct cmdlineInfo const cmdline,
       bool isgray;
 
       isgray = TRUE;  /* initial assumption */
-      pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
+      pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
       for (row = 0; row < rows && isgray; ++row) {
           unsigned int col;
-          pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
+          pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
           for (col = 0; col < cols && isgray; ++col) {
               p = xelrow[col];
               if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p))
@@ -2704,11 +2709,11 @@ convertpnm(struct cmdlineInfo const cmdline,
                "(use -downscale to override");
   }
 
-  findRedundantBits(ifp, rasterPos, cols, rows, maxval, format, alpha,
+  findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha,
                     cmdline.force, &pnm_meaningful_bits);
   
-  computeColorMap(ifp, rasterPos, cols, rows, maxval, format,
-                  cmdline.force, pfp,
+  computeColorMap(ifP, rasterPos, cols, rows, maxval, format,
+                  cmdline.force, pfP,
                   alpha, transparent >= 0, transcolor, transexact, 
                   !!cmdline.background, backcolor,
                   alpha_mask, alpha_maxval, pnm_meaningful_bits,
@@ -2716,7 +2721,7 @@ convertpnm(struct cmdlineInfo const cmdline,
                   &background_index, &noColormapReason);
 
   if (noColormapReason) {
-      if (pfp)
+      if (pfP)
           pm_error("You specified a particular palette, but this image "
                    "cannot be represented by any palette.  %s",
                    noColormapReason);
@@ -2775,7 +2780,7 @@ convertpnm(struct cmdlineInfo const cmdline,
     pngxP->info_ptr->palette = palette;
     pngxP->info_ptr->num_palette = palette_size;
 
-    doHistChunk(cmdline.hist, palette_pnm, ifp, rasterPos,
+    doHistChunk(cmdline.hist, palette_pnm, ifP, rasterPos,
                 cols, rows, maxval, format,
                 pngxP->info_ptr, cmdline.verbose);
   }
@@ -2791,7 +2796,7 @@ convertpnm(struct cmdlineInfo const cmdline,
 
   /* tEXT and zTXT chunks */
   if (cmdline.text || cmdline.ztxt)
-      pnmpng_read_text(pngxP->info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose);
+      pnmpng_read_text(pngxP->info_ptr, tfP, !!cmdline.ztxt, cmdline.verbose);
 
   doTimeChunk(cmdline, pngxP->info_ptr);
 
@@ -2800,7 +2805,7 @@ convertpnm(struct cmdlineInfo const cmdline,
 
   setZlibCompression(pngxP->png_ptr, cmdline.zlibCompression);
 
-  png_init_io(pngxP->png_ptr, ofp);
+  png_init_io(pngxP->png_ptr, ofP);
 
   /* write the png-info struct */
   png_write_info(pngxP->png_ptr, pngxP->info_ptr);
@@ -2816,7 +2821,7 @@ convertpnm(struct cmdlineInfo const cmdline,
   /* let libpng take care of, e.g., bit-depth conversions */
   png_set_packing(pngxP->png_ptr);
 
-  writeRaster(pngxP, ifp, rasterPos,
+  writeRaster(pngxP, ifP, rasterPos,
               cols, rows, maxval, format,
               png_maxval, depth, alpha, alpha_mask, cht, caht);
 
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
index 67a76eab..b13f2cad 100644
--- a/converter/ppm/xpmtoppm.c
+++ b/converter/ppm/xpmtoppm.c
@@ -31,6 +31,7 @@
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -159,15 +160,36 @@ getline(char * const line,
 
 
 static unsigned int
-getNumber(char * const p, unsigned int const size) {
+getColorNumber(const char * const pArg,
+               unsigned int const bytesPerPixel,
+               unsigned int const ncolors) {
+/*----------------------------------------------------------------------------
+   Return the color number (palette index) at 'p'.
+
+   It occupies 'bytesPerPixel' bytes.
+
+   Abort program if the number is too large for the format described
+   by 'bytesPerPixel' and 'ncolors'.
+
+-----------------------------------------------------------------------------*/
+    /* Color number is encoded as pure binary, big-endian, unsigned.
+       (which is stupid, since the rest of the file is text)
+    */
+    const unsigned char * const p = (const unsigned char *)pArg;
 
     unsigned int retval;
-    unsigned char * q;
+    const unsigned char * q;
+
+    assert(bytesPerPixel <= sizeof(unsigned int));
     
-    retval = 0;
-    for (q = p; q < p+size; ++q)
+    for (q = p, retval = 0; q < p + bytesPerPixel; ++q)
         retval = (retval << 8) + *q;
 
+    if (bytesPerPixel > 2 && retval >= ncolors)
+        pm_error("Color number %u in color map is too large, as the "
+                 "header says there are only %u colors in the image",
+                 retval, ncolors);
+
     return retval;
 }
 
@@ -191,11 +213,13 @@ getword(char * const output, char ** const cursorP) {
 
 
 static void
-addToColorMap(unsigned int const seqNum,
-              unsigned int const colorNumber, 
-              pixel * const colors, int * const ptab, 
-              char colorspec[], int const isTransparent,
-              int * const transparentP) {
+addToColorMap(unsigned int   const seqNum,
+              unsigned int   const colorNumber, 
+              pixel *        const colors,
+              unsigned int * const ptab, 
+              char           const colorspec[],
+              bool           const isTransparent,
+              int *          const transparentP) {
 /*----------------------------------------------------------------------------
    Add the color named by colorspec[] to the colormap contained in
    'colors' and 'ptab', as the color associated with XPM color number
@@ -223,14 +247,17 @@ addToColorMap(unsigned int const seqNum,
 
 
 static void
-interpretXpm3ColorTableLine(char line[], int const seqNum, 
-                            int const chars_per_pixel,
-                            pixel * const colors, int * const ptab,
-                            int * const transparentP) {
+interpretXpm3ColorTableLine(char           const line[],
+                            unsigned int   const seqNum, 
+                            unsigned int   const charsPerPixel,
+                            pixel *        const colors,
+                            unsigned int * const ptab,
+                            unsigned int   const ncolors,
+                            int *          const transparentP) {
 /*----------------------------------------------------------------------------
    Interpret one line of the color table in the XPM header.  'line' is
    the line from the XPM file.  It is the seqNum'th color table entry in
-   the file.  The file uses 'chars_per_pixel' characters per pixel.
+   the file.  The file uses 'charsPerPixel' characters per pixel.
 
    Add the information from this color table entry to the color table
    'colors' and, if it isn't NULL, the corresponding lookup shadow table
@@ -259,22 +286,22 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
            at least one token.
         */
     char curbuf[BUFSIZ];		/* current buffer */
-    int isTransparent;
+    bool isTransparent;
     
-    int colorNumber;
+    unsigned int colorNumber;
         /* A color number that appears in the raster */
     /* read the chars */
     t1 = strchr(line, '"');
     if (t1 == NULL)
         pm_error("A line that is supposed to be an entry in the color "
                  "table does not start with a quote.  The line is '%s'.  "
-                 "It is the %dth entry in the color table.", 
+                 "It is the %uth entry in the color table.", 
                  line, seqNum);
     else
         t1++;  /* Points now to first color number character */
 
-    colorNumber = getNumber(t1, chars_per_pixel);
-    t1 += chars_per_pixel;
+    colorNumber = getColorNumber(t1, charsPerPixel, ncolors);
+    t1 += charsPerPixel;
 
     /*
      * read color keys and values 
@@ -350,10 +377,14 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
 
 
 static void
-readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP,
-               pixel ** const colorsP, int ** const ptabP,
-               int * const transparentP) {
+readXpm3Header(FILE *          const stream,
+               int *           const widthP,
+               int *           const heightP, 
+               unsigned int *  const charsPerPixelP,
+               int *           const nColorsP,
+               pixel **        const colorsP,
+               unsigned int ** const ptabP,
+               int *           const transparentP) {
 /*----------------------------------------------------------------------------
   Read the header of the XPM file on stream 'stream'.  Assume the
   getline() stream is presently positioned to the beginning of the
@@ -384,7 +415,12 @@ readXpm3Header(FILE * const stream, int * const widthP, int * const heightP,
     char line[MAX_LINE+1];
     const char * xpm3_signature = "/* XPM */";
     
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1;
+    unsigned int colormapSize;
+    unsigned int width, height;
+    unsigned int nColors;
+    unsigned int charsPerPixel;
+    pixel * colors;
+    unsigned int * ptab;
 
     /* Read the XPM signature comment */
     getline(line, sizeof(line), stream);
@@ -407,54 +443,65 @@ readXpm3Header(FILE * const stream, int * const widthP, int * const heightP,
             getline(line, sizeof(line), stream);
         getline(line, sizeof(line), stream);
     }
-    if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP,
-               ncolorsP, chars_per_pixelP) != 4)
+    if (sscanf(line, "\"%u %u %u %u\",", &width, &height,
+               &nColors, &charsPerPixel) != 4)
         pm_error("error scanning hints line");
 
-    if (verbose == 1) 
-    {
-        pm_message("Width x Height:  %d x %d", *widthP, *heightP);
-        pm_message("no. of colors:  %d", *ncolorsP);
-        pm_message("chars per pixel:  %d", *chars_per_pixelP);
+    if (verbose == 1) {
+        pm_message("Width x Height:  %u x %u", width, height);
+        pm_message("no. of colors:  %u", nColors);
+        pm_message("chars per pixel:  %u", charsPerPixel);
     }
 
     /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
+    if (charsPerPixel <= 2) {
         /* Set up direct index (see above) */
-        *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256);
-        *ptabP = NULL;
+        colormapSize = charsPerPixel == 1 ? 256 : 256*256;
+        ptab = NULL;
     } else {
         /* Set up lookup table (see above) */
-        *colorsP = ppm_allocrow(*ncolorsP);
-        MALLOCARRAY(*ptabP, *ncolorsP);
-        if (*ptabP == NULL)
-            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
+        colormapSize = nColors;
+        MALLOCARRAY(ptab, nColors);
+        if (ptab == NULL)
+            pm_error("Unable to allocate memory for %u colors", nColors);
     }
+    colors = ppm_allocrow(colormapSize);
     
     { 
         /* Read the color table */
-        int seqNum;
+        unsigned int seqNum;
             /* Sequence number of entry within color table in XPM header */
 
         *transparentP = -1;  /* initial value */
 
-        for (seqNum = 0; seqNum < *ncolorsP; seqNum++) {
+        for (seqNum = 0; seqNum < nColors; ++seqNum) {
             getline(line, sizeof(line), stream);
             /* skip the comment line if any */
             if (!strncmp(line, "/*", 2))
                 getline(line, sizeof(line), stream);
             
-            interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, 
-                                        *colorsP, *ptabP, transparentP);
+            interpretXpm3ColorTableLine(line, seqNum, charsPerPixel,
+                                        colors, ptab, nColors, transparentP);
         }
     }
+    *widthP         = width;
+    *heightP        = height;
+    *charsPerPixelP = charsPerPixel;
+    *nColorsP       = nColors;
+    *colorsP        = colors;
+    *ptabP          = ptab;
 }
 
 
+
 static void
-readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP, 
-               pixel ** const colorsP, int ** const ptabP) {
+readXpm1Header(FILE *          const stream,
+               int *           const widthP,
+               int *           const heightP, 
+               unsigned int *  const charsPerPixelP,
+               int *           const ncolorsP, 
+               pixel **        const colorsP,
+               unsigned int ** const ptabP) {
 /*----------------------------------------------------------------------------
   Read the header of the XPM file on stream 'stream'.  Assume the
   getline() stream is presently positioned to the beginning of the
@@ -466,13 +513,14 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
     char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1];
     char *t1;
     char *t2;
-    int format;
-    unsigned int v;
+    int format, v;
     int i, j;
     bool processedStaticChar;  
         /* We have read up to and interpreted the "static char..." line */
+    bool gotPixel;
 
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1;
+    *widthP = *heightP = *ncolorsP = format = -1;
+    gotPixel = false;
 
     /* Read the initial defines. */
     processedStaticChar = FALSE;
@@ -493,8 +541,10 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
                 *heightP = v;
             else if (streq(t1, "ncolors"))
                 *ncolorsP = v;
-            else if (streq(t1, "pixel"))
-                *chars_per_pixelP = v;
+            else if (streq(t1, "pixel")) {
+                gotPixel = TRUE;
+                *charsPerPixelP = v;
+            }
         } else if (!strncmp(line, "static char", 11)) {
             if ((t1 = strrchr(line, '_')) == NULL)
                 t1 = line;
@@ -507,6 +557,8 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
        t1 points to position of last "_" in the line, or the beginning of
        the line if there is no "_"
     */
+    if (!gotPixel)
+        pm_error("No 'pixel' value (characters per pixel)");
     if (format == -1)
         pm_error("missing or invalid format");
     if (format != 1)
@@ -517,10 +569,9 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
         pm_error("missing or invalid height");
     if (*ncolorsP == -1)
         pm_error("missing or invalid ncolors");
-    if (*chars_per_pixelP == -1)
-        pm_error("missing or invalid *chars_per_pixelP");
-    if (*chars_per_pixelP > 2)
-        pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory");
+
+    if (*charsPerPixelP > 2)
+        pm_message("WARNING: > 2 characters per pixel uses a lot of memory");
 
     /* If there's a monochrome color table, skip it. */
     if (!strncmp(t1, "mono", 4)) {
@@ -531,10 +582,10 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
         }
     }
     /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
+    if (*charsPerPixelP <= 2) {
         /* Up to two chars per pixel, we can use an indexed table. */
         v = 1;
-        for (i = 0; i < *chars_per_pixelP; ++i)
+        for (i = 0; i < *charsPerPixelP; ++i)
             v *= 256;
         *colorsP = ppm_allocrow(v);
         *ptabP = NULL;
@@ -554,7 +605,7 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
             pm_error("D error scanning color table");
         if ((t2 = strchr(t1 + 1, '"')) == NULL)
             pm_error("E error scanning color table");
-        if (t2 - t1 - 1 != *chars_per_pixelP)
+        if (t2 - t1 - 1 != *charsPerPixelP)
             pm_error("wrong number of chars per pixel in color table");
         strncpy(str1, t1 + 1, t2 - t1 - 1);
         str1[t2 - t1 - 1] = '\0';
@@ -567,9 +618,9 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
         str2[t2 - t1 - 1] = '\0';
 
         v = 0;
-        for (j = 0; j < *chars_per_pixelP; ++j)
+        for (j = 0; j < *charsPerPixelP; ++j)
             v = (v << 8) + str1[j];
-        if (*chars_per_pixelP <= 2)
+        if (*charsPerPixelP <= 2)
             /* Index into table. */
             (*colorsP)[v] = ppm_parsecolor(str2,
                                            (pixval) PPM_MAXMAXVAL);
@@ -593,15 +644,15 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
 
 
 static void
-interpretXpmLine(char   const line[],
-                 int    const chars_per_pixel,
-                 int    const ncolors,
-                 int *  const ptab, 
-                 int ** const cursorP,
-                 int *  const maxCursor) {
+interpretXpmLine(char           const line[],
+                 unsigned int   const charsPerPixel,
+                 int            const ncolors,
+                 unsigned int * const ptab, 
+                 int **         const cursorP,
+                 int *          const maxCursor) {
 /*----------------------------------------------------------------------------
    Interpret one line of XPM input.  The line is in 'line', and its
-   format is 'chars_per_pixel' characters per pixel.  'ptab' is the
+   format is 'charsPerPixel' characters per pixel.  'ptab' is the
    color table that applies to the line, which table has 'ncolors'
    colors.
 
@@ -632,22 +683,24 @@ interpretXpmLine(char   const line[],
            the pixels Caller wants.
         */
         while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) {
-            int colorNumber;
-            int i;
+            unsigned int colorNumber;
+            unsigned int i;
             colorNumber = 0;  /* initial value */
-            for (i = 0; i < chars_per_pixel; ++i)
-                colorNumber = (colorNumber << 8) + *(lineCursor++);
+            for (i = 0; i < charsPerPixel; ++i) {
+                unsigned char const byte = (unsigned char)(*lineCursor++);
+                colorNumber = (colorNumber << 8) + byte;
+            }
             if (ptab == NULL)
                 /* colormap is indexed directly by XPM color number */
                 *(*cursorP)++ = colorNumber;
             else {
                 /* colormap shadows ptab[].  Find this color # in ptab[] */
-                int i;
+                unsigned int i;
                 for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i);
                 if (i < ncolors)
                     *(*cursorP)++ = i;
                 else
-                    pm_error("Color number %d is in raster, but not in "
+                    pm_error("Color number %u is in raster, but not in "
                              "colormap.  Line it's in: '%s'",
                              colorNumber, line);
             }
@@ -678,12 +731,12 @@ ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP,
 -----------------------------------------------------------------------------*/
     char line[MAX_LINE+1], str1[MAX_LINE+1];
     int totalpixels;
-    int *cursor;  /* cursor into *dataP */
-    int *maxcursor;  /* value of above cursor for last pixel in image */
-    int *ptab;   /* colormap - malloc'ed */
+    int * cursor;  /* cursor into *dataP */
+    int * maxcursor;  /* value of above cursor for last pixel in image */
+    unsigned int * ptab;   /* colormap - malloc'ed */
     int rc;
     int ncolors;
-    int chars_per_pixel;
+    unsigned int charsPerPixel;
 
     backup = FALSE;
 
@@ -694,11 +747,11 @@ ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP,
     rc = sscanf(line, "/* %s */", str1);
     if (rc == 1 && strncmp(str1, "XPM", 3) == 0) {
         /* It's an XPM version 3 file */
-        readXpm3Header(stream, widthP, heightP, &chars_per_pixel,
+        readXpm3Header(stream, widthP, heightP, &charsPerPixel,
                        &ncolors, colorsP, &ptab, transparentP);
     } else {				/* try as an XPM version 1 file */
         /* Assume it's an XPM version 1 file */
-        readXpm1Header(stream, widthP, heightP, &chars_per_pixel, 
+        readXpm1Header(stream, widthP, heightP, &charsPerPixel, 
                        &ncolors, colorsP, &ptab);
         *transparentP = -1;  /* No transparency in version 1 */
     }
@@ -714,7 +767,7 @@ ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP,
         if (strncmp(line, "/*", 2) == 0) {
             /* It's a comment.  Ignore it. */
         } else {
-            interpretXpmLine(line, chars_per_pixel, 
+            interpretXpmLine(line, charsPerPixel, 
                              ncolors, ptab, &cursor, maxcursor);
         }
         if (cursor <= maxcursor)
diff --git a/doc/HISTORY b/doc/HISTORY
index cd943a94..1a10063e 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,42 +4,57 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-09.12.29 BJH  Release 10.48.04
+09.12.30 BJH  Release 10.49.00
+
+              Add pnmmercator.
+
+              pnmconvol: Add -matrix, -matrixfile.
+
+              pambayer: add -nointerpolate.
+
+              jpeg2ktopam: Work with JP2 input as well as JPC.
+
+              pamscale: Work on multi image stream.
+
+              ppmrainbow: Make new directory for temporary files.
+              Prevents interference by other user who shares the main
+              temporary file directory.
 
               libnetpbm: When reading plain format PNM with PAM routines,
               validate pixel against maxval (necessary for integer non-overrun
               guarantees).
 
-              xpmtoppm: fix wild pointer with color index > 127.
+              pnmsmooth: change -dump option to output a new pnmconvol
+              -matrix option instead of a PGM file (because pnmsmooth now
+              uses -matrix).
+
+              pnmtofiasco: fix bug: doesn't work with Standard Input.
 
               pnmsmooth: fix incorrect call to pm_system_lp() that makes
               it never work (but it wouldn't anyway because pm_system_lp()
               was broken -- see below).
 
-              pm_system*: fix various bugs that make it never work.
-
-09.12.10 BJH  Release 10.48.03
-
-              pamtosvg: fix compile failure from 10.48.02.
+              pm_system*: fix various bugs making it never work.
 
-09.12.10 BJH  Release 10.48.02
+              xpmtoppm: fix wild pointer with color index > 127.
 
-              ppmrainbow: Make new directory for temporary files.
-              Prevents interference by other user who shares the main
-              temporary file directory.
+              xpmtoppm: fix wild pointer when the input has a color index
+              value that is too large to be consistent with the number of
+              colors and bytes per pixel.
 
               pamtouil: fix crash when -name option doesn't contain an
               underscore.
               
               pamtosvg: fix some bug with unset Z coordinate.
 
-09.12.03 BJH  Release 10.48.01
-
               pnmtofiasco, fiascotopnm: fix bug on bigendian machine.
 
               pngtopam: use png_create_read_struct() instead of
-              png_create_write_struct().  Fixes "call to NULL read
-              function" failure.
+              png_create_write_struct().  Broken since 10.48.
+
+              configure: advise if adding -lz -lm fixes libpng link problem.
+
+              makeman: some fixes or enhancements.
 
 09.09.27 BJH  Release 10.48.00
 
diff --git a/doc/USERDOC b/doc/USERDOC
index 3dcfb00d..e24dedf4 100644
--- a/doc/USERDOC
+++ b/doc/USERDOC
@@ -35,6 +35,12 @@ Subversion repository using the Subversion client program 'svn':
 This creates a directory "userguide" in your current directory containing
 all the same files that are on the web site.
 
+And a sometimes more convenient way to get those files from the
+Subversion repository is to get Sourceforge's Viewvc service to make a
+tarball of them:
+
+  wget http://netpbm.svn.sourceforge.net/viewvc/netpbm/userguide.tar.gz?view=tar --output-document=/tmp/userguide.tgz
+
 
 GETTING COMMAND HELP WITH A "MAN" COMMAND
 -----------------------------------------
diff --git a/editor/pamcut.c b/editor/pamcut.c
index 8d4c2240..f659ee70 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -674,7 +674,7 @@ main(int argc, char *argv[]) {
     FILE * const ofP = stdout;
 
     struct cmdlineInfo cmdline;
-    FILE* ifP;
+    FILE * ifP;
     bool eof;
 
     pnm_init(&argc, argv);
diff --git a/editor/pamdither.c b/editor/pamdither.c
deleted file mode 100644
index e084892c..00000000
--- a/editor/pamdither.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*=============================================================================
-                                 pamdither
-===============================================================================
-  By Bryan Henderson, July 2006.
-
-  Contributed to the public domain.
-
-  This is meant to replace Ppmdither by Christos Zoulas, 1991.
-=============================================================================*/
-
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "shhopt.h"
-#include "pam.h"
-
-/* Besides having to have enough memory available, the limiting factor
-   in the dithering matrix power is the size of the dithering value.
-   We need 2*dith_power bits in an unsigned int.  We also reserve
-   one bit to give headroom to do calculations with these numbers.
-*/
-#define MAX_DITH_POWER ((sizeof(unsigned int)*8 - 1) / 2)
-
-
-/* COLOR():
- *	returns the index in the colormap for the
- *      r, g, b values specified.
- */
-#define COLOR(r,g,b) (((r) * dith_ng + (g)) * dith_nb + (b))
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char * inputFileName;  /* File name of input file */
-    const char * mapFileName;    /* File name of colormap file */
-    unsigned int dim;
-    unsigned int verbose;
-};
-
-
-
-static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
-/*----------------------------------------------------------------------------
-   parse program command line described in Unix standard form by argc
-   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.
-
-   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;
-        /* Instructions to optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    unsigned int dimSpec, mapfileSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-    
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "dim",      OPT_UINT, 
-            &cmdlineP->dim,    &dimSpec, 0);
-    OPTENT3(0,   "mapfile",      OPT_STRING, 
-            &cmdlineP->mapFilespec,    &mapfileSpec, 0);
-    OPTENT3(0, "verbose",        OPT_FLAG,   NULL,                  
-            &cmdlineP->verbose,        0 );
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
-
-    if (!dimSpec)
-        cmdlineP->dim = 4;
-
-    if (cmdlineP->dim > MAX_DITH_POWER)
-        pm_error("Dithering matrix power %u (-dim) is too large.  "
-                 "Must be <= %d",
-                 dithPower, MAX_DITH_POWER);
-        
-    if (!mapfileSpec)
-        pm_error("You must specify the -mapfile option.");
-
-    if (argc-1 > 1)
-        pm_error("Program takes at most one argument: the input file "
-                 "specification.  "
-                 "You specified %d arguments.", argc-1);
-    if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
-    else
-        cmdlineP->inputFilespec = argv[1];
-}
-
-
-
-static unsigned int
-dither(sample       const p,
-       sample       const maxval,
-       unsigned int const d,
-       unsigned int const ditheredMaxval,
-       unsigned int const ditherMatrixArea) {
-/*----------------------------------------------------------------------------
-  Return the dithered brightness for a component of a pixel whose real 
-  brightness for that component is 'p' based on a maxval of 'maxval'.
-  The returned brightness is based on a maxval of ditheredMaxval.
-
-  'd' is the entry in the dithering matrix for the position of this pixel
-  within the dithered square.
-
-  'ditherMatrixArea' is the area (number of pixels in) the dithered square.
------------------------------------------------------------------------------*/
-    unsigned int const ditherSquareMaxval = ditheredMaxval * ditherMatrixArea;
-        /* This is the maxval for an intensity that an entire dithered
-           square can represent.
-        */
-    pixval const pScaled = ditherSquareMaxval * p / maxval;
-        /* This is the input intensity P expressed with a maxval of
-           'ditherSquareMaxval'
-        */
-    
-    /* Now we scale the intensity back down to the 'ditheredMaxval', and
-       as that will involve rounding, we round up or down based on the position
-       in the dithered square, as determined by 'd'
-    */
-
-    return (pScaled + d) / ditherMatrixArea;
-}
-
-
-
-static unsigned int
-dithValue(unsigned int const y,
-          unsigned int const x,
-          unsigned int const dithPower) { 
-/*----------------------------------------------------------------------------
-  Return the value of a dither matrix which is 2 ** dithPower elements
-  square at Row x, Column y.
-  [graphics gems, p. 714]
------------------------------------------------------------------------------*/
-    unsigned int d;
-        /*
-          Think of d as the density. At every iteration, d is shifted
-          left one and a new bit is put in the low bit based on x and y.
-          If x is odd and y is even, or visa versa, then a bit is shifted in.
-          This generates the checkerboard pattern seen in dithering.
-          This quantity is shifted again and the low bit of y is added in.
-          This whole thing interleaves a checkerboard pattern and y's bits
-          which is what you want.
-        */
-    unsigned int i;
-
-    for (i = 0, d = 0; i < dithPower; i++, x >>= 1, y >>= 1)
-        d = (d << 2) | (((x & 1) ^ (y & 1)) << 1) | (y & 1);
-
-    return(d);
-}
-
-
-
-static unsigned int **
-dithMatrix(unsigned int const dithPower) {
-/*----------------------------------------------------------------------------
-   Create the dithering matrix for dimension 'dithDim'.
-
-   Return it in newly malloc'ed storage.
-
-   Note that we assume 'dith_dim' is small enough that the dith_mat_sz
-   computed within fits in an int.  Otherwise, results are undefined.
------------------------------------------------------------------------------*/
-    unsigned int const dithDim = 1 << dithPower;
-
-    unsigned int ** dithMat;
-
-    assert(dithPower < sizeof(unsigned int) * 8);
-
-    {
-        unsigned int const dithMatSize = 
-            (dithDim * sizeof(*dithMat)) + /* pointers */
-            (dithDim * dithDim * sizeof(**dithMat)); /* data */
-        
-        dithMat = malloc(dithMatSize);
-        
-        if (dithMat == NULL) 
-            pm_error("Out of memory.  "
-                     "Cannot allocate %d bytes for dithering matrix.",
-                     dithMatSize);
-    }
-    {
-        unsigned int * const rowStorage = (unsigned int *)&dithMat[dithDim];
-        unsigned int y;
-        for (y = 0; y < dithDim; ++y)
-            dithMat[y] = &rowStorage[y * dithDim];
-    }
-    {
-        unsigned int y;
-        for (y = 0; y < dithDim; ++y) {
-            unsigned int x;
-            for (x = 0; x < dithDim; ++x)
-                dithMat[y][x] = dithValue(y, x, dithPower);
-        }
-    }
-    return dithMat;
-}
-
-    
-
-static void
-ditherImage(struct pam   const inpam,
-            tuple *      const colormap, 
-            unsigned int const dithPower,
-            struct pam   const outpam;
-            tuple **     const inTuples,
-            tuple ***    const outTuplesP) {
-
-    unsigned int const dithDim = 1 << dithPower;
-    unsigned int const ditherMatrixArea = SQR(dithDim);
-
-    unsigned int const modMask = (dithDim - 1);
-       /* And this into N to compute N % dithDim cheaply, since we
-          know (though the compiler doesn't) that dithDim is a power of 2
-       */
-    unsigned int ** const ditherMatrix = dithMatrix(dithPower);
-
-    tuple ** ouputTuples;
-    unsigned int row; 
-
-    assert(dithPower < sizeof(unsigned int) * 8);
-    assert(UINT_MAX / dithDim >= dithDim);
-
-    outTuples = ppm_allocpamarray(outpam);
-
-    for (row = 0; row < inpam.height; ++row) {
-        unsigned int col;
-        for (col = 0; col < inpam.width; ++col) {
-            unsigned int const d =
-                ditherMatrix[row & modMask][(width-col-1) & modMask];
-            tuple const inputTuple = inTuples[row][col];
-            unsigned int dithered[3];
-
-            unsigned int plane;
-
-            assert(inpam.depth >= 3);
-
-            for (plane = 0; plane < 3; ++plane)
-                dithered[plane] =
-                    dither(inputTuple[plane], inpam.maxval, d, outpam.maxval,
-                           ditherMatrixArea);
-
-            outTuples[row][col] = 
-                colormap[COLOR(dithered[RED_PLANE],
-                               dithered[GRN_PLANE],
-                               dithered[BLU_PLANE])];
-        }
-    }
-    free(ditherMatrix);
-    *outTuplesP = outTuples;
-}
-
-
-
-static void
-getColormap(const char * const mapFileName,
-            tuple **     const colormapP) {
-
-    TODO("write this");
-
-}
-
-
-
-int
-main(int argc,
-     char ** argv) {
-
-    struct cmdlineInfo cmdline;
-    FILE * ifP;
-    tuple ** inTuples;        /* Input image */
-    tuple ** outTuples;        /* Output image */
-    tuple * colormap;
-    int cols, rows;
-    pixval maxval;  /* Maxval of the input image */
-
-    struct pam outpamCommon;
-        /* Describes the output images.  Width and height fields are
-           not meaningful, because different output images might have
-           different dimensions.  The rest of the information is common
-           across all output images.
-        */
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(&argc, &argv);
-
-    pm_openr(cmdline.inputFileName);
-
-    inTuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
-    pm_close(ifP);
-
-    getColormap(cmdline.mapFileName, &colormap);
-
-    ditherImage(inpam, colormap, dithPower, inTuples, &outTuples);
-
-    ppm_writeppm(stdout, opixels, cols, rows, outputMaxval, 0);
-    pm_close(stdout);
-
-    free(colormap);
-
-    pnm_freepamarray(inTuples, &inpam);
-    pnm_freepamarray(outTuples, &outpam);
-
-    return 0;
-}
diff --git a/editor/pamedge.c b/editor/pamedge.c
index ac94e2ae..f4384535 100644
--- a/editor/pamedge.c
+++ b/editor/pamedge.c
@@ -156,11 +156,12 @@ writeMiddleRows(struct pam * const inpamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** argv) {
+
     FILE *ifP;
     struct pam inpam, outpam;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     if (argc-1 == 1) 
         ifP = pm_openr(argv[1]);
diff --git a/editor/pamflip.c b/editor/pamflip.c
index 1c479e54..61c6efce 100644
--- a/editor/pamflip.c
+++ b/editor/pamflip.c
@@ -745,7 +745,7 @@ typedef struct {
 /*----------------------------------------------------------------------------
    A description of the quilt of cells that make up the output image.
 -----------------------------------------------------------------------------*/
-    unsigned int ranks, files;
+    unsigned int rankCt, fileCt;
         /* Dimensions of the output image in cells */
     struct pam ** pam;
         /* pam[y][x] is the pam structure for the cell at rank y, file x
@@ -769,8 +769,12 @@ initOutCell(struct pam *     const outCellPamP,
             unsigned int     const inCellHeight,
             struct pam *     const inpamP,
             struct xformCore const xformCore) {
-
-    unsigned int outCellFiles, outCellRanks;
+/*----------------------------------------------------------------------------
+   Set up an output cell.  Create and open a temporary file to hold its
+   raster.  Figure out the dimensions of the cell.  Return a PAM structure
+   that describes the cell (including identifying that temporary file).
+-----------------------------------------------------------------------------*/
+    unsigned int outCellFileCt, outCellRankCt;
 
     *outCellPamP = *inpamP;  /* initial value */
 
@@ -779,10 +783,10 @@ initOutCell(struct pam *     const outCellPamP,
     outCellPamP->file = pm_tmpfile();
 
     xformDimensions(xformCore, inCellWidth, inCellHeight,
-                    &outCellFiles, &outCellRanks);
+                    &outCellFileCt, &outCellRankCt);
 
-    outCellPamP->width = outCellFiles;
-    outCellPamP->height = outCellRanks;
+    outCellPamP->width = outCellFileCt;
+    outCellPamP->height = outCellRankCt;
 }
 
 
@@ -792,9 +796,26 @@ createOutputMap(struct pam *       const inpamP,
                 unsigned int       const maxRows,
                 struct xformMatrix const cellXform,
                 struct xformCore   const xformCore) {
-
-    unsigned int const inCellFiles = 1;
-    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+/*----------------------------------------------------------------------------
+   Create and return the output map.  That's a map of all the output cells
+   (from which the output image can be assembled, once those cells are filled
+   in).
+
+   The map contains the dimensions of each output cell as well as file
+   stream handles for the temporary files containing the pixels of each
+   cell.  We create and open those files.
+
+   Note that a complexity of determining cell dimensions (which we handle)
+   is that the input image isn't necessarily a whole multiple of the input
+   cell size, so the last cell may be short.
+
+   The map does not contain the mapping from input cells to output cells
+   (e.g. in a top-bottom transformation of a 10-cell image, input cell
+   0 maps to output cell 9); caller can compute that when needed from
+   'cellXform'.
+-----------------------------------------------------------------------------*/
+    unsigned int const inCellFileCt = 1;
+    unsigned int const inCellRankCt = (inpamP->height + maxRows - 1) / maxRows;
 
     outputMap * mapP;
     unsigned int outCellRank;
@@ -802,27 +823,27 @@ createOutputMap(struct pam *       const inpamP,
 
     MALLOCVAR_NOFAIL(mapP);
 
-    xformDimensions(xformCore, inCellFiles, inCellRanks,
-                    &mapP->files, &mapP->ranks);
+    xformDimensions(xformCore, inCellFileCt, inCellRankCt,
+                    &mapP->fileCt, &mapP->rankCt);
 
-    MALLOCARRAY(mapP->pam, mapP->ranks);
+    MALLOCARRAY(mapP->pam, mapP->rankCt);
     if (mapP->pam == NULL)
         pm_error("Could not allocate a cell array for %u ranks of cells.",
-                 mapP->ranks);
+                 mapP->rankCt);
 
-    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < mapP->rankCt; ++outCellRank) {
 
-        MALLOCARRAY(mapP->pam[outCellRank], mapP->files);
+        MALLOCARRAY(mapP->pam[outCellRank], mapP->fileCt);
 
         if (mapP->pam[outCellRank] == NULL)
             pm_error("Failed to allocate rank %u of the cell array, "
-                     "%u cells wide", outCellRank, mapP->files);
+                     "%u cells wide", outCellRank, mapP->fileCt);
     }
 
-    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+    for (inCellRank = 0; inCellRank < inCellRankCt; ++inCellRank) {
         unsigned int const inCellFile = 0;
         unsigned int const inCellStartRow = inCellRank * maxRows;
-        unsigned int const inCellRows =
+        unsigned int const inCellRowCt =
             MIN(inpamP->height - inCellStartRow, maxRows);
 
         unsigned int outCellFile, outCellRank;
@@ -830,7 +851,7 @@ createOutputMap(struct pam *       const inpamP,
                        &outCellFile, &outCellRank);
     
         initOutCell(&mapP->pam[outCellRank][outCellFile],
-                    inpamP->width, inCellRows,
+                    inpamP->width, inCellRowCt,
                     inpamP, xformCore);
     }
     return mapP;
@@ -843,7 +864,7 @@ destroyOutputMap(outputMap * const mapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank)
+    for (outCellRank = 0; outCellRank < mapP->rankCt; ++outCellRank)
         free(mapP->pam[outCellRank]);
 
     free(mapP->pam);
@@ -858,9 +879,9 @@ rewindCellFiles(outputMap * const outputMapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < outputMapP->rankCt; ++outCellRank) {
         unsigned int outCellFile;
-        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+        for (outCellFile = 0; outCellFile < outputMapP->fileCt; ++outCellFile)
             pm_seek(outputMapP->pam[outCellRank][outCellFile].file, 0);
     }
 }
@@ -872,9 +893,9 @@ closeCellFiles(outputMap * const outputMapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < outputMapP->rankCt; ++outCellRank) {
         unsigned int outCellFile;
-        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+        for (outCellFile = 0; outCellFile < outputMapP->fileCt; ++outCellFile)
             pm_close(outputMapP->pam[outCellRank][outCellFile].file);
     }
 }
@@ -932,7 +953,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
 
     tupleRow = pnm_allocpamrow(outpamP);
 
-    for (outRank = 0; outRank < outputMapP->ranks; ++outRank) {
+    for (outRank = 0; outRank < outputMapP->rankCt; ++outRank) {
         unsigned int const cellRows = outputMapP->pam[outRank][0].height;
             /* Number of rows in every cell in this rank */
 
@@ -944,7 +965,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
 
             outCol = 0;
 
-            for (outFile = 0; outFile < outputMapP->files; ++outFile) {
+            for (outFile = 0; outFile < outputMapP->fileCt; ++outFile) {
                 struct pam * const outCellPamP = 
                     &outputMapP->pam[outRank][outFile];
 
@@ -973,7 +994,7 @@ transformNonPbmChunk(struct pam *     const inpamP,
                      unsigned int     const maxRows,
                      bool             const verbose) {
 /*----------------------------------------------------------------------------
-  Same as transformNonPbmChunk(), except we read 'maxRows' rows of the
+  Same as transformNonPbmWhole(), except we read 'maxRows' rows of the
   input into memory at a time, storing intermediate results in temporary
   files, to limit our use of virtual and real memory.
 
@@ -981,28 +1002,33 @@ transformNonPbmChunk(struct pam *     const inpamP,
   header).
 
   We call the strip of 'maxRows' rows that we read a source cell.  We
-  transform that cell according to 'xformCore' to create to create a
+  transform that cell according to 'xformCore' to create a
   target cell.  We store all the target cells in temporary files.
   We consider the target cells to be arranged in a column matrix the
   same as the source cells within the source image; we transform that
   matrix according to 'xformCore'.  The resulting cell matrix is the
   target image.
 -----------------------------------------------------------------------------*/
-    unsigned int const inCellFiles = 1;
-    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+    /* The cells of the source image ("inCell") are in a 1-column matrix.
+       "rank" is the vertical position of a cell in that matrix; "file" is
+       the horizontal position (always 0, of course).
+    */
+    unsigned int const inCellFileCt = 1;
+    unsigned int const inCellRankCt = (inpamP->height + maxRows - 1) / maxRows;
 
     struct xformMatrix cellXform;
     unsigned int inCellRank;
     outputMap * outputMapP;
 
     if (verbose)
-        pm_message("Transforming in %u chunks, using temp files", inCellRanks);
+        pm_message("Transforming in %u chunks, using temp files",
+                   inCellRankCt);
 
-    computeXformMatrix(&cellXform, inCellFiles, inCellRanks, xformCore);
+    computeXformMatrix(&cellXform, inCellFileCt, inCellRankCt, xformCore);
 
     outputMapP = createOutputMap(inpamP, maxRows, cellXform, xformCore);
 
-    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+    for (inCellRank = 0; inCellRank < inCellRankCt; ++inCellRank) {
         unsigned int const inCellFile = 0;
         unsigned int const inCellStartRow = inCellRank * maxRows;
         unsigned int const inCellRows =
diff --git a/editor/pammasksharpen.c b/editor/pammasksharpen.c
index e61237ca..f08e130c 100644
--- a/editor/pammasksharpen.c
+++ b/editor/pammasksharpen.c
@@ -17,7 +17,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -46,7 +46,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sharpSpec) {
@@ -109,7 +109,7 @@ sharpened(sample const inputSample,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char * *argv) {
 
     struct cmdlineInfo cmdline;
     struct pam inpam;
@@ -126,8 +126,8 @@ main(int argc, char *argv[]) {
            which they will be considered identical.
         */
     
-    pnm_init(&argc, argv);
-
+    pm_proginit(&argc, argv);
+    
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
diff --git a/editor/pamscale.c b/editor/pamscale.c
index 35c76b17..fce76d8c 100644
--- a/editor/pamscale.c
+++ b/editor/pamscale.c
@@ -31,9 +31,9 @@
 #include <assert.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 
 /****************************/
@@ -52,20 +52,22 @@
 #define EPSILON 1e-7
 
 
+
 /* x^2 and x^3 helper functions */
 static __inline__ double 
-pow2 (double x)
-{
-  return x*x;
+pow2(double const x) {
+    return x * x;
 }
 
+
+
 static __inline__ double 
-pow3 (double x) 
-{
-  return x*x*x;
+pow3(double const x) {
+    return x * x * x;
 }
 
 
+
 /* box, pulse, Fourier window, */
 /* box function also know as rectangle function */
 /* 1st order (constant) b-spline */
@@ -74,14 +76,15 @@ pow3 (double x)
 #define radius_box (0.5)
 
 static double 
-filter_box (double x)
-{
-    if (x <  0.0) x = -x;
-    if (x <= 0.5) return 1.0;
-    return 0.0;
+filter_box(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return (absx <= 0.5) ? 1.0 : 0.0;
 }
 
 
+
 /* triangle, Bartlett window, */
 /* triangle function also known as lambda function */
 /* 2nd order (linear) b-spline */
@@ -89,56 +92,66 @@ filter_box (double x)
 #define radius_triangle (1.0)
 
 static double 
-filter_triangle (double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return 1.0-x;
-    return 0.0;
+filter_triangle(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return absx < 1.0 ? 1.0 - absx : 0.0;
 }
 
 
+
 /* 3rd order (quadratic) b-spline */
 
 #define radius_quadratic (1.5)
 
 static double 
-filter_quadratic(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 0.5) return 0.75-pow2(x);
-    if (x < 1.5) return 0.50*pow2(x-1.5);
-    return 0.0;
+filter_quadratic(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 0.5 ? 0.75 - pow2(absx) :
+        absx < 1.5 ? 0.50 * pow2(absx - 1.5) :
+        0.0;
 }
 
 
+
 /* 4th order (cubic) b-spline */
 
 #define radius_cubic (2.0)
 
 static double 
-filter_cubic(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return 0.5*pow3(x) - pow2(x) + 2.0/3.0;
-    if (x < 2.0) return pow3(2.0-x)/6.0;
-    return 0.0;
+filter_cubic(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 1.0 ? 0.5 * pow3(absx) - pow2(absx) + 2.0/3.0 :
+        absx < 2.0 ? pow3(2.0-absx)/6.0 :
+        0.0;
 }
 
 
+
 /* Catmull-Rom spline, Overhauser spline */
 
 #define radius_catrom (2.0)
 
 static double 
-filter_catrom(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return  1.5*pow3(x) - 2.5*pow2(x)         + 1.0;
-    if (x < 2.0) return -0.5*pow3(x) + 2.5*pow2(x) - 4.0*x + 2.0;
-    return 0.0;
+filter_catrom(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 1.0 ?  1.5 * pow3(absx) - 2.5 * pow2(absx)         + 1.0 :
+        absx < 2.0 ? -0.5 * pow3(absx) + 2.5 * pow2(absx) - 4.0 * absx + 2.0 :
+        0.0;
 }
 
 
+
 /* Mitchell & Netravali's two-param cubic */
 /* see Mitchell&Netravali,  */
 /* "Reconstruction Filters in Computer Graphics", SIGGRAPH 88 */
@@ -149,98 +162,106 @@ static double
 filter_mitchell(double x)
 {
 
-    double b = 1.0/3.0;
-    double c = 1.0/3.0;
-
-    double p0 = (  6.0 -  2.0*b         ) / 6.0;
-    double p2 = (-18.0 + 12.0*b +  6.0*c) / 6.0;
-    double p3 = ( 12.0 -  9.0*b -  6.0*c) / 6.0;
-    double q0 = (         8.0*b + 24.0*c) / 6.0;
-    double q1 = (      - 12.0*b - 48.0*c) / 6.0;
-    double q2 = (         6.0*b + 30.0*c) / 6.0;
-    double q3 = (      -      b -  6.0*c) / 6.0;
-
-    if (x <  0.0) x = -x;
-    if (x <  1.0) return p3*pow3(x) + p2*pow2(x)        + p0;
-    if (x < 2.0) return q3*pow3(x) + q2*pow2(x) + q1*x + q0;
-    return 0.0;
+    double const b = 1.0/3.0;
+    double const c = 1.0/3.0;
+
+    double const p0 = (  6.0 -  2.0*b         ) / 6.0;
+    double const p2 = (-18.0 + 12.0*b +  6.0*c) / 6.0;
+    double const p3 = ( 12.0 -  9.0*b -  6.0*c) / 6.0;
+    double const q0 = (         8.0*b + 24.0*c) / 6.0;
+    double const q1 = (      - 12.0*b - 48.0*c) / 6.0;
+    double const q2 = (         6.0*b + 30.0*c) / 6.0;
+    double const q3 = (      -      b -  6.0*c) / 6.0;
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx <  1.0 ? p3 * pow3(absx) + p2 * pow2(absx)        + p0 :
+        absx < 2.0 ? q3 * pow3(absx) + q2 * pow2(absx) + q1 * absx + q0 :
+        0.0;
 }
 
 
+
 /* Gaussian filter (infinite) */
 
 #define radius_gauss (1.25)
 
 static double 
-filter_gauss(double x)
-{
+filter_gauss(double const x) {
+
     return exp(-2.0*pow2(x)) * sqrt(2.0/M_PI);
 }
 
 
+
 /* sinc, perfect lowpass filter (infinite) */
 
 #define radius_sinc (4.0)
 
 static double 
-filter_sinc(double x)
-{
+filter_sinc(double const x) {
     /* Note: Some people say sinc(x) is sin(x)/x.  Others say it's
        sin(PI*x)/(PI*x), a horizontal compression of the former which is
        zero at integer values.  We use the latter, whose Fourier transform
        is a canonical rectangle function (edges at -1/2, +1/2, height 1).
     */
-    if (x == 0.0) return 1.0;
-    return sin(M_PI*x)/(M_PI*x);
+    return 
+        x == 0.0 ? 1.0 :
+        sin(M_PI*x)/(M_PI*x);
 }
 
 
+
 /* Bessel (for circularly symm. 2-d filt, infinite) */
 /* See Pratt "Digital Image Processing" p. 97 for Bessel functions */
 
 #define radius_bessel (3.2383)
 
 static double 
-filter_bessel(double x)
-{
-    if (x == 0.0) return M_PI/4.0;
-    return j1(M_PI*x)/(2.0*x);
+filter_bessel(double const x) {
+
+    return 
+        x == 0.0 ? M_PI/4.0 :
+        j1(M_PI * x) / (2.0 * x);
 }
 
 
+
 /* Hanning window (infinite) */
 
 #define radius_hanning (1.0)
 
 static double 
-filter_hanning(double x)
-{
-    return 0.5*cos(M_PI*x) + 0.5;
+filter_hanning(double const x) {
+
+    return 0.5 * cos(M_PI * x) + 0.5;
 }
 
 
+
 /* Hamming window (infinite) */
 
 #define radius_hamming (1.0)
 
 static double 
-filter_hamming(double x)
-{
-  return 0.46*cos(M_PI*x) + 0.54;
+filter_hamming(double const x) {
+    return 0.46 * cos(M_PI * x) + 0.54;
 }
 
 
+
 /* Blackman window (infinite) */
 
 #define radius_blackman (1.0)
 
 static double 
-filter_blackman(double x)
-{
-    return 0.5*cos(M_PI*x) + 0.08*cos(2.0*M_PI*x) + 0.42;
+filter_blackman(double const x) {
+    return 0.5 * cos(M_PI * x) + 0.08 * cos(2.0 * M_PI * x) + 0.42;
 }
 
 
+
 /* parameterized Kaiser window (infinite) */
 /* from Oppenheim & Schafer, Hamming */
 
@@ -248,8 +269,7 @@ filter_blackman(double x)
 
 /* modified zeroth order Bessel function of the first kind. */
 static double 
-bessel_i0(double x)
-{
+bessel_i0(double const x) {
   
     int i;
     double sum, y, t;
@@ -257,64 +277,70 @@ bessel_i0(double x)
     sum = 1.0;
     y = pow2(x)/4.0;
     t = y;
-    for (i=2; t>EPSILON; i++) {
+    for (i=2; t>EPSILON; ++i) {
         sum += t;
         t   *= (double)y/pow2(i);
     }
     return sum;
 }
 
+
+
 static double 
-filter_kaiser(double x)
-{
-    /* typically 4<a<9 */
+filter_kaiser(double const x) {
+    /* typically 4 < a < 9 */
     /* param a trades off main lobe width (sharpness) */
     /* for side lobe amplitude (ringing) */
   
-    double a   = 6.5;
-    double i0a = 1.0/bessel_i0(a);
+    double const a   = 6.5;
+    double const i0a = 1.0/bessel_i0(a);
   
-    return i0a*bessel_i0(a*sqrt(1.0-pow2(x)));
+    return i0a * bessel_i0(a * sqrt(1.0-pow2(x)));
 }
 
 
+
 /* normal distribution (infinite) */
 /* Normal(x) = Gaussian(x/2)/2 */
 
 #define radius_normal (1.0)
 
 static double 
-filter_normal(double x)
-{
+filter_normal(double const x) {
     return exp(-pow2(x)/2.0) / sqrt(2.0*M_PI);
-    return 0.0;
 }
 
 
+
 /* Hermite filter */
 
 #define radius_hermite  (1.0)
 
 static double 
-filter_hermite(double x)
-{
+filter_hermite(double const x) {
     /* f(x) = 2|x|^3 - 3|x|^2 + 1, -1 <= x <= 1 */
-    if (x <  0.0) x = -x;
-    if (x <  1.0) return 2.0*pow3(x) - 3.0*pow2(x) + 1.0;
-    return 0.0;
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx <  1.0 ? 2.0 * pow3(absx) - 3.0 * pow2(absx) + 1.0 :
+        0.0;
 }
 
 
+
 /* Lanczos filter */
 
 #define radius_lanczos (3.0)
 
 static double 
-filter_lanczos(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x <  3.0) return filter_sinc(x) * filter_sinc(x/3.0);
-    return(0.0);
+filter_lanczos(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        x <  3.0 ? filter_sinc(absx) * filter_sinc(absx/3.0) :
+        0.0;
 }
 
 
@@ -491,7 +517,7 @@ processFilterOptions(unsigned int const         filterSpec,
 
 static void
 parseXyParms(int                  const argc, 
-             char **              const argv,
+             const char **        const argv,
              struct cmdlineInfo * const cmdlineP) {
 
     /* parameters are box width (columns), box height (rows), and
@@ -533,7 +559,7 @@ parseXyParms(int                  const argc,
 
 static void
 parseScaleParms(int                   const argc, 
-                char **               const argv,
+                const char **         const argv,
                 struct cmdlineInfo  * const cmdlineP) {
 
     /* parameters are scale factor and optional filespec */
@@ -559,7 +585,7 @@ parseScaleParms(int                   const argc,
 
 static void
 parseFilespecOnlyParms(int                   const argc, 
-                       char **               const argv,
+                       const char **         const argv,
                        struct cmdlineInfo  * const cmdlineP) {
 
     /* Only parameter allowed is optional filespec */
@@ -572,7 +598,7 @@ parseFilespecOnlyParms(int                   const argc,
 
 static void 
 parseCommandLine(int argc, 
-                 char ** argv, 
+                 const char ** argv, 
                  struct cmdlineInfo  * const cmdlineP) {
 /* --------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
@@ -585,8 +611,8 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def;
-    /* Instructions to optParseOptions3 on how to parse our options. */
     optStruct3 opt;
+        /* Instructions to optParseOptions3 on how to parse our options. */
   
     unsigned int option_def_index;
     unsigned int xyfit, xyfill;
@@ -633,7 +659,7 @@ parseCommandLine(int argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->nomix && filterSpec) 
@@ -2080,24 +2106,18 @@ scaleWithoutMixing(const struct pam * const inpamP,
 
 
 
-int
-main(int argc, char **argv ) {
-
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
+static void
+pamscale(FILE *             const ifP,
+         FILE *             const ofP,
+         struct cmdlineInfo const cmdline) {
+    
     struct pam inpam, outpam;
     float xscale, yscale;
 
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.inputFileName);
-
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     outpam = inpam;  /* initial value */
-    outpam.file = stdout;
+    outpam.file = ofP;
 
     if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE && !cmdline.nomix) {
         outpam.format = PGM_TYPE;
@@ -2146,6 +2166,29 @@ main(int argc, char **argv ) {
                  cmdline.windowFunction, cmdline.verbose,
                  cmdline.linear);
     }
+}
+
+
+
+int
+main(int argc, const char **argv ) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    bool eof;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    eof = FALSE;
+    while (!eof) {
+        pamscale(ifP, stdout, cmdline);
+        pnm_nextimage(ifP, &eof);
+    }
+
     pm_close(ifP);
     pm_close(stdout);
     
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 031feb66..b1f4adc8 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -1,6 +1,4 @@
-/* pnmconvol.c - general MxN convolution on a PNM image
-**
-** Version 2.0.1 January 30, 1995
+/* pnmconvol.c - general MxN convolution on a Netpbm image
 **
 ** Major rewriting by Mike Burns
 ** Copyright (C) 1994, 1995 by Mike Burns (burns@chem.psu.edu)
@@ -17,20 +15,273 @@
 
 /* A change history is at the bottom */
 
+#include <stdlib.h>
+#include <assert.h>
+
 #include "pm_c_util.h"
-#include "pnm.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "token.h"
+#include "io.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+static void
+validateKernelDimensions(unsigned int const width,
+                         unsigned int const height) {
+
+    if (height == 0)
+        pm_error("Convolution matrix height is zero");
+    if (width == 0)
+        pm_error("Convolution matrix width is zero");
+
+    if (height % 2 != 1)
+        pm_error("The convolution matrix must have an odd number of rows.  "
+                 "Yours has %u", height);
+
+    if (width % 2 != 1)
+        pm_error("The convolution matrix must have an odd number of columns.  "
+                 "Yours has %u", width);
+}
+
+
+
+struct matrixOpt {
+    unsigned int width;
+    unsigned int height;
+    float ** weight;
+};
+
+
+
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
-    const char *kernelFilespec;
+    const char * inputFileName;  /* '-' if stdin */
+    const char * pnmMatrixFileName;
     unsigned int nooffset;
+    const char ** matrixfile;
+    unsigned int matrixSpec;
+    struct matrixOpt matrix;
 };
 
+
+
+static void
+countMatrixOptColumns(const char *   const rowString,
+                      unsigned int * const colCtP) {
+
+    const char * cursor;
+    unsigned int colCt;
+
+    for (cursor = &rowString[0], colCt = 0; *cursor; ) {
+        const char * colString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ',', &colString, &next, &error);
+
+        if (error) {
+            pm_error("Unable to parse -matrix value row '%s'.  %s",
+                     rowString, error);
+            strfree(error);
+        } else {
+            ++colCt;
+
+            cursor = next;
+            if (*cursor) {
+                assert(*cursor == ',');
+                ++cursor;  /* advance over comma to next column */
+            }
+            strfree(colString);
+        }
+    }
+    *colCtP = colCt;
+}
+
+
+
+static void
+getMatrixOptDimensions(const char *   const matrixOptString,
+                       unsigned int * const widthP,
+                       unsigned int * const heightP) {
+/*----------------------------------------------------------------------------
+   Given the value of a -matrix option, 'matrixOptString', return the
+   height and width of the matrix it describes.
+
+   If it's not valid enough to determine that (e.g. it has rows of different
+   widths), abort.
+
+   An example of 'matrixOptString':
+
+     ".04,.15,.04;.15,.24,.15;.04,.15,.04"
+-----------------------------------------------------------------------------*/
+    unsigned int rowCt;
+    const char * cursor;
+
+    for (cursor = &matrixOptString[0], rowCt = 0; *cursor; ) {
+        const char * rowString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ';', &rowString, &next, &error);
+
+        if (error) {
+            pm_error("Unable to parse -matrix value '%s'.  %s",
+                     matrixOptString, error);
+            strfree(error);
+        } else {
+            unsigned int colCt;
+            ++rowCt;
+
+            countMatrixOptColumns(rowString, &colCt);
+
+            if (rowCt == 1)
+                *widthP = colCt;
+            else {
+                if (colCt != *widthP)
+                pm_error("-matrix option value contains rows of different "
+                         "widths: %u and %u", *widthP, colCt);
+            }            
+            strfree(rowString);
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ';');
+                ++cursor;  /* advance cursor over semicolon to next row */
+            }
+        }
+    }
+    *heightP = rowCt;
+}
+
+
+
+static void
+parseMatrixRow(const char * const matrixOptRowString,
+               unsigned int const width,
+               float *      const weight) {
+
+    unsigned int col;
+    const char * cursor;
+
+    for (col = 0, cursor = &matrixOptRowString[0]; col < width; ++col) {
+        const char * colString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ',', &colString, &next, &error);
+
+        if (error) {
+            pm_error("Failed parsing a row in the -matrix value.  %s", error);
+            strfree(error);
+        } else {
+            if (colString[0] == '\0')
+                pm_error("The Column %u element of the row '%s' in the "
+                         "-matrix value is a null string", col,
+                         matrixOptRowString);
+            else {
+                char * trailingJunk;
+                weight[col] = strtod(colString, &trailingJunk);
+
+                if (*trailingJunk != '\0') 
+                    pm_error("The Column %u element of the row '%s' in the "
+                             "-matrix value is not a valid floating point "
+                             "number", col, matrixOptRowString);
+            }
+            strfree(colString);
+
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ',');
+                ++cursor;  /* advance over comma to next column */
+            }
+        }
+    }
+}
+
+
+
+static void
+parseMatrixOptWithDimensions(const char * const matrixOptString,
+                             unsigned int const width,
+                             unsigned int const height,
+                             float **     const weight) {
+    
+    unsigned int row;
+    const char * cursor;
+
+    for (row = 0, cursor = &matrixOptString[0]; row < height; ++row) {
+        const char * rowString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ';', &rowString, &next, &error);
+
+        if (error) {
+            pm_error("Failed parsing -matrix value.  %s", error);
+            strfree(error);
+        } else {
+            parseMatrixRow(rowString, width, weight[row]);
+
+            strfree(rowString);
+
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ';');
+                ++cursor;  /* advance over semicolon to next row */
+            }
+        }
+    }
+}    
+
+
+
+static void
+parseMatrixOpt(const char *         const matrixOptString,
+               struct matrixOpt *   const matrixOptP) {
+/*----------------------------------------------------------------------------
+   An example of 'matrixOptString':
+
+     ".04,.15,.04;.15,.24,.15;.04,.15,.04"
+
+-----------------------------------------------------------------------------*/
+    unsigned int width, height;
+
+    getMatrixOptDimensions(matrixOptString, &width, &height);
+
+    validateKernelDimensions(width, height);
+
+    matrixOptP->height = height;
+    matrixOptP->width  = width;
+
+    {
+        unsigned int row;
+        MALLOCARRAY_NOFAIL(matrixOptP->weight, height);
+        for (row = 0; row < height; ++row)
+            MALLOCARRAY_NOFAIL(matrixOptP->weight[row], width);
+    }
+    parseMatrixOptWithDimensions(matrixOptString, width, height,
+                                 matrixOptP->weight);
+}
+
+
+
+static void
+validateMatrixfileOpt(const char ** const matrixFileOpt) {
+
+    if (matrixFileOpt[0] == NULL)
+        pm_error("You specified an empty string as the value of "
+                 "-matrixfile.  You must specify at least one file name");
+}
+
+
+
 static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -50,12 +301,18 @@ parseCommandLine(int argc, char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
+    unsigned int matrixfileSpec;
+    const char * matrixOpt;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "matrix",       OPT_STRING, &matrixOpt,
+            &cmdlineP->matrixSpec,     0)
+    OPTENT3(0, "matrixfile",   OPT_STRINGLIST, &cmdlineP->matrixfile,
+            &matrixfileSpec,           0)
     OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
-            &cmdlineP->nooffset,       0 );
+            &cmdlineP->nooffset,       0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -64,1759 +321,1722 @@ parseCommandLine(int argc, char ** argv,
     optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 < 1)
-        pm_error("Need at least one argument: file specification of the "
-                 "convolution kernel image.");
+    if (matrixfileSpec && cmdlineP->matrixSpec)
+        pm_error("You can't specify by -matrix and -matrixfile");
 
-    cmdlineP->kernelFilespec = argv[1];
+    if (cmdlineP->matrixSpec)
+        parseMatrixOpt(matrixOpt, &cmdlineP->matrix);
 
-    if (argc-1 >= 2)
-        cmdlineP->inputFilespec = argv[2];
+    if (matrixfileSpec)
+        validateMatrixfileOpt(cmdlineP->matrixfile);
     else
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->matrixfile = NULL;
+
+    if (matrixfileSpec || cmdlineP->matrixSpec) {
+        if (cmdlineP->nooffset)
+            pm_error("-nooffset is meaningless and not allowed with "
+                     "-matrix or -matrixfile");
+
+        cmdlineP->pnmMatrixFileName = NULL;
 
-    if (argc-1 > 2)
-        pm_error("Too many arguments.  Only acceptable arguments are: "
-                 "convolution file name and input file name");
+        if (argc-1 >= 1)
+            cmdlineP->inputFileName = argv[1];
+        else
+            cmdlineP->inputFileName = "-";
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  When you specify -matrix "
+                     "or -matrixfile, the only allowable non-option "
+                     "argument is the input file name");
+    } else {
+        /* It's an old style invocation we accept for backward compatibility */
+        
+        if (argc-1 < 1)
+            pm_error("You must specify either -matrix or -matrixfile "
+                     "at least one argument which names an old-style PGM "
+                     "convolution matrix file.");
+        else {
+            cmdlineP->pnmMatrixFileName = argv[1];
+
+            if (argc-1 >= 2)
+                cmdlineP->inputFileName = argv[2];
+            else
+                cmdlineP->inputFileName = "-";
+            
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  Only acceptable arguments are: "
+                         "convolution matrix file name and input file name");
+        }
+    }
 }
 
 
-/* Macros to verify that r,g,b values are within proper range */
 
-#define CHECK_GRAY \
-    if (tempgsum < 0L) g = 0; \
-    else if (tempgsum > maxval) g = maxval; \
-    else g = tempgsum;
+struct convKernel {
+    unsigned int cols;
+        /* Width of the convolution window */
+    unsigned int rows;
+        /* Height of the convolution window */
+    unsigned int planes;
+        /* Depth of the kernel -- this had better be the same as the
+           depth of the image being convolved.
+        */
+    float ** weight[3];
+        /* weight[PLANE][ROW][COL] is the weight to give to Plane PLANE
+           of the pixel at row ROW, column COL within the convolution window.
+           
+           One means full weight.
+
+           It can have magnitude greater than or less than one.  It can be
+           positive or negative.  
+        */
+};
 
-#define CHECK_RED \
-    if (temprsum < 0L) r = 0; \
-    else if (temprsum > maxval) r = maxval; \
-    else r = temprsum;
 
-#define CHECK_GREEN \
-    if (tempgsum < 0L) g = 0; \
-    else if (tempgsum > maxval) g = maxval; \
-    else g = tempgsum;
 
-#define CHECK_BLUE \
-    if (tempbsum < 0L) b = 0; \
-    else if (tempbsum > maxval) b = maxval; \
-    else b = tempbsum;
+static void
+warnBadKernel(struct convKernel * const convKernelP) {
 
-struct convolveType {
-    void (*ppmConvolver)(const float ** const rweights,
-                         const float ** const gweights,
-                         const float ** const bweights);
-    void (*pgmConvolver)(const float ** const weights);
-};
+    float sum[3];
+    unsigned int plane;
+    unsigned int row;
 
-static FILE * ifp;
-static int crows, ccols, ccolso2, crowso2;
-static int cols, rows;
-static xelval maxval;
-static int format, newformat;
+    for (plane = 0; plane < convKernelP->planes; ++plane)
+        sum[plane] = 0.0; /* initial value */
+    
+    for (row = 0; row < convKernelP->rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < convKernelP->cols; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < convKernelP->planes; ++plane)
+                sum[plane] += convKernelP->weight[plane][row][col];
+        }
+    }
+
+    if (convKernelP->planes == 3) {
+        unsigned int plane;
+        bool biased, negative;
+        for (plane = 0, biased = false, negative = false;
+             plane < convKernelP->planes;
+             ++plane) {
+            if (sum[plane] < 0.9 || sum[plane] > 1.1)
+                biased = true;
+            if (sum[plane] < 0.0)
+                negative = true;
+        }
+    
+        if (biased) {
+            pm_message("WARNING - this convolution matrix is biased.  " 
+                       "red, green, and blue average weights: %f, %f, %f "
+                       "(unbiased would be 1).",
+                       sum[PAM_RED_PLANE],
+                       sum[PAM_GRN_PLANE],
+                       sum[PAM_BLU_PLANE]);
+
+            if (negative)
+                pm_message("Maybe you want the -nooffset option?");
+        }
+    } else if (convKernelP->planes == 1) {
+        if (sum[0] < 0.9 || sum[0] > 1.1)
+            pm_message("WARNING - this convolution matrix is biased.  "
+                       "average weight = %f (unbiased would be 1)",
+                       sum[0]);
+        if (sum[0] < 0.0)
+            pm_message("Maybe you want the -nooffset option?");
+    }
+}
 
 
 
 static void
-computeWeights(xel * const *   const cxels, 
-               int             const ccols, 
-               int             const crows,
-               int             const cformat, 
-               xelval          const cmaxval,
-               bool            const offsetPgm,
-               float ***       const rweightsP,
-               float ***       const gweightsP,
-               float ***       const bweightsP) {
+convKernelCreatePnm(struct pam *         const cpamP,
+                    tuple * const *      const ctuples, 
+                    unsigned int         const depth,
+                    bool                 const offsetPgm,
+                    struct convKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Compute the convolution matrix in normalized form from the PGM
    form.  Each element of the output matrix is the actual weight we give an
    input pixel -- i.e. the thing by which we multiple a value from the
    input image.
 
+   'depth' is the required number of planes in the kernel.  If 'ctuples' has
+   fewer planes than that, we duplicate as necessary.  E.g. if 'ctuples' is
+   from a PGM input file and we're convolving a PPM image, we'll make a
+   3-plane convolution kernel by repeating the one plane in 'ctuples'.  If
+   'ctuples' has more planes than specified, we ignore the higher numbered
+   ones.
+
    'offsetPgm' means the PGM convolution matrix is defined in offset form so
    that it can represent negative values.  E.g. with maxval 100, 50 means
    0, 100 means 50, and 0 means -50.  If 'offsetPgm' is false, 0 means 0
    and there are no negative weights.
 -----------------------------------------------------------------------------*/
-    double const scale = (offsetPgm ? 2.0 : 1.0) / cmaxval;
+    double const scale = (offsetPgm ? 2.0 : 1.0) / cpamP->maxval;
     double const offset = offsetPgm ? - 1.0 : 0.0;
+    unsigned int const planes = MIN(3, depth);
 
-    float** rweights;
-    float** gweights;
-    float** bweights;
+    struct convKernel * convKernelP;
+    unsigned int plane;
 
-    float rsum, gsum, bsum;
+    MALLOCVAR_NOFAIL(convKernelP);
 
-    unsigned int crow;
+    convKernelP->cols   = cpamP->width;
+    convKernelP->rows   = cpamP->height;
+    convKernelP->planes = planes;
 
-    /* Set up the normalized weights. */
-    rweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
-    gweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
-    bweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
+    for (plane = 0; plane < planes; ++plane) {
+        unsigned int row;
 
-    rsum = gsum = bsum = 0.0;  /* initial value */
+        MALLOCARRAY_NOFAIL(convKernelP->weight[plane], cpamP->height);
     
-    for (crow = 0; crow < crows; ++crow) {
-        unsigned int ccol;
-        for (ccol = 0; ccol < ccols; ++ccol) {
-            switch (PNM_FORMAT_TYPE(cformat)) {
-            case PPM_TYPE:
-                rsum += rweights[crow][ccol] =
-                    (PPM_GETR(cxels[crow][ccol]) * scale + offset);
-                gsum += gweights[crow][ccol] =
-                    (PPM_GETG(cxels[crow][ccol]) * scale + offset);
-                bsum += bweights[crow][ccol] =
-                    (PPM_GETB(cxels[crow][ccol]) * scale + offset);
-                break;
-                
-            default:
-                gsum += gweights[crow][ccol] =
-                    (PNM_GET1(cxels[crow][ccol]) * scale + offset);
-                break;
+        for (row = 0; row < cpamP->height; ++row) {
+            unsigned int col;
+
+            MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row], cpamP->width);
+
+            for (col = 0; col < cpamP->width; ++col) {
+                sample const inValue = plane < cpamP->depth ?
+                    ctuples[row][col][plane] : ctuples[row][col][0];
+
+                convKernelP->weight[plane][row][col] =
+                    inValue * scale + offset;
             }
         }
     }
-    *rweightsP = rweights;
-    *gweightsP = gweights;
-    *bweightsP = bweights;
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        if (rsum < 0.9 || rsum > 1.1 || gsum < 0.9 || gsum > 1.1 ||
-            bsum < 0.9 || bsum > 1.1) {
-            pm_message("WARNING - this convolution matrix is biased.  " 
-                       "red, green, and blue average weights: %f, %f, %f "
-                       "(unbiased would be 1).",
-                       rsum, gsum, bsum);
+    *convKernelPP = convKernelP;
+}
 
-            if (rsum < 0 && gsum < 0 && bsum < 0)
-                pm_message("Maybe you want the -nooffset option?");
-        }
-        break;
 
-    default:
-        if (gsum < 0.9 || gsum > 1.1)
-            pm_message("WARNING - this convolution matrix is biased.  "
-                       "average weight = %f (unbiased would be 1)",
-                       gsum);
-        break;
+
+static void
+convKernelDestroy(struct convKernel * const convKernelP) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < convKernelP->planes; ++plane) {
+        unsigned int row;
+
+        for (row = 0; row < convKernelP->rows; ++row)
+            free(convKernelP->weight[plane][row]);
+
+        free(convKernelP->weight[plane]);
     }
+    free(convKernelP);
 }
 
 
 
-/* General PGM Convolution
-**
-** No useful redundancy in convolution matrix.
-*/
+static void
+getKernelPnm(const char *         const fileName,
+             unsigned int         const depth,
+             bool                 const nooffset,
+             struct convKernel ** const convKernelPP) {
+
+    struct pam cpam;
+    FILE * cifP;
+    tuple ** ctuples;
+
+    cifP = pm_openr(fileName);
+
+    /* Read in the convolution matrix. */
+    ctuples = pnm_readpam(cifP, &cpam, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(cifP);
+    
+    validateKernelDimensions(cpam.width, cpam.height);
+
+    convKernelCreatePnm(&cpam, ctuples, depth, nooffset, convKernelPP);
+}
+
+
 
 static void
-pgm_general_convolve(const float ** const weights) {
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row;
-    xel **rowptr, *temprptr;
-    int toprow, temprow;
-    int i, irow;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-       a row output buffer.
-    */
-    xelbuf = pnm_allocarray(cols, crows);
-    outputrow = pnm_allocrow(cols);
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray(1, crows);
-
-    pnm_writepnminit(stdout, cols, rows, maxval, newformat, 0);
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for (row = 0; row < crows - 1; ++row) {
-        pnm_readpnmrow(ifp, xelbuf[row], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[row], cols, maxval, format, 
-                                 maxval, newformat);
-        /* Write out just the part we're not going to convolve. */
-        if (row < crowso2)
-            pnm_writepnmrow(stdout, xelbuf[row], cols, maxval, newformat, 0);
+convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
+                          unsigned int         const depth,
+                          struct convKernel ** const convKernelPP) {
+
+    struct convKernel * convKernelP;
+    unsigned int plane;
+
+    MALLOCVAR(convKernelP);
+
+    convKernelP->cols = matrixOpt.width;
+    convKernelP->rows = matrixOpt.height;
+    convKernelP->planes = depth;
+
+    for (plane = 0; plane < depth; ++plane) {
+        unsigned int row;
+        MALLOCARRAY_NOFAIL(convKernelP->weight[plane], matrixOpt.height);
+
+        for (row = 0; row < matrixOpt.height; ++row) {
+            unsigned int col;
+
+            MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row],
+                               matrixOpt.width);
+    
+            for (col = 0; col < matrixOpt.width; ++col)
+                convKernelP->weight[plane][row][col] =
+                    matrixOpt.weight[row][col];
+        }
     }
+    *convKernelPP = convKernelP;
+}
 
-    /* Now the rest of the image - read in the row at the end of
-       xelbuf, and convolve and write out the row in the middle.
-    */
-    for (; row < rows; ++row) {
-        int col;
-        toprow = row + 1;
-        temprow = row % crows;
-        pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, 
-                                 maxval, newformat);
-
-        /* Arrange rowptr to eliminate the use of mod function to determine
-           which row of xelbuf is 0...crows.  Mod function can be very costly.
-        */
-        temprow = toprow % crows;
-        i = 0;
-        for (irow = temprow; irow < crows; ++i, ++irow)
-            rowptr[i] = xelbuf[irow];
-        for (irow = 0; irow < temprow; ++irow, ++i)
-            rowptr[i] = xelbuf[irow];
-
-        for (col = 0; col < cols; ++col) {
-            if (col < ccolso2 || col >= cols - ccolso2)
-                outputrow[col] = rowptr[crowso2][col];
+
+
+static void
+parsePlaneFileLine(const char *   const line,
+                   unsigned int * const widthP,
+                   float **       const weightP) {
+
+    unsigned int colCt;
+    const char * error;
+    float * weight;
+    const char * cursor;
+
+    colCt = 0;  /* initial value */
+    weight = NULL;
+
+    for (cursor = &line[0]; *cursor; ) {
+        const char * token;
+        const char * next;
+
+        REALLOCARRAY(weight, colCt + 1);
+
+        pm_gettoken(cursor, ' ', &token, &next, &error);
+
+        if (error)
+            pm_error("Invalid format of line in convolution matrix file: "
+                     "'%s'.  %s", line, error);
+
+        cursor = next;
+
+        if (*cursor) {
+            assert(*next == ' ');
+            ++cursor;  /* advance over space */
+        }
+        if (strlen(token) == 0)
+            pm_error("Column %u value in line '%s' of convolution matrix file "
+                     "is null string.", colCt, line);
+        else {
+            char * trailingJunk;
+            weight[colCt] = strtod(token, &trailingJunk);
+            if (*trailingJunk != '\0') 
+                pm_error("The Column %u element of the row '%s' in the "
+                         "-matrix value is not a valid floating point "
+                         "number", colCt, line);
+
+                ++colCt;
+        }
+        strfree(token);
+    }
+    *weightP = weight;
+    *widthP = colCt;
+}
+
+
+
+static void
+readPlaneFile(FILE *         const ifP, 
+              float ***      const weightP,
+              unsigned int * const widthP,
+              unsigned int * const heightP) {
+/*----------------------------------------------------------------------------
+   Read weights of one plane from a file.
+
+   The file is a simple matrix, one line per row, with columns separated
+   by a single space.
+
+   Each column is a floating point decimal ASCII number, positive zero,
+   or negative, with any magnitude.
+
+   If the rows don't all have the same number of columns, we abort.
+
+   Return the dimensions seen in the file as *widthP and *heightP.
+-----------------------------------------------------------------------------*/
+    unsigned int rowCt;
+    float ** weight;
+    unsigned int width;
+    bool eof;
+
+    weight = NULL;  /* initial value */
+
+    for (eof = false, rowCt = 0; !eof; ) {
+        const char * error;
+        const char * line;
+
+        pm_freadline(ifP, &line, &error);
+
+        if (error)
+            pm_error("Failed to read row %u "
+                     "from the convolutionmatrix file.  %s",
+                     rowCt, error);
+        else {
+            if (line == NULL)
+                eof = true;
             else {
-                int const leftcol = col - ccolso2;
-                int crow;
-                float gsum;
-                gsum = 0.0;
-                for (crow = 0; crow < crows; ++crow) {
-                    int ccol;
-                    temprptr = rowptr[crow] + leftcol;
-                    for (ccol = 0; ccol < ccols; ++ccol)
-                        gsum += PNM_GET1(*(temprptr + ccol))
-                            * weights[crow][ccol];
+                REALLOCARRAY(weight, rowCt + 1);
+            
+                if (weight == NULL)
+                    pm_error("Unable to allocate memory for "
+                             "convolution matrix");
+                else {
+                    unsigned int thisWidth;
+
+                    parsePlaneFileLine(line, &thisWidth, &weight[rowCt]);
+
+                    if (rowCt == 0)
+                        width = thisWidth;
+                    else {
+                        if (thisWidth != width)
+                            pm_error("Multiple row widths in the convolution "
+                                     "matrix file: %u columns and %u columns.",
+                                     width, thisWidth);
+                    }                    
+                    ++rowCt;
                 }
-                tempgsum = gsum + 0.5;
-            CHECK_GRAY;
-            PNM_ASSIGN1( outputrow[col], g );
+                strfree(line);
             }
         }
-        pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
     }
+    validateKernelDimensions(width, rowCt);
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for (irow = crowso2 + 1; irow < crows; ++irow)
-        pnm_writepnmrow(stdout, rowptr[irow], cols, maxval, newformat, 0 );
+    *weightP = weight;
+    *heightP = rowCt;
+    *widthP = width;
 }
 
 
 
-/* PGM Mean Convolution
-**
-** This is the common case where you just want the target pixel replaced with
-** the average value of its neighbors.  This can work much faster than the
-** general case because you can reduce the number of floating point operations
-** that are required since all the weights are the same.  You will only need
-** to multiply by the weight once, not for every pixel in the convolution
-** matrix.
-**
-** This algorithm works by creating sums for each column of crows height for
-** the whole width of the image.  Then add ccols column sums together to obtain
-** the total sum of the neighbors and multiply that sum by the weight.  As you
-** move right to left to calculate the next pixel, take the total sum you just
-** generated, add in the value of the next column and subtract the value of the
-** leftmost column.  Multiply that by the weight and that's it.  As you move
-** down a row, calculate new column sums by using previous sum for that column
-** and adding in pixel on current row and subtracting pixel in top row.
-**
-*/
+static void
+copyWeight(float **       const srcWeight,
+           unsigned int   const width,
+           unsigned int   const height, 
+           float ***      const dstWeightP) {
 
+    unsigned int row;
+    float ** dstWeight;
 
-static void
-pgm_mean_convolve(const float ** const weights) {
-    float const gmeanweight = weights[0][0];
-
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int subcol, addcol;
-    long gisum;
-    int tempcol, crowsp1;
-    long tempgsum;
-    long *gcolumnsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer.  MEAN uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf. MEAN uses an extra row. */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    gcolumnsum[col] = 0L;
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+    MALLOCARRAY(dstWeight, height);
 
-    /* Do first real row only */
-    subrow = crows;
-    addrow = crows - 1;
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
+    if (dstWeight == NULL)
+        pm_error("Could not allocate memory for convolution matrix");
+   
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
 
-    gisum = 0L;
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gcolumnsum[leftcol + ccol] += 
-            PNM_GET1( *(temprptr + ccol) );
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        gisum += gcolumnsum[leftcol + ccol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        gcolumnsum[addcol] += PNM_GET1( rowptr[crow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
+        MALLOCARRAY(dstWeight[row], width);
+
+        if (dstWeight[row] == NULL)
+            pm_error("Could not allocation memory for a "
+                     "convolution matrix row");
+
+        for (col = 0; col < width; ++col) {
+            dstWeight[row][col] = srcWeight[row][col];
         }
     }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    *dstWeightP = dstWeight;
+}
 
-    ++row;
-    /* For all subsequent rows do it this way as the columnsums have been
-    ** generated.  Now we can use them to reduce further calculations.
-    */
-    crowsp1 = crows + 1;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows + 1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* This rearrangement using crows+1 rowptrs and xelbufs will cause
-    ** rowptr[0..crows-1] to always hold active xelbufs and for 
-    ** rowptr[crows] to always hold the oldest (top most) xelbuf.
-    */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    gisum = 0L;
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            gcolumnsum[tempcol] = gcolumnsum[tempcol]
-            - PNM_GET1( rowptr[subrow][ccol] )
-            + PNM_GET1( rowptr[addrow][ccol] );
-            gisum += gcolumnsum[tempcol];
+
+
+static void
+convKernelCreateSimpleFile(const char **        const fileNameList,
+                           unsigned int         const depth,
+                           struct convKernel ** const convKernelPP) {
+
+    struct convKernel * convKernelP;
+    unsigned int fileCt;
+    unsigned int planeCt;
+    unsigned int plane;
+    unsigned int width, height;
+
+    fileCt = 0;
+    while (fileNameList[fileCt])
+        ++fileCt;
+    assert(fileCt > 0);
+
+    planeCt = MIN(3, depth);
+
+    MALLOCVAR_NOFAIL(convKernelP);
+
+    convKernelP->planes = planeCt;
+
+    for (plane = 0; plane < planeCt; ++plane) {
+        if (plane < fileCt) {
+            const char * const fileName = fileNameList[plane];
+
+            FILE * ifP;
+            unsigned int thisWidth, thisHeight;
+
+            ifP = pm_openr(fileName);
+
+            readPlaneFile(ifP, &convKernelP->weight[plane],
+                          &thisWidth, &thisHeight);
+
+            if (plane == 0) {
+                width = thisWidth;
+                height = thisHeight;
+            } else {
+                if (thisWidth != width)
+                    pm_error("Convolution matrix files show two different "
+                             "widths: %u and %u", width, thisWidth);
+                if (thisHeight != height)
+                    pm_error("Convolution matrix files show two different "
+                             "heights: %u and %u", height, thisHeight);
             }
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PNM_GET1( rowptr[subrow][addcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
+            pm_close(ifP);
+        } else {
+            assert(plane > 0);
+            copyWeight(convKernelP->weight[0], width, height,
+                       &convKernelP->weight[plane]);
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+    convKernelP->cols = width;
+    convKernelP->rows = height;
+    *convKernelPP = convKernelP;
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
-    }
+
+static void
+getKernel(struct cmdlineInfo   const cmdline,
+          unsigned int         const depth,
+          struct convKernel ** const convKernelPP) {
+
+    struct convKernel * convKernelP;
+
+    if (cmdline.pnmMatrixFileName)
+        getKernelPnm(cmdline.pnmMatrixFileName, depth, cmdline.nooffset,
+                     &convKernelP);
+    else if (cmdline.matrixfile)
+        convKernelCreateSimpleFile(cmdline.matrixfile, depth, &convKernelP);
+    else if (cmdline.matrixSpec)
+        convKernelCreateMatrixOpt(cmdline.matrix, depth, &convKernelP);
+
+    warnBadKernel(convKernelP);
+
+    *convKernelPP = convKernelP;
+}
 
 
-/* PGM Horizontal Convolution
-**
-** Similar idea to using columnsums of the Mean and Vertical convolution,
-** but uses temporary sums of row values.  Need to multiply by weights crows
-** number of times.  Each time a new line is started, must recalculate the
-** initials rowsums for the newest row only.  Uses queue to still access
-** previous row sums.
-**
-*/
 
 static void
-pgm_horizontal_convolve(const float ** const weights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int temprow;
-    int subcol, addcol;
-    float gsum;
-    int addrow, subrow;
-    long **growsum, **growsumptr;
-    int crowsp1;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate intermediate row sums.  HORIZONTAL uses an extra row. */
-    /* crows current rows and 1 extra for newest added row.           */
-    growsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    growsumptr = (long **) pnm_allocarray( 1, crows + 1);
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+validateEnoughImageToConvolve(const struct pam *        const inpamP,
+                              const struct convKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Abort program if the image isn't big enough in both directions to have
+   at least one convolved pixel.
+
+   The program could theoretically operate with an image smaller than that by
+   simply outputting the input unchanged (like it does with the edges of an
+   image anyway), but we're too lazy to write code for this special case.  The
+   simple code expects the unconvolved edges to exist full-size and some of it
+   convolves the first convolveable row and/or column specially and expects it
+   to exist.
+-----------------------------------------------------------------------------*/
+
+    if (inpamP->height < convKernelP->rows + 1)
+        pm_error("Image is too short (%u rows) to convolve with this "
+                 "%u-row convolution kernel.",
+                 inpamP->height, convKernelP->rows);
+    
+    if (inpamP->width < convKernelP->cols + 1)
+        pm_error("Image is too narrow (%u columns) to convolve with this "
+                 "%u-column convolution kernel.",
+                 inpamP->width, convKernelP->cols);
+}
+
+
+
+static tuple **
+allocRowbuf(struct pam * const pamP,
+            unsigned int const height) {
+
+    tuple ** rowbuf;
+
+    MALLOCARRAY(rowbuf, height);
+
+    if (rowbuf == NULL)
+        pm_error("Failed to allocate %u-row buffer", height);
+    else {
+        unsigned int row;
+    
+        for (row = 0; row < height; ++row)
+            rowbuf[row] = pnm_allocpamrow(pamP);
     }
 
-    /* First row only */
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+    return rowbuf;
+}
 
-    temprow = (row + 1) % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
 
-    for ( crow = 0; crow < crows; ++crow )
-    growsumptr[crow] = growsum[crow];
- 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        gsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        growsumptr[crow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            growsumptr[crow][leftcol] += 
-                PNM_GET1( *(temprptr + ccol) );
-        gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        growsumptr[crow][leftcol] = growsumptr[crow][subcol]
-            - PNM_GET1( rowptr[crow][subcol] )
-            + PNM_GET1( rowptr[crow][addcol] );
-        gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
 
+static void
+freeRowbuf(tuple **     const rowbuf,
+           unsigned int const height) {
 
-    /* For all subsequent rows */
+    unsigned int row;
 
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    temprow = row % crowsp1;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+    for (row = 0; row < height; ++row)
+        pnm_freepamrow(rowbuf[row]);
 
-    temprow = (row + 2) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        {
-        rowptr[i] = xelbuf[irow];
-        growsumptr[i] = growsum[irow];
-        }
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        {
-        rowptr[i] = xelbuf[irow];
-        growsumptr[i] = growsum[irow];
-        }
+    free(rowbuf);
+}
 
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        growsumptr[addrow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            growsumptr[addrow][leftcol] += 
-            PNM_GET1( rowptr[addrow][leftcol + ccol] );
-        for ( crow = 0; crow < crows; ++crow )
-            gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        growsumptr[addrow][leftcol] = growsumptr[addrow][subcol]
-            - PNM_GET1( rowptr[addrow][subcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        for ( crow = 0; crow < crows; ++crow )
-            gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
-    }
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
-    }
+static void
+readAndScaleRow(struct pam * const inpamP,
+                tuple *      const inrow,
+                sample       const newMaxval,
+                unsigned int const newDepth) {
 
+    pnm_readpamrow(inpamP, inrow);
+
+    if (newMaxval != inpamP->maxval)
+        pnm_scaletuplerow(inpamP, inrow, inrow, newMaxval);
+
+    if (newDepth == 3 && inpamP->depth == 1)
+        pnm_makerowrgb(inpamP, inrow);
+}
 
-/* PGM Vertical Convolution
-**
-** Uses column sums as in Mean Convolution.
-**
-*/
 
 
 static void
-pgm_vertical_convolve(const float ** const weights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int tempcol;
-    float gsum;
-    long *gcolumnsum;
-    int crowsp1;
-    int addcol;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. VERTICAL uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1 );
-
-    /* Allocate space for intermediate column sums */
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    gcolumnsum[col] = 0L;
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+readAndScaleRows(struct pam *              const inpamP,
+                 unsigned int              const count,
+                 tuple **                  const rowbuf,
+                 sample                    const outputMaxval,
+                 unsigned int              const outputDepth) {
+/*----------------------------------------------------------------------------
+  Read in 'count' rows into rowbuf[].
+  
+  Scale the contents to maxval 'outputMaxval' and expand to depth
+  'outputDepth'.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < count; ++row)
+        readAndScaleRow(inpamP, rowbuf[row], outputMaxval, outputDepth);
+}
+
+
+
+static void
+writeUnconvolvedTop(struct pam *              const outpamP,
+                    const struct convKernel * const convKernelP,
+                    tuple **                  const rowbuf) {
+/*----------------------------------------------------------------------------
+   Write out the top part that we can't convolve because the convolution
+   kernel runs off the top of the image.
+
+   Assume those rows are in the window rowbuf[], with the top row of the
+   image as the first row in rowbuf[].
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < convKernelP->rows/2; ++row)
+        pnm_writepamrow(outpamP, rowbuf[row]);
+}
+
+
+
+static void
+writeUnconvolvedBottom(struct pam *              const outpamP,
+                       const struct convKernel * const convKernelP,
+                       unsigned int              const windowHeight,
+                       tuple **                  const circMap) {
+/*----------------------------------------------------------------------------
+  Write out the bottom part that we can't convolve because the convolution
+  kernel runs off the bottom of the image.
+
+  Assume the 'windowHeight' rows at the bottom of the image is in the row
+  buffer, mapped by 'circMap' such that the top of the window is circMap[0].
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = windowHeight - convKernelP->rows / 2;
+         row < windowHeight;
+         ++row) {
+
+        pnm_writepamrow(outpamP, circMap[row]);
     }
+}
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    /* For first row only */
 
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
 
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = toprow % crows;
+static void
+setupCircMap(tuple **     const circMap,
+             tuple **     const rowbuf,
+             unsigned int const windowHeight,
+             unsigned int const topRowbufRow) {
+/*----------------------------------------------------------------------------
+  Set up circMap[] to reflect the case that index 'topRowbufRow' of rowbuf[]
+  is for the topmost row in the window.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    unsigned int i;
+
     i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gcolumnsum[leftcol + ccol] += 
-            PNM_GET1( *(temprptr + ccol) );
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        gcolumnsum[addcol] += PNM_GET1( rowptr[crow][addcol] );
-        for ( ccol = 0; ccol < ccols; ++ccol )
-        gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
+    for (row = topRowbufRow; row < windowHeight; ++i, ++row)
+        circMap[i] = rowbuf[row];
+
+    for (row = 0; row < topRowbufRow; ++row, ++i)
+        circMap[i] = rowbuf[row];
+}
+
+
+
+static void
+convolveGeneralRowPlane(struct pam *              const pamP,
+                        tuple **                  const window,
+                        const struct convKernel * const convKernelP,
+                        unsigned int              const plane,
+                        tuple *                   const outputrow) {
+/*----------------------------------------------------------------------------
+   Given a window of input window[], where window[0] is the top row of the
+   window and the window is the height of the convolution kernel, convolve
+   Plane 'plane' of the row at the center of the window.
+
+   Return the convolved row as outputrow[].
+
+   *pamP describes the rows in window[] (but not the number of rows).
+
+   *convKernelP is the convolution kernel to use.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int col;
+    
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2)
+            outputrow[col][plane] = window[crowso2][col][plane];
+        else {
+            unsigned int const leftcol = col - ccolso2;
+            unsigned int crow;
+            float sum;
+            sum = 0.0;
+            for (crow = 0; crow < convKernelP->rows; ++crow) {
+                const tuple * const leftrptr = &window[crow][leftcol];
+                unsigned int ccol;
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sum += leftrptr[ccol][plane] *
+                        convKernelP->weight[plane][crow][ccol];
+            }
+            outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
         }
     }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
-
-    /* For all subsequent rows */
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows +1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
+}
+
+
+
+static void
+convolveGeneral(struct pam *              const inpamP,
+                struct pam *              const outpamP,
+                const struct convKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Do the convolution without taking advantage of any useful redundancy in the
+   convolution matrix.
+-----------------------------------------------------------------------------*/
+    tuple ** rowbuf;
+        /* A vertical window of the input image.  It holds as many rows as the
+           convolution kernel covers -- the rows we're currently using to
+           create output rows.  It is a circular buffer.
+        */
+    tuple ** circMap;
+        /* A map from image row number within window to element of rowbuf[].
+           E.g. if rowbuf[] if 5 rows high and rowbuf[2] contains the
+           topmost row, then circMap[0] == 2, circMap[1] = 3,
+           circMap[4] = 1.  You could calculate the same thing with a mod
+           function, but that is sometimes more expensive.
+        */
+    tuple * outputrow;
+        /* The convolved row to be output */
+    unsigned int row;
+        /* Row number of the bottom of the current convolution window;
+           i.e. the row to be read or just read from the input file.
+        */
+
+    rowbuf = allocRowbuf(outpamP, convKernelP->rows);
+    MALLOCARRAY_NOFAIL(circMap, convKernelP->rows);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    pnm_writepaminit(outpamP);
+
+    assert(convKernelP->rows > 0);
+
+    readAndScaleRows(inpamP, convKernelP->rows - 1, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    /* Now the rest of the image - read in the row at the bottom of the
+       window, then convolve and write out the row in the middle of the
+       window.
     */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            gcolumnsum[tempcol] = gcolumnsum[tempcol] 
-            - PNM_GET1( rowptr[subrow][ccol] )
-            + PNM_GET1( rowptr[addrow][ccol] );
-            gsum = gsum + gcolumnsum[tempcol] * weights[0][ccol];
-            }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        addcol = col + ccolso2;
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PNM_GET1( rowptr[subrow][addcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    for (row = convKernelP->rows - 1; row < inpamP->height; ++row) {
+        unsigned int const rowbufRow = row % convKernelP->rows;
+
+        unsigned int plane;
+
+        setupCircMap(circMap, rowbuf, convKernelP->rows,
+                     (row + 1) % convKernelP->rows);
+
+        readAndScaleRow(inpamP, rowbuf[rowbufRow],
+                        outpamP->maxval, outpamP->depth);
+
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveGeneralRowPlane(outpamP, circMap, convKernelP, plane,
+                                    outputrow);
+
+        pnm_writepamrow(outpamP, outputrow);
     }
+    writeUnconvolvedBottom(outpamP, convKernelP, convKernelP->rows, circMap);
+
+    freeRowbuf(rowbuf, convKernelP->rows);
+}
+
+
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
+static sample **
+allocSum(unsigned int const depth,
+         unsigned int const size) {
 
+    sample ** sum;
+
+    MALLOCARRAY(sum, depth);
+
+    if (!sum)
+        pm_error("Could not allocate memory for %u planes of sums", depth);
+    else {
+        unsigned int plane;
+
+        for (plane = 0; plane < depth; ++plane) {
+            MALLOCARRAY(sum[plane], size);
+            
+            if (!sum[plane])
+                pm_error("Could not allocate memory for %u sums", size);
+        }
     }
+    return sum;
+}
 
 
 
+static void
+freeSum(sample **    const sum,
+        unsigned int const depth) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < depth; ++plane)
+        free(sum[plane]);
+
+    free(sum);
+}
+
 
-/* PPM General Convolution Algorithm
-**
-** No redundancy in convolution matrix.  Just use brute force.
-** See pgm_general_convolve() for more details.
-*/
 
 static void
-ppm_general_convolve(const float ** const rweights,
-                     const float ** const gweights,
-                     const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    float rsum, gsum, bsum;
-    xel **rowptr, *temprptr;
-    int toprow, temprow;
-    int i, irow;
-    int leftcol;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows );
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+computeInitialColumnSums(struct pam *              const pamP,
+                         tuple **                  const window,
+                         const struct convKernel * const convKernelP,
+                         sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Add up the sum of each column of window[][], whose rows are described
+  by *inpamP.  The window's height is that of tthe convolution kernel
+  *convKernelP.
+
+  Return it as convColumnSum[][].
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int col;
+
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int row;
+            for (row = 0, convColumnSum[plane][col] = 0;
+                 row < convKernelP->rows;
+                 ++row)
+                convColumnSum[plane][col] += window[row][col][plane];
+        }            
     }
+}
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else
-        {
-        leftcol = col - ccolso2;
-        rsum = gsum = bsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-            {
-            temprptr = rowptr[crow] + leftcol;
-            for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rsum += PPM_GETR( *(temprptr + ccol) )
-                * rweights[crow][ccol];
-            gsum += PPM_GETG( *(temprptr + ccol) )
-                * gweights[crow][ccol];
-            bsum += PPM_GETB( *(temprptr + ccol) )
-                * bweights[crow][ccol];
-            }
+
+
+static void
+convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
+                              struct pam *              const pamP,
+                              tuple **                  const window,
+                              tuple *                   const outputrow,
+                              sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve the rows in window[][] -- one convolution kernel's worth, where
+  window[0] is the top.  Put the result in outputrow[].
+
+  Use convColumnSum[][]: the sum of the pixels in each column over the
+  convolution window, where convColumnSum[P][C] is the sum for Plane P of
+  Column C.
+
+  Assume the convolution weight is the same everywhere within the convolution
+  matrix.  Ergo, we don't need any more information about the contents of a
+  column than the sum of its pixels.
+
+  Except that we need the individual input pixels for the edges (which can't
+  be convolved because the convolution window runs off the edge).
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+    
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int const crowso2 = convKernelP->rows / 2;
+        unsigned int const ccolso2 = convKernelP->cols / 2;
+        float const weight = convKernelP->weight[plane][0][0];
+
+        unsigned int col;
+        sample gisum;
+
+        gisum = 0;
+        for (col = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2)
+                outputrow[col][plane] = window[crowso2][col][plane];
+            else if (col == ccolso2) {
+                unsigned int const leftcol = col - ccolso2;
+
+                unsigned int ccol;
+
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    gisum += convColumnSum[plane][leftcol + ccol];
+
+                outputrow[col][plane] =
+                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+            } else {
+                /* Column numbers to subtract or add to isum */
+                unsigned int const subcol = col - ccolso2 - 1;
+                unsigned int const addcol = col + ccolso2;  
+
+                gisum -= convColumnSum[plane][subcol];
+                gisum += convColumnSum[plane][addcol];
+
+                outputrow[col][plane] =
+                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
             }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN( outputrow[col], r, g, b );
         }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
+
+static void
+convolveRowWithColumnSumsVertical(
+    const struct convKernel * const convKernelP,
+    struct pam *              const pamP,
+    tuple **                  const window,
+    tuple *                   const outputrow,
+    sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve the rows in window[][] -- one convolution kernel's worth, where
+  window[0] is the top.  Put the result in outputrow[].
+
+  Use convColumnSum[][]: the sum of the pixels in each column over the
+  convolution window, where convColumnSum[P][C] is the sum for Plane P of
+  Column C.
+
+  Assume the convolution weight is the same everywhere within a column.  Ergo,
+  we don't need any more information about the contents of a column than the
+  sum of its pixels.
+
+  Except that we need the individual input pixels for the edges (which can't
+  be convolved because the convolution window runs off the edge).
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int col;
+    
+        for (col = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2)
+                outputrow[col][plane] = window[crowso2][col][plane];
+            else {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int ccol;
+                float sum;
+
+                sum = 0.0;
+
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sum += convColumnSum[plane][leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+
+                outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
+            }
+        }
     }
+}
 
 
-/* PPM Mean Convolution
-**
-** Same as pgm_mean_convolve() but for PPM.
-**
-*/
 
 static void
-ppm_mean_convolve(const float ** const rweights,
-                  const float ** const gweights,
-                  const float ** const bweights) {
-    /* All weights of a single color are the same so just grab any one
-       of them.  
-    */
-    float const rmeanweight = rweights[0][0];
-    float const gmeanweight = gweights[0][0];
-    float const bmeanweight = bweights[0][0];
-
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int subcol, addcol;
-    long risum, gisum, bisum;
-    long temprsum, tempgsum, tempbsum;
-    int tempcol, crowsp1;
-    long *rcolumnsum, *gcolumnsum, *bcolumnsum;
-
-
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer.  MEAN uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf. MEAN uses an extra row. */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    rcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    bcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    {
-    rcolumnsum[col] = 0L;
-    gcolumnsum[col] = 0L;
-    bcolumnsum[col] = 0L;
-    }
+convolveMeanRowPlane(struct pam *              const pamP,
+                     tuple **                  const window,
+                     const struct convKernel * const convKernelP,
+                     unsigned int              const plane,
+                     tuple *                   const outputrow,
+                     sample *                  const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve plane 'plane' of one row of the image.  window[] is a vertical
+  window of the input image, one convolution kernel plus one row high.  The
+  top row (window[0] is the row that just passed out of the convolution
+  window, whereas the bottom row is the row that just entered it.
 
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
+  *pamP describes the tuple rows in window[] and also 'outputrow' (they are
+  the same).
 
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+  Return the convolution result as outputrow[].
 
-    /* Do first real row only */
-    subrow = crows;
-    addrow = crows - 1;
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
-
-    risum = 0L;
-    gisum = 0L;
-    bisum = 0L;
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rcolumnsum[leftcol + ccol] += 
-            PPM_GETR( *(temprptr + ccol) );
-            gcolumnsum[leftcol + ccol] += 
-            PPM_GETG( *(temprptr + ccol) );
-            bcolumnsum[leftcol + ccol] += 
-            PPM_GETB( *(temprptr + ccol) );
+  We update convColumnSum[] for use in convolving later rows.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+    float const weight = convKernelP->weight[plane][0][0];
+    unsigned int const subrow = 0;
+        /* Row just above convolution window -- what we subtract from
+           running sum
+        */
+    unsigned int const addrow = 1 + (convKernelP->rows - 1);
+        /* Bottom row of convolution window: What we add to running sum */
+
+    unsigned int col;
+    sample gisum;
+
+    gisum = 0;
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2)
+            outputrow[col][plane] = window[crowso2][col][plane];
+        else if (col == ccolso2) {
+            unsigned int const leftcol = col - ccolso2;
+
+            unsigned int ccol;
+
+            for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                sample * const thisColumnSumP =
+                    &convColumnSum[leftcol + ccol];
+                *thisColumnSumP = *thisColumnSumP
+                    - window[subrow][ccol][plane]
+                    + window[addrow][ccol][plane];
+                gisum += *thisColumnSumP;
             }
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        {
-        risum += rcolumnsum[leftcol + ccol];
-        gisum += gcolumnsum[leftcol + ccol];
-        bisum += bcolumnsum[leftcol + ccol];
-        }
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-    else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        rcolumnsum[addcol] += PPM_GETR( rowptr[crow][addcol] );
-        gcolumnsum[addcol] += PPM_GETG( rowptr[crow][addcol] );
-        bcolumnsum[addcol] += PPM_GETB( rowptr[crow][addcol] );
-        }
-        risum = risum - rcolumnsum[subcol] + rcolumnsum[addcol];
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        bisum = bisum - bcolumnsum[subcol] + bcolumnsum[addcol];
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+        } else {
+            /* Column numbers to subtract or add to isum */
+            unsigned int const subcol = col - ccolso2 - 1;
+            unsigned int const addcol = col + ccolso2;  
+
+            convColumnSum[addcol] = convColumnSum[addcol]
+                - window[subrow][addcol][plane]
+                + window[addrow][addcol][plane];
+
+            gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
+
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
         }
     }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+}
+
+
+
+typedef void convolver(struct pam *              const inpamP,
+                       struct pam *              const outpamP,
+                       const struct convKernel * const convKernelP);
+
+
+
+static convolver convolveMean;
+
+static void
+convolveMean(struct pam *              const inpamP,
+             struct pam *              const outpamP,
+             const struct convKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+  Mean Convolution
+
+  This is for the common case where you just want the target pixel replaced
+  with the average value of its neighbors.  This can work much faster than the
+  general case because you can reduce the number of floating point operations
+  that are required since all the weights are the same.  You will only need to
+  multiply by the weight once, not for every pixel in the convolution matrix.
+
+  This algorithm works as follows: At a certain vertical position in the
+  image, create sums for each column fragment of the convolution height all
+  the way across the image.  Then add those sums across the convolution width
+  to obtain the total sum over the convolution area and multiply that sum by
+  the weight.  As you move left to right, to calculate the next output pixel,
+  take the total sum you just generated, add in the value of the next column
+  and subtract the value of the leftmost column.  Multiply that by the weight
+  and that's it.  As you move down a row, calculate new column sums by using
+  previous sum for that column and adding in pixel on current row and
+  subtracting pixel in top row.
+
+  We assume the convolution kernel is uniform -- same weights everywhere.
+
+  We assume the output is PGM and the input is PGM or PBM.
+-----------------------------------------------------------------------------*/
+    unsigned int const windowHeight = convKernelP->rows + 1;
+        /* The height of the window we keep in the row buffer.  The buffer
+           contains the rows covered by the convolution kernel, plus the row
+           immediately above that.  The latter is there because to compute
+           the sliding mean, we need to subtract off the row that the
+           convolution kernel just slid past.
+        */
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Number of rows of the convolution window above/below the current
+           row.  Note that the convolution window is always an odd number
+           of rows, so this rounds down.
+        */
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int row;
+        /* Row number of the row currently being convolved; i.e. the row
+           at the center of the current convolution window and the row of
+           the output file to be output next.
+        */
+    sample ** convColumnSum;  /* Malloc'd */
+        /* convColumnSum[plane][col] is the sum of Plane 'plane' of all the
+           pixels in the Column 'col' of the image within the current vertical
+           convolution window.  I.e. if our convolution kernel is 5 rows high
+           and we're now looking at Rows 10-15, convColumn[0][3] is the sum of
+           Plane 0 of Column 3, Rows 10-15.
+        */
+
+    rowbuf = allocRowbuf(outpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    convColumnSum = allocSum(outpamP->depth, outpamP->width);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
+
+    /* Convolve the first window the long way */
+    computeInitialColumnSums(inpamP, circMap, convKernelP, convColumnSum);
+
+    convolveRowWithColumnSumsMean(convKernelP, outpamP, circMap,
+                                  outputrow, convColumnSum);
+
+    pnm_writepamrow(outpamP, outputrow);
 
-    ++row;
     /* For all subsequent rows do it this way as the columnsums have been
-    ** generated.  Now we can use them to reduce further calculations.
+       generated.  Now we can use them to reduce further calculations.  We
+       slide the window down a row at a time by reading a row into the bottom
+       of the circular buffer, adding it to the column sums, then subtracting
+       out the row at the top of the circular buffer.
     */
-    crowsp1 = crows + 1;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows + 1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* This rearrangement using crows+1 rowptrs and xelbufs will cause
-    ** rowptr[0..crows-1] to always hold active xelbufs and for 
-    ** rowptr[crows] to always hold the oldest (top most) xelbuf.
-    */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    risum = 0L;
-    gisum = 0L;
-    bisum = 0L;
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            rcolumnsum[tempcol] = rcolumnsum[tempcol]
-            - PPM_GETR( rowptr[subrow][ccol] )
-            + PPM_GETR( rowptr[addrow][ccol] );
-            risum += rcolumnsum[tempcol];
-            gcolumnsum[tempcol] = gcolumnsum[tempcol]
-            - PPM_GETG( rowptr[subrow][ccol] )
-            + PPM_GETG( rowptr[addrow][ccol] );
-            gisum += gcolumnsum[tempcol];
-            bcolumnsum[tempcol] = bcolumnsum[tempcol]
-            - PPM_GETB( rowptr[subrow][ccol] )
-            + PPM_GETB( rowptr[addrow][ccol] );
-            bisum += bcolumnsum[tempcol];
+    for (row = crowso2 + 1; row < inpamP->height - crowso2; ++row) {
+        unsigned int const windowBotRow = row + crowso2;
+            /* Row number of bottom-most row present in rowbuf[],
+               which is the bottom of the convolution window for the current
+               row.
+            */
+        unsigned int const windowTopRow = row - crowso2 - 1;
+            /* Row number of top-most row present in rowbuf[], which is
+               the top row of the convolution window for the previous row:
+               just above the convolution window for the current row.
+            */
+        unsigned int plane;
+
+        readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                        outpamP->maxval, outpamP->depth);
+
+        setupCircMap(circMap, rowbuf, windowHeight,
+                     windowTopRow % windowHeight);
+
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveMeanRowPlane(outpamP, circMap, convKernelP, plane,
+                                 outputrow, convColumnSum[plane]);
+
+        pnm_writepamrow(outpamP, outputrow);
+    }
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
+
+    freeSum(convColumnSum, outpamP->depth);
+    freeRowbuf(rowbuf, windowHeight);
+}
+
+
+
+static sample ***
+allocRowSum(unsigned int const depth,
+            unsigned int const height,
+            unsigned int const width) {
+
+    sample *** sum;
+
+    MALLOCARRAY(sum, depth);
+
+    if (!sum)
+        pm_error("Could not allocate memory for %u planes of sums", depth);
+    else {
+        unsigned int plane;
+
+        for (plane = 0; plane < depth; ++plane) {
+            MALLOCARRAY(sum[plane], height);
+            
+            if (!sum[plane])
+                pm_error("Could not allocate memory for %u rows of sums",
+                         height);
+            else {
+                unsigned int row;
+
+                for (row = 0; row < height; ++row) {
+                    MALLOCARRAY(sum[plane][row], width);
+                    
+                    if (!sum[plane][row])
+                        pm_error("Could not allocate memory "
+                                 "for a row of sums");
+                }
             }
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-        else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        rcolumnsum[addcol] = rcolumnsum[addcol]
-            - PPM_GETR( rowptr[subrow][addcol] )
-            + PPM_GETR( rowptr[addrow][addcol] );
-        risum = risum - rcolumnsum[subcol] + rcolumnsum[addcol];
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PPM_GETG( rowptr[subrow][addcol] )
-            + PPM_GETG( rowptr[addrow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        bcolumnsum[addcol] = bcolumnsum[addcol]
-            - PPM_GETB( rowptr[subrow][addcol] )
-            + PPM_GETB( rowptr[addrow][addcol] );
-        bisum = bisum - bcolumnsum[subcol] + bcolumnsum[addcol];
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+    return sum;
+}
+
+
+
+static void
+freeRowSum(sample ***   const sum,
+           unsigned int const depth,
+           unsigned int const height) {
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
+    unsigned int plane;
 
+    for (plane = 0; plane < depth; ++plane) {
+        unsigned int row;
+
+        for (row = 0; row < height; ++row)
+            free(sum[plane][row]);
+
+        free(sum[plane]);
     }
+    free(sum);
+}
 
 
-/* PPM Horizontal Convolution
-**
-** Same as pgm_horizontal_convolve()
-**
-**/
 
 static void
-ppm_horizontal_convolve(const float ** const rweights,
-                        const float ** const gweights,
-                        const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int temprow;
-    int subcol, addcol;
-    float rsum, gsum, bsum;
-    int addrow, subrow;
-    long **rrowsum, **rrowsumptr;
-    long **growsum, **growsumptr;
-    long **browsum, **browsumptr;
-    int crowsp1;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate intermediate row sums.  HORIZONTAL uses an extra row */
-    rrowsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    rrowsumptr = (long **) pnm_allocarray( 1, crows + 1);
-    growsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    growsumptr = (long **) pnm_allocarray( 1, crows + 1);
-    browsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    browsumptr = (long **) pnm_allocarray( 1, crows + 1);
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+convolveHorizontalRowPlane0(struct pam *              const outpamP,
+                            tuple **                  const window,
+                            const struct convKernel * const convKernelP,
+                            unsigned int              const plane,
+                            tuple *                   const outputrow,
+                            sample **                 const sumWindow) {
+/*----------------------------------------------------------------------------
+   Convolve the first convolvable row and generate the row sums from scratch.
+   (For subsequent rows, Caller can just incrementally modify the row sums).
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int col;
+
+    for (col = 0; col < outpamP->width; ++col) {
+        if (col < ccolso2 || col >= outpamP->width - ccolso2)
+            outputrow[col][plane] = window[crowso2][col][plane];
+        else if (col == ccolso2) {
+            /* This is the first column for which the entire convolution
+               kernel fits within the image horizontally.  I.e. the window
+               starts at the left edge of the image.
+            */
+            unsigned int const leftcol = 0;
+            
+            float matrixSum;
+            unsigned int crow;
+
+            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
+                tuple * const tuplesInWindow = &window[crow][leftcol];
+
+                unsigned int ccol;
+                
+                sumWindow[crow][col] = 0;
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sumWindow[crow][col] += tuplesInWindow[ccol][plane];
+                matrixSum +=
+                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
+            }
+            outputrow[col][plane] =
+                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
+        } else {
+            unsigned int const subcol  = col - ccolso2 - 1;
+            unsigned int const addcol  = col + ccolso2;
+
+            float matrixSum;
+            unsigned int crow;
+
+            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
+                sumWindow[crow][col] = sumWindow[crow][col-1] +
+                    + window[crow][addcol][plane]
+                    - window[crow][subcol][plane];
+                matrixSum +=
+                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
+            }
+            outputrow[col][plane] =
+                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
+        }
     }
+}
+
+
+
+static void
+setupCircMap2(tuple **     const rowbuf,
+              sample **    const convRowSum,
+              tuple **     const circMap,
+              sample **    const sumCircMap,
+              unsigned int const windowTopRow,
+              unsigned int const windowHeight) {
+
+    unsigned int const toprow = windowTopRow % windowHeight;
+    
+    unsigned int crow;
+    unsigned int i;
 
-    /* First row only */
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
 
-    temprow = (row + 1) % crows;
     i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
 
-    for ( crow = 0; crow < crows; ++crow )
-    {
-    rrowsumptr[crow] = rrowsum[crow];
-    growsumptr[crow] = growsum[crow];
-    browsumptr[crow] = browsum[crow];
+    for (crow = toprow; crow < windowHeight; ++i, ++crow) {
+        circMap[i] = rowbuf[crow];
+        sumCircMap[i] = convRowSum[crow];
     }
- 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        rrowsumptr[crow][leftcol] = 0L;
-        growsumptr[crow][leftcol] = 0L;
-        browsumptr[crow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rrowsumptr[crow][leftcol] += 
-                PPM_GETR( *(temprptr + ccol) );
-            growsumptr[crow][leftcol] += 
-                PPM_GETG( *(temprptr + ccol) );
-            browsumptr[crow][leftcol] += 
-                PPM_GETB( *(temprptr + ccol) );
-            }
-        rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-        gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-        bsum += browsumptr[crow][leftcol] * bweights[crow][0];
-        }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-    else
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        rrowsumptr[crow][leftcol] = rrowsumptr[crow][subcol]
-            - PPM_GETR( rowptr[crow][subcol] )
-            + PPM_GETR( rowptr[crow][addcol] );
-        rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-        growsumptr[crow][leftcol] = growsumptr[crow][subcol]
-            - PPM_GETG( rowptr[crow][subcol] )
-            + PPM_GETG( rowptr[crow][addcol] );
-        gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-        browsumptr[crow][leftcol] = browsumptr[crow][subcol]
-            - PPM_GETB( rowptr[crow][subcol] )
-            + PPM_GETB( rowptr[crow][addcol] );
-        bsum += browsumptr[crow][leftcol] * bweights[crow][0];
-        }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    for (crow = 0; crow < toprow; ++crow, ++i) {
+        circMap[i] = rowbuf[crow];
+        sumCircMap[i] = convRowSum[crow];
+    }
+}
 
 
-    /* For all subsequent rows */
 
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    temprow = row % crowsp1;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+static void
+convolveHorizontalRowPlane(struct pam *              const pamP,
+                           tuple **                  const window,
+                           const struct convKernel * const convKernelP,
+                           unsigned int              const plane,
+                           tuple *                   const outputrow,
+                           sample **                 const sumWindow) {
+/*----------------------------------------------------------------------------
+   Convolve the row at the center of the convolution window described
+   by *convKernelP, where window[][] contains the input image tuples
+   for the window.  *pamP describes the rows in it, but its height is
+   one convolution window.
+
+   Convolve only the Plane 'plane' samples.
+
+   sumWindow[][] mirrors window[].  sumWindow[R] applies to window[R].
+   sumWindow[R][C] is the sum of samples in row R of the convolution window
+   centered on Column C.  We assume the convolution weights are the same
+   everywhere within a row of the kernel, so that we can generate these
+   sums incrementally, moving to the right through the image.
+-----------------------------------------------------------------------------*/
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+    unsigned int const crowso2 = convKernelP->rows / 2;
 
-    temprow = (row + 2) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        {
-        rowptr[i] = xelbuf[irow];
-        rrowsumptr[i] = rrowsum[irow];
-        growsumptr[i] = growsum[irow];
-        browsumptr[i] = browsum[irow];
-        }
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        {
-        rowptr[i] = xelbuf[irow];
-        rrowsumptr[i] = rrowsum[irow];
-        growsumptr[i] = growsum[irow];
-        browsumptr[i] = browsum[irow];
-        }
+    unsigned int const newrow  = convKernelP->rows - 1;
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2)
+            outputrow[col][plane] = window[crowso2][col][plane];
+        else if (col == ccolso2) {
+            unsigned int const leftcol = 0;
+                // Window is up againt left edge of image
+
+            float matrixSum;
 
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        rrowsumptr[addrow][leftcol] = 0L;
-        growsumptr[addrow][leftcol] = 0L;
-        browsumptr[addrow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
             {
-            rrowsumptr[addrow][leftcol] += 
-            PPM_GETR( rowptr[addrow][leftcol + ccol] );
-            growsumptr[addrow][leftcol] += 
-            PPM_GETG( rowptr[addrow][leftcol + ccol] );
-            browsumptr[addrow][leftcol] += 
-            PPM_GETB( rowptr[addrow][leftcol + ccol] );
+                unsigned int ccol;
+                sumWindow[newrow][col] = 0;
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sumWindow[newrow][col] +=
+                        window[newrow][leftcol + ccol][plane];
             }
-        for ( crow = 0; crow < crows; ++crow )
             {
-            rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-            gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-            bsum += browsumptr[crow][leftcol] * bweights[crow][0];
+                unsigned int crow;
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    matrixSum += sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
             }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-        else
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        rrowsumptr[addrow][leftcol] = rrowsumptr[addrow][subcol]
-            - PPM_GETR( rowptr[addrow][subcol] )
-            + PPM_GETR( rowptr[addrow][addcol] );
-        growsumptr[addrow][leftcol] = growsumptr[addrow][subcol]
-            - PPM_GETG( rowptr[addrow][subcol] )
-            + PPM_GETG( rowptr[addrow][addcol] );
-        browsumptr[addrow][leftcol] = browsumptr[addrow][subcol]
-            - PPM_GETB( rowptr[addrow][subcol] )
-            + PPM_GETB( rowptr[addrow][addcol] );
-        for ( crow = 0; crow < crows; ++crow )
-            {
-            rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-            gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-            bsum += browsumptr[crow][leftcol] * bweights[crow][0];
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
+        } else {
+            unsigned int const subcol  = col - ccolso2 - 1;
+            unsigned int const addcol  = col + ccolso2;  
+
+            float matrixSum;
+            unsigned int crow;
+
+            sumWindow[newrow][col] =
+                sumWindow[newrow][col-1]
+                + window[newrow][addcol][plane]
+                - window[newrow][subcol][plane];
+
+            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
+                matrixSum += sumWindow[crow][col] *
+                    convKernelP->weight[plane][crow][0];
             }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
-    }
 
-
-/* PPM Vertical Convolution
-**
-** Same as pgm_vertical_convolve()
-**
-*/
+static convolver convolveHorizontal;
 
 static void
-ppm_vertical_convolve(const float ** const rweights,
-                      const float ** const gweights,
-                      const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int tempcol;
-    long *rcolumnsum, *gcolumnsum, *bcolumnsum;
-    int crowsp1;
-    int addcol;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. VERTICAL uses an extra row. */
-    xelbuf = pnm_allocarray(cols, crows + 1);
-    outputrow = pnm_allocrow(cols);
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray(1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    MALLOCARRAY_NOFAIL(rcolumnsum, cols);
-    MALLOCARRAY_NOFAIL(gcolumnsum, cols);
-    MALLOCARRAY_NOFAIL(bcolumnsum, cols);
-
-    for (col = 0; col < cols; ++col) {
-        rcolumnsum[col] = 0L;
-        gcolumnsum[col] = 0L;
-        bcolumnsum[col] = 0L;
+convolveHorizontal(struct pam *              const inpamP,
+                   struct pam *              const outpamP,
+                   const struct convKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+  Horizontal Convolution
+
+  Similar idea to using columnsums of the Mean and Vertical convolution, but
+  uses temporary sums of row values.  Need to multiply by weights once for
+  each row in the convolution kernel.  Each time we start a new line, we must
+  recalculate the initials rowsums for the newest row only.  Uses queue to
+  still access previous row sums.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Same as in convolveMean */
+    unsigned int const windowHeight = convKernelP->rows;
+        /* Same as in convolveMean */
+
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int plane;
+    sample *** convRowSum;  /* Malloc'd */
+    sample ** sumCircMap;  /* Malloc'd */
+
+    rowbuf = allocRowbuf(inpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    convRowSum = allocRowSum(outpamP->depth, windowHeight, outpamP->width);
+    MALLOCARRAY_NOFAIL(sumCircMap, windowHeight);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
+
+    /* Convolve the first convolvable row and generate convRowSum[][] */
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        unsigned int crow;
+
+        for (crow = 0; crow < convKernelP->rows; ++crow)
+            sumCircMap[crow] = convRowSum[plane][crow];
+ 
+        convolveHorizontalRowPlane0(outpamP, circMap, convKernelP, plane,
+                                    outputrow, sumCircMap);
     }
+    pnm_writepamrow(outpamP, outputrow);
+
+    /* Convolve the rest of the rows, using convRowSum[] */
+
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        unsigned int row;
+            /* Same as in convolveMean */
 
-    pnm_writepnminit(stdout, cols, rows, maxval, newformat, 0);
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for (row = 0; row < crows - 1; ++row) {
-        pnm_readpnmrow(ifp, xelbuf[row], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[row], cols, maxval, format, 
-                                 maxval, newformat);
-        /* Write out just the part we're not going to convolve. */
-        if (row < crowso2)
-            pnm_writepnmrow(stdout, xelbuf[row], cols, maxval, newformat, 0);
+        for (row = convKernelP->rows/2 + 1;
+             row < inpamP->height - convKernelP->rows/2;
+             ++row) {
+
+            unsigned int const windowBotRow = row + crowso2;
+            unsigned int const windowTopRow = row - crowso2;
+                /* Same as in convolveMean */
+
+            readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                            outpamP->maxval, outpamP->depth);
+            
+            setupCircMap2(rowbuf, convRowSum[plane], circMap, sumCircMap,
+                          windowTopRow, windowHeight);
+
+            convolveHorizontalRowPlane(outpamP, circMap, convKernelP, plane,
+                                       outputrow, sumCircMap);
+            
+            pnm_writepamrow(outpamP, outputrow);
+        }
     }
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    /* For first row only */
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
 
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-    if (PNM_FORMAT_TYPE(format) != newformat)
-        pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, maxval, 
-                             newformat);
+    freeRowSum(convRowSum, outpamP->depth, windowHeight);
+    freeRowbuf(rowbuf, windowHeight);
+}
 
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for (col = 0; col < cols; ++col) {
-        if (col < ccolso2 || col >= cols - ccolso2)
-            outputrow[col] = rowptr[crowso2][col];
+
+
+static void
+convolveVerticalRowPlane(struct pam *              const pamP,
+                         tuple **                  const circMap,
+                         const struct convKernel * const convKernelP,
+                         unsigned int              const plane,
+                         tuple *                   const outputrow,
+                         sample *                  const convColumnSum) {
+
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int const subrow = 0;
+        /* Row just above convolution window -- what we subtract from
+           running sum
+        */
+    unsigned int const addrow = 1 + (convKernelP->rows - 1);
+        /* Bottom row of convolution window: What we add to running sum */
+    
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2)
+            outputrow[col][plane] = circMap[crowso2][col][plane];
         else if (col == ccolso2) {
-            int const leftcol = col - ccolso2;
-            float rsum, gsum, bsum;
-            rsum = 0.0;
-            gsum = 0.0;
-            bsum = 0.0;
-            for (crow = 0; crow < crows; ++crow) {
-                temprptr = rowptr[crow] + leftcol;
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    rcolumnsum[leftcol + ccol] += 
-                        PPM_GETR(*(temprptr + ccol));
-                    gcolumnsum[leftcol + ccol] += 
-                        PPM_GETG(*(temprptr + ccol));
-                    bcolumnsum[leftcol + ccol] += 
-                        PPM_GETB(*(temprptr + ccol));
-                }
+            unsigned int const leftcol = 0;
+                // Convolution window is againt left edge of image
+
+            float matrixSum;
+            unsigned int ccol;
+
+            // Slide window down in the first kernel's worth of columns
+            for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                convColumnSum[leftcol + ccol] +=
+                    circMap[addrow][leftcol + ccol][plane];
+                convColumnSum[leftcol + ccol] -=
+                    circMap[subrow][leftcol + ccol][plane];
             }
-            for (ccol = 0; ccol < ccols; ++ccol) {
-                rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
+            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
+                matrixSum += convColumnSum[leftcol + ccol] *
+                    convKernelP->weight[plane][0][ccol];
             }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN(outputrow[col], r, g, b);
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         } else {
-            int const leftcol = col - ccolso2;
-            float rsum, gsum, bsum;
-            rsum = 0.0;
-            gsum = 0.0;
-            bsum = 0.0;
-            addcol = col + ccolso2;  
-            for (crow = 0; crow < crows; ++crow) {
-                rcolumnsum[addcol] += PPM_GETR( rowptr[crow][addcol]);
-                gcolumnsum[addcol] += PPM_GETG( rowptr[crow][addcol]);
-                bcolumnsum[addcol] += PPM_GETB( rowptr[crow][addcol]);
-            }
-            for (ccol = 0; ccol < ccols; ++ccol) {
-                rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
+            unsigned int const leftcol = col - ccolso2;
+            unsigned int const addcol  = col + ccolso2;
+
+            float matrixSum;
+            unsigned int ccol;
+
+            // Slide window down in the column that just entered the window
+            convColumnSum[addcol] += circMap[addrow][addcol][plane];
+            convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+
+            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
+                matrixSum += convColumnSum[leftcol + ccol] *
+                    convKernelP->weight[plane][0][ccol];
             }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN(outputrow[col], r, g, b);
+            outputrow[col][plane] =
+                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         }
     }
-    pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
-    
-    /* For all subsequent rows */
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for (; row < rows; ++row) {
-        toprow = row + 1;
-        temprow = row % (crows +1);
-        pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, 
-                                 maxval, newformat);
-
-        /* Arrange rowptr to eliminate the use of mod function to determine
-        ** which row of xelbuf is 0...crows.  Mod function can be very costly.
+}
+
+
+
+static convolver convolveVertical;
+
+static void
+convolveVertical(struct pam *              const inpamP,
+                 struct pam *              const outpamP,
+                 const struct convKernel * const convKernelP) {
+
+    /* Uses column sums as in mean convolution, above */
+
+    unsigned int const windowHeight = convKernelP->rows + 1;
+        /* Same as in convolveMean */
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Same as in convolveMean */
+
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int row;
+        /* Row number of next row to read in from the file */
+    sample ** convColumnSum;  /* Malloc'd */
+        /* Same as in convolveMean() */
+
+    rowbuf = allocRowbuf(inpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    convColumnSum = allocSum(outpamP->depth, outpamP->width);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
+
+    /* Convolve the first window the long way */
+    computeInitialColumnSums(inpamP, circMap, convKernelP, convColumnSum);
+
+    convolveRowWithColumnSumsVertical(convKernelP, outpamP, circMap,
+                                      outputrow, convColumnSum);
+
+    pnm_writepamrow(outpamP, outputrow);
+
+    for (row = crowso2 + 1; row < inpamP->height - crowso2; ++row) {
+        unsigned int const windowBotRow = row + crowso2;
+            /* Same as in convolveMean */
+        unsigned int const windowTopRow = row - crowso2 - 1;
+            /* Same as in convolveMean */
+        unsigned int plane;
+
+        readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                        outpamP->maxval, outpamP->depth);
+
+        /* Remember the window is one row higher than the convolution
+           kernel.  The top row in the window is not part of this convolution.
         */
-        temprow = (toprow + 1) % crowsp1;
-        i = 0;
-        for (irow = temprow; irow < crowsp1; ++i, ++irow)
-            rowptr[i] = xelbuf[irow];
-        for (irow = 0; irow < temprow; ++irow, ++i)
-            rowptr[i] = xelbuf[irow];
-
-        for (col = 0; col < cols; ++col) {
-            if (col < ccolso2 || col >= cols - ccolso2)
-                outputrow[col] = rowptr[crowso2][col];
-            else if (col == ccolso2) {
-                int const leftcol = col - ccolso2;
-                float rsum, gsum, bsum;
-                rsum = 0.0;
-                gsum = 0.0;
-                bsum = 0.0;
-
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    tempcol = leftcol + ccol;
-                    rcolumnsum[tempcol] = rcolumnsum[tempcol] 
-                        - PPM_GETR(rowptr[subrow][ccol])
-                        + PPM_GETR(rowptr[addrow][ccol]);
-                    rsum = rsum + rcolumnsum[tempcol] * rweights[0][ccol];
-                    gcolumnsum[tempcol] = gcolumnsum[tempcol] 
-                        - PPM_GETG(rowptr[subrow][ccol])
-                        + PPM_GETG(rowptr[addrow][ccol]);
-                    gsum = gsum + gcolumnsum[tempcol] * gweights[0][ccol];
-                    bcolumnsum[tempcol] = bcolumnsum[tempcol] 
-                        - PPM_GETB(rowptr[subrow][ccol])
-                        + PPM_GETB(rowptr[addrow][ccol]);
-                    bsum = bsum + bcolumnsum[tempcol] * bweights[0][ccol];
-                }
-                temprsum = rsum + 0.5;
-                tempgsum = gsum + 0.5;
-                tempbsum = bsum + 0.5;
-                CHECK_RED;
-                CHECK_GREEN;
-                CHECK_BLUE;
-                PPM_ASSIGN(outputrow[col], r, g, b);
-            } else {
-                int const leftcol = col - ccolso2;
-                float rsum, gsum, bsum;
-                rsum = 0.0;
-                gsum = 0.0;
-                bsum = 0.0;
-                addcol = col + ccolso2;
-                rcolumnsum[addcol] = rcolumnsum[addcol]
-                    - PPM_GETR(rowptr[subrow][addcol])
-                    + PPM_GETR(rowptr[addrow][addcol]);
-                gcolumnsum[addcol] = gcolumnsum[addcol]
-                    - PPM_GETG(rowptr[subrow][addcol])
-                    + PPM_GETG(rowptr[addrow][addcol]);
-                bcolumnsum[addcol] = bcolumnsum[addcol]
-                    - PPM_GETB(rowptr[subrow][addcol])
-                    + PPM_GETB(rowptr[addrow][addcol]);
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                    gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                    bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
-                }
-                temprsum = rsum + 0.5;
-                tempgsum = gsum + 0.5;
-                tempbsum = bsum + 0.5;
-                CHECK_RED;
-                CHECK_GREEN;
-                CHECK_BLUE;
-                PPM_ASSIGN(outputrow[col], r, g, b);
-            }
-        }
-        pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
-    }
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for (irow = crowso2 + 1; irow < crows; ++irow)
-        pnm_writepnmrow(stdout, rowptr[irow], cols, maxval, newformat, 0);
+        setupCircMap(circMap, rowbuf, windowHeight,
+                     windowTopRow % windowHeight);
 
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveVerticalRowPlane(outpamP, circMap, convKernelP, plane,
+                                     outputrow, convColumnSum[plane]);
+
+        pnm_writepamrow(outpamP, outputrow);
+    }
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
+
+    freeSum(convColumnSum, outpamP->depth);
+    freeRowbuf(rowbuf, windowHeight);
 }
 
 
 
-static void
-determineConvolveType(xel * const *         const cxels,
-                      struct convolveType * const typeP) {
-/*----------------------------------------------------------------------------
-   Determine which form of convolution is best.  The general form always
-   works, but with some special case convolution matrices, faster forms
-   of convolution are possible.
+struct convolveType {
+    convolver * convolve;
+};
 
-   We don't check for the case that one of the PPM colors can have 
-   differing types.  We handle only cases where all PPMs are of the same
-   special case.
------------------------------------------------------------------------------*/
-    int horizontal, vertical;
-    int tempcxel, rtempcxel, gtempcxel, btempcxel;
-    int crow, ccol;
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        horizontal = TRUE;  /* initial assumption */
-        crow = 0;
-        while (horizontal && (crow < crows)) {
-            ccol = 1;
-            rtempcxel = PPM_GETR(cxels[crow][0]);
-            gtempcxel = PPM_GETG(cxels[crow][0]);
-            btempcxel = PPM_GETB(cxels[crow][0]);
-            while (horizontal && (ccol < ccols)) {
-                if ((PPM_GETR(cxels[crow][ccol]) != rtempcxel) ||
-                    (PPM_GETG(cxels[crow][ccol]) != gtempcxel) ||
-                    (PPM_GETB(cxels[crow][ccol]) != btempcxel)) 
-                    horizontal = FALSE;
-                ++ccol;
-            }
-            ++crow;
-        }
 
-        vertical = TRUE;   /* initial assumption */
-        ccol = 0;
-        while (vertical && (ccol < ccols)) {
-            crow = 1;
-            rtempcxel = PPM_GETR(cxels[0][ccol]);
-            gtempcxel = PPM_GETG(cxels[0][ccol]);
-            btempcxel = PPM_GETB(cxels[0][ccol]);
-            while (vertical && (crow < crows)) {
-                if ((PPM_GETR(cxels[crow][ccol]) != rtempcxel) |
-                    (PPM_GETG(cxels[crow][ccol]) != gtempcxel) |
-                    (PPM_GETB(cxels[crow][ccol]) != btempcxel))
-                    vertical = FALSE;
-                ++crow;
-            }
-            ++ccol;
-        }
-        break;
-        
-    default:
-        horizontal = TRUE; /* initial assumption */
-        crow = 0;
-        while (horizontal && (crow < crows)) {
-            ccol = 1;
-            tempcxel = PNM_GET1(cxels[crow][0]);
-            while (horizontal && (ccol < ccols)) {
-                if (PNM_GET1(cxels[crow][ccol]) != tempcxel)
+
+static bool
+convolutionIncludesHorizontal(const struct convKernel * const convKernelP) {
+
+    bool horizontal;
+    unsigned int row;
+
+    for (row = 0, horizontal = TRUE;
+         row < convKernelP->rows && horizontal;
+        ++row) {
+        unsigned int col;
+        for (col = 1, horizontal = TRUE;
+             col < convKernelP->cols && horizontal;
+             ++col) {
+
+            unsigned int plane;
+
+            for (plane = 0; plane < convKernelP->planes; ++plane) {
+                if (convKernelP->weight[plane][row][col] !=
+                    convKernelP->weight[plane][row][0])
                     horizontal = FALSE;
-                ++ccol;
             }
-            ++crow;
         }
-        
-        vertical = TRUE;  /* initial assumption */
-        ccol = 0;
-        while (vertical && (ccol < ccols)) {
-            crow = 1;
-            tempcxel = PNM_GET1(cxels[0][ccol]);
-            while (vertical && (crow < crows)) {
-                if (PNM_GET1(cxels[crow][ccol]) != tempcxel)
+    }
+    return horizontal;
+}
+
+
+
+static bool
+convolutionIncludesVertical(const struct convKernel * const convKernelP) {
+
+    bool vertical;
+    unsigned int col;
+
+    for (col = 0, vertical = TRUE;
+         col < convKernelP->cols && vertical;
+        ++col) {
+        unsigned int row;
+        for (row = 1, vertical = TRUE;
+             row < convKernelP->rows && vertical;
+             ++row) {
+
+            unsigned int plane;
+
+            for (plane = 0; plane < convKernelP->planes; ++plane) {
+                if (convKernelP->weight[plane][row][col] !=
+                    convKernelP->weight[plane][0][col])
                     vertical = FALSE;
-                ++crow;
             }
-            ++ccol;
         }
-        break;
-    }
-    
-    /* Which type do we have? */
-    if (horizontal && vertical) {
-        typeP->ppmConvolver = ppm_mean_convolve;
-        typeP->pgmConvolver = pgm_mean_convolve;
-    } else if (horizontal) {
-        typeP->ppmConvolver = ppm_horizontal_convolve;
-        typeP->pgmConvolver = pgm_horizontal_convolve;
-    } else if (vertical) {
-        typeP->ppmConvolver = ppm_vertical_convolve;
-        typeP->pgmConvolver = pgm_vertical_convolve;
-    } else {
-        typeP->ppmConvolver = ppm_general_convolve;
-        typeP->pgmConvolver = pgm_general_convolve;
     }
+    return vertical;
 }
 
 
 
 static void
-convolveIt(int                 const format,
-           struct convolveType const convolveType,
-           const float**       const rweights,
-           const float**       const gweights,
-           const float**       const bweights) {
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        convolveType.ppmConvolver(rweights, gweights, bweights);
-        break;
-
-    default:
-        convolveType.pgmConvolver(gweights);
+determineConvolveType(const struct convKernel * const convKernelP,
+                      struct convolveType *     const typeP) {
+/*----------------------------------------------------------------------------
+   Determine which form of convolution is best to convolve the kernel
+   *convKernelP over tuples[][].  The general form always works, but with some
+   special case convolution matrices, faster forms of convolution are
+   possible.
+
+   We don't check for the case that the planes can have differing types.  We
+   handle only cases where all planes are of the same special case.
+-----------------------------------------------------------------------------*/
+    bool const horizontal = convolutionIncludesHorizontal(convKernelP);
+    bool const vertical   = convolutionIncludesVertical(convKernelP);
+
+    if (horizontal && vertical) {
+        pm_message("Convolution is a simple mean horizontally and vertically");
+        typeP->convolve = convolveMean;
+    } else if (horizontal) {
+        pm_message("Convolution is a simple mean horizontally");
+        typeP->convolve = convolveHorizontal;
+    } else if (vertical) {
+        pm_message("Convolution is a simple mean vertically");
+        typeP->convolve = convolveVertical;
+    } else {
+        typeP->convolve = convolveGeneral;
     }
 }
 
@@ -1826,69 +2046,50 @@ int
 main(int argc, char * argv[]) {
 
     struct cmdlineInfo cmdline;
-    FILE* cifp;
-    xel** cxels;
-    int cformat;
-    xelval cmaxval;
+    FILE * ifP;
     struct convolveType convolveType;
-    float ** rweights;
-    float ** gweights;
-    float ** bweights;
+    struct convKernel * convKernelP;
+    struct pam inpam;
+    struct pam outpam;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    cifp = pm_openr(cmdline.kernelFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
-    /* Read in the convolution matrix. */
-    cxels = pnm_readpnm(cifp, &ccols, &crows, &cmaxval, &cformat);
-    pm_close(cifp);
-
-    if (ccols % 2 != 1 || crows % 2 != 1)
-        pm_error("the convolution matrix must have an odd number of "
-                 "rows and columns" );
-
-    ccolso2 = ccols / 2;
-    crowso2 = crows / 2;
-
-    ifp = pm_openr(cmdline.inputFilespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-    if (cols < ccols || rows < crows)
-        pm_error("the image is smaller than the convolution matrix" );
-
-    newformat = MAX(PNM_FORMAT_TYPE(cformat), PNM_FORMAT_TYPE(format));
-    if (PNM_FORMAT_TYPE(cformat) != newformat)
-        pnm_promoteformat(cxels, ccols, crows, cmaxval, cformat, 
-                          cmaxval, newformat);
-    if (PNM_FORMAT_TYPE(format) != newformat) {
-        switch (PNM_FORMAT_TYPE(newformat)) {
-        case PPM_TYPE:
-            if (PNM_FORMAT_TYPE(format) != newformat)
-                pm_message("promoting to PPM");
-            break;
-        case PGM_TYPE:
-            if (PNM_FORMAT_TYPE(format) != newformat)
-                pm_message("promoting to PGM");
-            break;
-        }
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    getKernel(cmdline, inpam.depth, &convKernelP);
+
+    outpam = inpam;  /* initial value */
+
+    outpam.file = stdout;
+
+    if ((PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE ||
+         PNM_FORMAT_TYPE(inpam.format) == PGM_TYPE) &&
+        convKernelP->planes == 3) {
+
+        pm_message("promoting to PPM");
+        outpam.format = PPM_FORMAT;
     }
 
-    computeWeights(cxels, ccols, crows, newformat, cmaxval, !cmdline.nooffset,
-                   &rweights, &gweights, &bweights);
+    outpam.depth = MAX(inpam.depth, convKernelP->planes);
+
+    pnm_setminallocationdepth(&inpam, MAX(inpam.depth, outpam.depth));
+
+    validateEnoughImageToConvolve(&inpam, convKernelP);
 
     /* Handle certain special cases when runtime can be improved. */
 
-    determineConvolveType(cxels, &convolveType);
+    determineConvolveType(convKernelP, &convolveType);
 
-    convolveIt(format, convolveType, 
-               (const float **)rweights, 
-               (const float **)gweights, 
-               (const float **)bweights);
+    convolveType.convolve(&inpam, &outpam, convKernelP);
 
+    convKernelDestroy(convKernelP);
     pm_close(stdout);
-    pm_close(ifp);
+    pm_close(ifP);
+
     return 0;
 }
 
diff --git a/editor/pnmsmooth.c b/editor/pnmsmooth.c
index 16d8ec33..14116ddf 100644
--- a/editor/pnmsmooth.c
+++ b/editor/pnmsmooth.c
@@ -21,6 +21,7 @@
 
 
 #include <unistd.h>
+#include <assert.h>
 #include <string.h>
 #include <errno.h>
 
@@ -39,13 +40,13 @@ struct cmdlineInfo {
     const char * inputFilespec;  /* Filespec of input file */
     unsigned int width;
     unsigned int height;
-    const char * dump;
+    unsigned int dump;
 };
 
 
 
 static void
-parseCommandLine (int argc, char ** argv,
+parseCommandLine (int argc, const char ** argv,
                   struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
@@ -64,13 +65,13 @@ parseCommandLine (int argc, char ** argv,
 
     unsigned int option_def_index;
 
-    unsigned int widthSpec, heightSpec, dumpSpec, sizeSpec;
+    unsigned int widthSpec, heightSpec, sizeSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
     
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "dump",          OPT_STRING,   
-            &cmdlineP->dump,            &dumpSpec, 0);
+    OPTENT3(0,   "dump",          OPT_FLAG,   
+            NULL,                       &cmdlineP->dump, 0);
     OPTENT3(0,   "width",         OPT_UINT,
             &cmdlineP->width,           &widthSpec, 0);
     OPTENT3(0,   "height",        OPT_UINT,
@@ -82,7 +83,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!widthSpec)
@@ -91,9 +92,6 @@ parseCommandLine (int argc, char ** argv,
     if (!heightSpec)
         cmdlineP->height = 3;
 
-    if (!dumpSpec)
-        cmdlineP->dump = NULL;
-
     if (sizeSpec) {
         /* -size is strictly for backward compatibility.  This program
            used to use a different command line processor and had
@@ -143,68 +141,82 @@ parseCommandLine (int argc, char ** argv,
 
 
 
-static void
-writeConvolutionImage(FILE *       const cofp,
-                      unsigned int const cols,
-                      unsigned int const rows,
-                      int          const format) {
-
-    xelval const convmaxval = rows * cols * 2;
-        /* normalizing factor for our convolution matrix */
-    xelval const g = rows * cols + 1;
-        /* weight of all pixels in our convolution matrix */
-    int row;
-    xel *outputrow;
-
-    if (convmaxval > PNM_OVERALLMAXVAL)
-        pm_error("The convolution matrix is too large.  "
-                 "Width x Height x 2\n"
-                 "must not exceed %d and it is %d.",
-                 PNM_OVERALLMAXVAL, convmaxval);
-
-    pnm_writepnminit(cofp, cols, rows, convmaxval, format, 0);
-    outputrow = pnm_allocrow(cols);
-
-    for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(outputrow[col], g);
-        pnm_writepnmrow(cofp, outputrow, cols, convmaxval, format, 0);
+static const char *
+makeConvolutionKernel(unsigned int const cols,
+                      unsigned int const rows) {
+/*----------------------------------------------------------------------------
+   Return a convolution kernel with dimensions 'cols' by 'rows' that is
+   uniform.  Make it in the form of the value of a Pnmconvol '-matrix'
+   option.
+-----------------------------------------------------------------------------*/
+    double const weight = 1.0/(rows * cols);
+
+    char * matrix;
+
+    unsigned int const maxOptSize = (cols * 10 + 1) * rows + 1;
+
+    MALLOCARRAY(matrix, maxOptSize);
+
+    if (matrix == NULL)
+        pm_error("Could not get memory for a %u x %u convolution matrix",
+                 rows, cols);
+    else {
+        unsigned int row;
+        char * cursor;
+        
+        cursor = &matrix[0];  /* Start at beginning of string */
+
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+
+            if (row > 0)
+                *cursor++ = ';';
+
+            for (col = 0; col < cols; ++col) {
+                char weightStr[20];
+                unsigned int i;
+
+                assert(cursor - matrix + 10 < maxOptSize);
+
+                if (col > 0)
+                    *cursor++ = ',';
+
+                snprintf(weightStr, sizeof(weightStr), "%f", weight);
+
+                for (i = 0; weightStr[i]; ++i)
+                    *cursor++ = weightStr[i];
+            }
+        }
     }
-    pnm_freerow(outputrow);
+    return matrix;
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
-    FILE * convFileP;
-    const char * tempfileName;
+    const char * matrixOptValue;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.dump)
-        convFileP = pm_openw(cmdline.dump);
-    else
-        pm_make_tmpfile(&convFileP, &tempfileName);
-        
-    writeConvolutionImage(convFileP, cmdline.width, cmdline.height,
-                          PGM_FORMAT);
-
-    pm_close(convFileP);
+    matrixOptValue = makeConvolutionKernel(cmdline.width, cmdline.height);
 
     if (cmdline.dump) {
-        /* We're done.  Convolution image is in user's file */
+        fprintf(stdout, "%s\n", matrixOptValue);
     } else {
+        const char * matrixOpt;
+        asprintfN(&matrixOpt, "-matrix=%s", matrixOptValue);
+
         pm_system_lp("pnmconvol", NULL, NULL, NULL, NULL,
-                     "pnmconvol", tempfileName, cmdline.inputFilespec, NULL);
+                     "pnmconvol", matrixOpt, cmdline.inputFilespec, NULL);
 
-        unlink(tempfileName);
-        strfree(tempfileName);
+        strfree(matrixOpt);
     }
+    strfree(matrixOptValue);
+
     return 0;
 }
diff --git a/editor/specialty/Makefile b/editor/specialty/Makefile
index eda54882..72ab6964 100644
--- a/editor/specialty/Makefile
+++ b/editor/specialty/Makefile
@@ -16,6 +16,7 @@ PORTBINARIES = pamdeinterlace \
 	       pgmbentley \
 	       pgmmorphconv \
 	       pnmindex \
+	       pnmmercator \
 	       ppm3d \
 	       ppmglobe \
 	       ppmntsc \
diff --git a/editor/specialty/pnmmercator.c b/editor/specialty/pnmmercator.c
new file mode 100644
index 00000000..31ba3d82
--- /dev/null
+++ b/editor/specialty/pnmmercator.c
@@ -0,0 +1,429 @@
+/* pammercator.c - convert a map in PNM image format from 360x180 degrees
+**                 to Mercator projection or vice versa
+**
+** This program borrowed a lot of code from PnmScale which again was derived
+** from PpmScale.
+**
+** Copyright (C) 2009 by Willem van Schaik <willem@schaik.com>
+** Copyright (C) 1989, 1991 by Jef Poskanzer.
+**
+** 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
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+*/
+
+#include <math.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pnm.h"
+
+/* The pnm library allows us to code this program without branching cases for
+   PGM and PPM, but we do the branch anyway to speed up processing of PGM
+   images. 
+*/
+
+
+
+struct cmdlineInfo 
+{
+    /*
+    All the information the user supplied in the command line,
+    in a form easy for the program to use. 
+    */
+
+    const char * input_filespec;  /* Filespecs of input files */
+    const char * inputFileName;  /* Filespec of input file */
+    unsigned int inverse; /* from Mercator to Degrees */
+    unsigned int nomix;
+    unsigned int verbose;
+    unsigned int vverbose;
+};
+
+
+
+static void
+parseCommandLine(int                        argc, 
+                 const char **              argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+
+    optEntry * option_def;
+    optStruct3 opt;
+        /* Instructions to optParseOptions3 on how to parse our options. */
+    
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "inverse",   OPT_FLAG,    NULL,       &cmdlineP->inverse,   0);
+    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,     0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,   0);
+    OPTENT3(0, "vverbose",  OPT_FLAG,    NULL,       &cmdlineP->vverbose,  0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    /* Only parameter allowed is optional filespec */
+    if (argc-1 < 1)
+        cmdlineP->input_filespec = "-";
+    else
+        cmdlineP->input_filespec = argv[1];
+}
+
+
+
+static void
+computeOutputDimensions(const struct cmdlineInfo cmdline, 
+                        const int rows, const int cols,
+                        int * newrowsP, int * newcolsP) 
+{
+    *newcolsP = cols;
+    if (!cmdline.inverse)
+        *newrowsP = 2 * rows;
+    else
+        *newrowsP = rows / 2;
+
+    if (*newcolsP < 1) *newcolsP = 1;
+    if (*newrowsP < 1) *newrowsP = 1;
+
+    if (cmdline.verbose) {
+        if (!cmdline.inverse)
+            pm_message("Creating Mercator map, new size is %dx%d",
+                       *newcolsP, *newrowsP);
+        else
+            pm_message("Creating Degrees map, new size is %dx%d",
+                       *newcolsP, *newrowsP);
+    }
+}        
+
+
+
+static void
+transformWithMixing(FILE * const ifP,
+                    int const cols, int const rows,
+                    xelval const maxval, int const format,
+                    int const newcols, int const newrows,
+                    xelval const newmaxval, int const newformat,
+                    bool const inverse,
+                    bool const verbose, bool const vverbose) 
+{
+    /* 
+    Transform the map image on input file 'ifP' (which is described by 
+    'cols', 'rows', 'format', and 'maxval') to a Mercator projection
+    and write the result to standard output as format 'newformat' and 
+    with maxval 'newmaxval'.
+
+    We'll blend colors from subsequent rows in the output pixels. 
+    */
+
+    xel* oddxelrow;  /* an input row */
+    xel* evenxelrow;
+    xel* newxelrow;  /* the output row */
+
+    int row;
+    double fRow;
+    int rowInXelrow;
+    int inputRow;
+    int col;
+
+    double fFract;
+    double fLatRad;
+    double fLatMerc;
+
+    oddxelrow = pnm_allocrow(cols); 
+    evenxelrow = pnm_allocrow(cols); 
+    rowInXelrow = 0;
+
+    newxelrow = pnm_allocrow(newcols);
+
+    for (row = 0; row < newrows; ++row) {
+        
+        fRow = (double)row + 0.5; /* center on half the pixel */
+        if (!inverse) {
+            /* the result is mercator, calculate back to degrees */
+            fLatMerc = 2.0 * M_PI * (fRow / (double)newrows - 0.5);
+                /* merc goes from -pi to +pi */
+            fLatRad = 2.0 * (atan(exp(fLatMerc)) - M_PI / 4.0);
+            fRow = ((fLatRad / M_PI) + 0.5) * (double)rows;
+                /* lat goes from -pi/2 to +pi/2 */
+        } else {
+            /* the result is in degrees, calculate back to mercator */
+            fLatRad = M_PI * (fRow / (double)newrows - 0.5);
+                /* lat goes from -pi/2 to +pi/2 */
+            fLatMerc = log(tan((M_PI / 4.0 + fLatRad / 2.0)));
+            fRow = ((fLatMerc / M_PI / 2.0) + 0.5) * (double)rows;
+                /* merc goes from -pi to +pi */
+        }
+        fRow -= 0.5;
+
+        inputRow = (int) floor(fRow);
+        if (inputRow < 0)
+            inputRow = 0;
+        if (inputRow > rows - 1)
+            inputRow = rows - 1;
+
+        /* calculate the fraction */
+        fFract = 1.0 - ((fRow) - (double) inputRow);
+
+        if (vverbose) {
+            if (!inverse) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 20 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latMerc=%f latRad=%f inputRow=%d "
+                               "fRow=%f fFract=%f",
+                               row, fLatMerc, fLatRad, inputRow, fRow, fFract);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 10 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latRad=%f latMerc=%f inputRow=%d"
+                               "fRow=%f fFract=%f",
+                               row, fLatRad, fLatMerc, inputRow, fRow, fFract);
+            }
+        }
+
+        while ((rowInXelrow <= inputRow + 1) && 
+               (rowInXelrow < rows)) {
+            /* we need to read one row ahead */
+            if (rowInXelrow % 2 == 0) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+                    pm_message("read even row - rowInXelrow=%d inputRow=%d",
+                               rowInXelrow, inputRow);
+#endif
+                pnm_readpnmrow(ifP, evenxelrow, cols, newmaxval, format);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+                    pm_message("read odd row - rowInXelrow=%d inputRow=%d",
+                               rowInXelrow, inputRow);
+#endif
+                pnm_readpnmrow(ifP, oddxelrow, cols, newmaxval, format);
+            }
+            ++rowInXelrow;
+        }
+
+        for (col = 0; col < newcols; ++col) {
+            if (inputRow == 0)
+                newxelrow[col] = evenxelrow[col];
+            else if (inputRow == rows - 1)
+                newxelrow[col] = oddxelrow[col];
+            else if (inputRow % 2 == 0) {
+                /* the even row is the low one, the odd the high one */
+                newxelrow[col].r = fFract * evenxelrow[col].r +
+                    (1.0 - fFract) * oddxelrow[col].r;
+                newxelrow[col].g = fFract * evenxelrow[col].g +
+                    (1.0 - fFract) * oddxelrow[col].g;
+                newxelrow[col].b = fFract * evenxelrow[col].b +
+                    (1.0 - fFract) * oddxelrow[col].b;
+            } else {
+                newxelrow[col].r = fFract * oddxelrow[col].r +
+                    (1.0 - fFract) * evenxelrow[col].r;
+                newxelrow[col].g = fFract * oddxelrow[col].g +
+                    (1.0 - fFract) * evenxelrow[col].g;
+                newxelrow[col].b = fFract * oddxelrow[col].b +
+                    (1.0 - fFract) * evenxelrow[col].b;
+            }
+        }
+
+        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0 );
+    }
+
+    pnm_freerow(oddxelrow);
+    pnm_freerow(evenxelrow);
+    pnm_freerow(newxelrow);
+}
+
+
+
+static void
+transformNoneMixing(FILE * const ifP,
+                   int const cols, int const rows,
+                   xelval const maxval, int const format,
+                   int const newcols, int const newrows,
+                   xelval const newmaxval, int const newformat,
+                   bool const inverse,
+                   bool const verbose, bool const vverbose) 
+{
+    /*
+    Transform the map image on input file 'ifP' (which is described by 
+    'cols', 'rows', 'format', and 'maxval') to a Mercator projection and
+    write the result to standard output as format 'newformat' and with 
+    maxval 'newmaxval'.
+
+    Don't mix colors from different input pixels together in the output
+    pixels.  Each output pixel is an exact copy of some corresponding 
+    input pixel.
+    */
+
+    xel* xelrow;          /* an input row */
+    xel* newxelrow;         /* the output row */
+
+    int row;
+    double fRow;
+    int rowInXelrow;
+    int inputRow;
+    int col;
+
+    double fLatRad;
+    double fLatMerc;
+
+    xelrow = pnm_allocrow(cols); 
+    rowInXelrow = 0;
+
+    newxelrow = pnm_allocrow(newcols);
+
+    for (row = 0; row < newrows; ++row) {
+        
+        fRow = (double)row + 0.5; /* center on half the pixel */
+        if (!inverse) {
+            /* the result is mercator, calculate back to degrees */
+            fLatMerc = 2.0 * M_PI * (fRow / (double)newrows - 0.5);
+                /* merc goes from -pi to +pi */
+            fLatRad = 2.0 * (atan(exp(fLatMerc)) - M_PI / 4.0);
+            fRow = ((fLatRad / M_PI) + 0.5) * (double)rows;
+                /* lat goes from -pi/2 to +pi/2 */
+        } else {
+            /* the result is in degrees, calculate back to mercator */
+            fLatRad = M_PI * (fRow / (double)newrows - 0.5);
+                /* lat goes from -pi/2 to +pi/2 */
+            fLatMerc = log(tan((M_PI / 4.0 + fLatRad / 2.0)));
+            fRow = ((fLatMerc / M_PI / 2.0) + 0.5) * (double)rows;
+                /* merc goes from -pi to +pi */
+        }
+        /* fRow -= 0.5; */        /* it's weird that this isn't needed */
+
+        inputRow = (int) floor(fRow);
+        if (inputRow < 0)
+            inputRow = 0;
+        if (inputRow > rows - 1)
+            inputRow = rows - 1;
+
+        if (vverbose) {
+            if (!inverse) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 20 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latMerc=%f latRad=%f inputRow=%d"
+                               "fRow=%f",
+                               row, fLatMerc, fLatRad, inputRow, fRow);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 10 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latRad=%f latMerc=%f inputRow=%d"
+                               "fRow=%f",
+                               row, fLatRad, fLatMerc, inputRow, fRow);
+            }
+        }
+
+        while ((rowInXelrow <= inputRow) && (rowInXelrow < rows)) {
+#ifdef DBG_EDGES
+            if ((row < 10) || (row > (newrows - 10)))
+                pm_message("read row - rowInXelrow=%d inputRow=%d",
+                           rowInXelrow, inputRow);
+#endif
+            pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
+            ++rowInXelrow;
+        }
+        for (col = 0; col < newcols; ++col) {
+            newxelrow[col] = xelrow[col];
+        }
+
+        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0 );
+    }
+
+    pnm_freerow(xelrow);
+    pnm_freerow(newxelrow);
+}
+
+
+
+int
+main(int argc, const char ** argv ) 
+{
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    int rows, cols, format, newformat, newrows, newcols;
+    xelval maxval, newmaxval;
+    bool verbose;
+
+    pm_proginit( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose || cmdline.vverbose;
+
+    ifP = pm_openr(cmdline.input_filespec);
+
+    pnm_readpnminit( ifP, &cols, &rows, &maxval, &format );
+
+    /* Promote PBM file to PGM. */
+    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) {
+        newformat = PGM_TYPE;
+        newmaxval = PGM_MAXMAXVAL;
+        pm_message( "promoting from PBM to PGM" );
+    } else {
+        newformat = format;
+        newmaxval = maxval;
+    }
+
+    computeOutputDimensions(cmdline, rows, cols, &newrows, &newcols);
+
+    pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
+
+    if (cmdline.nomix) {
+        if (verbose)
+            pm_message("Transforming map without mixing/blending colors");
+        transformNoneMixing(ifP, cols, rows, maxval, format,
+                            newcols, newrows, newmaxval, newformat, 
+                            cmdline.inverse, verbose, cmdline.vverbose);
+    } else {
+        if (verbose)
+            pm_message("Transforming map while using intermediate colors");
+        transformWithMixing(ifP, cols, rows, maxval, format,
+                            newcols, newrows, newmaxval, newformat, 
+                            cmdline.inverse, verbose, cmdline.vverbose);
+    }
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/lib/Makefile b/lib/Makefile
index 5632b8f0..22488188 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -41,11 +41,13 @@ LIBOBJECTS += libpbmvms.o
 endif
 # Library objects to be linked but not built by common.mk:
 LIBOBJECTS_X = \
-  util/shhopt.o \
-  util/nstring.o \
-  util/vasprintf.o \
   util/filename.o \
+  util/io.o \
   util/nsleep.o \
+  util/nstring.o \
+  util/shhopt.o \
+  util/token.o \
+  util/vasprintf.o \
 
 MANUALS3 = libnetpbm
 MANUALS5 = pbm pgm ppm pnm pam
diff --git a/lib/libpam.c b/lib/libpam.c
index ab75fab6..b890434e 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -227,12 +227,11 @@ allocPamRow(const struct pam * const pamP) {
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    /* The tuple row data structure starts with 'width' pointers to
-       the tuples, immediately followed by the 'width' tuples
-       themselves.  Each tuple consists of 'depth' samples.  
+    /* The tuple row data structure starts with pointers to the tuples,
+       immediately followed by the tuples themselves.
     */
 
-    int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample);
+    unsigned int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample);
     tuple * tuplerow;
 
     tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple));
diff --git a/lib/libpm.c b/lib/libpm.c
index 099d4bf7..467a25f5 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -713,6 +713,8 @@ showNetpbmHelp(const char progname[]) {
         if (docurl == NULL)
             pm_message("No 'docurl=' line in Netpbm configuration file '%s'.",
                        netpbmConfigFileName);
+
+        fclose(netpbmConfigFile);
     }
     if (docurl == NULL)
         pm_message("We have no reliable indication of where the Netpbm "
diff --git a/lib/util/Makefile b/lib/util/Makefile
index f5f0e04c..8d93731e 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -9,7 +9,14 @@ include $(BUILDDIR)/config.mk
 
 # nstring is required for asprintf(), etc.  Also some systems don't have
 # snprintf(), e.g. Solaris 2.5.1.  2002.03.29.
-UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o nsleep.o
+UTILOBJECTS = \
+  filename.o \
+  io.o \
+  nsleep.o \
+  nstring.o \
+  shhopt.o \
+  token.o \
+  vasprintf.o \
 
 MERGE_OBJECTS =
 
diff --git a/lib/util/io.c b/lib/util/io.c
new file mode 100644
index 00000000..edec4a7c
--- /dev/null
+++ b/lib/util/io.c
@@ -0,0 +1,90 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "io.h"
+
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Read a line (assuming the file is text with lines delimited by newlines)
+   for file *fileP.  Return that line in newly malloced storage as
+   *lineP.
+
+   The newline delimiter is not part of the line.
+
+   As a special case, if the file doesn't end in a newline, the characters
+   after the last newline are a line.
+
+   If there are no more lines in the file, return *lineP == NULL.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    size_t bufferSize;
+    size_t cursor;
+    bool gotLine;
+    bool eof;
+
+    bufferSize = 1024;  /* initial value */
+    *errorP = NULL; /* initial value */
+    
+    MALLOCARRAY(buffer, bufferSize);
+
+    for (cursor = 0, gotLine = false, eof = false;
+         !gotLine && !eof && !*errorP; ) {
+        if (cursor + 1 >= bufferSize) {
+            if (bufferSize > INT_MAX/2) {
+                free(buffer);
+                buffer = NULL;
+            } else {
+                bufferSize *= 2;
+                REALLOCARRAY(buffer, bufferSize);
+            }
+        }
+
+        if (!buffer)
+            asprintfN(errorP,
+                      "Couldn't get memory for a %u-byte file read buffer.",
+                      (unsigned int)bufferSize);
+        else {
+            int const rc = getc(fileP);
+        
+            if (rc < 0) {
+                if (feof(fileP))
+                    eof = true;
+                else
+                    asprintfN(errorP, "Failed to read a character from file.  "
+                              "Errno = %d (%s)",
+                              errno, strerror(errno));
+            } else {
+                char const c = (char)rc;
+
+                if (c == '\n')
+                    gotLine = true;
+                else {
+                    buffer[cursor++] = c;
+                }
+            }
+        }
+    }
+    if (*errorP) {
+        if (buffer)
+            free(buffer);
+    } else {
+        if (eof && cursor == 0) {
+            *lineP = NULL;
+            free(buffer);
+        } else {
+            assert(cursor < bufferSize);
+            buffer[cursor++] = '\0';
+
+            *lineP = buffer;
+        }
+    }
+}
diff --git a/lib/util/io.h b/lib/util/io.h
new file mode 100644
index 00000000..39ccd14b
--- /dev/null
+++ b/lib/util/io.h
@@ -0,0 +1,11 @@
+#ifndef IO_H_INCLUDED
+#define IO_H_INCLUDED
+
+#include <stdio.h>
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP);
+
+#endif
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index 718186fa..7f6e2899 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -31,6 +31,7 @@
 
 #include "mallocvar.h"
 #include "nstring.h"
+#include "token.h"
 #include "shhopt.h"
 
 /*-----------------------------------------------------------------------+
@@ -237,6 +238,7 @@ optNeedsArgument(const optEntry opt)
 	|| opt.type == OPT_ULONG
     || opt.type == OPT_FLOAT
     || opt.type == OPT_NAMELIST
+    || opt.type == OPT_STRINGLIST
         ;
 }
 
@@ -281,46 +283,12 @@ getToken(const char *  const tokenStart,
    we return an empty string and *nextP == tokenStart, i.e. *nextP
    doesn't necessarily advance.
 -----------------------------------------------------------------------------*/
-    char * token;
-    const char * cursor;
-    unsigned int charCount;
-
-    /* Run through the token, counting characters */
-
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\') {
-            ++cursor;
-            if (*cursor == '\0')
-                optFatal("string ends with an escape character (\\)");
-        }
-        ++cursor;
-        ++charCount;
-    }
+    const char * error;
     
-    token = malloc(charCount + 1);
-    if (token == NULL)
-        optFatal("Could not allocate %u bytes of memory to parse a string",
-                 charCount + 1);
-
-    /* Go back and do it again, this time copying the characters */
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\')
-            ++cursor;
-
-        assert(*cursor != '\0');
+    pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
 
-        token[charCount++] = *cursor++;
-    }
-    token[charCount] = '\0';
-
-    *tokenP = token;
-    *nextP = cursor;
+    if (error)
+        optFatal("error parsing a token: %s", error);
 }
 
 
@@ -375,6 +343,41 @@ parseNameList(const char *           const listText,
 
 
 
+static void
+parseStringList(const char *   const listText,
+                const char *** const listP) {
+
+    unsigned int const maxStringCount = 100;
+
+    const char * cursor;
+    unsigned int stringCount;
+    const char ** list;
+
+    MALLOCARRAY_NOFAIL(list, maxStringCount+1);
+
+    cursor = &listText[0];  /* initial value */
+
+    stringCount = 0;  /* initial value */
+
+    while (stringCount < maxStringCount && *cursor != '\0') {
+        const char * next;
+
+        getToken(cursor, ',', &list[stringCount++], &next);
+
+        cursor = next;
+
+        if (*cursor != '\0') {
+            assert(*cursor == ',');
+            ++cursor;
+        }
+    }
+    list[stringCount] = NULL;
+
+    *listP = list;
+}
+
+
+
 /*------------------------------------------------------------------------
  |  NAME          optExecute
  |
@@ -479,6 +482,15 @@ optExecute(optEntry  const opt, char *arg, int lng)
             parseNameList(arg, (struct optNameValue **)opt.arg);
 
     } break;
+    case OPT_STRINGLIST: {
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+
+        if (opt.arg)
+            parseStringList(arg, (const char ***)opt.arg);
+
+    } break;
     default:
         break;
     }
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 99096a76..2175fc5d 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -90,6 +90,7 @@ typedef enum {
     OPT_LONG,              /* signed long integer argument. */
     OPT_ULONG,             /* unsigned long integer argument. */
     OPT_FLOAT,             /* floating point argument. */
+    OPT_STRINGLIST,        /* list like "arg1,arg2.arg3" */
     OPT_NAMELIST           /* list like "key1=val1,key2=val2" */
 } optArgType;
 
diff --git a/lib/util/token.c b/lib/util/token.c
new file mode 100644
index 00000000..c4fe0aed
--- /dev/null
+++ b/lib/util/token.c
@@ -0,0 +1,79 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "nstring.h"
+
+#include "token.h"
+
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Find the token starting at 'tokenStart' up to but not including
+   the first 'delimiter' character or end of string.  Return it in newly
+   malloced memory as *tokenP, NUL-terminated.
+
+   Make *nextP point just past the token, i.e. to the delimiter or
+   end of string NUL character.
+
+   Note that if the string is empty, or starts with the delimiter,
+   we return an empty string and *nextP == tokenStart, i.e. *nextP
+   doesn't necessarily advance.
+-----------------------------------------------------------------------------*/
+    char * token;
+    const char * cursor;
+    unsigned int charCount;
+
+    /* Run through the token, counting characters */
+
+    charCount = 0;        /* initial value */
+    cursor = tokenStart;  /* initial value */
+    *errorP = NULL;       /* initial value */
+
+    while (*cursor != delimiter && *cursor != '\0' && !*errorP) {
+        if (*cursor == '\\') {
+            ++cursor;
+            if (*cursor == '\0')
+                asprintfN(errorP, "string ends with an escape character (\\)");
+        } else {
+            ++cursor;
+            ++charCount;
+        }
+    }
+    if (!*errorP) {
+        token = malloc(charCount + 1);
+        if (token == NULL)
+            asprintfN(errorP, "Could not allocate %u bytes of memory "
+                      "to parse a string",
+                      charCount + 1);
+        else {
+            /* Go back and do it again, this time copying the characters */
+            charCount = 0;
+            cursor = tokenStart;
+
+            while (*cursor != delimiter && *cursor != '\0') {
+                if (*cursor == '\\')
+                    ++cursor;
+
+                assert(*cursor != '\0');
+
+                token[charCount++] = *cursor++;
+            }
+            token[charCount] = '\0';
+            
+            *tokenP = token;
+            *nextP = cursor;
+        }
+    }
+}
+
+
+
+
+
+
+
diff --git a/lib/util/token.h b/lib/util/token.h
new file mode 100644
index 00000000..292a9164
--- /dev/null
+++ b/lib/util/token.h
@@ -0,0 +1,11 @@
+#ifndef TOKEN_H_INCLUDE
+#define TOKEN_H_INCLUDE
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP);
+
+#endif
diff --git a/other/pambayer.c b/other/pambayer.c
index f8ce0db8..9ff90a51 100644
--- a/other/pambayer.c
+++ b/other/pambayer.c
@@ -42,18 +42,19 @@ enum bayerType {
 struct cmdlineInfo {
     const char * inputFilespec;
     enum bayerType bayerType;
+    unsigned int nointerpolate;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -67,12 +68,14 @@ parseCommandLine(int argc, char ** argv,
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "type",     OPT_UINT, &type,
             &typeSpec, 0);
+    OPTENT3(0, "nointerpolate", OPT_FLAG, NULL,
+            &cmdlineP->nointerpolate, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -102,28 +105,47 @@ calc_4(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
     X . X
     . . .
     X . X
+
+  For the Plane 'plane' sample values, an even pixel of outtuples[] gets the
+  same value as intuples[][].  An odd pixel of outtuples[] gets the mean of
+  the four surrounding even pixels, north, south, east, and west.  But zero if
+  Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     
-    for (y = yoffset; y < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x][plane] = intuples[y][x][0];
-            outtuples[y][x + 1][plane] =
-                (intuples[y][x][0] + intuples[y][x + 2][0]) / 2;
+    /* Do the even rows -- the even column pixels get copied from the input,
+       while the odd column pixels get the mean of adjacent even ones
+    */
+    for (row = yoffset; row < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col][plane] = intuples[row][col][0];
+            outtuples[row][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col][0] + intuples[row][col + 2][0]) / 2;
         }
     }
-    for (y = yoffset; y + 2 < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x < pamP->width; ++x)
-            outtuples[y + 1][x][plane] =
-                (outtuples[y][x][plane] + outtuples[y + 2][x][plane]) / 2;
+
+    /* Do the odd rows -- every pixel is the mean of the one above and below */
+    for (row = yoffset; row + 2 < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col < pamP->width; ++col) {
+            outtuples[row + 1][col][plane] = 
+                noInterpolation ?
+                0 :
+                (outtuples[row][col][plane] +
+                 outtuples[row + 2][col][plane]) / 2;
+        }
     }
 }
 
@@ -134,25 +156,37 @@ calc_5(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
    . X .
    X . X
    . X .
+
+  For the Plane 'plane' sample values, an pixel on an even diagonal of
+  outtuples[] gets the same value as intuples[][].  An pixel on an odd
+  diagonal gets the mean of the four surrounding even pixels, north,
+  south, east, and west.  But zero if Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     unsigned int j;
 
     j = 0;  /* initial value */
 
-    for (y = yoffset; y + 2 < pamP->height; ++y) {
-        unsigned int x;
-        for (x = xoffset + j; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x + 1][plane] = intuples[y][x + 1][0];
-            outtuples[y + 1][x + 1][plane] = 
-                (intuples[y][x + 1][0] + intuples[y + 1][x][0] +
-                 intuples[y + 2][x + 1][0] + intuples[y + 1][x + 2][0]) / 4;
+    for (row = yoffset; row + 2 < pamP->height; ++row) {
+        unsigned int col;
+        for (col = xoffset + j; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col + 1][plane] = intuples[row][col + 1][0];
+            outtuples[row + 1][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col + 1][0] +
+                 intuples[row + 1][col][0] +
+                 intuples[row + 2][col + 1][0] +
+                 intuples[row + 1][col + 2][0]) / 4;
         }
         j = 1 - j;
     }
@@ -167,6 +201,7 @@ struct compAction {
                  tuple **           const intuples,
                  tuple **           const outtuples,
                  unsigned int       const plane,
+                 bool               const noInterpolation,
                  unsigned int       const xoffset,
                  unsigned int       const yoffset);
 };
@@ -260,7 +295,7 @@ actionTableForType(enum bayerType const bayerType) {
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
@@ -271,8 +306,8 @@ main(int argc, char **argv) {
     const struct compAction * compActionTable;
     unsigned int plane;
 
-    pnm_init(&argc, argv);
-
+    pm_proginit(&argc, argv);
+    
     parseCommandLine(argc, argv, &cmdline);
     
     ifP = pm_openr(cmdline.inputFilespec);
@@ -289,6 +324,7 @@ main(int argc, char **argv) {
         struct compAction const compAction = compActionTable[plane];
 
         compAction.calc(&inpam, intuples, outtuples, plane,
+                        cmdline.nointerpolate,
                         compAction.xoffset, compAction.yoffset);
     }
     pnm_writepam(&outpam, outtuples);
diff --git a/version.mk b/version.mk
index 2e0ee63f..9cc690c7 100644
--- a/version.mk
+++ b/version.mk
@@ -1,4 +1,4 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 48
-NETPBM_POINT_RELEASE = 4
+NETPBM_MINOR_RELEASE = 49
+NETPBM_POINT_RELEASE = 0