/* * Written by Leonid Rosenboim, Tel-Aviv University, Israel. This code is * placed in Public Domain on Feb 1988. May be copied freely for any purpose. * * Corrections for image inversion due to batcheldern@hannah.dec.com * (Ned Batchelder), and implemented by Mike Khaw (mkhaw@teknowledge.arpa) */ static char sccsid[] = "@(#)psraster.c 2.3 leonid@math.tau.ac.IL 88/06/16"; /* * Standard rasterfile to PostScript filter. */ #include #include #include #include #include #define INCH * 72 #define PG_SIZE_X ( 8.0 INCH ) /* Canvas is 7.5" x 9.5" area on a * 8" x 10.8" paper */ #define PG_SIZE_Y ( 10.8 INCH ) #define MAXBOX_X ( 7.5 INCH ) #define MAXBOX_Y ( 9.0 INCH ) #ifndef DESTLIB # define DESTLIB "/usr/local/lib/ps" #endif DESTLIB #define PROLOGUE "/psraster.ps" #define FALSE 0 #define TRUE !FALSE #define MAX_LINE 0x7f #define FLAG 0x80 #define min(x,y) (((x)<(y))?(x):(y)) #define max(x,y) (((x)<(y))?(y):(x)) #define SERROR(err_message) \ {(void)fprintf(stderr, "%s: %s\n", Progname, err_message); exit(1);} #define ERR_RASTYPE "Input raster has incorrect ras_type" #define ERR_MAPTYPE "Input raster has incorrect ras_maptype" #define ERR_UNITS "Unrecognized units code" #define MY_RAS_TYPE RT_BYTE_ENCODED struct box { /* Bounding box structure, all in 1/72" * points */ short size_x; short size_y; short off_x; short off_y; }; struct box Canvas; /* The drawing canvas, default almost the * whole page */ struct box Bounds; /* The actual BoundingBox for image */ extern int pr_load_colormap(), pr_load_header(); extern struct pixrect *pr_load_std_image(); extern double atof(), strtod(); FILE *infile, *outfile; char *file_name; /* Input filename or stdin */ char *Progname; /* Program Name for error message generation */ unsigned char *greymap; /* Grey level map */ /* Program behaviour modifiers */ int flg_inverse = 0;/* Whether the program must perform negation */ int flg_square = 0; /* Put the image in a square */ int flg_portrait = 0; /* Force Portrait Mode */ int flg_landscape = 0; /* Force Landscape Mode */ int flg_noshow = 0; /* Suppress showpage */ int flg_nogrey = 0; /* Suppress grey level calculation */ float opt_scale = 0.; /* Scaling Factor */ /* * Translate a string from inches/centimeters/points to points. */ double points(s) char *s; { double v; char *p = s; v = strtod(s, &p); if ((p == NULL)) return (v); switch (*--p) { case 'c': v *= (1 / 2.54); case 'i': v *= 72; case 'p': case '\0': case '0': break; default: SERROR(ERR_UNITS); } return (v); } main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; int c; /* Initialize default Canvas */ Canvas.size_x = MAXBOX_X; Canvas.size_y = MAXBOX_Y; Canvas.off_x = -1; Canvas.off_y = -1; for (; optind < argc || optind == 1; optind++) { /* * Process argc/argv. */ Progname = argv[0]; while ((c = getopt(argc, argv, "plisnx:y:X:Y:z:")) != EOF) switch (c) { case 'i': flg_inverse++; break; case 's': flg_square++; break; case 'p': flg_portrait++; break; case 'l': flg_landscape++; break; case 'n': flg_noshow++; break; case 'z': opt_scale = atof(optarg); break; case 'x': Canvas.off_x = points(optarg); break; case 'y': Canvas.off_y = points(optarg); break; case 'X': Canvas.size_x = points(optarg); break; case 'Y': Canvas.size_y = points(optarg); break; } /* Center the canvas on the page */ if (Canvas.off_x == -1) Canvas.off_x = (PG_SIZE_X - Canvas.size_x) / 2; if (Canvas.off_y == -1) Canvas.off_y = (PG_SIZE_Y - Canvas.size_y) / 2; outfile = stdout; if (optind == argc) { file_name = "Standard Input"; load(stdin); } else { if ((infile = fopen(argv[optind], "r")) == NULL) { perror(argv[optind]); continue; } file_name = argv[optind]; load(infile); } flg_square = flg_portrait = flg_landscape = flg_noshow = 0; opt_scale = 0; Canvas.size_x = MAXBOX_X; Canvas.size_y = MAXBOX_Y; Canvas.off_x = -1; Canvas.off_y = -1; } } /* * Process each raster file passed as argument, but make sure that the * prologue is written only once. Here we load the raster image and call * another routine to do the job. */ load(infile) FILE *infile; { extern char *malloc(); #define MALLOC(size) (unsigned char *)malloc((unsigned int)(size)) struct pixrect *input_pr; struct rasterfile rh; colormap_t colormap; /* * Load the rasterfile header and the colormap (if there is one). * Also perform a few sanity checks on the rasterfile. */ if (pr_load_header(infile, &rh) == PIX_ERR) SERROR(PR_IO_ERR_RASREAD); switch (colormap.type = rh.ras_maptype) { case RMT_NONE: greymap = NULL; break; case RMT_EQUAL_RGB: #if 1 /* greyscale seems to map to inverse of monochrome */ flg_inverse = !flg_inverse; #endif colormap.length = rh.ras_maplength / 3; colormap.map[0] = MALLOC(colormap.length); colormap.map[1] = MALLOC(colormap.length); colormap.map[2] = MALLOC(colormap.length); greymap = MALLOC(colormap.length); if (pr_load_colormap(infile, &rh, &colormap) == PIX_ERR) SERROR(PR_IO_ERR_RASREAD); if (!flg_nogrey) { register i; for (i = 0; i < colormap.length; i++) #if 0 greymap[i] = (unsigned char) rint(sqrt(( pow((double) colormap.map[0][i], 2.) + pow((double) colormap.map[1][i], 2.) + pow((double) colormap.map[2][i], 2.)) / 3.)); #else /* coefficients from NTSC standards */ greymap[i] = (unsigned char) rint(0.299 * (double) colormap.map[0][i] + 0.587 * (double) colormap.map[1][i] + 0.114 * (double) colormap.map[2][i]); #endif } break; default: SERROR(ERR_MAPTYPE); } /* * If the rasterfile is a standard type, use pr_load_std_image to * read the image. If the rasterfile is non-standard type, use an * appropriate routine to read the image (for RT_BYTE_ENCODED, this * turns out to also be pr_load_std_image). In either case, transform * the input to the other type and output it. */ switch (rh.ras_type) { case RT_STANDARD: if ((input_pr = pr_load_std_image(infile, &rh)) == NULL) SERROR(PR_IO_ERR_RASREAD); dump(input_pr); break; case MY_RAS_TYPE: if ((input_pr = pr_load_std_image(infile, &rh)) == NULL) SERROR(PR_IO_ERR_RASREAD); dump(input_pr); break; default: SERROR(ERR_RASTYPE); } } /* * This is the main procedure which dumps a raster it receives as argument on * the output in PostScript format. Routines below are used for assist */ dump(pr) struct pixrect *pr; { register int br, br1;/* Bytes per row */ struct mpr_data *mpr = (struct mpr_data *) pr->pr_data; register unsigned char *p; register x, y; /* * Compute the number of bytes per row: Here we have a slight * problem - rasterfile(5) number of bytes for each pixel row is * rounded to short, but in PostScript it must be rounded to 1 byte. * Hence, br is assigned the byte-per-row from the input raster, and * br1 will have the correct value for PostScript. */ br = mpr->md_linebytes; br1 = (pr->pr_size.x * pr->pr_depth - 1) / 8 + 1; if ((br != br1) && ((br - br1) != 1)) (void) fprintf(stderr, "Warning: Bytes per line %d -> %d\n", br, br1); put_prologue(pr, br1); /* Start of memory image itself */ p = (unsigned char *) mpr->md_image; for (y = 0; y < pr->pr_size.y; y++) { if (greymap != NULL) for (x = 0; x < br1; x++) p[x] = greymap[p[x]]; put_line(p, br1); p += br; } (void) fprintf(outfile, "grestore\n"); if (!flg_noshow) (void) fprintf(outfile, "showpage\n"); } /* * Write the PostScript prologue, the constant and the variable parts. The * variable part is output for each image, and the appropriate * transformations are calculated. */ put_prologue(pr, br) struct pixrect *pr; int br; { static int count = 0; /* Image counter */ float cv_aspect, im_aspect; /* Canvas and image aspect * ratios */ int x_size, y_size; /* Write the constant prologue */ if (count == 0) get_prologue(); count++; (void) fprintf(outfile, "gsave\n"); (void) fprintf(outfile, "%%Rasterfile Image: %s\n", file_name); (void) fprintf(outfile, "%%Format:%d width %d hight %d depth\n", pr->pr_size.x, pr->pr_size.y, pr->pr_depth); /* Calculate aspect ratios ( y / x ) */ cv_aspect = (float) Canvas.size_y / (float) Canvas.size_x; im_aspect = (float) pr->pr_size.y / (float) pr->pr_size.x; /* Decide whether to use Portrait or Landscape mode */ if (flg_portrait == flg_landscape) { /* * Here we don't make any assumption as to the canvas * dimensions since it may be arbitrarily set by the user */ if ((cv_aspect > 1) && (im_aspect > 1) || (cv_aspect < 1) && (im_aspect < 1)) flg_portrait = TRUE; else flg_portrait = FALSE; flg_landscape = !flg_portrait; } if (flg_landscape) { x_size = pr->pr_size.y; y_size = pr->pr_size.x; } else { x_size = pr->pr_size.x; y_size = pr->pr_size.y; } /* Now we'll handle the scaling factor */ if (opt_scale == 0.) { opt_scale = min((float) Canvas.size_x / (float) x_size, (float) Canvas.size_y / (float) y_size); } /* Fill in the Bounds structure */ Bounds.size_x = (float) x_size *opt_scale; Bounds.size_y = (float) y_size *opt_scale; Bounds.off_x = Canvas.off_x + (Canvas.size_x - Bounds.size_x) / 2; Bounds.off_y = Canvas.off_y + (Canvas.size_y - Bounds.size_y) / 2; if (flg_square) { /* But if aspect ratio is to be ignored we'll * use a trivial formula */ Bounds = Canvas; } #ifdef DEBUG (void) fprintf(stderr, "Drawing %s in %s mode, with scaling of %f\n", file_name, (flg_portrait) ? "portrait" : "landscape", opt_scale); (void) fprintf(stderr, "Canvas size %d %d, offset %d %d\n", Canvas.size_x, Canvas.size_y, Canvas.off_x, Canvas.off_y); (void) fprintf(stderr, "Image size %d %d, offset %d %d\n", Bounds.size_x, Bounds.size_y, Bounds.off_x, Bounds.off_y); #endif (void) fprintf(outfile, "%%%%BoundingBox: %d %d %d %d\n", Bounds.off_x, Bounds.off_y, Bounds.off_x + Bounds.size_x, Bounds.off_y + Bounds.size_y); if (flg_portrait) { (void) fprintf(outfile, "%%%% Using Portrait mode\n"); (void) fprintf(outfile, "%d %d translate %d %d scale\n", Bounds.off_x, Bounds.off_y, Bounds.size_x, Bounds.size_y); } else { (void) fprintf(outfile, "%%%% Using Landscape mode\n"); (void) fprintf(outfile, "%d %d translate -90 rotate %d %d scale\n", Bounds.off_x, Bounds.off_y + Bounds.size_y, Bounds.size_y, Bounds.size_x); } #if 0 if (flg_inverse) (void) fprintf(outfile, "{1 exch sub} settransfer "); #endif (void) fprintf(outfile, "\n"); (void) fprintf(outfile, "/picst %d string def \n", br); /* Dimensions */ (void) fprintf(outfile, "%d %d %d \n", pr->pr_size.x, pr->pr_size.y, pr->pr_depth); /* Unit array */ (void) fprintf(outfile, "[ %d 0 0 -%d 0 %d ]\n", pr->pr_size.x, pr->pr_size.y, pr->pr_size.y); /* Call Image */ (void) fprintf(outfile, "{GetImageRow} image\n"); } /* * Output a single pixel row in the encoded hex format. Here is the format * description: * * Each output line consists of a record that starts with one hex byte C and * ends with a newline. if C > 0, then following will be C hex bytes of * data. if C < 0, it will be followed by one hex byte which must be * repeated (-C) times. */ put_line(p, br) unsigned char *p; int br; { register i; register unsigned char *q; while (br) { for (q = p, i = 0; i < min(MAX_LINE, br); i++, q++) if (rep_count(q, (int) 6) > 5) break; if (i != 0) { br -= i; (void) fprintf(outfile, "%02x", (unsigned char) i); #if 0 for (; i; i--) (void) fprintf(outfile, "%02x", (unsigned char) *p++); #else for (; i; p++, i--) (void) fprintf(outfile, "%02x", (flg_inverse ? ~((unsigned char) *p) : (unsigned char) *p)); #endif } else { i = rep_count(p, br); i = min(i, min(MAX_LINE, br)); (void) fprintf(outfile, "%02x", (unsigned char) (i | FLAG)); #if 0 (void) fprintf(outfile, "%02x", (unsigned char) *p); #else (void) fprintf(outfile, "%02x", (flg_inverse ? ~((unsigned char) *p) : (unsigned char) *p)); #endif p += i; br -= i; } (void) fprintf(outfile, "\n"); } } /* * Count the number of repeated bytes starting at *p. Search is limited to n * subsequent bytes. */ int rep_count(p, n) unsigned char *p; int n; { register i; register char unsigned c; if (((c = *p) != *(p + 1)) || (c != *(p + 2))) return (0); for (i = 0; (c == *p) && (i < n); i++, p++); return (i); } /* * Print the invariable part of the PostScript prologue. */ get_prologue() { FILE *pro; char buffer[BUFSIZ]; int c; (void) strcpy(buffer, DESTLIB); (void) strcat(buffer, PROLOGUE); pro = fopen(buffer, "r"); if (pro == NULL) { perror(buffer); exit(1); } (void) fprintf(outfile, "%%!\n%%%%Generated by %s version %s\n", Progname, sccsid); c = fread(buffer, 1, sizeof(buffer), pro); for (; c > 0;) { (void) fwrite(buffer, 1, c, outfile); c = fread(buffer, 1, sizeof(buffer), pro); } }