about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/HISTORY3
-rw-r--r--editor/pgmmedian.c230
2 files changed, 142 insertions, 91 deletions
diff --git a/doc/HISTORY b/doc/HISTORY
index 0b4e106b..8ca99e69 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -14,6 +14,9 @@ not yet  BJH  Release 10.98.00
               program.  And the broken code is inconsistent with new random
               number logic elsewhere in the package.
 
+              pgmmedian: fix crash when median matrix is wider or higher than
+              the input image.
+
               picttoppm: Fix incorrect output for 32 bit per pixel images
               that have only 3 planes.  Broken in Netpbm 10.34 (June 2006).
 
diff --git a/editor/pgmmedian.c b/editor/pgmmedian.c
index 4bfc81d6..9cebeac8 100644
--- a/editor/pgmmedian.c
+++ b/editor/pgmmedian.c
@@ -33,7 +33,7 @@
 #include "mallocvar.h"
 #include "nstring.h"
 
-enum medianMethod {MEDIAN_UNSPECIFIED, SELECT_MEDIAN, HISTOGRAM_SORT_MEDIAN};
+enum MedianMethod {MEDIAN_UNSPECIFIED, SELECT_MEDIAN, HISTOGRAM_SORT_MEDIAN};
 #define MAX_MEDIAN_TYPES      2
 
 struct CmdlineInfo {
@@ -44,17 +44,22 @@ struct CmdlineInfo {
     unsigned int width;
     unsigned int height;
     unsigned int cutoff;
-    enum medianMethod type;
+    enum MedianMethod type;
 };
 
 
-/* Global variables common to each median sort routine. */
 static int const forceplain = 0;
-static int format;
-static gray maxval;
-static gray **grays;
-static gray *grayrow;
-static int ccolso2, crowso2;
+
+
+
+/* Global variables common to each median sort routine. */
+static gray ** grays;
+    /* The convolution buffer.  This is a circular buffer that contains the
+       rows of the input image that are being convolved into the current 
+       output row.
+    */
+static gray * grayrow;
+    /* A buffer for building the current output row */
 
 
 
@@ -133,6 +138,32 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static void
+setWindow(gray **      const convBuffer,
+          unsigned int const crows,
+          gray **      const cgrayrow,
+          unsigned int const lastRow) {
+/*----------------------------------------------------------------------------
+   Set 'cgrayrow' so it points into the circular buffer 'convBuffer' such
+   that cgrayrow[0] is the topmost row in the buffer, given that the
+   bottommost row in the buffer is row number 'lastRow'.
+-----------------------------------------------------------------------------*/
+    unsigned int const windowTopRow = (lastRow + 1) % crows;
+
+    unsigned int bufferRow;
+    unsigned int wrow;
+
+    wrow = 0;
+
+    for (bufferRow = windowTopRow; bufferRow < crows; ++wrow, ++bufferRow)
+        cgrayrow[wrow] = grays[bufferRow];
+
+    for (bufferRow = 0; bufferRow < windowTopRow; ++wrow, ++bufferRow)
+        cgrayrow[wrow] = grays[bufferRow];
+}
+
+
+
+static void
 select489(gray * const a,
           int *  const parray,
           int    const n,
@@ -192,25 +223,30 @@ selectMedian(FILE *       const ifP,
              unsigned int const crows,
              unsigned int const cols,
              unsigned int const rows,
+             int          const format,
+             gray         const maxval,
              unsigned int const median,
              unsigned int const firstRow) {
 
+    unsigned int const ccolso2 = ccols / 2;
+    unsigned int const crowso2 = crows / 2;
+
     unsigned int const numValues = crows * ccols;
 
     unsigned int col;
     gray * garray;  /* Array of the currently gray values */
     int * parray;
     int * subcol;
-    gray ** rowptr;
+    gray ** cgrayrow;
     unsigned int row;
 
     garray = pgm_allocrow(numValues);
 
-    MALLOCARRAY(rowptr, crows);
+    MALLOCARRAY(cgrayrow, crows);
     MALLOCARRAY(parray, numValues);
     MALLOCARRAY(subcol, cols);
 
-    if (rowptr == NULL || parray == NULL || subcol == NULL)
+    if (cgrayrow == NULL || parray == NULL || subcol == NULL)
         pm_error("Unable to allocate memory");
 
     for (col = 0; col < cols; ++col)
@@ -218,29 +254,22 @@ selectMedian(FILE *       const ifP,
 
     /* Apply median to main part of image. */
     for (row = firstRow; row < rows; ++row) {
-        int crow;
-        int rownum, irow, temprow;
+        unsigned int crow;
         unsigned int col;
 
         pgm_readpgmrow(ifP, grays[row % crows], cols, maxval, format);
 
-        /* Rotate pointers to rows, so rows can be accessed in order. */
-        temprow = (row + 1) % crows;
-        rownum = 0;
-        for (irow = temprow; irow < crows; ++rownum, ++irow)
-            rowptr[rownum] = grays[irow];
-        for (irow = 0; irow < temprow; ++rownum, ++irow)
-            rowptr[rownum] = grays[irow];
+        setWindow(grays, crows, cgrayrow, row);
 
         for (col = 0; col < cols; ++col) {
             if (col < ccolso2 || col >= cols - ccolso2) {
-                grayrow[col] = rowptr[crowso2][col];
+                grayrow[col] = cgrayrow[crowso2][col];
             } else if (col == ccolso2) {
                 unsigned int const leftcol = col - ccolso2;
                 unsigned int i;
                 i = 0;
                 for (crow = 0; crow < crows; ++crow) {
-                    gray * const temprptr = rowptr[crow] + leftcol;
+                    gray * const temprptr = cgrayrow[crow] + leftcol;
                     unsigned int ccol;
                     for (ccol = 0; ccol < ccols; ++ccol) {
                         garray[i] = *(temprptr + ccol);
@@ -255,7 +284,7 @@ selectMedian(FILE *       const ifP,
                 unsigned int crow;
                 unsigned int tsum;
                 for (crow = 0, tsum = 0; crow < crows; ++crow, tsum += ccols)
-                    garray[tsum + subcol[col]] = *(rowptr[crow] + addcol );
+                    garray[tsum + subcol[col]] = *(cgrayrow[crow] + addcol );
                 select489( garray, parray, numValues, median );
                 grayrow[col] = garray[parray[median]];
             }
@@ -264,7 +293,7 @@ selectMedian(FILE *       const ifP,
     }
     free(subcol);
     free(parray);
-    free(rowptr);
+    free(cgrayrow);
     pgm_freerow(garray);
 }
 
@@ -276,25 +305,32 @@ histogramSortMedian(FILE *       const ifP,
                     unsigned int const crows,
                     unsigned int const cols,
                     unsigned int const rows,
+                    int          const format,
+                    gray         const maxval,
                     unsigned int const median,
                     unsigned int const firstRow) {
 
+    unsigned int const ccolso2 = ccols / 2;
+    unsigned int const crowso2 = crows / 2;
     unsigned int const histmax = maxval + 1;
 
     unsigned int * hist;
     unsigned int mdn, ltmdn;
     gray * leftCol;
     gray * rghtCol;
-    gray ** rowptr;
+    gray ** cgrayrow;
+        /* The window of the image currently being convolved, with
+           cgrayrow[0] being the top row of the window.  Pointers into grays[]
+        */
     unsigned int row;
         /* Row number in input -- bottommost row in the window we're currently
            convolving
         */
 
-    MALLOCARRAY(rowptr, crows);
+    MALLOCARRAY(cgrayrow, crows);
     MALLOCARRAY(hist, histmax);
 
-    if (rowptr == NULL || hist == NULL)
+    if (cgrayrow == NULL || hist == NULL)
         pm_error("Unable to allocate memory");
 
     leftCol = pgm_allocrow(crows);
@@ -303,9 +339,6 @@ histogramSortMedian(FILE *       const ifP,
     /* Apply median to main part of image. */
     for (row = firstRow; row < rows; ++row) {
         unsigned int col;
-        unsigned int temprow;
-        unsigned int rownum;
-        unsigned int irow;
         unsigned int i;
         /* initialize hist[] */
         for (i = 0; i < histmax; ++i)
@@ -313,24 +346,18 @@ histogramSortMedian(FILE *       const ifP,
 
         pgm_readpgmrow(ifP, grays[row % crows], cols, maxval, format);
 
-        /* Rotate pointers to rows, so rows can be accessed in order. */
-        temprow = (row + 1) % crows;
-        rownum = 0;
-        for (irow = temprow; irow < crows; ++rownum, ++irow)
-            rowptr[rownum] = grays[irow];
-        for (irow = 0; irow < temprow; ++rownum, ++irow)
-            rowptr[rownum] = grays[irow];
+        setWindow(grays, crows, cgrayrow, row);
 
         for (col = 0; col < cols; ++col) {
             if (col < ccolso2 || col >= cols - ccolso2)
-                grayrow[col] = rowptr[crowso2][col];
+                grayrow[col] = cgrayrow[crowso2][col];
             else if (col == ccolso2) {
                 unsigned int const leftcol = col - ccolso2;
                 unsigned int crow;
                 i = 0;
                 for (crow = 0; crow < crows; ++crow) {
                     unsigned int ccol;
-                    gray * const temprptr = rowptr[crow] + leftcol;
+                    gray * const temprptr = cgrayrow[crow] + leftcol;
                     for (ccol = 0; ccol < ccols; ++ccol) {
                         gray const g = *(temprptr + ccol);
                         ++hist[g];
@@ -350,8 +377,8 @@ histogramSortMedian(FILE *       const ifP,
                 unsigned int const addcol = col + ccolso2;
                 unsigned int crow;
                 for (crow = 0; crow < crows; ++crow) {
-                    leftCol[crow] = *(rowptr[crow] + subcol);
-                    rghtCol[crow] = *(rowptr[crow] + addcol);
+                    leftCol[crow] = *(cgrayrow[crow] + subcol);
+                    rghtCol[crow] = *(cgrayrow[crow] + addcol);
                 }
                 for (crow = 0; crow < crows; ++crow) {
                     {
@@ -392,33 +419,26 @@ histogramSortMedian(FILE *       const ifP,
     pgm_freerow(leftCol);
     pgm_freerow(rghtCol);
     free(hist);
-    free(rowptr);
+    free(cgrayrow);
 }
 
 
 
-int
-main(int          argc,
-     const char * argv[]) {
+static void
+convolve(FILE *            const ifP,
+         unsigned int      const cols,
+         unsigned int      const rows,
+         gray              const maxval,
+         int               const format,
+         unsigned int      const ccols,
+         unsigned int      const crows,
+         enum MedianMethod const medianMethod,
+         unsigned int      const median) {
+
+    unsigned int const crowso2 = crows / 2;
 
-    struct CmdlineInfo cmdline;
-    FILE * ifP;
-    int cols, rows;
-    int median;
-    enum medianMethod medianMethod;
     unsigned int row;
 
-    pm_proginit(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.inputFileName);
-
-    assert(cmdline.height > 0 && cmdline.width > 0);
-
-    ccolso2 = cmdline.width / 2;
-    crowso2 = cmdline.height / 2;
-
     /* An even-size convolution window is biased toward the top and left.  So
        if it is 8 rows, the window covers 4 rows above the target row and 3
        rows below it, plus the target row itself.  'crowso2' is the number of
@@ -426,19 +446,12 @@ main(int          argc,
        above it and either crowso2 or crowso2-1 rows below it.
     */
 
-    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
-
-    crows = MIN(cmdline.height, rows);
-    ccols = MIN(cmdline.width,  cols);
-
-    pgm_writepgminit(stdout, cols, rows, maxval, forceplain);
-
     /* Allocate space for number of rows in mask size. */
-    grays = pgm_allocarray(cols, cmdline.height);
+    grays = pgm_allocarray(cols, crows);
     grayrow = pgm_allocrow(cols);
 
     /* Prime the convolution window -- fill it except the last row */
-    for (row = 0; row < cmdline.height - 1; ++row)
+    for (row = 0; row < crows - 1; ++row)
         pgm_readpgmrow(ifP, grays[row], cols, maxval, format);
 
     /* Copy the top half out verbatim, since convolution kernel for these rows
@@ -447,26 +460,15 @@ main(int          argc,
     for (row = 0; row < crowso2; ++row)
         pgm_writepgmrow(stdout, grays[row], cols, maxval, forceplain);
 
-    median = (cmdline.height * cmdline.width) / 2;
-
-    /* Choose which sort to run. */
-    if (cmdline.type == MEDIAN_UNSPECIFIED) {
-        if ((maxval / ((cmdline.width * cmdline.height) - 1)) < cmdline.cutoff)
-            medianMethod = HISTOGRAM_SORT_MEDIAN;
-        else
-            medianMethod = SELECT_MEDIAN;
-    } else
-        medianMethod = cmdline.type;
-
     switch (medianMethod) {
     case SELECT_MEDIAN:
-        selectMedian(ifP, cmdline.width, cmdline.height, cols, rows, median,
-                     cmdline.height-1);
+        selectMedian(ifP, ccols, crows, cols, rows, format, maxval,
+                     median, crows-1);
         break;
 
     case HISTOGRAM_SORT_MEDIAN:
-        histogramSortMedian(ifP, cmdline.width, cmdline.height,
-                            cols, rows, median, cmdline.height-1);
+        histogramSortMedian(ifP, ccols, crows, cols, rows, format, maxval,
+                            median, crows-1);
         break;
     case MEDIAN_UNSPECIFIED:
         pm_error("INTERNAL ERROR: median unspecified");
@@ -476,18 +478,64 @@ main(int          argc,
        since convolution kernel for these rows runs off the bottom of the
        image.
     */
-    assert(cmdline.height >= crowso2 + 1);
+    assert(crows >= crowso2 + 1);
 
-    for (row = rows - (cmdline.height-crowso2-1); row < rows; ++row)
-        pgm_writepgmrow(stdout, grays[row % cmdline.height], cols, maxval,
+    for (row = rows - (crows-crowso2-1); row < rows; ++row)
+        pgm_writepgmrow(stdout, grays[row % crows], cols, maxval,
                         forceplain);
 
+    pgm_freearray(grays, crows);
+    pgm_freerow(grayrow);
+}
+
+
+
+int
+main(int          argc,
+     const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    int cols, rows;
+    int format;
+    gray maxval;
+    unsigned int ccols, crows;
+    unsigned int median;
+    enum MedianMethod medianMethod;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    assert(cmdline.height > 0 && cmdline.width > 0);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    ccols = MIN(cmdline.width,  cols+1);
+    crows = MIN(cmdline.height, rows+1);
+
+    pgm_writepgminit(stdout, cols, rows, maxval, forceplain);
+
+    median = (crows * ccols) / 2;
+
+    /* Choose which sort to run. */
+    if (cmdline.type == MEDIAN_UNSPECIFIED) {
+        if ((maxval / ((ccols * crows) - 1)) < cmdline.cutoff)
+            medianMethod = HISTOGRAM_SORT_MEDIAN;
+        else
+            medianMethod = SELECT_MEDIAN;
+    } else
+        medianMethod = cmdline.type;
+
+
+    convolve(ifP, cols, rows, maxval, format, ccols, crows, medianMethod,
+             median);
+
     pm_close(ifP);
     pm_close(stdout);
 
-    pgm_freearray(grays, cmdline.height);
-    pgm_freerow(grayrow);
-
     return 0;
 }