/* ppmtoicr.c - convert a portable pixmap to NCSA ICR protocol ** ** Copyright (C) 1990 by Kanthan Pillay (svpillay@Princeton.EDU) ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #include #include #include #include "ppm.h" #include "pm_c_util.h" #include "shhopt.h" #include "mallocvar.h" #include "nstring.h" #define MAXCOLORCT 256 #define CLUTCOLORCT 768 /* The following are arbitrary limits. We could not find an official format specification for this. */ #define MAXSIZE 32767 #define MAXDISP 1024 #define MAXNAMELEN 80 struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilename; const char * windowname; /* NULL means not specified */ unsigned int expand; unsigned int display; }; static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optEntry * option_def; optStruct3 opt; unsigned int option_def_index; unsigned int windowNameSpec; unsigned int rleSpec; unsigned int expandSpec; unsigned int displaySpec; MALLOCARRAY(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "windowname", OPT_STRING, &cmdlineP->windowname, &windowNameSpec, 0); OPTENT3(0, "expand", OPT_UINT, &cmdlineP->expand, &expandSpec, 0); OPTENT3(0, "display", OPT_UINT, &cmdlineP->display, &displaySpec, 0); OPTENT3(0, "rle", OPT_FLAG, NULL, &rleSpec, 0); opt.opt_table = option_def; opt.short_allowed = false; /* We have no short (old-fashioned) options */ opt.allowNegNum = false; /* We may have parms that are negative numbers */ pm_optParseOptions4(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!expandSpec) cmdlineP->expand = 1; if (!displaySpec) cmdlineP->display = 0; if (rleSpec) pm_error("The -rle command line option no longer exists."); if (cmdlineP->expand == 0) pm_error("-expand value must be positive."); if (cmdlineP->display > MAXDISP) pm_error("-display value is too large. Maximum is %u", MAXDISP); if (argc-1 < 1) { cmdlineP->inputFilename = "-"; } else cmdlineP->inputFilename = argv[1]; if (argc-1 > 1) pm_error("Program takes zero or one argument (filename). You " "specified %u", argc-1); if (windowNameSpec) { if (strlen(cmdlineP->windowname) > MAXNAMELEN) pm_error("-windowname value is too long. (max %u chars)", MAXNAMELEN); { unsigned int i; for (i = 0; cmdlineP->windowname[i]; ++i) { if (!isprint (cmdlineP->windowname[i])) pm_error("-window option contains nonprintable character"); if (cmdlineP->windowname[i] =='^') { /* '^' terminates the window name string in ICR */ pm_error("-windowname option value '%s' contains " "disallowed '^' character.", cmdlineP->windowname); } } } } else cmdlineP->windowname = NULL; } static void validateComputableSize(unsigned int const cols, unsigned int const rows, unsigned int const expand) { /*---------------------------------------------------------------------------- We don't have any information on what the limit for these values should be. The ICR protocol was used around 1990 when PC main memory was measured in megabytes. -----------------------------------------------------------------------------*/ if (cols > MAXSIZE / expand) pm_error("image width (%f) too large to be processed", (float) cols * expand); if (rows > MAXSIZE / expand) pm_error("image height (%f) too large to be processed", (float) rows * expand); } static void makeIcrColormap(colorhist_vector const chv, unsigned int const colorCt, pixval const maxval, char * const rgb) { unsigned int i; if (maxval > 255) pm_message("Maxval is not 255 - automatically rescaling colors" ); for (i = 0; i < CLUTCOLORCT; ++i) rgb[i] = 0; for (i = 0; i < colorCt; ++i) { unsigned int j; j = (3 * i); if (maxval == 255) { rgb[j++] = PPM_GETR(chv[i].color) ; rgb[j++] = PPM_GETG(chv[i].color) ; rgb[j++] = PPM_GETB(chv[i].color) ; } else { rgb[j++] = (unsigned int) PPM_GETR(chv[i].color) * 255 / maxval; rgb[j++] = (unsigned int) PPM_GETG(chv[i].color) * 255 / maxval; rgb[j++] = (unsigned int) PPM_GETB(chv[i].color) * 255 / maxval; } } } static int colorIndexAtPosition(unsigned int const x, unsigned int const y, pixel ** const pixels, colorhash_table const cht) { int rc; rc = ppm_lookupcolor(cht, &pixels[y][x]); /* Every color in the image is in the palette */ assert(rc >= 0); return rc; } static void downloadColormap(char const rgb[CLUTCOLORCT], const char * const windowName) { unsigned int i; pm_message("Downloading colormap for %s ...", windowName); printf("\033^M;%d;%d;%d;%s^", 0, MAXCOLORCT, CLUTCOLORCT, windowName); for (i = 0; i < CLUTCOLORCT; ++i) { unsigned char const c = rgb[i]; if (c > 31 && c < 123) { /* printable ASCII */ putchar(c); } else { /* non-printable, so encode it */ putchar((c >> 6) + 123); putchar((c & 0x3f) + 32); } } fflush(stdout); } static void sendOutPicture(pixel ** const pixels, unsigned int const rows, unsigned int const cols, colorhash_table const cht, int const expand, const char * const windowName) { unsigned int row; pm_message("Sending picture data ..." ); for (row = 0; row < rows; ++row) { unsigned int col; printf("\033^P;%d;%d;%d;%d;%s^", 0, row * expand, expand, cols, windowName); for (col = 0; col < cols; ++col) { unsigned char const c = colorIndexAtPosition(col, row, pixels, cht); if (c > 31 && c < 123) { putchar(c); } else { putchar((c >> 6) + 123); putchar((c & 0x3f) + 32); } } } fflush(stdout); } static const char * windowNameFmFileName(const char * const fileName) { /* Use the input file name, with unprintable characters and '^' replaced with '.'. '^' terminates the window name string in the output file. Truncate if necessary. */ char * windowName; /* malloced */ unsigned int i; windowName = malloc(MAXNAMELEN+1); if (!windowName) pm_error("Failed to get %u bytes of memory for window name " "buffer", MAXNAMELEN+1); for (i = 0; i < MAXNAMELEN && fileName[i]; ++i) { const char thisChar = fileName[i]; if (!isprint(thisChar) || thisChar =='^') windowName[i] = '.'; else windowName[i] = thisChar; } windowName[i] = '\0'; return windowName; } int main(int argc, const char ** const argv) { FILE * ifP; int rows, cols; int colorCt; pixval maxval; colorhist_vector chv; char rgb[CLUTCOLORCT]; pixel ** pixels; colorhash_table cht; struct CmdlineInfo cmdline; const char * windowName; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilename); pixels = ppm_readppm(ifP, &cols, &rows, &maxval); validateComputableSize(cols, rows, cmdline.expand); pm_close(ifP); /* Figure out the colormap. */ pm_message("Computing colormap..." ); chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORCT, &colorCt); if (!chv) pm_error("Too many colors - try doing a 'pnmquant %u'", MAXCOLORCT); pm_message("%u colors found", colorCt ); makeIcrColormap(chv, colorCt, maxval, rgb); /* And make a hash table for fast lookup. */ cht = ppm_colorhisttocolorhash(chv, colorCt); ppm_freecolorhist(chv); /************** Create a new window using ICR protocol *********/ /* Format is "ESC^W;left;top;width;height;display;windowname" */ if (cmdline.windowname) windowName = pm_strdup(cmdline.windowname); else { if (streq(cmdline.inputFilename, "-")) windowName = pm_strdup("untitled"); else windowName = windowNameFmFileName(cmdline.inputFilename); } pm_message("Creating window '%s' ...", windowName); printf("\033^W;%d;%d;%d;%d;%d;%s^", 0, 0, cols * cmdline.expand, rows * cmdline.expand, cmdline.display, windowName); fflush(stdout); /****************** Download the colormap. ********************/ downloadColormap(rgb, windowName); sendOutPicture(pixels, rows, cols, cht, cmdline.expand, windowName); pm_strfree(windowName); return 0; }