~ubuntu-branches/ubuntu/jaunty/netpbm-free/jaunty

« back to all changes in this revision

Viewing changes to pnm/pamstretch.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2004-07-29 20:25:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040729202546-5x43bcfsdlt7dspl
Tags: upstream-10.0
ImportĀ upstreamĀ versionĀ 10.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* pnminterp - scale up portable anymap by interpolating between pixels.
 
2
 * Copyright (C) 1998,2000 Russell Marks.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or (at
 
7
 * your option) any later version.
 
8
 * 
 
9
 * This program is distributed in the hope that it will be useful, but
 
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * General Public License for more details.
 
13
 * 
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 */
 
18
 
 
19
#include <stdio.h>
 
20
#include <string.h>
 
21
#include <stdlib.h>
 
22
#include <ctype.h>
 
23
#include "pam.h"
 
24
 
 
25
enum an_edge_mode {
 
26
    EDGE_DROP,
 
27
        /* drop one (source) pixel at right/bottom edges. */
 
28
    EDGE_INTERP_TO_BLACK,
 
29
        /* interpolate right/bottom edge pixels to black. */
 
30
    EDGE_NON_INTERP
 
31
        /* don't interpolate right/bottom edge pixels 
 
32
           (default, and what zgv does). */
 
33
};
 
34
 
 
35
 
 
36
struct cmdline_info {
 
37
    /* All the information the user supplied in the command line,
 
38
       in a form easy for the program to use.
 
39
    */
 
40
    char *input_filespec;  /* Filespecs of input files */
 
41
    enum an_edge_mode edge_mode;
 
42
    unsigned int xscale;
 
43
    unsigned int yscale;
 
44
};
 
45
 
 
46
 
 
47
 
 
48
tuple blackTuple;  
 
49
   /* A "black" tuple.  Unless our input image is PBM, PGM, or PPM, we
 
50
      don't really know what "black" means, so this is just something
 
51
      arbitrary in that case.
 
52
      */
 
53
 
 
54
 
 
55
static void
 
56
parse_command_line(int argc, char ** argv,
 
57
                   struct cmdline_info *cmdline_p) {
 
58
/*----------------------------------------------------------------------------
 
59
   Note that the file spec array we return is stored in the storage that
 
60
   was passed to us as the argv array.
 
61
-----------------------------------------------------------------------------*/
 
62
    optStruct3 opt;  /* set by OPTENT3 */
 
63
    optEntry *option_def = malloc(100*sizeof(optStruct));
 
64
    unsigned int option_def_index;
 
65
 
 
66
    unsigned int blackedge;
 
67
    unsigned int dropedge;
 
68
    unsigned int xscale_spec;
 
69
    unsigned int yscale_spec;
 
70
 
 
71
    option_def_index = 0;   /* incremented by OPTENTRY */
 
72
    OPTENT3('b', "blackedge",    OPT_FLAG, NULL, &blackedge,            0);
 
73
    OPTENT3('d', "dropedge",     OPT_FLAG, NULL, &dropedge,             0);
 
74
    OPTENT3(0,   "xscale",       OPT_UINT, 
 
75
            &cmdline_p->xscale, &xscale_spec, 0);
 
76
    OPTENT3(0,   "yscale",       OPT_UINT, 
 
77
            &cmdline_p->yscale, &yscale_spec, 0);
 
78
 
 
79
    opt.opt_table = option_def;
 
80
    opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
 
81
    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
82
 
 
83
    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
84
        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
85
 
 
86
    if (blackedge && dropedge) 
 
87
        pm_error("Can't specify both -blackedge and -dropedge options.");
 
88
    else if (blackedge)
 
89
        cmdline_p->edge_mode = EDGE_INTERP_TO_BLACK;
 
90
    else if (dropedge)
 
91
        cmdline_p->edge_mode = EDGE_DROP;
 
92
    else
 
93
        cmdline_p->edge_mode = EDGE_NON_INTERP;
 
94
 
 
95
    if (xscale_spec && cmdline_p->xscale == 0)
 
96
        pm_error("You specified zero for the X scale factor.");
 
97
    if (yscale_spec && cmdline_p->yscale == 0)
 
98
        pm_error("You specified zero for the Y scale factor.");
 
99
 
 
100
    if (xscale_spec && !yscale_spec)
 
101
        cmdline_p->yscale = 1;
 
102
    if (yscale_spec && !xscale_spec)
 
103
        cmdline_p->xscale = 1;
 
104
 
 
105
    if (!(xscale_spec || yscale_spec)) {
 
106
        /* scale must be specified in an argument */
 
107
        if ((argc-1) != 1 && (argc-1) != 2)
 
108
            pm_error("Wrong number of arguments (%d).  Without scale options, "
 
109
                     "you must supply 1 or 2 arguments:  scale and "
 
110
                     "optional file specification", argc-1);
 
111
        
 
112
        {
 
113
            char *endptr;   /* ptr to 1st invalid character in scale arg */
 
114
            unsigned int scale;
 
115
            
 
116
            scale = strtol(argv[1], &endptr, 10);
 
117
            if (*argv[1] == '\0') 
 
118
                pm_error("Scale argument is a null string.  "
 
119
                         "Must be a number.");
 
120
            else if (*endptr != '\0')
 
121
                pm_error("Scale argument contains non-numeric character '%c'.",
 
122
                         *endptr);
 
123
            else if (scale < 2)
 
124
                pm_error("Scale argument must be at least 2.  "
 
125
                         "You specified %d", scale);
 
126
            cmdline_p->xscale = scale;
 
127
            cmdline_p->yscale = scale;
 
128
        }
 
129
        if (argc-1 > 1) 
 
130
            cmdline_p->input_filespec = argv[2];
 
131
        else
 
132
            cmdline_p->input_filespec = "-";
 
133
    } else {
 
134
        /* No scale argument allowed */
 
135
        if ((argc-1) > 1)
 
136
            pm_error("Too many arguments (%d).  With a scale option, "
 
137
                     "the only argument is the "
 
138
                     "optional file specification", argc-1);
 
139
        if (argc-1 > 0) 
 
140
            cmdline_p->input_filespec = argv[1];
 
141
        else
 
142
            cmdline_p->input_filespec = "-";
 
143
    }
 
144
}
 
145
 
 
146
 
 
147
 
 
148
static void
 
149
stretch_line(struct pam * const inpamP, 
 
150
             const tuple * const line, const tuple * const line_stretched, 
 
151
             unsigned int const scale, enum an_edge_mode const edge_mode) {
 
152
/*----------------------------------------------------------------------------
 
153
   Stretch the line of tuples 'line' into the output buffer 'line_stretched',
 
154
   by factor 'scale'.
 
155
-----------------------------------------------------------------------------*/
 
156
    int scaleincr;
 
157
    int sisize;   
 
158
        /* normalizing factor to make fractions representable as integers.
 
159
           E.g. if sisize = 100, one half is represented as 50.
 
160
        */
 
161
    unsigned int col;
 
162
    unsigned int outcol;
 
163
    
 
164
    sisize=0;
 
165
    while (sisize<256) 
 
166
        sisize += scale;
 
167
    scaleincr = sisize/scale;  /* (1/scale, normalized) */
 
168
 
 
169
    outcol = 0;  /* initial value */
 
170
 
 
171
    for (col = 0; col < inpamP->width; ++col) {
 
172
        unsigned int pos;
 
173
            /* The fraction of the way we are from curline to nextline,
 
174
               normalized by sisize.
 
175
            */
 
176
        if (col >= inpamP->width-1) {
 
177
            /* We're at the edge.  There is no column to the right with which
 
178
               to interpolate.
 
179
            */
 
180
            switch(edge_mode) {
 
181
            case EDGE_DROP:
 
182
                /* No output column needed for this input column */
 
183
                break;
 
184
            case EDGE_INTERP_TO_BLACK: {
 
185
                unsigned int pos;
 
186
                for (pos = 0; pos < sisize; pos += scaleincr) {
 
187
                    unsigned int plane;
 
188
                    for (plane = 0; plane < inpamP->depth; ++plane)
 
189
                        line_stretched[outcol][plane] = 
 
190
                            (line[col][plane] * (sisize-pos)) / sisize;
 
191
                    ++outcol;
 
192
                }
 
193
            }
 
194
            break;
 
195
            case EDGE_NON_INTERP: {
 
196
                unsigned int pos;
 
197
                for (pos = 0; pos < sisize; pos += scaleincr) {
 
198
                    unsigned int plane;
 
199
                    for (plane = 0; plane < inpamP->depth; ++plane)
 
200
                        line_stretched[outcol][plane] = line[col][plane];
 
201
                    ++outcol;
 
202
                }
 
203
            }
 
204
            break;
 
205
            default: 
 
206
                pm_error("INTERNAL ERROR: invalid value for edge_mode");
 
207
            }
 
208
        } else {
 
209
            /* Interpolate with the next input column to the right */
 
210
            for (pos = 0; pos < sisize; pos += scaleincr) {
 
211
                unsigned int plane;
 
212
                for (plane = 0; plane < inpamP->depth; ++plane)
 
213
                    line_stretched[outcol][plane] = 
 
214
                        (line[col][plane] * (sisize-pos) 
 
215
                         +  line[col+1][plane] * pos) / sisize;
 
216
                ++outcol;
 
217
            }
 
218
        }
 
219
    }
 
220
}
 
221
 
 
222
 
 
223
 
 
224
static void 
 
225
write_interp_rows(struct pam *      const outpamP,
 
226
                  const tuple *     const curline,
 
227
                  const tuple *     const nextline, 
 
228
                  tuple *           const outbuf,
 
229
                  int               const scale) {
 
230
/*----------------------------------------------------------------------------
 
231
   Write out 'scale' rows, being 'curline' followed by rows that are 
 
232
   interpolated between 'curline' and 'nextline'.
 
233
-----------------------------------------------------------------------------*/
 
234
    unsigned int scaleincr;
 
235
    unsigned int sisize;
 
236
    unsigned int pos;
 
237
 
 
238
    sisize=0;
 
239
    while(sisize<256) sisize+=scale;
 
240
    scaleincr=sisize/scale;
 
241
 
 
242
    for (pos = 0; pos < sisize; pos += scaleincr) {
 
243
        unsigned int col;
 
244
        for (col = 0; col < outpamP->width; ++col) {
 
245
            unsigned int plane;
 
246
            for (plane = 0; plane < outpamP->depth; ++plane) 
 
247
                outbuf[col][plane] = (curline[col][plane] * (sisize-pos)
 
248
                    + nextline[col][plane] * pos) / sisize;
 
249
        }
 
250
        pnm_writepamrow(outpamP, outbuf);
 
251
    }
 
252
}
 
253
 
 
254
 
 
255
 
 
256
static void
 
257
swap_buffers(tuple ** const buffer1P, tuple ** const buffer2P) {
 
258
    /* Advance "next" line to "current" line by switching
 
259
       line buffers 
 
260
    */
 
261
    tuple *tmp;
 
262
 
 
263
    tmp = *buffer1P;
 
264
    *buffer1P = *buffer2P;
 
265
    *buffer2P = tmp;
 
266
}
 
267
 
 
268
 
 
269
static void 
 
270
stretch(struct pam * const inpamP, struct pam * const outpamP,
 
271
        int const xscale, int const yscale,
 
272
        enum an_edge_mode const edge_mode) {
 
273
 
 
274
    tuple *linebuf1, *linebuf2;  /* Input buffers for two rows at a time */
 
275
    tuple *curline, *nextline;   /* Pointers to one of the two above buffers */
 
276
    /* And the stretched versions: */
 
277
    tuple *stretched_linebuf1, *stretched_linebuf2;
 
278
    tuple *curline_stretched, *nextline_stretched;
 
279
 
 
280
    tuple *outbuf;   /* One-row output buffer */
 
281
    unsigned int row;
 
282
    unsigned int rowsToStretch;
 
283
    
 
284
    linebuf1 =           pnm_allocpamrow(inpamP);
 
285
    linebuf2 =           pnm_allocpamrow(inpamP);
 
286
    stretched_linebuf1 = pnm_allocpamrow(outpamP);
 
287
    stretched_linebuf2 = pnm_allocpamrow(outpamP);
 
288
    outbuf =             pnm_allocpamrow(outpamP);
 
289
 
 
290
    curline = linebuf1;
 
291
    curline_stretched = stretched_linebuf1;
 
292
    nextline = linebuf2;
 
293
    nextline_stretched = stretched_linebuf2;
 
294
 
 
295
    pnm_readpamrow(inpamP, curline);
 
296
    stretch_line(inpamP, curline, curline_stretched, xscale, edge_mode);
 
297
 
 
298
    if (edge_mode == EDGE_DROP) 
 
299
        rowsToStretch = inpamP->height - 1;
 
300
    else
 
301
        rowsToStretch = inpamP->height;
 
302
    
 
303
    for (row = 0; row < rowsToStretch; row++) {
 
304
        if (row == inpamP->height-1) {
 
305
            /* last line is about to be output. there is no further
 
306
             * `next line'.  if EDGE_DROP, we stop here, with output
 
307
             * of rows-1 rows.  if EDGE_INTERP_TO_BLACK we make next
 
308
             * line black.  if EDGE_NON_INTERP (default) we make it a
 
309
             * copy of the current line.  
 
310
             */
 
311
            switch (edge_mode) {
 
312
            case EDGE_INTERP_TO_BLACK: {
 
313
                int col;
 
314
                for (col = 0; col < outpamP->width; col++)
 
315
                    nextline_stretched[col] = blackTuple;
 
316
            } 
 
317
            break;
 
318
            case EDGE_NON_INTERP: {
 
319
                /* EDGE_NON_INTERP */
 
320
                int col;
 
321
                for (col = 0; col < outpamP->width; col++)
 
322
                    nextline_stretched[col] = curline_stretched[col];
 
323
            }
 
324
            break;
 
325
            case EDGE_DROP: 
 
326
                pm_error("INTERNAL ERROR: processing last row, but "
 
327
                         "edge_mode is EDGE_DROP.");
 
328
            }
 
329
        } else {
 
330
            pnm_readpamrow(inpamP, nextline);
 
331
            stretch_line(inpamP, nextline, nextline_stretched, xscale,
 
332
                         edge_mode);
 
333
        }
 
334
        
 
335
        /* interpolate curline towards nextline into outbuf */
 
336
        write_interp_rows(outpamP, curline_stretched, nextline_stretched,
 
337
                          outbuf, yscale);
 
338
 
 
339
        swap_buffers(&curline, &nextline);
 
340
        swap_buffers(&curline_stretched, &nextline_stretched);
 
341
    }
 
342
    pnm_freerow(outbuf);
 
343
    pnm_freerow(stretched_linebuf2);
 
344
    pnm_freerow(stretched_linebuf1);
 
345
    pnm_freerow(linebuf2);
 
346
    pnm_freerow(linebuf1);
 
347
}
 
348
 
 
349
 
 
350
 
 
351
int 
 
352
main(int argc,char *argv[]) {
 
353
 
 
354
    FILE *ifp;
 
355
 
 
356
    struct cmdline_info cmdline; 
 
357
    struct pam inpam, outpam;
 
358
    
 
359
    pnm_init(&argc, argv);
 
360
 
 
361
    parse_command_line(argc, argv, &cmdline);
 
362
 
 
363
    ifp = pm_openr(cmdline.input_filespec);
 
364
 
 
365
    pnm_readpaminit(ifp, &inpam, sizeof(inpam));
 
366
 
 
367
    if (inpam.width < 2)
 
368
        pm_error("Image is too narrow.  Must be at least 2 columns.");
 
369
    if (inpam.height < 2)
 
370
        pm_error("Image is too short.  Must be at least 2 lines.");
 
371
 
 
372
 
 
373
    outpam = inpam;  /* initial value */
 
374
    outpam.file = stdout;
 
375
 
 
376
    if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE) {
 
377
        outpam.format = PGM_TYPE;
 
378
        /* usual filter message when reading PBM but writing PGM: */
 
379
        pm_message("pamstretch: promoting from PBM to PGM");
 
380
    } else {
 
381
        outpam.format = PPM_TYPE;
 
382
    }
 
383
    {
 
384
        unsigned int const dropped = cmdline.edge_mode == EDGE_DROP ? 1 : 0;
 
385
 
 
386
        outpam.width = (inpam.width - dropped) * cmdline.xscale;
 
387
        outpam.height = (inpam.height - dropped) * cmdline.yscale;
 
388
 
 
389
        pnm_writepaminit(&outpam);
 
390
    }
 
391
 
 
392
    createBlackTuple(&outpam, &blackTuple);
 
393
 
 
394
    stretch(&inpam, &outpam, 
 
395
            cmdline.xscale, cmdline.yscale, cmdline.edge_mode);
 
396
 
 
397
    pm_close(ifp);
 
398
 
 
399
    exit(0);
 
400
}
 
401
 
 
402
 
 
403