about summary refs log tree commit diff
path: root/editor/pamcut.c
diff options
context:
space:
mode:
Diffstat (limited to 'editor/pamcut.c')
-rw-r--r--editor/pamcut.c284
1 files changed, 197 insertions, 87 deletions
diff --git a/editor/pamcut.c b/editor/pamcut.c
index c5d53f44..8d4c2240 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -12,6 +12,8 @@
 
 #include <limits.h>
 #include <assert.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -94,8 +96,10 @@ parseCommandLine(int argc, char ** const argv,
         pm_error("-height may not be negative.");
 
     if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5)
-        pm_error("Wrong number of arguments.  "
-                 "Must be 0, 1, 4, or 5 arguments.");
+        pm_error("Wrong number of arguments: %u.  The only argument in "
+                 "the preferred syntax is an optional input file name.  "
+                 "In older syntax, there are also forms with 4 and 5 "
+                 "arguments.", argc-1);
 
     switch (argc-1) {
     case 0:
@@ -269,44 +273,51 @@ computeCutBounds(const int cols, const int rows,
 
 
 static void
-rejectOutOfBounds(const int cols, const int rows, 
-                  const int leftcol, const int rightcol, 
-                  const int toprow, const int bottomrow) {
-
-    /* Reject coordinates off the edge */
-
-    if (leftcol < 0)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", leftcol);
-    if (leftcol > cols-1)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", leftcol, cols-1);
-    if (rightcol < 0)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", rightcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (leftcol > rightcol) 
-        pm_error("You have specified a left edge (%d) that is to the right\n"
-                 "of the right edge you specified (%d)", 
+rejectOutOfBounds(unsigned int const cols,
+                  unsigned int const rows,
+                  int          const leftcol,
+                  int          const rightcol,
+                  int          const toprow,
+                  int          const bottomrow,
+                  bool         const pad) {
+
+     /* Reject coordinates off the edge */
+
+    if (!pad) {
+        if (leftcol < 0)
+            pm_error("You have specified a left edge (%d) that is beyond "
+                     "the left edge of the image (0)", leftcol);
+        if (leftcol > (int)(cols-1))
+            pm_error("You have specified a left edge (%d) that is beyond "
+                     "the right edge of the image (%u)", leftcol, cols-1);
+        if (rightcol < 0)
+            pm_error("You have specified a right edge (%d) that is beyond "
+                     "the left edge of the image (0)", rightcol);
+        if (rightcol > (int)(cols-1))
+            pm_error("You have specified a right edge (%d) that is beyond "
+                     "the right edge of the image (%u)", rightcol, cols-1);
+        if (toprow < 0)
+            pm_error("You have specified a top edge (%d) that is above "
+                     "the top edge of the image (0)", toprow);
+        if (toprow > (int)(rows-1))
+            pm_error("You have specified a top edge (%d) that is below "
+                     "the bottom edge of the image (%u)", toprow, rows-1);
+        if (bottomrow < 0)
+            pm_error("You have specified a bottom edge (%d) that is above "
+                     "the top edge of the image (0)", bottomrow);
+        if (bottomrow > (int)(rows-1))
+            pm_error("You have specified a bottom edge (%d) that is below "
+                     "the bottom edge of the image (%u)", bottomrow, rows-1);
+    }
+
+    if (leftcol > rightcol)
+        pm_error("You have specified a left edge (%d) that is to the right of "
+                 "the right edge you specified (%d)",
                  leftcol, rightcol);
-    
-    if (toprow < 0)
-        pm_error("You have specified a top edge (%d) that is above the top "
-                 "edge of the image (0)", toprow);
-    if (toprow > rows-1)
-        pm_error("You have specified a top edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", toprow, rows-1);
-    if (bottomrow < 0)
-        pm_error("You have specified a bottom edge (%d) that is above the\n"
-                 "top edge of the image (0)", bottomrow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (toprow > bottomrow) 
-        pm_error("You have specified a top edge (%d) that is below\n"
-                 "the bottom edge you specified (%d)", 
+
+    if (toprow > bottomrow)
+        pm_error("You have specified a top edge (%d) that is below "
+                 "the bottom edge you specified (%d)",
                  toprow, bottomrow);
 }
 
@@ -398,13 +409,15 @@ struct rowCutter {
    create a new one then.
 */
 
+
+
 static void
-createRowCutter(struct pam *        const inpamP,
-                struct pam *        const outpamP,
+createRowCutter(const struct pam *  const inpamP,
+                const struct pam *  const outpamP,
                 int                 const leftcol,
                 int                 const rightcol,
                 struct rowCutter ** const rowCutterPP) {
-    
+
     struct rowCutter * rowCutterP;
     tuple * inputPointers;
     tuple * outputPointers;
@@ -412,7 +425,7 @@ createRowCutter(struct pam *        const inpamP,
     tuple blackTuple;
     tuple discardTuple;
     int col;
-    
+
     assert(inpamP->depth >= outpamP->depth);
         /* Entry condition.  If this weren't true, we could not simply
            treat an input tuple as an output tuple.
@@ -483,15 +496,144 @@ destroyRowCutter(struct rowCutter * const rowCutterP) {
 
 
 static void
+extractRowsGen(const struct pam * const inpamP,
+               const struct pam * const outpamP,
+               int                const leftcol,
+               int                const rightcol,
+               int                const toprow,
+               int                const bottomrow) {
+
+    struct rowCutter * rowCutterP;
+    int row;
+
+    /* Write out top padding */
+    if (0 - toprow > 0)
+        writeBlackRows(outpamP, 0 - toprow);
+
+    createRowCutter(inpamP, outpamP, leftcol, rightcol, &rowCutterP);
+
+    /* Read input and write out rows extracted from it */
+    for (row = 0; row < inpamP->height; ++row) {
+        if (row >= toprow && row <= bottomrow){
+            pnm_readpamrow(inpamP, rowCutterP->inputPointers);
+            pnm_writepamrow(outpamP, rowCutterP->outputPointers);
+        } else  /* row < toprow || row > bottomrow */
+            pnm_readpamrow(inpamP, NULL);
+        
+        /* Note that we may be tempted just to quit after reaching the bottom
+           of the extracted image, but that would cause a broken pipe problem
+           for the process that's feeding us the image.
+        */
+    }
+
+    destroyRowCutter(rowCutterP);
+    
+    /* Write out bottom padding */
+    if ((bottomrow - (inpamP->height-1)) > 0)
+        writeBlackRows(outpamP, bottomrow - (inpamP->height-1));
+}
+
+
+
+static void
+makeBlackPBMRow(unsigned char * const bitrow,
+                unsigned int    const cols) {
+
+    unsigned int const colByteCnt = pbm_packed_bytes(cols);
+
+    unsigned int i;
+
+    for (i = 0; i < colByteCnt; ++i)
+        bitrow[i] = PBM_BLACK * 0xff;
+
+    if (PBM_BLACK != 0 && cols % 8 > 0)
+        bitrow[colByteCnt-1] <<= (8 - cols % 8);
+}
+
+
+
+static void
+extractRowsPBM(const struct pam * const inpamP,
+               const struct pam * const outpamP,
+               int                const leftcol,
+               int                const rightcol,
+               int                const toprow,
+               int                const bottomrow) {
+
+    unsigned char * bitrow;
+    int             readOffset, writeOffset;
+    int             row;
+    unsigned int    totalWidth;
+
+    assert(leftcol <= rightcol);
+    assert(toprow <= bottomrow);
+
+    if (leftcol > 0) {
+        totalWidth = MAX(rightcol+1, inpamP->width) + 7;
+        if (totalWidth > INT_MAX)
+            /* Prevent overflows in pbm_allocrow_packed() */
+            pm_error("Specified right edge is too far "
+                     "from the right end of input image");
+        
+        readOffset  = 0;
+        writeOffset = leftcol;
+    } else {
+        totalWidth = -leftcol + MAX(rightcol+1, inpamP->width);
+        if (totalWidth > INT_MAX)
+            pm_error("Specified left/right edge is too far "
+                     "from the left/right end of input image");
+        
+        readOffset = -leftcol;
+        writeOffset = 0;
+    }
+
+    bitrow = pbm_allocrow_packed(totalWidth);
+
+    if (toprow < 0 || leftcol < 0 || rightcol >= inpamP->width){
+        makeBlackPBMRow(bitrow, totalWidth);
+        if (toprow < 0) {
+            int row;
+            for (row=0; row < 0 - toprow; ++row)
+                pbm_writepbmrow_packed(outpamP->file, bitrow,
+                                       outpamP->width, 0);
+        }
+    }
+
+    for (row = 0; row < inpamP->height; ++row){
+        if (row >= toprow && row <= bottomrow) {
+            pbm_readpbmrow_bitoffset(inpamP->file, bitrow, inpamP->width,
+                                     inpamP->format, readOffset);
+
+            pbm_writepbmrow_bitoffset(outpamP->file, bitrow, outpamP->width,
+                                      0, writeOffset);
+  
+            if (rightcol >= inpamP->width)
+                /* repair right padding */
+                bitrow[writeOffset/8 + pbm_packed_bytes(outpamP->width) - 1] =
+                    0xff * PBM_BLACK;
+        } else
+            pnm_readpamrow(inpamP, NULL);    /* read and discard */
+    }
+
+    if (bottomrow - (inpamP->height-1) > 0) {
+        int row;
+        makeBlackPBMRow(bitrow, outpamP->width);
+        for (row = 0; row < bottomrow - (inpamP->height-1); ++row)
+            pbm_writepbmrow_packed(outpamP->file, bitrow, outpamP->width, 0);
+    }
+    pbm_freerow_packed(bitrow);
+}
+
+
+
+static void
 cutOneImage(FILE *             const ifP,
             struct cmdlineInfo const cmdline,
             FILE *             const ofP) {
 
-    int row;
     int leftcol, rightcol, toprow, bottomrow;
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
-    struct rowCutter * rowCutterP;
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
     
@@ -501,22 +643,11 @@ cutOneImage(FILE *             const ifP,
                      cmdline.width, cmdline.height, 
                      &leftcol, &rightcol, &toprow, &bottomrow);
 
-    if (!cmdline.pad)
-        rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, 
-                          toprow, bottomrow);
-    else {
-        if (cmdline.left > cmdline.right) 
-            pm_error("You have specified a left edge (%d) that is to the right\n"
-                     "of the right edge you specified (%d)", 
-                     cmdline.left, cmdline.right);
-        
-        if (cmdline.top > cmdline.bottom) 
-            pm_error("You have specified a top edge (%d) that is below\n"
-                     "the bottom edge you specified (%d)", 
-                     cmdline.top, cmdline.bottom);
-    }
+    rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, 
+                      toprow, bottomrow, cmdline.pad);
+
     if (cmdline.verbose) {
-        pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d",
+        pm_message("Image goes from Row 0, Column 0 through Row %u, Column %u",
                    inpam.height-1, inpam.width-1);
         pm_message("Cutting from Row %d, Column %d through Row %d Column %d",
                    toprow, leftcol, bottomrow, rightcol);
@@ -524,36 +655,15 @@ cutOneImage(FILE *             const ifP,
 
     outpam = inpam;    /* Initial value -- most fields should be same */
     outpam.file   = ofP;
-    outpam.width  = rightcol-leftcol+1;
-    outpam.height = bottomrow-toprow+1;
+    outpam.width  = rightcol - leftcol + 1;
+    outpam.height = bottomrow - toprow + 1;
 
     pnm_writepaminit(&outpam);
 
-    /* Write out top padding */
-    if (0 - toprow > 0)
-        writeBlackRows(&outpam, 0 - toprow);
-
-    createRowCutter(&inpam, &outpam, leftcol, rightcol, &rowCutterP);
-
-    /* Read input and write out rows extracted from it */
-    for (row = 0; row < inpam.height; ++row) {
-        if (row >= toprow && row <= bottomrow){
-            pnm_readpamrow(&inpam, rowCutterP->inputPointers);
-            pnm_writepamrow(&outpam, rowCutterP->outputPointers);
-        } else  /* row < toprow || row > bottomrow */
-            pnm_readpamrow(&inpam, NULL);
-        
-        /* Note that we may be tempted just to quit after reaching the bottom
-           of the extracted image, but that would cause a broken pipe problem
-           for the process that's feeding us the image.
-        */
-    }
-
-    destroyRowCutter(rowCutterP);
-    
-    /* Write out bottom padding */
-    if ((bottomrow - (inpam.height-1)) > 0)
-        writeBlackRows(&outpam, bottomrow - (inpam.height-1));
+    if (PNM_FORMAT_TYPE(outpam.format) == PBM_TYPE)
+        extractRowsPBM(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
+    else
+        extractRowsGen(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
 }