/****************************************************************************** ppmsvgalib ******************************************************************************* Display a PPM image on a Linux console using Svgalib. By Bryan Henderson, San Jose CA 2002.01.06. Contributed to the public domain. ******************************************************************************/ #define _XOPEN_SOURCE /* Make sure modern signal stuff is in signal.h */ #include #include #include #include #include #include #include "pm_c_util.h" #include "ppm.h" #include "shhopt.h" #include "mallocvar.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char *inputFilespec; /* Filespec of input file */ unsigned int mode; unsigned int verbose; }; static void parseCommandLine (int argc, char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. If command line is internally inconsistent (invalid options, etc.), issue error message to stderr and abort program. Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ optEntry * option_def; optStruct3 opt; unsigned int option_def_index; unsigned int modeSpec; MALLOCARRAY(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "mode", OPT_UINT, &cmdlineP->mode, &modeSpec, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ if (!modeSpec) pm_error("You must specify the -mode option."); if (argc-1 > 1) pm_error("Program takes at most one argument: the input file " "specification. " "You specified %d arguments.", argc-1); if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else cmdlineP->inputFilespec = argv[1]; } static void displayImage(FILE * const ifP, int const cols, int const rows, pixval const maxval, int const format, int const originCol, int const originRow) { /*---------------------------------------------------------------------------- Draw the PPM image which is in file 'ifP', which is positioned after the PPM header on the screen with its upper left corner (originCol, originRow). The image is 'cols' x 'rows' with maxval 'maxval' and PNM format 'format'. Svgalib is initialized and the mode selected. The image fits on the screen. -----------------------------------------------------------------------------*/ unsigned int svgalibMaxval = 255; /* This is the maxval for intensity values passed to Svgalib */ unsigned int row; pixel * pixelrow; pixelrow = ppm_allocrow(cols); /* Implementation note: It might be faster to use vga_drawscansegment() instead of vga_drawpixel() */ for (row = 0; row < rows; ++row) { unsigned int col; ppm_readppmrow(ifP, pixelrow, cols, maxval, format); for (col = 0; col < cols; ++col) { pixel const p = pixelrow[col]; int const red = PPM_GETR(p) * svgalibMaxval / maxval; int const grn = PPM_GETG(p) * svgalibMaxval / maxval; int const blu = PPM_GETB(p) * svgalibMaxval / maxval; vga_setrgbcolor(red, grn, blu); vga_drawpixel(originCol + col, originRow + row); } } ppm_freerow(pixelrow); } static void sigintHandler(int const signal) { /*---------------------------------------------------------------------------- This is a signal handler for the SIGINT signal (Control-C). It does nothing; The handler exists only to replace the default action, which is to terminate the process. Though the handler does nothing, the signal still causes the wait() system call, assuming it's in progress, to terminate so that this program can terminate. -----------------------------------------------------------------------------*/ } static void waitforSigint(void) { struct sigaction oldsigaction; struct sigaction newsigaction; int rc; newsigaction.sa_handler = &sigintHandler; sigemptyset(&newsigaction.sa_mask); newsigaction.sa_flags = 0; rc = sigaction(SIGINT, &newsigaction, &oldsigaction); if (rc != 0) pm_error("Unable to set up SIGINTR signal handler. Errno=%d (%s)", errno, strerror(errno)); pause(); /* Wait for a signal, e.g. control-C */ sigaction(SIGINT, &oldsigaction, NULL); } static void display(FILE * const ifP, int const cols, int const rows, pixval const maxval, int const format, int const videoMode, bool const verbose) { int xmax, ymax; vga_modeinfo *modeinfo; modeinfo = vga_getmodeinfo(videoMode); if (verbose) { pm_message("Screen Width: %d Height: %d Colors: %d", modeinfo->width, modeinfo->height, modeinfo->colors); pm_message("DisplayStartRange: %xh Maxpixels: %d Blit: %s", modeinfo->startaddressrange, modeinfo->maxpixels, modeinfo->haveblit ? "YES" : "NO"); } if (modeinfo->colors <= 256) pm_error("This video mode has %d or fewer colors, which means " "it is colormapped (aka paletted, aka pseudocolor). " "This program cannot drive colormapped modes.", modeinfo->colors); if (cols > modeinfo->width) pm_error("Image is too wide (%d columns) for screen (%d columns). " "Use Pamcut to select part to display.", cols, modeinfo->width); if (rows > modeinfo->height) pm_error("Image is too tall (%d rows) for screen (%d rows). " "Use Pamcut to select part to display.", rows, modeinfo->height); /* The program must not terminate after we set the video mode and before we reset it to text mode. Note that vga_setmode() sets up handlers for signals such as SIGINT that attempt to restore modes and then exit the program. */ vga_setmode(videoMode); vga_screenoff(); xmax = vga_getxdim() - 1; ymax = vga_getydim() - 1; /* Draw white border */ vga_setcolor(vga_white()); vga_drawline(0, 0, xmax, 0); vga_drawline(xmax, 0, xmax, ymax); vga_drawline(xmax, ymax, 0, ymax); vga_drawline(0, ymax, 0, 0); vga_screenon(); { int const originCol = (modeinfo->width - cols) / 2; int const originRow = (modeinfo->height - rows) / 2; displayImage(ifP, cols, rows, maxval, format, originCol, originRow); } waitforSigint(); vga_setmode(TEXT); } int main(int argc, char *argv[]) { FILE * ifP; struct cmdlineInfo cmdline; int cols, rows; pixval maxval; int format; int rc; ppm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); ppm_readppminit(ifP, &cols, &rows, &maxval, &format); { enum pm_check_code checkResult; ppm_check(ifP, PM_CHECK_BASIC, format, cols, rows, maxval, &checkResult); } /* We wait to initialize Svgalib to prevent it from interfering with error messages the code above might issue, and leaving the console in an undesirable state if the above code aborts the program. */ rc = vga_init(); /* Initialize. */ if (rc < 0) pm_error("Svgalib unable to allocate a virtual console."); if (vga_hasmode(cmdline.mode)) display(ifP, cols, rows, maxval, format, cmdline.mode, cmdline.verbose); else { pm_error("Svgalib video mode #%d not available. Either the " "video controller isn't capable of that mode or the " "Svgalib video driver doesn't know how to use it.", cmdline.mode); } pm_close(ifP); return 0; }