about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-15 03:34:46 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-12-15 03:34:46 +0000
commit7d8b417a007bc1f33d855cf482ad9d45e313ebb6 (patch)
treeaa4b281ebf3aecf8a466e7e265dbbb8c02b868f4 /editor
parent57932708c852eea264011f8019acf39f4a997252 (diff)
downloadnetpbm-mirror-7d8b417a007bc1f33d855cf482ad9d45e313ebb6.tar.gz
netpbm-mirror-7d8b417a007bc1f33d855cf482ad9d45e313ebb6.tar.xz
netpbm-mirror-7d8b417a007bc1f33d855cf482ad9d45e313ebb6.zip
finish Pamdither
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1053 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r--editor/Makefile2
-rw-r--r--editor/pamdither.c368
2 files changed, 257 insertions, 113 deletions
diff --git a/editor/Makefile b/editor/Makefile
index bc0f5913..83e0b0b8 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -17,7 +17,7 @@ SUBDIRS = specialty
 # build.
 
 PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
-	       pamdice pamditherbw pamedge \
+	       pamdice pamdither pamditherbw pamedge \
 	       pamenlarge \
 	       pamflip pamfunc pammasksharpen \
 	       pamperspective \
diff --git a/editor/pamdither.c b/editor/pamdither.c
index 13338cc3..3b8d121b 100644
--- a/editor/pamdither.c
+++ b/editor/pamdither.c
@@ -7,9 +7,11 @@
 
   This is meant to replace Ppmdither by Christos Zoulas, 1991.
 =============================================================================*/
+#include <assert.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
 
@@ -21,23 +23,30 @@
 #define MAX_DITH_POWER ((sizeof(unsigned int)*8 - 1) / 2)
 
 
+struct colorResolution {
+    unsigned int c[3];
+        /* comp[PAM_RED_PLANE] is number of distinct red levels, etc. */
+};
+
+#define RED PAM_RED_PLANE
+#define GRN PAM_GRN_PLANE
+#define BLU PAM_BLU_PLANE
+
 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 */
     unsigned int dim;
-    unsigned int red;
-    unsigned int green;
-    unsigned int blue;
+    struct colorResolution colorRes;
     unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -55,7 +64,7 @@ parseCommandLine (int argc, char ** argv,
 
     unsigned int option_def_index;
 
-    unsigned int dimSpec, redSpec, grnSpec, bluSpec;
+    unsigned int dimSpec, redSpec, greenSpec, blueSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
     
@@ -63,19 +72,19 @@ parseCommandLine (int argc, char ** argv,
     OPTENT3(0, "dim",          OPT_UINT, 
             &cmdlineP->dim,            &dimSpec,                  0);
     OPTENT3(0, "red",          OPT_UINT, 
-            &cmdlineP->red,            &redSpec,                  0);
+            &cmdlineP->colorRes.c[RED],   &redSpec,       0);
     OPTENT3(0, "green",        OPT_UINT, 
-            &cmdlineP->green,          &greenSpec,                  0);
-    OPTENT3(0, "blue",         OPT_UINT, 
-            &cmdlineP->blue,           &blueSpec,                  0);
-    OPTENT3(0, "verbose",      OPT_FLAG,   NULL,                  
+            &cmdlineP->colorRes.c[GRN],   &greenSpec,     0);
+    OPTENT3(0, "blue",         OPT_UINT,
+            &cmdlineP->colorRes.c[BLU],   &blueSpec,      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 );
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!dimSpec)
@@ -83,81 +92,144 @@ parseCommandLine (int argc, char ** argv,
 
     if (cmdlineP->dim > MAX_DITH_POWER)
         pm_error("Dithering matrix power %u (-dim) is too large.  "
-                 "Must be <= %d",
-                 dithPower, MAX_DITH_POWER);
+                 "Must be <= %u",
+                 cmdlineP->dim, MAX_DITH_POWER);
         
-    if (!redSpec)
-        cmdlineP->red = 2;
-    if (!greenSpec)
-        cmdlineP->green = 2;
-    if (!blueSpec)
-        cmdlineP->blue = 2;
+    if (redSpec) {
+        if (cmdlineP->colorRes.c[RED] < 2)
+            pm_error("-red must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[RED]);
+    } else
+        cmdlineP->colorRes.c[RED] = 5;
+
+    if (greenSpec) {
+        if (cmdlineP->colorRes.c[GRN] < 2)
+            pm_error("-green must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[GRN]);
+    } else
+        cmdlineP->colorRes.c[GRN] = 9;
+
+    if (blueSpec) {
+        if (cmdlineP->colorRes.c[BLU] < 2)
+            pm_error("-blue must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[BLU]);
+    } else
+        cmdlineP->colorRes.c[BLU] = 5;
 
     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 = "-";
+        cmdlineP->inputFileName = "-";
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 }
 
 
 
 typedef struct {
+/*----------------------------------------------------------------------------
+   A scaler object scales a red/green/blue triple, each component having its
+   own maxval, to a tuple having another maxval.  That maxval is the same for
+   all three components.  The input and output maxvals are characteristic of
+   the scaler.
+
+   Example: The scaler scales from a red value of 0-3, green value of
+   0-3, and blue value of 0-1 to a tuple with maxval 255.  So you can
+   ask it to scale (1,1,1) and it responds with (85, 85, 255).
+-----------------------------------------------------------------------------*/
+    struct colorResolution colorRes;
+        /* Number of values of each color component possible, i.e. maxval
+           plus 1
+        */
     tuple * out;
-    unsigned int redCt;
-    unsigned int grnCt;
-    unsigned int bluCt;
+        /* Malloced array that provides the scaled output when indexed by a
+           certain function (see scaler_scale()) of the input red, green, and
+           blue values.
+        */
 } scaler;    
 
 
-static unsigned int
-scaler_index(unsigned int red,
-             unsigned int grn,
-             unsigned int blu) {
 
-    return ((red * scalerP->grnCt) + grn) * scalerP->bluCt;
+static tuple *
+allocScalerMap(unsigned int const size) {
+    /* The tuple row data structure starts with 'size' pointers to
+       the tuples, immediately followed by the 'size' tuples
+       themselves.  Each tuple consists of 3 samples.  
+    */
+
+    unsigned int const depth = 3;
+    unsigned int const bytesPerTuple = depth * sizeof(sample);
+
+    tuple * map;
+
+    map = malloc(size * (sizeof(tuple *) + bytesPerTuple));
+                      
+    if (map != NULL) {
+        /* Now we initialize the pointers to the individual tuples
+           to make this a regulation C two dimensional array.  
+        */
+        char * p;
+        unsigned int i;
+        
+        p = (char*) (map + size);  /* location of Tuple 0 */
+        for (i = 0; i < size; ++i) {
+            map[i] = (tuple) p;
+            p += bytesPerTuple;
+        }
+    }
+    return map;
 }
 
 
 
 static void
-scaler_create(sample       const outputMaxval,
-              unsigned int const redCt,
-              unsigned int const grnCt,
-              unsigned int const bluCt,
-              scaler **    const scalerPP) {
+scaler_create(sample                 const outputMaxval,
+              struct colorResolution const colorRes,
+              scaler **              const scalerPP) {
 
     scaler * scalerP;
+    unsigned int mapSize;
     
-    if (UINT_MAX / redCt / grnCt / bluCt < 1)
+    if (UINT_MAX / colorRes.c[RED] / colorRes.c[GRN] / colorRes.c[BLU] < 1)
         pm_error("red/green/blue dimensions %u/%u/%u is uncomputably large",
-                 redCt, grnCt, bluCt);
+                 colorRes.c[RED], colorRes.c[GRN], colorRes.c[BLU]);
 
+    {
+        unsigned int plane;
+        for (plane = 0, mapSize = 1; plane < 3; ++plane)
+            mapSize *= colorRes.c[plane];
+    }
     MALLOCVAR_NOFAIL(scalerP);
 
-    MALLOCARRAY(scalerP->out, redCt * grnCt * bluCt);
+    scalerP->colorRes = colorRes;
+
+    scalerP->out = allocScalerMap(mapSize);
 
     if (scalerP->out == NULL)
         pm_error("Unable to allocate memory for %u colors "
                  "(%u red x %u green x %u blue)",
-                 redCt * grnCt * bluCt, redCt, grnCt, bluCt);
+                 mapSize, colorRes.c[RED], colorRes.c[GRN], colorRes.c[BLU]);
 
     {
         unsigned int r;
-        for (r = 0; r < redCt; ++r) {
+        for (r = 0; r < colorRes.c[RED]; ++r) {
             unsigned int g;
-            for (g = 0; g < grnCt; ++g) {
+            for (g = 0; g < colorRes.c[GRN]; ++g) {
                 unsigned int b;
-                for (b = 0; b < bluCt; ++b) {
-                    unsigned int const index = ((r * grnCt) + g) * bluCt;
+                for (b = 0; b < colorRes.c[BLU]; ++b) {
+                    unsigned int const index =
+                        (r * colorRes.c[GRN] + g)
+                        * colorRes.c[BLU] + b;
                     tuple const t = scalerP->out[index];
-
-                    t[PAM_RED_PLANE] = r * outputMaxval / (redCt - 1);
-                    t[PAM_GRN_PLANE] = g * outputMaxval / (grnCt - 1);
-                    t[PAM_BLU_PLANE] = b * outputMaxval / (bluCt - 1);
+                         
+                    t[PAM_RED_PLANE] =
+                        r * outputMaxval / (colorRes.c[RED] - 1);
+                    t[PAM_GRN_PLANE] = 
+                        g * outputMaxval / (colorRes.c[GRN] - 1);
+                    t[PAM_BLU_PLANE] =
+                        b * outputMaxval / (colorRes.c[BLU] - 1);
                 }
             }
         }
@@ -177,13 +249,19 @@ scaler_destroy(scaler * const scalerP) {
 
 
 
-sample
+static tuple
 scaler_scale(const scaler * const scalerP,
              unsigned int   const red,
              unsigned int   const grn,
              unsigned int   const blu) {
 
-    unsigned int const index = ((red * scalerP->grnCt) + grn) * scalerP->bluCt;
+    unsigned int const index =
+        ((red * scalerP->colorRes.c[GRN]) + grn)
+        * scalerP->colorRes.c[BLU] + blu;
+
+    assert(red < scalerP->colorRes.c[RED]);
+    assert(grn < scalerP->colorRes.c[GRN]);
+    assert(blu < scalerP->colorRes.c[BLU]);
 
     return scalerP->out[index];
 }
@@ -210,7 +288,7 @@ dither(sample       const p,
         /* This is the maxval for an intensity that an entire dithered
            square can represent.
         */
-    pixval const pScaled = ditherSquareMaxval * p / maxval;
+    unsigned int const pScaled = ditherSquareMaxval * p / maxval;
         /* This is the input intensity P expressed with a maxval of
            'ditherSquareMaxval'
         */
@@ -226,8 +304,8 @@ dither(sample       const p,
 
 
 static unsigned int
-dithValue(unsigned int const y,
-          unsigned int const x,
+dithValue(unsigned int const yArg,
+          unsigned int const xArg,
           unsigned int const dithPower) { 
 /*----------------------------------------------------------------------------
   Return the value of a dither matrix which is 2 ** dithPower elements
@@ -244,12 +322,15 @@ dithValue(unsigned int const y,
           This whole thing interleaves a checkerboard pattern and y's bits
           which is what you want.
         */
+    unsigned int x, y;
     unsigned int i;
 
-    for (i = 0, d = 0; i < dithPower; i++, x >>= 1, y >>= 1)
+    for (i = 0, d = 0, x = xArg, y = yArg;
+         i < dithPower;
+         ++i, x >>= 1, y >>= 1)
         d = (d << 2) | (((x & 1) ^ (y & 1)) << 1) | (y & 1);
 
-    return(d);
+    return d;
 }
 
 
@@ -300,13 +381,77 @@ dithMatrix(unsigned int const dithPower) {
 }
 
 
+
+static void
+validateNoDitherOverflow(unsigned int           const ditherMatrixArea,
+                         struct pam *           const inpamP,
+                         struct colorResolution const colorRes) {
+/*----------------------------------------------------------------------------
+   Validate that we'll be able to do the dithering calculations based on
+   the parameters above without busting out of an integer.
+-----------------------------------------------------------------------------*/
+    unsigned int maxDitherMaxval;
+    unsigned int plane;
+
+    for (plane = 0, maxDitherMaxval = 1; plane < 0; ++plane) {
+        assert(colorRes.c[plane] >= 2);
+        maxDitherMaxval = MAX(maxDitherMaxval, colorRes.c[plane]-1);
+    }
+
+    if (UINT_MAX / ditherMatrixArea / inpamP->maxval / maxDitherMaxval < 1)
+        pm_error("Numbers are too large to compute.  You must reduce "
+                 "the dither power, the input maxval, or the number of "
+                 "component levels in the output");
+}
+
+
+
 static void
-ditherImage(struct pam *   const inpamP,
-            const scaler * const scalerP,
-            unsigned int   const dithPower,
-            struct pam *   const outpamP;
-            tuple **       const inTuples,
-            tuple ***      const outTuplesP) {
+ditherRow(struct pam *           const inpamP,
+          const tuple *          const inrow,
+          const scaler *         const scalerP,
+          unsigned int **        const ditherMatrix,
+          unsigned int           const ditherMatrixArea,
+          struct colorResolution const colorRes,
+          unsigned int           const row,
+          unsigned int           const modMask,
+          struct pam *           const outpamP,
+          tuple *                const outrow) {
+
+    unsigned int col;
+
+    for (col = 0; col < inpamP->width; ++col) {
+        unsigned int const d =
+            ditherMatrix[row & modMask][(inpamP->width-col-1) & modMask];
+
+        unsigned int dithered[3];
+        unsigned int plane;
+
+        assert(inpamP->depth >= 3);
+
+        for (plane = 0; plane < 3; ++plane)
+            dithered[plane] =
+                dither(inrow[col][plane], inpamP->maxval, d,
+                       colorRes.c[plane]-1, ditherMatrixArea);
+
+        pnm_assigntuple(outpamP,
+                        outrow[col],
+                        scaler_scale(scalerP,
+                                     dithered[PAM_RED_PLANE],
+                                     dithered[PAM_GRN_PLANE],
+                                     dithered[PAM_BLU_PLANE]));
+    }
+}
+
+
+
+static void
+ditherImage(struct pam *           const inpamP,
+            const scaler *         const scalerP,
+            unsigned int           const dithPower,
+            struct colorResolution const colorRes,
+            struct pam *           const outpamP,
+            tuple ***              const outTuplesP) {
 
     unsigned int const dithDim = 1 << dithPower;
     unsigned int const ditherMatrixArea = SQR(dithDim);
@@ -317,91 +462,90 @@ ditherImage(struct pam *   const inpamP,
        */
     unsigned int ** const ditherMatrix = dithMatrix(dithPower);
 
-    tuple ** ouputTuples;
+    tuple * inrow;
+    tuple ** outTuples;
     unsigned int row; 
+    struct pam ditherPam;
+        /* Describes the tuples that ditherRow() sees */
 
     assert(dithPower < sizeof(unsigned int) * 8);
     assert(UINT_MAX / dithDim >= dithDim);
+    
+    validateNoDitherOverflow(ditherMatrixArea, inpamP, colorRes);
 
-    outTuples = ppm_allocpamarray(outpamP);
-
-    for (row = 0; row < inpamP->height; ++row) {
-        unsigned int col;
-        for (col = 0; col < inpamP->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(inpamP->depth >= 3);
-
-            for (plane = 0; plane < 3; ++plane)
-                dithered[plane] =
-                    dither(inputTuple[plane], inpamP->maxval, d,
-                           outpamP->maxval, ditherMatrixArea);
-
-            pnm_assignTuple(outpamP,
-                            outTuples[row][col],
-                            scaler_scale(scalerP,
-                                         dithered[RED_PLANE],
-                                         dithered[GRN_PLANE],
-                                         dithered[BLU_PLANE]));
-        }
-    }
-    free(ditherMatrix);
-    *outTuplesP = outTuples;
-}
+    inrow = pnm_allocpamrow(inpamP);
 
+    outTuples = pnm_allocpamarray(outpamP);
 
+    /* We will modify the input to promote it to depth 3 */
+    ditherPam = *inpamP;
+    ditherPam.depth = 3;
 
-static void
-getColormap(const char * const mapFileName,
-            tuple **     const colormapP) {
+    for (row = 0; row < inpamP->height; ++row) {
+        pnm_readpamrow(inpamP, inrow);
 
-    TODO("write this");
+        pnm_makerowrgb(inpamP, inrow);
 
+        ditherRow(&ditherPam, inrow, scalerP, ditherMatrix, ditherMatrixArea,
+                  colorRes, row, modMask,
+                  outpamP, outTuples[row]);
+    }
+    free(ditherMatrix);
+    pnm_freepamrow(inrow);
+    *outTuplesP = outTuples;
 }
 
 
 
 int
-main(int argc,
-     char ** argv) {
+main(int           argc,
+     const char ** argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    tuple ** inTuples;        /* Input image */
     tuple ** outTuples;        /* Output image */
     scaler * scalerP;
-    int cols, rows;
-    pixval maxval;  /* Maxval of the input image */
+    struct pam inpam;
+    struct pam outpam;
 
     pm_proginit(&argc, argv);
 
-    parseCommandLine(&argc, &argv);
+    parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    inTuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
 
-    pm_close(ifP);
-
-    outpam = inpam;
-    outpam.file = stdout;
-
-    scaler_create(outpam.maxval, cmdline.red, cmdline.green, cmdline.blue,
-                  &scalerP);
-
-    ditherImage(inpam, scalerP, dithPower, inTuples, &outTuples);
+    pnm_setminallocationdepth(&inpam, 3);
+    
+    outpam.size               = sizeof(outpam);
+    outpam.len                = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file               = stdout;
+    outpam.width              = inpam.width;
+    outpam.height             = inpam.height;
+    outpam.depth              = 3;
+    outpam.maxval             =
+        pm_lcm(cmdline.colorRes.c[RED]-1,
+               cmdline.colorRes.c[GRN]-1,
+               cmdline.colorRes.c[BLU]-1,
+               PPM_MAXMAXVAL);
+    outpam.bytes_per_sample   = inpam.bytes_per_sample;
+    STRSCPY(outpam.tuple_type, "RGB");
+    outpam.format             = RPPM_FORMAT;
+    outpam.plainformat        = false;
+
+    scaler_create(outpam.maxval, cmdline.colorRes, &scalerP);
+
+    ditherImage(&inpam, scalerP, cmdline.dim, cmdline.colorRes,
+                &outpam, &outTuples);
 
     pnm_writepam(&outpam, outTuples);
 
     scaler_destroy(scalerP);
 
-    pnm_freepamarray(inTuples, &inpam);
     pnm_freepamarray(outTuples, &outpam);
 
+    pm_close(ifP);
+
     return 0;
 }