1 /* Copyright (C) 2001-2017 Peter Selinger.
2 This file is part of Potrace. It is free software and it is covered
3 by the GNU General Public License. See the file COPYING for details. */
4
5 /* mkbitmap.c: a standalone program for converting greymaps to bitmaps
6 while optionally applying the following enhancements: highpass
7 filter (evening out background gradients), lowpass filter
8 (smoothing foreground details), interpolated scaling, inversion. */
9
10 #define _XOPEN_SOURCE 500
11
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <math.h>
21 #include <getopt.h>
22
23 #include "greymap.h"
24 #include "bitmap_io.h"
25 #include "platform.h"
26
27 #define SAFE_CALLOC(var, n, typ) \
28 if ((var = (typ *)calloc(n, sizeof(typ))) == NULL) goto calloc_error
29
30 /* structure to hold command line options */
31 struct info_s {
32 char *outfile; /* output file */
33 char **infiles; /* input files */
34 int infilecount; /* how many input files? */
35 int invert; /* invert input? */
36 int highpass; /* use highpass filter? */
37 double lambda; /* highpass filter radius */
38 int lowpass; /* use lowpass filter? */
39 double lambda1; /* lowpass filter radius */
40 int scale; /* scaling factor */
41 int linear; /* linear scaling? */
42 int bilevel; /* convert to bilevel? */
43 double level; /* cutoff grey level */
44 const char *outext; /* default output file extension */
45 };
46 typedef struct info_s info_t;
47
48 static info_t info;
49
50 /* apply lowpass filter (an approximate Gaussian blur) to greymap.
51 Lambda is the standard deviation of the kernel of the filter (i.e.,
52 the approximate filter radius). */
lowpass(greymap_t * gm,double lambda)53 static void lowpass(greymap_t *gm, double lambda) {
54 double f, g;
55 double c, d;
56 double B;
57 int x, y;
58
59 if (gm->h == 0 || gm->w == 0) {
60 return;
61 }
62
63 /* calculate filter coefficients from given lambda */
64 B = 1+2/(lambda*lambda);
65 c = B-sqrt(B*B-1);
66 d = 1-c;
67
68 for (y=0; y<gm->h; y++) {
69 /* apply low-pass filter to row y */
70 /* left-to-right */
71 f = g = 0;
72 for (x=0; x<gm->w; x++) {
73 f = f*c + GM_UGET(gm, x, y)*d;
74 g = g*c + f*d;
75 GM_UPUT(gm, x, y, g);
76 }
77
78 /* right-to-left */
79 for (x=gm->w-1; x>=0; x--) {
80 f = f*c + GM_UGET(gm, x, y)*d;
81 g = g*c + f*d;
82 GM_UPUT(gm, x, y, g);
83 }
84
85 /* left-to-right mop-up */
86 for (x=0; x<gm->w; x++) {
87 f = f*c;
88 g = g*c + f*d;
89 if (f+g < 1/255.0) {
90 break;
91 }
92 GM_UPUT(gm, x, y, GM_UGET(gm, x, y)+g);
93 }
94 }
95
96 for (x=0; x<gm->w; x++) {
97 /* apply low-pass filter to column x */
98 /* bottom-to-top */
99 f = g = 0;
100 for (y=0; y<gm->h; y++) {
101 f = f*c + GM_UGET(gm, x, y)*d;
102 g = g*c + f*d;
103 GM_UPUT(gm, x, y, g);
104 }
105
106 /* top-to-bottom */
107 for (y=gm->h-1; y>=0; y--) {
108 f = f*c + GM_UGET(gm, x, y)*d;
109 g = g*c + f*d;
110 GM_UPUT(gm, x, y, g);
111 }
112
113 /* bottom-to-top mop-up */
114 for (y=0; y<gm->h; y++) {
115 f = f*c;
116 g = g*c + f*d;
117 if (f+g < 1/255.0) {
118 break;
119 }
120 GM_UPUT(gm, x, y, GM_UGET(gm, x, y)+g);
121 }
122 }
123 }
124
125 /* apply highpass filter to greymap. Return 0 on success, 1 on error
126 with errno set. */
highpass(greymap_t * gm,double lambda)127 static int highpass(greymap_t *gm, double lambda) {
128 greymap_t *gm1;
129 double f;
130 int x, y;
131
132 if (gm->h == 0 || gm->w == 0) {
133 return 0;
134 }
135
136 /* create a copy */
137 gm1 = gm_dup(gm);
138 if (!gm1) {
139 return 1;
140 }
141
142 /* apply lowpass filter to the copy */
143 lowpass(gm1, lambda);
144
145 /* subtract copy from original */
146 for (y=0; y<gm->h; y++) {
147 for (x=0; x<gm->w; x++) {
148 f = GM_UGET(gm, x, y);
149 f -= GM_UGET(gm1, x, y);
150 f += 128; /* normalize! */
151 GM_UPUT(gm, x, y, f);
152 }
153 }
154 gm_free(gm1);
155 return 0;
156 }
157
158 /* Convert greymap to bitmap by using cutoff threshold c (0=black,
159 1=white). On error, return NULL with errno set. */
threshold(greymap_t * gm,double c)160 static potrace_bitmap_t *threshold(greymap_t *gm, double c) {
161 int w, h;
162 potrace_bitmap_t *bm_out = NULL;
163 double c1;
164 int x, y;
165 double p;
166
167 w = gm->w;
168 h = gm->h;
169
170 /* allocate output bitmap */
171 bm_out = bm_new(w, h);
172 if (!bm_out) {
173 return NULL;
174 }
175
176 /* thresholding */
177 c1 = c * 255;
178
179 for (y=0; y<h; y++) {
180 for (x=0; x<w; x++) {
181 p = GM_UGET(gm, x, y);
182 BM_UPUT(bm_out, x, y, p < c1);
183 }
184 }
185 return bm_out;
186 }
187
188 /* scale greymap by factor s, using linear interpolation. If
189 bilevel=0, return a pointer to a greymap_t. If bilevel=1, return a
190 pointer to a potrace_bitmap_t and use cutoff threshold c (0=black,
191 1=white). On error, return NULL with errno set. */
192
interpolate_linear(greymap_t * gm,int s,int bilevel,double c)193 static void *interpolate_linear(greymap_t *gm, int s, int bilevel, double c) {
194 int p00, p01, p10, p11;
195 int i, j, x, y;
196 double xx, yy, av;
197 double c1 = 0;
198 int w, h;
199 double p0, p1;
200 greymap_t *gm_out = NULL;
201 potrace_bitmap_t *bm_out = NULL;
202
203 w = gm->w;
204 h = gm->h;
205
206 /* allocate output bitmap/greymap */
207 if (bilevel) {
208 bm_out = bm_new(w*s, h*s);
209 if (!bm_out) {
210 return NULL;
211 }
212 c1 = c * 255;
213 } else {
214 gm_out = gm_new(w*s, h*s);
215 if (!gm_out) {
216 return NULL;
217 }
218 }
219
220 /* interpolate */
221 for (i=0; i<w; i++) {
222 for (j=0; j<h; j++) {
223 p00 = GM_BGET(gm, i, j);
224 p01 = GM_BGET(gm, i, j+1);
225 p10 = GM_BGET(gm, i+1, j);
226 p11 = GM_BGET(gm, i+1, j+1);
227
228 if (bilevel) {
229 /* treat two special cases which are very common */
230 if (p00 < c1 && p01 < c1 && p10 < c1 && p11 < c1) {
231 for (x=0; x<s; x++) {
232 for (y=0; y<s; y++) {
233 BM_UPUT(bm_out, i*s+x, j*s+y, 1);
234 }
235 }
236 continue;
237 }
238 if (p00 >= c1 && p01 >= c1 && p10 >= c1 && p11 >= c1) {
239 continue;
240 }
241 }
242
243 /* the general case */
244 for (x=0; x<s; x++) {
245 xx = x/(double)s;
246 p0 = p00*(1-xx) + p10*xx;
247 p1 = p01*(1-xx) + p11*xx;
248 for (y=0; y<s; y++) {
249 yy = y/(double)s;
250 av = p0*(1-yy) + p1*yy;
251 if (bilevel) {
252 BM_UPUT(bm_out, i*s+x, j*s+y, av < c1);
253 } else {
254 GM_UPUT(gm_out, i*s+x, j*s+y, av);
255 }
256 }
257 }
258 }
259 }
260 if (bilevel) {
261 return (void *)bm_out;
262 } else {
263 return (void *)gm_out;
264 }
265 }
266
267 /* same as interpolate_linear, except use cubic interpolation (slower
268 and better). */
269
270 /* we need this typedef so that the SAFE_CALLOC macro will work */
271 typedef double double4[4];
272
interpolate_cubic(greymap_t * gm,int s,int bilevel,double c)273 static void *interpolate_cubic(greymap_t *gm, int s, int bilevel, double c) {
274 int w, h;
275 double4 *poly = NULL; /* poly[s][4]: fixed interpolation polynomials */
276 double p[4]; /* four current points */
277 double4 *window = NULL; /* window[s][4]: current state */
278 double t, v;
279 int k, l, i, j, x, y;
280 double c1 = 0;
281 greymap_t *gm_out = NULL;
282 potrace_bitmap_t *bm_out = NULL;
283
284 SAFE_CALLOC(poly, s, double4);
285 SAFE_CALLOC(window, s, double4);
286
287 w = gm->w;
288 h = gm->h;
289
290 /* allocate output bitmap/greymap */
291 if (bilevel) {
292 bm_out = bm_new(w*s, h*s);
293 if (!bm_out) {
294 goto calloc_error;
295 }
296 c1 = c * 255;
297 } else {
298 gm_out = gm_new(w*s, h*s);
299 if (!gm_out) {
300 goto calloc_error;
301 }
302 }
303
304 /* pre-calculate interpolation polynomials */
305 for (k=0; k<s; k++) {
306 t = k/(double)s;
307 poly[k][0] = 0.5 * t * (t-1) * (1-t);
308 poly[k][1] = -(t+1) * (t-1) * (1-t) + 0.5 * (t-1) * (t-2) * t;
309 poly[k][2] = 0.5 * (t+1) * t * (1-t) - t * (t-2) * t;
310 poly[k][3] = 0.5 * t * (t-1) * t;
311 }
312
313 /* interpolate */
314 for (y=0; y<h; y++) {
315 x=0;
316 for (i=0; i<4; i++) {
317 for (j=0; j<4; j++) {
318 p[j] = GM_BGET(gm, x+i-1, y+j-1);
319 }
320 for (k=0; k<s; k++) {
321 window[k][i] = 0.0;
322 for (j=0; j<4; j++) {
323 window[k][i] += poly[k][j] * p[j];
324 }
325 }
326 }
327 while (1) {
328 for (l=0; l<s; l++) {
329 for (k=0; k<s; k++) {
330 v = 0.0;
331 for (i=0; i<4; i++) {
332 v += window[k][i] * poly[l][i];
333 }
334 if (bilevel) {
335 BM_UPUT(bm_out, x*s+l, y*s+k, v < c1);
336 } else {
337 GM_UPUT(gm_out, x*s+l, y*s+k, v);
338 }
339 }
340 }
341 x++;
342 if (x>=w) {
343 break;
344 }
345 for (i=0; i<3; i++) {
346 for (k=0; k<s; k++) {
347 window[k][i] = window[k][i+1];
348 }
349 }
350 i=3;
351 for (j=0; j<4; j++) {
352 p[j] = GM_BGET(gm, x+i-1, y+j-1);
353 }
354 for (k=0; k<s; k++) {
355 window[k][i] = 0.0;
356 for (j=0; j<4; j++) {
357 window[k][i] += poly[k][j] * p[j];
358 }
359 }
360 }
361 }
362
363 free(poly);
364 free(window);
365
366 if (bilevel) {
367 return (void *)bm_out;
368 } else {
369 return (void *)gm_out;
370 }
371
372 calloc_error:
373 free(poly);
374 free(window);
375 return NULL;
376 }
377
378 /* ---------------------------------------------------------------------- */
379
380 /* process a single file, containing one or more images. On error,
381 print error message to stderr and exit with code 2. On warning,
382 print warning message to stderr. */
383
process_file(FILE * fin,FILE * fout,const char * infile,const char * outfile)384 static void process_file(FILE *fin, FILE *fout, const char *infile, const char *outfile) {
385 int r;
386 greymap_t *gm;
387 potrace_bitmap_t *bm;
388 void *sm;
389 int x, y;
390 int count;
391
392 for (count=0; ; count++) {
393 r = gm_read(fin, &gm);
394 switch (r) {
395 case -1: /* system error */
396 fprintf(stderr, "" MKBITMAP ": %s: %s\n", infile, strerror(errno));
397 exit(2);
398 case -2: /* corrupt file format */
399 fprintf(stderr, "" MKBITMAP ": %s: file format error: %s\n", infile, gm_read_error);
400 exit(2);
401 case -3: /* empty file */
402 if (count>0) { /* end of file */
403 return;
404 }
405 fprintf(stderr, "" MKBITMAP ": %s: empty file\n", infile);
406 exit(2);
407 case -4: /* wrong magic */
408 if (count>0) {
409 fprintf(stderr, "" MKBITMAP ": %s: warning: junk at end of file\n", infile);
410 return;
411 }
412 fprintf(stderr, "" MKBITMAP ": %s: file format not recognized\n", infile);
413 fprintf(stderr, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
414 exit(2);
415 case 1: /* unexpected end of file */
416 fprintf(stderr, "" MKBITMAP ": %s: warning: premature end of file\n", infile);
417 break;
418 }
419
420 if (info.invert) {
421 for (y=0; y<gm->h; y++) {
422 for (x=0; x<gm->w; x++) {
423 GM_UPUT(gm, x, y, 255-GM_UGET(gm, x, y));
424 }
425 }
426 }
427
428 if (info.highpass) {
429 r = highpass(gm, info.lambda);
430 if (r) {
431 fprintf(stderr, "" MKBITMAP ": %s: %s\n", infile, strerror(errno));
432 exit(2);
433 }
434 }
435
436 if (info.lowpass) {
437 lowpass(gm, info.lambda1);
438 }
439
440 if (info.scale == 1 && info.bilevel) { /* no interpolation necessary */
441 sm = threshold(gm, info.level);
442 gm_free(gm);
443 } else if (info.scale == 1) {
444 sm = gm;
445 } else if (info.linear) { /* linear interpolation */
446 sm = interpolate_linear(gm, info.scale, info.bilevel, info.level);
447 gm_free(gm);
448 } else { /* cubic interpolation */
449 sm = interpolate_cubic(gm, info.scale, info.bilevel, info.level);
450 gm_free(gm);
451 }
452 if (!sm) {
453 fprintf(stderr, "" MKBITMAP ": %s: %s\n", infile, strerror(errno));
454 exit(2);
455 }
456
457 if (info.bilevel) {
458 bm = (potrace_bitmap_t *)sm;
459 bm_writepbm(fout, bm);
460 bm_free(bm);
461 } else {
462 gm = (greymap_t *)sm;
463 gm_writepgm(fout, gm, NULL, 1, GM_MODE_POSITIVE, 1.0);
464 gm_free(gm);
465 }
466 }
467 }
468
469 /* ---------------------------------------------------------------------- */
470 /* some info functions and option processing */
471
license(FILE * f)472 static int license(FILE *f) {
473 fprintf(f,
474 "This program is free software; you can redistribute it and/or modify\n"
475 "it under the terms of the GNU General Public License as published by\n"
476 "the Free Software Foundation; either version 2 of the License, or\n"
477 "(at your option) any later version.\n"
478 "\n"
479 "This program is distributed in the hope that it will be useful,\n"
480 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
481 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
482 "GNU General Public License for more details.\n"
483 "\n"
484 "You should have received a copy of the GNU General Public License\n"
485 "along with this program; if not, write to the Free Software Foundation\n"
486 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
487 );
488 return 0;
489 }
490
usage(FILE * f)491 static int usage(FILE *f) {
492 fprintf(f, "Usage: " MKBITMAP " [options] [file...]\n");
493 fprintf(f, "Options:\n");
494 fprintf(f, " -h, --help - print this help message and exit\n");
495 fprintf(f, " -v, --version - print version info and exit\n");
496 fprintf(f, " -l, --license - print license info and exit\n");
497 fprintf(f, " -o, --output <file> - output to file\n");
498 fprintf(f, " -x, --nodefaults - turn off default options\n");
499 fprintf(f, "Inversion:\n");
500 fprintf(f, " -i, --invert - invert the input (undo 'blackboard' effect)\n");
501 fprintf(f, "Highpass filtering:\n");
502 fprintf(f, " -f, --filter <n> - apply highpass filter with radius n (default 4)\n");
503 fprintf(f, " -n, --nofilter - no highpass filtering\n");
504 fprintf(f, " -b, --blur <n> - apply lowpass filter with radius n (default: none)\n");
505 fprintf(f, "Scaling:\n");
506 fprintf(f, " -s, --scale <n> - scale by integer factor n (default 2)\n");
507 fprintf(f, " -1, --linear - use linear interpolation\n");
508 fprintf(f, " -3, --cubic - use cubic interpolation (default)\n");
509 fprintf(f, "Thresholding:\n");
510 fprintf(f, " -t, --threshold <n> - set threshold for bilevel conversion (default 0.45)\n");
511 fprintf(f, " -g, --grey - no bilevel conversion, output a greymap\n");
512
513 fprintf(f, "\n");
514 fprintf(f, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
515 fprintf(f, "The default options are: -f 4 -s 2 -3 -t 0.45\n");
516
517 return 0;
518 }
519
520 static struct option longopts[] = {
521 {"help", 0, 0, 'h'},
522 {"version", 0, 0, 'v'},
523 {"license", 0, 0, 'l'},
524 {"output", 1, 0, 'o'},
525 {"reset", 0, 0, 'x'},
526 {"invert", 0, 0, 'i'},
527 {"filter", 1, 0, 'f'},
528 {"nofilter", 0, 0, 'n'},
529 {"blur", 1, 0, 'b'},
530 {"scale", 1, 0, 's'},
531 {"linear", 0, 0, '1'},
532 {"cubic", 0, 0, '3'},
533 {"grey", 0, 0, 'g'},
534 {"threshold", 1, 0, 't'},
535 {0, 0, 0, 0}
536 };
537
538 static const char *shortopts = "hvlo:xif:nb:s:13gt:";
539
540 /* process options. On error, print error message to stderr and exit
541 with code 1 */
dopts(int ac,char * av[])542 static void dopts(int ac, char *av[]) {
543 int c;
544 char *p;
545
546 /* set defaults for command line parameters */
547 info.outfile = NULL; /* output file */
548 info.infiles = NULL; /* input files */
549 info.infilecount = 0; /* how many input files? */
550 info.invert = 0; /* invert input? */
551 info.highpass = 1; /* use highpass filter? */
552 info.lambda = 4; /* highpass filter radius */
553 info.lowpass = 0; /* use lowpass filter? */
554 info.lambda1 = 0; /* lowpass filter radius */
555 info.scale = 2; /* scaling factor */
556 info.linear = 0; /* linear scaling? */
557 info.bilevel = 1; /* convert to bilevel? */
558 info.level = 0.45; /* cutoff grey level */
559 info.outext = ".pbm"; /* output file extension */
560
561 while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) {
562 switch (c) {
563 case 'h':
564 fprintf(stdout, "" MKBITMAP " " VERSION ". Transforms images into bitmaps with scaling and filtering.\n\n");
565 usage(stdout);
566 exit(0);
567 break;
568 case 'v':
569 fprintf(stdout, "" MKBITMAP " " VERSION ". Copyright (C) 2001-2017 Peter Selinger.\n");
570 exit(0);
571 break;
572 case 'l':
573 fprintf(stdout, "" MKBITMAP " " VERSION ". Copyright (C) 2001-2017 Peter Selinger.\n\n");
574 license(stdout);
575 exit(0);
576 break;
577 case 'o':
578 free(info.outfile);
579 info.outfile = strdup(optarg);
580 if (!info.outfile) {
581 fprintf(stderr, "" MKBITMAP ": %s\n", strerror(errno));
582 exit(2);
583 }
584 break;
585 case 'x':
586 info.invert = 0;
587 info.highpass = 0;
588 info.scale = 1;
589 info.bilevel = 0;
590 info.outext = ".pgm";
591 break;
592 case 'i':
593 info.invert = 1;
594 break;
595 case 'f':
596 info.highpass = 1;
597 info.lambda = strtod(optarg, &p);
598 if (*p || info.lambda<0) {
599 fprintf(stderr, "" MKBITMAP ": invalid filter radius -- %s\n", optarg);
600 exit(1);
601 }
602 break;
603 case 'n':
604 info.highpass = 0;
605 break;
606 case 'b':
607 info.lowpass = 1;
608 info.lambda1 = strtod(optarg, &p);
609 if (*p || info.lambda1<0) {
610 fprintf(stderr, "" MKBITMAP ": invalid filter radius -- %s\n", optarg);
611 exit(1);
612 }
613 break;
614 case 's':
615 info.scale = strtol(optarg, &p, 0);
616 if (*p || info.scale<=0) {
617 fprintf(stderr, "" MKBITMAP ": invalid scaling factor -- %s\n", optarg);
618 exit(1);
619 }
620 break;
621 case '1':
622 info.linear = 1;
623 break;
624 case '3':
625 info.linear = 0;
626 break;
627 case 'g':
628 info.bilevel = 0;
629 info.outext = ".pgm";
630 break;
631 case 't':
632 info.bilevel = 1;
633 info.outext = ".pbm";
634 info.level = strtod(optarg, &p);
635 if (*p || info.level<0) {
636 fprintf(stderr, "" MKBITMAP ": invalid threshold -- %s\n", optarg);
637 exit(1);
638 }
639 break;
640 case '?':
641 fprintf(stderr, "Try --help for more info\n");
642 exit(1);
643 break;
644 default:
645 fprintf(stderr, "" MKBITMAP ": Unimplemented option -- %c\n", c);
646 exit(1);
647 }
648 }
649 info.infiles = &av[optind];
650 info.infilecount = ac-optind;
651 return;
652 }
653
654 /* ---------------------------------------------------------------------- */
655 /* auxiliary functions for file handling */
656
657 /* open a file for reading. Return stdin if filename is NULL or "-" */
my_fopen_read(const char * filename)658 static FILE *my_fopen_read(const char *filename) {
659 if (filename == NULL || strcmp(filename, "-") == 0) {
660 return stdin;
661 }
662 return fopen(filename, "rb");
663 }
664
665 /* open a file for writing. Return stdout if filename is NULL or "-" */
my_fopen_write(const char * filename)666 static FILE *my_fopen_write(const char *filename) {
667 if (filename == NULL || strcmp(filename, "-") == 0) {
668 return stdout;
669 }
670 return fopen(filename, "wb");
671 }
672
673 /* close a file, but do nothing is filename is NULL or "-" */
my_fclose(FILE * f,const char * filename)674 static void my_fclose(FILE *f, const char *filename) {
675 if (filename == NULL || strcmp(filename, "-") == 0) {
676 return;
677 }
678 fclose(f);
679 }
680
681 /* make output filename from input filename. Return an allocated value. */
make_outfilename(const char * infile,const char * ext)682 static char *make_outfilename(const char *infile, const char *ext) {
683 char *outfile;
684 char *p;
685
686 if (strcmp(infile, "-") == 0) {
687 return strdup("-");
688 }
689
690 outfile = (char *) malloc(strlen(infile)+strlen(ext)+5);
691 if (!outfile) {
692 return NULL;
693 }
694 strcpy(outfile, infile);
695 p = strrchr(outfile, '.');
696 if (p) {
697 *p = 0;
698 }
699 strcat(outfile, ext);
700
701 /* check that input and output filenames are different */
702 if (strcmp(infile, outfile) == 0) {
703 strcpy(outfile, infile);
704 strcat(outfile, "-out");
705 }
706
707 return outfile;
708 }
709
710 /* ---------------------------------------------------------------------- */
711 /* Main function */
712
main(int ac,char * av[])713 int main(int ac, char *av[]) {
714 FILE *fin, *fout;
715 int i;
716 char *outfile;
717
718 /* platform-specific initializations, e.g., set file i/o to binary */
719 platform_init();
720
721 /* process options */
722 dopts(ac, av);
723
724 /* there are several ways to call us:
725 mkbitmap -- stdin to stdout
726 mkbitmap -o outfile -- stdin to outfile
727 mkbitmap file... -- encode each file and generate outfile names
728 mkbitmap file... -o outfile -- concatenate files and write to outfile
729 */
730
731 if (info.infilecount == 0 && info.outfile == NULL) { /* stdin to stdout */
732
733 process_file(stdin, stdout, "stdin", "stdout");
734 return 0;
735
736 } else if (info.infilecount == 0) { /* stdin to outfile */
737
738 fout = my_fopen_write(info.outfile);
739 if (!fout) {
740 fprintf(stderr, "" MKBITMAP ": %s: %s\n", info.outfile, strerror(errno));
741 exit(2);
742 }
743 process_file(stdin, fout, "stdin", info.outfile);
744 my_fclose(fout, info.outfile);
745 free(info.outfile);
746 return 0;
747
748 } else if (info.outfile == NULL) { /* infiles -> multiple outfiles */
749
750 for (i=0; i<info.infilecount; i++) {
751 outfile = make_outfilename(info.infiles[i], info.outext);
752 if (!outfile) {
753 fprintf(stderr, "" MKBITMAP ": %s\n", strerror(errno));
754 exit(2);
755 }
756 fin = my_fopen_read(info.infiles[i]);
757 if (!fin) {
758 fprintf(stderr, "" MKBITMAP ": %s: %s\n", info.infiles[i], strerror(errno));
759 exit(2);
760 }
761 fout = my_fopen_write(outfile);
762 if (!fout) {
763 fprintf(stderr, "" MKBITMAP ": %s: %s\n", outfile, strerror(errno));
764 exit(2);
765 }
766 process_file(fin, fout, info.infiles[i], outfile);
767 my_fclose(fin, info.infiles[i]);
768 my_fclose(fout, outfile);
769 free(outfile);
770 }
771 return 0;
772
773 } else { /* infiles to single outfile */
774
775 fout = my_fopen_write(info.outfile);
776 if (!fout) {
777 fprintf(stderr, "" MKBITMAP ": %s: %s\n", info.outfile, strerror(errno));
778 exit(2);
779 }
780 for (i=0; i<info.infilecount; i++) {
781 fin = my_fopen_read(info.infiles[i]);
782 if (!fin) {
783 fprintf(stderr, "" MKBITMAP ": %s: %s\n", info.infiles[i], strerror(errno));
784 exit(2);
785 }
786 process_file(fin, fout, info.infiles[i], info.outfile);
787 my_fclose(fin, info.infiles[i]);
788 }
789 my_fclose(fout, info.outfile);
790 free(info.outfile);
791 return 0;
792
793 }
794
795 /* not reached */
796 }
797