00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <stdio.h>
00010 #include <errno.h>
00011 #include <string.h>
00012 #include <stdlib.h>
00013 #include <math.h>
00014 #include <getopt.h>
00015
00016 #include "greymap.h"
00017 #include "bitmap_io.h"
00018 #include "platform.h"
00019
00020 #ifdef HAVE_CONFIG_H
00021 #include "config.h"
00022 #endif
00023
00024 #define SAFE_MALLOC(var, n, typ) \
00025 if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error
00026
00027
00028 struct info_s {
00029 char *outfile;
00030 char **infiles;
00031 int infilecount;
00032 int invert;
00033 int highpass;
00034 double lambda;
00035 int scale;
00036 int linear;
00037 int bilevel;
00038 double level;
00039 char *outext;
00040 };
00041 typedef struct info_s info_t;
00042
00043 static info_t info;
00044
00045
00046
00047 static int highpass(greymap_t *gm, double lambda) {
00048 greymap_t *gm1;
00049 double f, g;
00050 double c, d;
00051 double B;
00052 int x, y;
00053
00054
00055
00056
00057 gm1 = gm_new(gm->w, gm->h);
00058 if (!gm1) {
00059 return 1;
00060 }
00061
00062
00063 B = 1+2/(lambda*lambda);
00064 c = B-sqrt(B*B-1);
00065 d = 1-c;
00066
00067 for (y=0; y<gm->h; y++) {
00068
00069
00070 f = g = 0;
00071 for (x=0; x<gm->w; x++) {
00072 f = f*c + GM_GET(gm, x, y)*d;
00073 g = g*c + f*d;
00074 GM_UPUT(gm1, x, y, g);
00075 }
00076
00077
00078 for (x=gm->w-1; x>=0; x--) {
00079 f = f*c + GM_GET(gm1, x, y)*d;
00080 g = g*c + f*d;
00081 GM_UPUT(gm1, x, y, g);
00082 }
00083
00084
00085 for (x=0; x<gm->w; x++) {
00086 f = f*c;
00087 g = g*c + f*d;
00088 if (f+g < 1/255.0) {
00089 break;
00090 }
00091 GM_UPUT(gm1, x, y, GM_GET(gm1, x, y)+g);
00092 }
00093 }
00094
00095 for (x=0; x<gm->w; x++) {
00096
00097
00098 f = g = 0;
00099 for (y=0; y<gm->h; y++) {
00100 f = f*c + GM_GET(gm1, x, y)*d;
00101 g = g*c + f*d;
00102 GM_UPUT(gm1, x, y, g);
00103 }
00104
00105
00106 for (y=gm->h-1; y>=0; y--) {
00107 f = f*c + GM_GET(gm1, x, y)*d;
00108 g = g*c + f*d;
00109 GM_UPUT(gm1, x, y, g);
00110 }
00111
00112
00113 for (y=0; y<gm->h; y++) {
00114 f = f*c;
00115 g = g*c + f*d;
00116 if (f+g < 1/255.0) {
00117 break;
00118 }
00119 GM_UPUT(gm1, x, y, GM_GET(gm1, x, y)+g);
00120 }
00121 }
00122
00123
00124 for (y=0; y<gm->h; y++) {
00125 for (x=0; x<gm->w; x++) {
00126 f = GM_GET(gm, x, y);
00127 f -= GM_GET(gm1, x, y);
00128 f += 128;
00129 GM_PUT(gm, x, y, f);
00130 }
00131 }
00132 gm_free(gm1);
00133 return 0;
00134 }
00135
00136
00137
00138
00139
00140
00141 static void *interpolate_linear(greymap_t *gm, int s, int bilevel, double c) {
00142 int p00, p01, p10, p11;
00143 int i, j, x, y;
00144 double xx, yy, av;
00145 int c1 = 0;
00146 int w, h;
00147 double p0, p1;
00148 greymap_t *gm_out = NULL;
00149 potrace_bitmap_t *bm_out = NULL;
00150
00151 w = gm->w;
00152 h = gm->h;
00153
00154
00155 if (bilevel) {
00156 bm_out = bm_new(w*s, h*s);
00157 if (!bm_out) {
00158 return NULL;
00159 }
00160 bm_clear(bm_out, 0);
00161 c1 = (int)(c*255);
00162 } else {
00163 gm_out = gm_new(w*s, h*s);
00164 if (!gm_out) {
00165 return NULL;
00166 }
00167 }
00168
00169
00170 for (i=0; i<w; i++) {
00171 for (j=0; j<h; j++) {
00172 p00 = GM_GET(gm, i, j);
00173 p01 = GM_GET(gm, i, j+1);
00174 p10 = GM_GET(gm, i+1, j);
00175 p11 = GM_GET(gm, i+1, j+1);
00176
00177 if (bilevel) {
00178
00179 if (p00 < c1 && p01 < c1 && p10 < c1 && p11 < c1) {
00180 for (x=0; x<s; x++) {
00181 for (y=0; y<s; y++) {
00182 BM_PUT(bm_out, i*s+x, j*s+y, 1);
00183 }
00184 }
00185 continue;
00186 }
00187 if (p00 >= c1 && p01 >= c1 && p10 >= c1 && p11 >= c1) {
00188 continue;
00189 }
00190 }
00191
00192
00193 for (x=0; x<s; x++) {
00194 xx = x/(double)s;
00195 p0 = p00*(1-xx) + p10*xx;
00196 p1 = p01*(1-xx) + p11*xx;
00197 for (y=0; y<s; y++) {
00198 yy = y/(double)s;
00199 av = p0*(1-yy) + p1*yy;
00200 if (bilevel) {
00201 BM_PUT(bm_out, i*s+x, j*s+y, av < c1);
00202 } else {
00203 GM_PUT(gm_out, i*s+x, j*s+y, av);
00204 }
00205 }
00206 }
00207 }
00208 }
00209 if (bilevel) {
00210 return (void *)bm_out;
00211 } else {
00212 return (void *)gm_out;
00213 }
00214 }
00215
00216
00217
00218
00219
00220 typedef double double4[4];
00221
00222 static void *interpolate_cubic(greymap_t *gm, int s, int bilevel, double c) {
00223 int w, h;
00224 double4 *poly = NULL;
00225 double p[4];
00226 double4 *window = NULL;
00227 double t, v;
00228 int k, l, i, j, x, y;
00229 double c1 = 0;
00230 greymap_t *gm_out = NULL;
00231 potrace_bitmap_t *bm_out = NULL;
00232
00233 SAFE_MALLOC(poly, s, double4);
00234 SAFE_MALLOC(window, s, double4);
00235
00236 w = gm->w;
00237 h = gm->h;
00238
00239
00240 if (bilevel) {
00241 bm_out = bm_new(w*s, h*s);
00242 if (!bm_out) {
00243 goto malloc_error;
00244 }
00245 bm_clear(bm_out, 0);
00246 c1 = c * 255;
00247 } else {
00248 gm_out = gm_new(w*s, h*s);
00249 if (!gm_out) {
00250 goto malloc_error;
00251 }
00252 }
00253
00254
00255 for (k=0; k<s; k++) {
00256 t = k/(double)s;
00257 poly[k][0] = 0.5 * t * (t-1) * (1-t);
00258 poly[k][1] = -(t+1) * (t-1) * (1-t) + 0.5 * (t-1) * (t-2) * t;
00259 poly[k][2] = 0.5 * (t+1) * t * (1-t) - t * (t-2) * t;
00260 poly[k][3] = 0.5 * t * (t-1) * t;
00261 }
00262
00263
00264 for (y=0; y<h; y++) {
00265 x=0;
00266 for (i=0; i<4; i++) {
00267 for (j=0; j<4; j++) {
00268 p[j] = GM_BGET(gm, x+i-1, y+j-1);
00269 }
00270 for (k=0; k<s; k++) {
00271 window[k][i] = 0.0;
00272 for (j=0; j<4; j++) {
00273 window[k][i] += poly[k][j] * p[j];
00274 }
00275 }
00276 }
00277 while (1) {
00278 for (l=0; l<s; l++) {
00279 for (k=0; k<s; k++) {
00280 v = 0.0;
00281 for (i=0; i<4; i++) {
00282 v += window[k][i] * poly[l][i];
00283 }
00284 if (bilevel) {
00285 BM_PUT(bm_out, x*s+l, y*s+k, v < c1);
00286 } else {
00287 GM_PUT(gm_out, x*s+l, y*s+k, v);
00288 }
00289 }
00290 }
00291 x++;
00292 if (x>=w) {
00293 break;
00294 }
00295 for (i=0; i<3; i++) {
00296 for (k=0; k<s; k++) {
00297 window[k][i] = window[k][i+1];
00298 }
00299 }
00300 i=3;
00301 for (j=0; j<4; j++) {
00302 p[j] = GM_BGET(gm, x+i-1, y+j-1);
00303 }
00304 for (k=0; k<s; k++) {
00305 window[k][i] = 0.0;
00306 for (j=0; j<4; j++) {
00307 window[k][i] += poly[k][j] * p[j];
00308 }
00309 }
00310 }
00311 }
00312
00313 free(poly);
00314 free(window);
00315
00316 if (bilevel) {
00317 return (void *)bm_out;
00318 } else {
00319 return (void *)gm_out;
00320 }
00321
00322 malloc_error:
00323 free(poly);
00324 free(window);
00325 return NULL;
00326 }
00327
00328
00329
00330
00331
00332
00333
00334 static void process_file(FILE *fin, FILE *fout, char *infile, char *outfile) {
00335 int r;
00336 greymap_t *gm;
00337 potrace_bitmap_t *bm;
00338 void *sm;
00339 int x, y;
00340 int count;
00341
00342 for (count=0; ; count++) {
00343 r = gm_read(fin, &gm);
00344 switch (r) {
00345 case -1:
00346 fprintf(stderr, ""MKBITMAP": %s: %s\n", infile, strerror(errno));
00347 exit(2);
00348 case -2:
00349 fprintf(stderr, ""MKBITMAP": %s: file format error: %s\n", infile, gm_read_error);
00350 exit(2);
00351 case -3:
00352 if (count>0) {
00353 return;
00354 }
00355 fprintf(stderr, ""MKBITMAP": %s: empty file\n", infile);
00356 exit(2);
00357 case -4:
00358 if (count>0) {
00359 fprintf(stderr, ""MKBITMAP": %s: warning: junk at end of file\n", infile);
00360 return;
00361 }
00362 fprintf(stderr, ""MKBITMAP": %s: file format not recognized\n", infile);
00363 fprintf(stderr, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
00364 exit(2);
00365 case 1:
00366 fprintf(stderr, ""MKBITMAP": %s: warning: premature end of file\n", infile);
00367 break;
00368 }
00369
00370 if (info.invert) {
00371 for (y=0; y<gm->h; y++) {
00372 for (x=0; x<gm->w; x++) {
00373 GM_UPUT(gm, x, y, 255-GM_UGET(gm, x, y));
00374 }
00375 }
00376 }
00377
00378 if (info.highpass) {
00379 r = highpass(gm, info.lambda);
00380 if (r) {
00381 fprintf(stderr, ""MKBITMAP": %s: %s\n", infile, strerror(errno));
00382 exit(2);
00383 }
00384 }
00385
00386 if (info.linear) {
00387 sm = interpolate_linear(gm, info.scale, info.bilevel, info.level);
00388 } else {
00389 sm = interpolate_cubic(gm, info.scale, info.bilevel, info.level);
00390 }
00391 if (!sm) {
00392 fprintf(stderr, ""MKBITMAP": %s: %s\n", infile, strerror(errno));
00393 exit(2);
00394 }
00395 gm_free(gm);
00396
00397 if (info.bilevel) {
00398 bm = (potrace_bitmap_t *)sm;
00399 bm_writepbm(fout, bm);
00400 bm_free(bm);
00401 } else {
00402 gm = (greymap_t *)sm;
00403 gm_writepgm(fout, gm, NULL, 1, GM_MODE_POSITIVE, 1.0);
00404 gm_free(gm);
00405 }
00406 }
00407 }
00408
00409
00410
00411
00412 static int license(FILE *f) {
00413 fprintf(f,
00414 "This program is free software; you can redistribute it and/or modify\n"
00415 "it under the terms of the GNU General Public License as published by\n"
00416 "the Free Software Foundation; either version 2 of the License, or\n"
00417 "(at your option) any later version.\n"
00418 "\n"
00419 "This program is distributed in the hope that it will be useful,\n"
00420 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00421 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00422 "GNU General Public License for more details.\n"
00423 "\n"
00424 "You should have received a copy of the GNU General Public License\n"
00425 "along with this program; if not, write to the Free Software\n"
00426 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.\n"
00427 );
00428 return 0;
00429 }
00430
00431 static int usage(FILE *f) {
00432 fprintf(f, "Usage: "MKBITMAP" [options] [file...]\n");
00433 fprintf(f, "Options:\n");
00434 fprintf(f, " -h, --help - print this help message and exit\n");
00435 fprintf(f, " -v, --version - print version info and exit\n");
00436 fprintf(f, " -l, --license - print license info and exit\n");
00437 fprintf(f, " -o, --output <file> - output to file\n");
00438 fprintf(f, " -x, --nodefaults - turn off default options\n");
00439 fprintf(f, "Inversion:\n");
00440 fprintf(f, " -i, --invert - invert the input (undo 'blackboard' effect)\n");
00441 fprintf(f, "Highpass filtering:\n");
00442 fprintf(f, " -f, --filter <n> - apply highpass filter with radius n (default 4)\n");
00443 fprintf(f, " -n, --nofilter - no highpass filtering\n");
00444 fprintf(f, "Scaling:\n");
00445 fprintf(f, " -s, --scale <n> - scale by integer factor n (default 2)\n");
00446 fprintf(f, " -1, --linear - use linear interpolation\n");
00447 fprintf(f, " -3, --cubic - use cubic interpolation (default)\n");
00448 fprintf(f, "Thresholding:\n");
00449 fprintf(f, " -t, --threshold <n> - set threshold for bilevel conversion (default 0.45)\n");
00450 fprintf(f, " -g, --grey - no bilevel conversion, output a greymap\n");
00451
00452 fprintf(f, "\n");
00453 fprintf(f, "Possible input file formats are: pnm (pbm, pgm, ppm), bmp.\n");
00454 fprintf(f, "The default options are: -f 4 -s 2 -3 -t 0.45\n");
00455
00456 return 0;
00457 }
00458
00459 static struct option longopts[] = {
00460 {"help", 0, 0, 'h'},
00461 {"version", 0, 0, 'v'},
00462 {"license", 0, 0, 'l'},
00463 {"output", 1, 0, 'o'},
00464 {"reset", 0, 0, 'x'},
00465 {"invert", 0, 0, 'i'},
00466 {"filter", 1, 0, 'f'},
00467 {"nofilter", 0, 0, 'n'},
00468 {"scale", 1, 0, 's'},
00469 {"linear", 0, 0, '1'},
00470 {"cubic", 0, 0, '3'},
00471 {"grey", 0, 0, 'g'},
00472 {"threshold", 1, 0, 't'},
00473 {0, 0, 0, 0}
00474 };
00475
00476 static char *shortopts = "hvlo:xif:ns:13gt:";
00477
00478
00479
00480 static void dopts(int ac, char *av[]) {
00481 int c;
00482 char *p;
00483
00484
00485 info.outfile = NULL;
00486 info.infiles = NULL;
00487 info.infilecount = 0;
00488 info.invert = 0;
00489 info.highpass = 1;
00490 info.lambda = 4;
00491 info.scale = 2;
00492 info.linear = 0;
00493 info.bilevel = 1;
00494 info.level = 0.45;
00495 info.outext = ".pbm";
00496
00497 while ((c = getopt_long(ac, av, shortopts, longopts, NULL)) != -1) {
00498 switch (c) {
00499 case 'h':
00500 fprintf(stdout, ""MKBITMAP" "VERSION". Transforms images into bitmaps with scaling and filtering.\n\n");
00501 usage(stdout);
00502 exit(0);
00503 break;
00504 case 'v':
00505 fprintf(stdout, ""MKBITMAP" "VERSION". Copyright (C) 2001-2007 Peter Selinger.\n");
00506 exit(0);
00507 break;
00508 case 'l':
00509 fprintf(stdout, ""MKBITMAP" "VERSION". Copyright (C) 2001-2007 Peter Selinger.\n\n");
00510 license(stdout);
00511 exit(0);
00512 break;
00513 case 'o':
00514 free(info.outfile);
00515 info.outfile = strdup(optarg);
00516 break;
00517 case 'x':
00518 info.invert = 0;
00519 info.highpass = 0;
00520 info.scale = 1;
00521 info.bilevel = 0;
00522 info.outext = ".pgm";
00523 break;
00524 case 'i':
00525 info.invert = 1;
00526 break;
00527 case 'f':
00528 info.highpass = 1;
00529 info.lambda = strtod(optarg, &p);
00530 if (*p || info.lambda<0) {
00531 fprintf(stderr, ""MKBITMAP": invalid filter radius -- %s\n", optarg);
00532 exit(1);
00533 }
00534 break;
00535 case 'n':
00536 info.highpass = 0;
00537 break;
00538 case 's':
00539 info.scale = strtol(optarg, &p, 0);
00540 if (*p || info.scale<=0) {
00541 fprintf(stderr, ""MKBITMAP": invalid scaling factor -- %s\n", optarg);
00542 exit(1);
00543 }
00544 break;
00545 case '1':
00546 info.linear = 1;
00547 break;
00548 case '3':
00549 info.linear = 0;
00550 break;
00551 case 'g':
00552 info.bilevel = 0;
00553 info.outext = ".pgm";
00554 break;
00555 case 't':
00556 info.bilevel = 1;
00557 info.outext = ".pbm";
00558 info.level = strtod(optarg, &p);
00559 if (*p || info.level<0) {
00560 fprintf(stderr, ""MKBITMAP": invalid threshold -- %s\n", optarg);
00561 exit(1);
00562 }
00563 break;
00564 case '?':
00565 fprintf(stderr, "Try --help for more info\n");
00566 exit(1);
00567 break;
00568 default:
00569 fprintf(stderr, ""MKBITMAP": Unimplemented option -- %c\n", c);
00570 exit(1);
00571 }
00572 }
00573 info.infiles = &av[optind];
00574 info.infilecount = ac-optind;
00575 return;
00576 }
00577
00578
00579
00580
00581
00582 static FILE *my_fopen_read(char *filename) {
00583 if (filename == NULL || strcmp(filename, "-") == 0) {
00584 return stdin;
00585 }
00586 return fopen(filename, "rb");
00587 }
00588
00589
00590 static FILE *my_fopen_write(char *filename) {
00591 if (filename == NULL || strcmp(filename, "-") == 0) {
00592 return stdout;
00593 }
00594 return fopen(filename, "wb");
00595 }
00596
00597
00598 static void my_fclose(FILE *f, char *filename) {
00599 if (filename == NULL || strcmp(filename, "-") == 0) {
00600 return;
00601 }
00602 fclose(f);
00603 }
00604
00605
00606 static char *make_outfilename(char *infile, char *ext) {
00607 char *outfile;
00608 char *p;
00609
00610 if (strcmp(infile, "-") == 0) {
00611 return strdup("-");
00612 }
00613
00614 outfile = (char *) malloc(strlen(infile)+strlen(ext)+5);
00615 if (!outfile) {
00616 return NULL;
00617 }
00618 strcpy(outfile, infile);
00619 p = strrchr(outfile, '.');
00620 if (p) {
00621 *p = 0;
00622 }
00623 strcat(outfile, ext);
00624
00625
00626 if (strcmp(infile, outfile) == 0) {
00627 strcpy(outfile, infile);
00628 strcat(outfile, "-out");
00629 }
00630
00631 return outfile;
00632 }
00633
00634
00635
00636
00637 int main(int ac, char *av[]) {
00638 FILE *fin, *fout;
00639 int i;
00640 char *outfile;
00641
00642
00643 platform_init();
00644
00645
00646 dopts(ac, av);
00647
00648
00649
00650
00651
00652
00653
00654
00655 if (info.infilecount == 0 && info.outfile == NULL) {
00656
00657 process_file(stdin, stdout, "stdin", "stdout");
00658 return 0;
00659
00660 } else if (info.infilecount == 0) {
00661
00662 fout = my_fopen_write(info.outfile);
00663 if (!fout) {
00664 fprintf(stderr, ""MKBITMAP": %s: %s\n", info.outfile, strerror(errno));
00665 exit(2);
00666 }
00667 process_file(stdin, fout, "stdin", info.outfile);
00668 my_fclose(fout, info.outfile);
00669 return 0;
00670
00671 } else if (info.outfile == NULL) {
00672
00673 for (i=0; i<info.infilecount; i++) {
00674 outfile = make_outfilename(info.infiles[i], info.outext);
00675 if (!outfile) {
00676 fprintf(stderr, ""MKBITMAP": %s\n", strerror(errno));
00677 exit(2);
00678 }
00679 fin = my_fopen_read(info.infiles[i]);
00680 if (!fin) {
00681 fprintf(stderr, ""MKBITMAP": %s: %s\n", info.infiles[i], strerror(errno));
00682 exit(2);
00683 }
00684 fout = my_fopen_write(outfile);
00685 if (!fout) {
00686 fprintf(stderr, ""MKBITMAP": %s: %s\n", outfile, strerror(errno));
00687 exit(2);
00688 }
00689 process_file(fin, fout, info.infiles[i], outfile);
00690 my_fclose(fin, info.infiles[i]);
00691 my_fclose(fout, outfile);
00692 free(outfile);
00693 }
00694 return 0;
00695
00696 } else {
00697
00698 fout = my_fopen_write(info.outfile);
00699 if (!fout) {
00700 fprintf(stderr, ""MKBITMAP": %s: %s\n", info.outfile, strerror(errno));
00701 exit(2);
00702 }
00703 for (i=0; i<info.infilecount; i++) {
00704 fin = my_fopen_read(info.infiles[i]);
00705 if (!fin) {
00706 fprintf(stderr, ""MKBITMAP": %s: %s\n", info.infiles[i], strerror(errno));
00707 exit(2);
00708 }
00709 process_file(fin, fout, info.infiles[i], info.outfile);
00710 my_fclose(fin, info.infiles[i]);
00711 }
00712 my_fclose(fout, info.outfile);
00713 return 0;
00714
00715 }
00716
00717
00718 }
00719
00720