xref: /potrace-1.14/src/mkbitmap.c (revision b3fce824)
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