about summary refs log tree commit diff
path: root/editor/pamstretch.c
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
commit1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch)
tree64c8c96cf54d8718847339a403e5e67b922e8c3f /editor/pamstretch.c
downloadnetpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor/pamstretch.c')
-rw-r--r--editor/pamstretch.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/editor/pamstretch.c b/editor/pamstretch.c
new file mode 100644
index 00000000..0e9e6abf
--- /dev/null
+++ b/editor/pamstretch.c
@@ -0,0 +1,408 @@
+/* pamstretch - scale up portable anymap by interpolating between pixels.
+ * 
+ * This program is based on 'pnminterp' by Russell Marks, rename
+ * pnmstretch for inclusion in Netpbm, then rewritten and renamed to
+ * pamstretch by Bryan Henderson in December 2001.
+ *
+ * Copyright (C) 1998,2000 Russell Marks.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "pam.h"
+#include "shhopt.h"
+
+enum an_edge_mode {
+    EDGE_DROP,
+        /* drop one (source) pixel at right/bottom edges. */
+    EDGE_INTERP_TO_BLACK,
+        /* interpolate right/bottom edge pixels to black. */
+    EDGE_NON_INTERP
+        /* don't interpolate right/bottom edge pixels 
+           (default, and what zgv does). */
+};
+
+
+struct cmdline_info {
+    /* 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 */
+    enum an_edge_mode edge_mode;
+    unsigned int xscale;
+    unsigned int yscale;
+};
+
+
+
+tuple blackTuple;  
+   /* A "black" tuple.  Unless our input image is PBM, PGM, or PPM, we
+      don't really know what "black" means, so this is just something
+      arbitrary in that case.
+      */
+
+
+static void
+parse_command_line(int argc, char ** argv,
+                   struct cmdline_info *cmdline_p) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+    unsigned int option_def_index;
+
+    unsigned int blackedge;
+    unsigned int dropedge;
+    unsigned int xscale_spec;
+    unsigned int yscale_spec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3('b', "blackedge",    OPT_FLAG, NULL, &blackedge,            0);
+    OPTENT3('d', "dropedge",     OPT_FLAG, NULL, &dropedge,             0);
+    OPTENT3(0,   "xscale",       OPT_UINT, 
+            &cmdline_p->xscale, &xscale_spec, 0);
+    OPTENT3(0,   "yscale",       OPT_UINT, 
+            &cmdline_p->yscale, &yscale_spec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE; /* We have some 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 (blackedge && dropedge) 
+        pm_error("Can't specify both -blackedge and -dropedge options.");
+    else if (blackedge)
+        cmdline_p->edge_mode = EDGE_INTERP_TO_BLACK;
+    else if (dropedge)
+        cmdline_p->edge_mode = EDGE_DROP;
+    else
+        cmdline_p->edge_mode = EDGE_NON_INTERP;
+
+    if (xscale_spec && cmdline_p->xscale == 0)
+        pm_error("You specified zero for the X scale factor.");
+    if (yscale_spec && cmdline_p->yscale == 0)
+        pm_error("You specified zero for the Y scale factor.");
+
+    if (xscale_spec && !yscale_spec)
+        cmdline_p->yscale = 1;
+    if (yscale_spec && !xscale_spec)
+        cmdline_p->xscale = 1;
+
+    if (!(xscale_spec || yscale_spec)) {
+        /* scale must be specified in an argument */
+        if ((argc-1) != 1 && (argc-1) != 2)
+            pm_error("Wrong number of arguments (%d).  Without scale options, "
+                     "you must supply 1 or 2 arguments:  scale and "
+                     "optional file specification", argc-1);
+        
+        {
+            char *endptr;   /* ptr to 1st invalid character in scale arg */
+            unsigned int scale;
+            
+            scale = strtol(argv[1], &endptr, 10);
+            if (*argv[1] == '\0') 
+                pm_error("Scale argument is a null string.  "
+                         "Must be a number.");
+            else if (*endptr != '\0')
+                pm_error("Scale argument contains non-numeric character '%c'.",
+                         *endptr);
+            else if (scale < 2)
+                pm_error("Scale argument must be at least 2.  "
+                         "You specified %d", scale);
+            cmdline_p->xscale = scale;
+            cmdline_p->yscale = scale;
+        }
+        if (argc-1 > 1) 
+            cmdline_p->input_filespec = argv[2];
+        else
+            cmdline_p->input_filespec = "-";
+    } else {
+        /* No scale argument allowed */
+        if ((argc-1) > 1)
+            pm_error("Too many arguments (%d).  With a scale option, "
+                     "the only argument is the "
+                     "optional file specification", argc-1);
+        if (argc-1 > 0) 
+            cmdline_p->input_filespec = argv[1];
+        else
+            cmdline_p->input_filespec = "-";
+    }
+}
+
+
+
+static void
+stretch_line(struct pam * const inpamP, 
+             const tuple * const line, const tuple * const line_stretched, 
+             unsigned int const scale, enum an_edge_mode const edge_mode) {
+/*----------------------------------------------------------------------------
+   Stretch the line of tuples 'line' into the output buffer 'line_stretched',
+   by factor 'scale'.
+-----------------------------------------------------------------------------*/
+    int scaleincr;
+    int sisize;   
+        /* normalizing factor to make fractions representable as integers.
+           E.g. if sisize = 100, one half is represented as 50.
+        */
+    unsigned int col;
+    unsigned int outcol;
+    
+    sisize=0;
+    while (sisize<256) 
+        sisize += scale;
+    scaleincr = sisize/scale;  /* (1/scale, normalized) */
+
+    outcol = 0;  /* initial value */
+
+    for (col = 0; col < inpamP->width; ++col) {
+        unsigned int pos;
+            /* The fraction of the way we are from curline to nextline,
+               normalized by sisize.
+            */
+        if (col >= inpamP->width-1) {
+            /* We're at the edge.  There is no column to the right with which
+               to interpolate.
+            */
+            switch(edge_mode) {
+            case EDGE_DROP:
+                /* No output column needed for this input column */
+                break;
+            case EDGE_INTERP_TO_BLACK: {
+                unsigned int pos;
+                for (pos = 0; pos < sisize; pos += scaleincr) {
+                    unsigned int plane;
+                    for (plane = 0; plane < inpamP->depth; ++plane)
+                        line_stretched[outcol][plane] = 
+                            (line[col][plane] * (sisize-pos)) / sisize;
+                    ++outcol;
+                }
+            }
+            break;
+            case EDGE_NON_INTERP: {
+                unsigned int pos;
+                for (pos = 0; pos < sisize; pos += scaleincr) {
+                    unsigned int plane;
+                    for (plane = 0; plane < inpamP->depth; ++plane)
+                        line_stretched[outcol][plane] = line[col][plane];
+                    ++outcol;
+                }
+            }
+            break;
+            default: 
+                pm_error("INTERNAL ERROR: invalid value for edge_mode");
+            }
+        } else {
+            /* Interpolate with the next input column to the right */
+            for (pos = 0; pos < sisize; pos += scaleincr) {
+                unsigned int plane;
+                for (plane = 0; plane < inpamP->depth; ++plane)
+                    line_stretched[outcol][plane] = 
+                        (line[col][plane] * (sisize-pos) 
+                         +  line[col+1][plane] * pos) / sisize;
+                ++outcol;
+            }
+        }
+    }
+}
+
+
+
+static void 
+write_interp_rows(struct pam *      const outpamP,
+                  const tuple *     const curline,
+                  const tuple *     const nextline, 
+                  tuple *           const outbuf,
+                  int               const scale) {
+/*----------------------------------------------------------------------------
+   Write out 'scale' rows, being 'curline' followed by rows that are 
+   interpolated between 'curline' and 'nextline'.
+-----------------------------------------------------------------------------*/
+    unsigned int scaleincr;
+    unsigned int sisize;
+    unsigned int pos;
+
+    sisize=0;
+    while(sisize<256) sisize+=scale;
+    scaleincr=sisize/scale;
+
+    for (pos = 0; pos < sisize; pos += scaleincr) {
+        unsigned int col;
+        for (col = 0; col < outpamP->width; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < outpamP->depth; ++plane) 
+                outbuf[col][plane] = (curline[col][plane] * (sisize-pos)
+                    + nextline[col][plane] * pos) / sisize;
+        }
+        pnm_writepamrow(outpamP, outbuf);
+    }
+}
+
+
+
+static void
+swap_buffers(tuple ** const buffer1P, tuple ** const buffer2P) {
+    /* Advance "next" line to "current" line by switching
+       line buffers 
+    */
+    tuple *tmp;
+
+    tmp = *buffer1P;
+    *buffer1P = *buffer2P;
+    *buffer2P = tmp;
+}
+
+
+static void 
+stretch(struct pam * const inpamP, struct pam * const outpamP,
+        int const xscale, int const yscale,
+        enum an_edge_mode const edge_mode) {
+
+    tuple *linebuf1, *linebuf2;  /* Input buffers for two rows at a time */
+    tuple *curline, *nextline;   /* Pointers to one of the two above buffers */
+    /* And the stretched versions: */
+    tuple *stretched_linebuf1, *stretched_linebuf2;
+    tuple *curline_stretched, *nextline_stretched;
+
+    tuple *outbuf;   /* One-row output buffer */
+    unsigned int row;
+    unsigned int rowsToStretch;
+    
+    linebuf1 =           pnm_allocpamrow(inpamP);
+    linebuf2 =           pnm_allocpamrow(inpamP);
+    stretched_linebuf1 = pnm_allocpamrow(outpamP);
+    stretched_linebuf2 = pnm_allocpamrow(outpamP);
+    outbuf =             pnm_allocpamrow(outpamP);
+
+    curline = linebuf1;
+    curline_stretched = stretched_linebuf1;
+    nextline = linebuf2;
+    nextline_stretched = stretched_linebuf2;
+
+    pnm_readpamrow(inpamP, curline);
+    stretch_line(inpamP, curline, curline_stretched, xscale, edge_mode);
+
+    if (edge_mode == EDGE_DROP) 
+        rowsToStretch = inpamP->height - 1;
+    else
+        rowsToStretch = inpamP->height;
+    
+    for (row = 0; row < rowsToStretch; row++) {
+        if (row == inpamP->height-1) {
+            /* last line is about to be output. there is no further
+             * `next line'.  if EDGE_DROP, we stop here, with output
+             * of rows-1 rows.  if EDGE_INTERP_TO_BLACK we make next
+             * line black.  if EDGE_NON_INTERP (default) we make it a
+             * copy of the current line.  
+             */
+            switch (edge_mode) {
+            case EDGE_INTERP_TO_BLACK: {
+                int col;
+                for (col = 0; col < outpamP->width; col++)
+                    nextline_stretched[col] = blackTuple;
+            } 
+            break;
+            case EDGE_NON_INTERP: {
+                /* EDGE_NON_INTERP */
+                int col;
+                for (col = 0; col < outpamP->width; col++)
+                    nextline_stretched[col] = curline_stretched[col];
+            }
+            break;
+            case EDGE_DROP: 
+                pm_error("INTERNAL ERROR: processing last row, but "
+                         "edge_mode is EDGE_DROP.");
+            }
+        } else {
+            pnm_readpamrow(inpamP, nextline);
+            stretch_line(inpamP, nextline, nextline_stretched, xscale,
+                         edge_mode);
+        }
+        
+        /* interpolate curline towards nextline into outbuf */
+        write_interp_rows(outpamP, curline_stretched, nextline_stretched,
+                          outbuf, yscale);
+
+        swap_buffers(&curline, &nextline);
+        swap_buffers(&curline_stretched, &nextline_stretched);
+    }
+    pnm_freerow(outbuf);
+    pnm_freerow(stretched_linebuf2);
+    pnm_freerow(stretched_linebuf1);
+    pnm_freerow(linebuf2);
+    pnm_freerow(linebuf1);
+}
+
+
+
+int 
+main(int argc,char *argv[]) {
+
+    FILE *ifp;
+
+    struct cmdline_info cmdline; 
+    struct pam inpam, outpam;
+    
+    pnm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    ifp = pm_openr(cmdline.input_filespec);
+
+    pnm_readpaminit(ifp, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (inpam.width < 2)
+        pm_error("Image is too narrow.  Must be at least 2 columns.");
+    if (inpam.height < 2)
+        pm_error("Image is too short.  Must be at least 2 lines.");
+
+
+    outpam = inpam;  /* initial value */
+    outpam.file = stdout;
+
+    if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE) {
+        outpam.format = PGM_TYPE;
+        /* usual filter message when reading PBM but writing PGM: */
+        pm_message("promoting from PBM to PGM");
+    } else {
+        outpam.format = inpam.format;
+    }
+    {
+        unsigned int const dropped = cmdline.edge_mode == EDGE_DROP ? 1 : 0;
+
+        outpam.width = (inpam.width - dropped) * cmdline.xscale;
+        outpam.height = (inpam.height - dropped) * cmdline.yscale;
+
+        pnm_writepaminit(&outpam);
+    }
+
+    pnm_createBlackTuple(&outpam, &blackTuple);
+
+    stretch(&inpam, &outpam, 
+            cmdline.xscale, cmdline.yscale, cmdline.edge_mode);
+
+    pm_close(ifp);
+
+    exit(0);
+}
+
+
+