about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2022-08-15 17:56:35 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2022-08-15 17:56:35 +0000
commitb475ca85760cdaeb7ed58f8557dceccf234d735d (patch)
tree5dd2c0be8c8756e957334d7b2effb6c116e6f27d /editor
parent839daa362954ebcf7a44636c8e344bfa89676b30 (diff)
downloadnetpbm-mirror-b475ca85760cdaeb7ed58f8557dceccf234d735d.tar.gz
netpbm-mirror-b475ca85760cdaeb7ed58f8557dceccf234d735d.tar.xz
netpbm-mirror-b475ca85760cdaeb7ed58f8557dceccf234d735d.zip
Produce proper tuple type for PAM output with all visual image input
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@4409 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r--editor/pamcat.c339
1 files changed, 241 insertions, 98 deletions
diff --git a/editor/pamcat.c b/editor/pamcat.c
index 744cb600..1c68a02e 100644
--- a/editor/pamcat.c
+++ b/editor/pamcat.c
@@ -30,12 +30,6 @@ enum PadColorMethod {PAD_BLACK, PAD_WHITE, PAD_AUTO};
   */
 
 
-enum PlanePadMethod {PLANEPAD_ZERO, PLANEPAD_EXTEND};
-  /* The method for adding additional planes when some images have fewer
-     planes than others.  The additional plane is either all zeroes or
-     equal to the highest plane in the original image.
-  */
-
 enum Orientation {TOPBOTTOM, LEFTRIGHT};
   /* Direction of concatenation */
 
@@ -49,7 +43,6 @@ struct CmdlineInfo {
     const char **       inputFileName;
     unsigned int        fileCt;
     enum PadColorMethod padColorMethod;
-    enum PlanePadMethod planePadMethod;
     enum Orientation    orientation;
     enum Justification  justification;
     unsigned int        verbose;
@@ -72,7 +65,7 @@ parseCommandLine(int argc, const char ** const argv,
     unsigned int option_def_index;
 
     unsigned int leftright, topbottom;
-    unsigned int black, white, extendplane;
+    unsigned int black, white;
     unsigned int jtop, jbottom, jleft, jright, jcenter;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
@@ -89,7 +82,6 @@ parseCommandLine(int argc, const char ** const argv,
     OPTENT3(0, "jleft",       OPT_FLAG,   NULL, &jleft,             0);
     OPTENT3(0, "jright",      OPT_FLAG,   NULL, &jright,            0);
     OPTENT3(0, "jcenter",     OPT_FLAG,   NULL, &jcenter,           0);
-    OPTENT3(0, "extendplane", OPT_FLAG,   NULL, &extendplane,       0);
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
 
     opt.opt_table = option_def;
@@ -120,8 +112,6 @@ parseCommandLine(int argc, const char ** const argv,
     else
         cmdlineP->padColorMethod = PAD_AUTO;
 
-    cmdlineP->planePadMethod = extendplane ? PLANEPAD_EXTEND : PLANEPAD_ZERO;
-
     if (jtop + jbottom + jleft + jright + jcenter > 1)
         pm_error("You may specify only one of -jtop, -jbottom, "
                  "-jleft, and -jright");
@@ -183,6 +173,100 @@ parseCommandLine(int argc, const char ** const argv,
 
 
 
+static const char *
+tupletypeX(bool         const allVisual,
+           unsigned int const colorDepth,
+           sample       const maxMaxval,
+           bool         const haveOpacity) {
+
+    const char * retval;
+
+    if (allVisual) {
+        switch (colorDepth) {
+        case 1:
+            if (maxMaxval == 1)
+                retval = haveOpacity ? "BLACKANDWHITE_ALPHA" : "BLACKANDWHITE";
+            else
+                retval = haveOpacity ? "GRAYSCALE_ALPHA"     : "GRAYSCALE";
+            break;
+        case 3:
+            retval = haveOpacity ? "RGB_ALPHA"           : "RGB";
+            break;
+        default:
+            assert(false);
+        }
+    } else
+        retval = "";
+
+    return retval;
+}
+
+
+
+typedef struct {
+    /* This describes a transformation from one tuple type to another,
+       e.g. from BLACKANDWHITE to GRAY_ALPHA.
+
+       For transformations bewteen the defined ones for visual images, the
+       only "up" transformations are covered.
+    */
+    bool mustPromoteColor;
+        /* Plane 0, which is the black/white or grayscale plane and also
+           the red plane must be copied as the red, green, and blue planes
+           (0, 1, and 2).
+        */
+    bool mustPromoteOpacity;
+        /* Plane 1, which is the opacity plane for black and white or
+           grayscale tuples, must be copied as the RGB opacity plane (3).
+        */
+    bool mustCreateOpacity;
+        /* The opacity plane value must be set to opaque */
+
+    bool mustPadZero;
+        /* Where the target tuple type is deeper than the source tuple
+           type, all higher numbered planes must be cleared to zero.
+
+           This is mutually exclusive with the rest of the musts.
+        */
+
+} TtTransform;
+
+
+
+static TtTransform
+ttXformForImg(const struct pam * const inpamP,
+              const struct pam * const outpamP) {
+/*----------------------------------------------------------------------------
+  The transform required to transform tuples of the kind described by *inpamP
+  to tuples of the kind described by *outpamP (e.g. from grayscale to RGB,
+  which involves replicating one plane into three).
+
+  We assume *outpamP tuples are of a type that is at least as expressive than
+  *inpamP tuples.  So e.g. outpamP->tuple_type cannot be "GRAYSCALE" if
+  inpamP->tuple_type is "RGB".
+-----------------------------------------------------------------------------*/
+    TtTransform retval;
+
+    if (inpamP->visual && outpamP->visual) {
+        retval.mustPromoteColor   =
+            (outpamP->color_depth > inpamP->color_depth);
+        retval.mustPromoteOpacity =
+            (outpamP->color_depth > inpamP->color_depth &&
+             (outpamP->have_opacity && inpamP->have_opacity));
+        retval.mustCreateOpacity  =
+            (outpamP->have_opacity && !inpamP->have_opacity);
+        retval.mustPadZero = false;
+    } else {
+        retval.mustPromoteColor   = false;
+        retval.mustPromoteOpacity = false;
+        retval.mustCreateOpacity  = false;
+        retval.mustPadZero        = true;
+    }
+    return retval;
+}
+
+
+
 static void
 reportPlans(unsigned int       const fileCt,
             const struct pam * const outpamP) {
@@ -216,12 +300,10 @@ reportPlans(unsigned int       const fileCt,
             pm_message("Output format: PAM");
 
             if (strlen(outpamP->tuple_type) > 0)
-
-                pm_message("Output tuple type (same as all inputs): '%s'",
-                           outpamP->tuple_type);
+                pm_message("Output tuple type: '%s'", outpamP->tuple_type);
             else
                 pm_message("Output tuple type is null string because "
-                           "input images have various tuple types");
+                           "input images have various non-visual tuple types");
             break;
         }
     }
@@ -236,17 +318,21 @@ computeOutputParms(unsigned int       const fileCt,
                    bool               const verbose,
                    struct pam *       const outpamP) {
 
-    double newCols, newRows, newDepth;
-    sample newMaxval;
+    double newCols, newRows;
+    unsigned int maxDepth;
+    sample maxMaxval;
     int newFormat;
-    const char * newTupletype;
-    bool tupleTypeVaries;
-        /* We've seen multiple tuple types among the input images */
+    const char * firstTupletype;
+    bool allSameTt;
+    bool allVisual;
+    unsigned int maxColorDepth;
+    bool haveOpacity;
     unsigned int i;
 
-    for (i = 0, newCols = 0, newRows = 0, newDepth = 0, newMaxval = 0,
+    for (i = 0, newCols = 0, newRows = 0, maxDepth = 0, maxMaxval = 0,
              newFormat = 0,
-             newTupletype = NULL, tupleTypeVaries = false;
+             allVisual = true, maxColorDepth = 0, haveOpacity = false,
+             firstTupletype = NULL, allSameTt = true;
          i < fileCt;
          ++i) {
 
@@ -262,22 +348,30 @@ computeOutputParms(unsigned int       const fileCt,
             newCols = MAX(newCols, inpamP->width);
             break;
         }
-        if (newTupletype) {
-            if (!streq(inpamP->tuple_type, newTupletype))
-                tupleTypeVaries = true;
-        } else
-            newTupletype = inpamP->tuple_type;
 
-        newDepth  = MAX(newDepth,  inpamP->depth);
-        newMaxval = MAX(newMaxval, inpamP->maxval);
+        if (!firstTupletype)
+            firstTupletype = inpamP->tuple_type;
+        if (inpamP->tuple_type != firstTupletype)
+            allSameTt = false;
+
+        if (!inpamP->visual)
+            allVisual = false;
+
+        if (inpamP->have_opacity)
+            haveOpacity = true;
+
+        maxDepth      = MAX(maxDepth,      inpamP->depth);
+        maxColorDepth = MAX(maxColorDepth, inpamP->color_depth);
+        maxMaxval     = MAX(maxMaxval,     inpamP->maxval);
 
-        if (PNM_FORMAT_TYPE(inpamP->format) > PNM_FORMAT_TYPE(newFormat))
+        if (PAM_FORMAT_TYPE(inpamP->format) > PAM_FORMAT_TYPE(newFormat))
             newFormat = inpamP->format;
     }
-    assert(newCols   > 0);
-    assert(newRows   > 0);
-    assert(newMaxval > 0);
-    assert(newFormat > 0);
+    assert(newCols       > 0);
+    assert(newRows       > 0);
+    assert(maxMaxval     > 0);
+    assert(maxColorDepth > 0);
+    assert(newFormat     > 0);
 
     if (newCols > INT_MAX)
        pm_error("Output width too large: %.0f.", newCols);
@@ -285,7 +379,7 @@ computeOutputParms(unsigned int       const fileCt,
        pm_error("Output height too large: %.0f.", newRows);
 
     outpamP->size = sizeof(*outpamP);
-    outpamP->len  = PAM_STRUCT_SIZE(raster_pos);
+    outpamP->len  = PAM_STRUCT_SIZE(tuple_type);
 
     /* Note that while 'double' is not in general a precise numerical type,
        in the case of a sum of integers which is less than INT_MAX, it
@@ -293,11 +387,16 @@ computeOutputParms(unsigned int       const fileCt,
     */
     outpamP->height           = (unsigned int)newRows;
     outpamP->width            = (unsigned int)newCols;
-    outpamP->depth            = newDepth;
-    outpamP->allocation_depth = newDepth;
-    outpamP->maxval           = newMaxval;
+    outpamP->depth            = MAX(maxDepth,
+                                    maxColorDepth + (haveOpacity ? 1 : 0));
+    outpamP->allocation_depth = 0;  /* This means same as depth */
+    outpamP->maxval           = maxMaxval;
     outpamP->format           = newFormat;
-    STRSCPY(outpamP->tuple_type, tupleTypeVaries ? "" : newTupletype);
+    if (allSameTt)
+        STRSCPY(outpamP->tuple_type, firstTupletype);
+    else
+        STRSCPY(outpamP->tuple_type,
+                tupletypeX(allVisual, maxColorDepth, maxMaxval, haveOpacity));
     outpamP->comment_p        = NULL;
     outpamP->plainformat      = false;
 
@@ -663,36 +762,55 @@ concatenateTopBottomPbm(const struct pam *  const outpamP,
 
 
 static void
-padPlanesRow(enum PlanePadMethod const planePadMethod,
-             const struct pam *  const inpamP,
+padPlanesRow(const struct pam *  const inpamP,
              tuple *             const outrow,
              const struct pam *  const outpamP) {
 /*----------------------------------------------------------------------------
-   Add additional planes to *outrow as needed to pad out to the depth
-   indicated in *outpamP from the depth indicated in *inpamP.
-
-   'planePadMethod' tells what to use for the new planes.
+  Rearrange the planes of *outrow as needed to transform them into tuples
+  as described by *outpamP from tuples as described by *inpamP.
 -----------------------------------------------------------------------------*/
-    unsigned int plane;
+    TtTransform const ttTransform = ttXformForImg(inpamP, outpamP);
 
     assert(inpamP->allocation_depth >= outpamP->depth);
 
-    for (plane = inpamP->depth; plane < outpamP->depth; ++plane) {
+    if (ttTransform.mustPromoteOpacity) {
         unsigned int col;
 
+        assert(outpamP->depth >= PAM_TRN_PLANE);
+
         for (col = 0; col < inpamP->width; ++col) {
-            switch (planePadMethod) {
-            case PLANEPAD_ZERO:
+            outrow[col][outpamP->opacity_plane] =
+                outrow[col][inpamP->opacity_plane];
+        }
+    }
+    if (ttTransform.mustPromoteColor) {
+        unsigned int col;
+
+        assert(outpamP->depth >= PAM_GRN_PLANE);
+        assert(outpamP->depth >= PAM_BLU_PLANE);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            assert(PAM_RED_PLANE == 0);
+            outrow[col][PAM_GRN_PLANE] = outrow[col][0];
+            outrow[col][PAM_BLU_PLANE] = outrow[col][0];
+        }
+    }
+
+    if (ttTransform.mustCreateOpacity) {
+        unsigned int col;
+
+        for (col = 0; col < inpamP->width; ++col)
+            outrow[col][outpamP->opacity_plane] = outpamP->maxval;
+    }
+
+    if (ttTransform.mustPadZero) {
+        unsigned int plane;
+
+        for (plane = inpamP->depth; plane < outpamP->depth; ++plane) {
+            unsigned int col;
+
+            for (col = 0; col < inpamP->width; ++col)
                 outrow[col][plane] = 0;
-                break;
-            case PLANEPAD_EXTEND:
-                assert(plane >= 1);
-                    /* Because depth is always at least 1 and we started at
-                       inpam[i].depth:
-                    */
-                outrow[col][plane] = outrow[col][plane-1];
-                break;
-            }
         }
     }
 }
@@ -727,7 +845,6 @@ createLrImgCtlArray(const struct pam *  const inpam,  /* array */
                     const struct pam *  const outpamP,
                     enum Justification  const justification,
                     enum PadColorMethod const padColorMethod,
-                    enum PlanePadMethod const planePadMethod,
                     LrImgCtl **         const imgCtlP) {
 
     LrImgCtl * imgCtl;  /* array */
@@ -766,8 +883,7 @@ createLrImgCtlArray(const struct pam *  const inpam,  /* array */
                 pnm_readpamrow(inpamP, thisEntryP->cachedRow);
                 pnm_scaletuplerow(&inpam[i], thisEntryP->cachedRow,
                                   thisEntryP->cachedRow, outpamP->maxval);
-                padPlanesRow(planePadMethod, &inpam[i], thisEntryP->cachedRow,
-                             outpamP);
+                padPlanesRow(&inpam[i], thisEntryP->cachedRow, outpamP);
                 {
                     struct pam cachedRowPam;
                     cachedRowPam = *outpamP;
@@ -786,6 +902,12 @@ createLrImgCtlArray(const struct pam *  const inpam,  /* array */
                 break;
             }
         }
+        /* Opacity sample of background color sample is meaningless at this
+           point; make it opaque.
+        */
+        if (outpamP->have_opacity)
+            thisEntryP->background[outpamP->opacity_plane] = outpamP->maxval;
+
     }
     *imgCtlP = imgCtl;
 }
@@ -815,8 +937,7 @@ concatenateLeftRightGen(const struct pam *  const outpamP,
                         const struct pam *  const inpam,  /* array */
                         unsigned int        const fileCt,
                         enum Justification  const justification,
-                        enum PadColorMethod const padColorMethod,
-                        enum PlanePadMethod const planePadMethod) {
+                        enum PadColorMethod const padColorMethod) {
 
     tuple * const outrow = pnm_allocpamrow(outpamP);
 
@@ -824,7 +945,7 @@ concatenateLeftRightGen(const struct pam *  const outpamP,
     unsigned int row;
 
     createLrImgCtlArray(inpam, fileCt, outrow, outpamP,
-                        justification, padColorMethod, planePadMethod,
+                        justification, padColorMethod,
                         &imgCtl);
 
     for (row = 0; row < outpamP->height; ++row) {
@@ -862,8 +983,7 @@ concatenateLeftRightGen(const struct pam *  const outpamP,
                 pnm_readpamrow(&inpam[i], thisEntryP->out);
                 pnm_scaletuplerow(&inpam[i], thisEntryP->out,
                                   thisEntryP->out, outpamP->maxval);
-                padPlanesRow(planePadMethod, &inpam[i], thisEntryP->out,
-                             outpamP);
+                padPlanesRow(&inpam[i], thisEntryP->out, outpamP);
             } else {
                 /* It's a row of padding, so image i's part of outrow[] is
                    already set appropriately.
@@ -882,6 +1002,37 @@ concatenateLeftRightGen(const struct pam *  const outpamP,
 
 
 
+static tuple
+initialBackgroundColor(const struct pam *  const outpamP,
+                       enum PadColorMethod const padColorMethod) {
+
+    tuple retval;
+
+    switch (padColorMethod) {
+    case PAD_AUTO:
+        /* Background is different for each input image */
+        retval = pnm_allocpamtuple(outpamP);
+            /* Dummy value; just need something to free */
+        break;
+    case PAD_BLACK:
+        pnm_createBlackTuple(outpamP, &retval);
+        break;
+    case PAD_WHITE:
+        pnm_createWhiteTuple(outpamP, &retval);
+        break;
+    }
+
+    /* Opacity sample of background color sample is meaningless at this point;
+       make it opaque.
+    */
+    if (outpamP->have_opacity)
+        retval[outpamP->opacity_plane] = outpamP->maxval;
+
+    return retval;
+}
+
+
+
 static unsigned int
 leftPadAmount(const struct pam * const outpamP,
               const struct pam * const inpamP,
@@ -944,7 +1095,6 @@ static void
 readFirstTBRowAndDetermineBackground(const struct pam *  const inpamP,
                                      const struct pam *  const outpamP,
                                      tuple *             const out,
-                                     enum PlanePadMethod const planePadMethod,
                                      tuple *             const backgroundP) {
 /*----------------------------------------------------------------------------
    Read the first row of an input image into 'out', adjusting it to conform
@@ -955,22 +1105,29 @@ readFirstTBRowAndDetermineBackground(const struct pam *  const inpamP,
    From this row, determine the background color for the input image and
    return it as *backgroundP (a newly malloced tuple).
 -----------------------------------------------------------------------------*/
-    struct pam partialOutpam;
-        /* Descriptor for the input image with depth and maxval adjusted to
-           that of the output image.
-        */
-
     pnm_readpamrow(inpamP, out);
 
     pnm_scaletuplerow(inpamP, out, out, outpamP->maxval);
 
-    padPlanesRow(planePadMethod, inpamP, out, outpamP);
+    padPlanesRow(inpamP, out, outpamP);
 
     {
+        struct pam partialOutpam;
+            /* Descriptor for the input image with depth and maxval adjusted to
+               that of the output image.
+            */
+        tuple background;
+
         partialOutpam = *outpamP;
         partialOutpam.width = inpamP->width;
 
-        *backgroundP = pnm_backgroundtuplerow(&partialOutpam, out);
+        background = pnm_backgroundtuplerow(&partialOutpam, out);
+
+        /* Make the background opaque */
+        if (outpamP->have_opacity)
+            background[outpamP->opacity_plane] = outpamP->maxval;
+
+        *backgroundP = background;
     }
 }
 
@@ -981,8 +1138,7 @@ concatenateTopBottomGen(const struct pam *  const outpamP,
                         const struct pam *  const inpam,  /* array */
                         unsigned int        const fileCt,
                         enum Justification  const justification,
-                        enum PadColorMethod const padColorMethod,
-                        enum PlanePadMethod const planePadMethod) {
+                        enum PadColorMethod const padColorMethod) {
 
     tuple * const newTuplerow = pnm_allocpamrow(outpamP);
     tuple * out;
@@ -993,19 +1149,7 @@ concatenateTopBottomGen(const struct pam *  const outpamP,
     tuple background;
     tuple backgroundPrev;
 
-    switch (padColorMethod) {
-    case PAD_AUTO:
-        /* Backgournd is different for each input image */
-        background = pnm_allocpamtuple(outpamP);
-            /* Dummy value; just need something to free */
-        break;
-    case PAD_BLACK:
-        pnm_createBlackTuple(outpamP, &background);
-        break;
-    case PAD_WHITE:
-        pnm_createWhiteTuple(outpamP, &background);
-        break;
-    }
+    background = initialBackgroundColor(outpamP, padColorMethod);
 
     for (i = 0; i < fileCt; ++i) {
         const struct pam * const inpamP = &inpam[i];
@@ -1030,7 +1174,7 @@ concatenateTopBottomGen(const struct pam *  const outpamP,
                 out = &newTuplerow[padLeft];
                 backgroundPrev = background;
                 readFirstTBRowAndDetermineBackground(
-                    inpamP, outpamP, out, planePadMethod, &background);
+                    inpamP, outpamP, out, &background);
 
                 backChanged =
                     i == 0 ||
@@ -1059,7 +1203,7 @@ concatenateTopBottomGen(const struct pam *  const outpamP,
 
             pnm_scaletuplerow(inpamP, out, out, outpamP->maxval);
 
-            padPlanesRow(planePadMethod, inpamP, out, outpamP);
+            padPlanesRow(inpamP, out, outpamP);
 
             pnm_writepamrow(outpamP, newTuplerow);
         }
@@ -1087,14 +1231,15 @@ main(int           argc,
 
     for (i = 0; i < cmdline.fileCt; ++i) {
         FILE * const ifP = pm_openr(cmdline.inputFileName[i]);
-        pnm_readpaminit(ifP, &inpam[i], PAM_STRUCT_SIZE(allocation_depth));
+        inpam[i].comment_p = NULL;  /* Don't want to see the comments */
+        pnm_readpaminit(ifP, &inpam[i], PAM_STRUCT_SIZE(opacity_plane));
     }
 
-    outpam.file = stdout;
-
     computeOutputParms(cmdline.fileCt, cmdline.orientation, inpam,
                        cmdline.verbose, &outpam);
 
+    outpam.file = stdout;
+
     for (i = 0; i < cmdline.fileCt; ++i)
         pnm_setminallocationdepth(&inpam[i], outpam.depth);
 
@@ -1118,14 +1263,12 @@ main(int           argc,
         case LEFTRIGHT:
             concatenateLeftRightGen(&outpam, inpam, cmdline.fileCt,
                                     cmdline.justification,
-                                    cmdline.padColorMethod,
-                                    cmdline.planePadMethod);
+                                    cmdline.padColorMethod);
             break;
         case TOPBOTTOM:
             concatenateTopBottomGen(&outpam, inpam, cmdline.fileCt,
                                     cmdline.justification,
-                                    cmdline.padColorMethod,
-                                    cmdline.planePadMethod);
+                                    cmdline.padColorMethod);
             break;
         }
     }