/* * jdmain.c * * Copyright (C) 1991, 1992, Thomas G. Lane. * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file contains an MS-DOS user interface for the JPEG decompressor. * It follows DOS practice more closely than does the standard jdmain.c. * This compiles under Borland C or Microsoft C; don't know about others. */ #include "jinclude.h" #include /* to declare exit() */ #include /* string functions */ #include /* to declare signal() */ #ifdef __TURBOC__ /* Borland C declarations */ #include /* for findfirst/findnext */ typedef struct ffblk FF_DATA; #define FF_NAME ff_name #define FINDFIRST(path,blk) findfirst(path, blk, 0) #define FINDNEXT(blk) findnext(blk) #else /* !__TURBOC__ */ /* Microsoft C declarations */ #include /* for findfirst/findnext */ typedef struct find_t FF_DATA; #define FF_NAME name #define FINDFIRST(path,blk) _dos_findfirst(path, _A_NORMAL, blk) #define FINDNEXT(blk) _dos_findnext(blk) #endif /* __TURBOC__ */ #define READ_BINARY "rb" #define WRITE_BINARY "wb" #ifndef EXIT_FAILURE /* define exit() codes if not provided */ #define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #include "jversion.h" /* for version message */ /* * PD version of getopt(3). */ #include "egetopt.c" /* * This list defines the known output image formats * (not all of which need be supported by a given version). * You can change the default output format by defining DEFAULT_FMT; * indeed, you had better do so if you undefine GIF_SUPPORTED. */ typedef enum { FMT_GIF, /* GIF format */ FMT_PPM, /* PPM/PGM (PBMPLUS formats) */ FMT_RLE, /* RLE format */ FMT_TARGA, /* Targa format */ FMT_TIFF /* TIFF format */ } IMAGE_FORMATS; #ifndef DEFAULT_FMT /* so can override from CFLAGS in Makefile */ #define DEFAULT_FMT FMT_GIF #endif static IMAGE_FORMATS requested_fmt; /* * This routine gets control after the input file header has been read. * It must determine what output file format is to be written, * and make any other decompression parameter changes that are desirable. */ METHODDEF void d_ui_method_selection (decompress_info_ptr cinfo) { /* if grayscale or CMYK input, force similar output; */ /* else leave the output colorspace as set by options. */ if (cinfo->jpeg_color_space == CS_GRAYSCALE) cinfo->out_color_space = CS_GRAYSCALE; else if (cinfo->jpeg_color_space == CS_CMYK) cinfo->out_color_space = CS_CMYK; /* select output file format */ /* Note: jselwxxx routine may make additional parameter changes, * such as forcing color quantization if it's a colormapped format. */ switch (requested_fmt) { #ifdef GIF_SUPPORTED case FMT_GIF: jselwgif(cinfo); break; #endif #ifdef PPM_SUPPORTED case FMT_PPM: jselwppm(cinfo); break; #endif #ifdef RLE_SUPPORTED case FMT_RLE: jselwrle(cinfo); break; #endif #ifdef TARGA_SUPPORTED case FMT_TARGA: jselwtarga(cinfo); break; #endif default: ERREXIT(cinfo->emethods, "Unsupported output file format"); break; } } /* * Signal catcher to ensure that temporary files are removed before aborting. * Doesn't do anything during intervals where emethods is NULL. */ static external_methods_ptr emethods; /* for access to free_all */ LOCAL void signal_catcher (int signum) { if (emethods != NULL) { emethods->trace_level = 0; /* turn off trace output */ (*emethods->free_all) (); /* clean up memory allocation & temp files */ } exit(EXIT_FAILURE); } LOCAL void usage (void) /* complain about bad command line */ { fprintf(stderr, "Usage: djpeg [switches] inputfile(s)\n"); fprintf(stderr, "List of input files may use wildcards (* and ?)\n"); fprintf(stderr, "If input file name has no extension, .JPG is assumed\n"); fprintf(stderr, "Output filename is same as input filename, except for extension\n"); fprintf(stderr, "Optional switches:\n"); #ifdef GIF_SUPPORTED fprintf(stderr, " -G Select GIF output, extension .GIF (default)\n"); #endif #ifdef PPM_SUPPORTED fprintf(stderr, " -P Select PPM/PGM output, extension .PPM\n"); #endif #ifdef RLE_SUPPORTED fprintf(stderr, " -R Select Utah RLE output, extension .RLE\n"); #endif #ifdef TARGA_SUPPORTED fprintf(stderr, " -T Select Targa output, extension .TGA\n"); #endif fprintf(stderr, " -g Force grayscale output\n"); fprintf(stderr, " -q colors Quantize to no more than N colors\n"); fprintf(stderr, " -1 Use 1-pass quantization (fast, low quality)\n"); fprintf(stderr, " -D Don't use dithering in quantization\n"); fprintf(stderr, " -b Apply cross-block smoothing\n"); fprintf(stderr, " -m memory Maximum memory to use (default 300K); see USAGE\n"); fprintf(stderr, " -d Generate debug output\n"); exit(EXIT_FAILURE); } /* Display progress report */ METHODDEF void progress_monitor (decompress_info_ptr cinfo, long loopcounter, long looplimit) { if (cinfo->total_passes > 1) { fprintf(stderr, "\rPass %d/%d: %3d%% ", cinfo->completed_passes+1, cinfo->total_passes, (int) (loopcounter*100L/looplimit)); } else { fprintf(stderr, "\r %3d%% ", (int) (loopcounter*100L/looplimit)); } fflush(stderr); } /* * Check for overwrite of an existing file; clear it with user */ LOCAL boolean is_write_ok (char * outfilename) { FILE * ofile; int ch; ofile = fopen(outfilename, READ_BINARY); if (ofile == NULL) return TRUE; /* not present */ fclose(ofile); /* oops, it is present */ for (;;) { fprintf(stderr, "%s already exists, overwrite it? [y/n] ", outfilename); fflush(stderr); ch = getc(stdin); fflush(stdin); /* flush rest of line */ switch (ch) { case 'Y': case 'y': return TRUE; case 'N': case 'n': return FALSE; /* otherwise, ask again */ } } } /* * Save current switch values here --- necessary for multiple input files! */ static boolean b_smooth; /* -b */ static boolean gray_scale; /* -g */ static int num_colors; /* -q, or 0 if not seen */ static boolean two_pass_q; /* not -1 */ static boolean do_dither; /* not -D */ static long cur_mem; /* -m */ static int cur_trace; /* -d */ #define MAX_FNAME_LEN 150 /* * Process a single input file */ LOCAL void process_one_file (char * infilename) { struct decompress_info_struct cinfo; struct decompress_methods_struct dc_methods; struct external_methods_struct e_methods; char outfilename[MAX_FNAME_LEN]; int i; /* Select the input and output files */ if ((cinfo.input_file = fopen(infilename, READ_BINARY)) == NULL) { fprintf(stderr, "djpeg: can't open %s\n", infilename); return; } /* Make outfilename be infilename with appropriate extension */ strcpy(outfilename, infilename); for (i = strlen(outfilename)-1; i >= 0; i--) { switch (outfilename[i]) { case ':': case '/': case '\\': i = 0; /* stop scanning */ break; case '.': outfilename[i] = '\0'; /* lop off existing extension */ i = 0; /* stop scanning */ break; default: break; /* keep scanning */ } } switch (requested_fmt) { case FMT_GIF: strcat(outfilename, ".GIF"); break; case FMT_PPM: strcat(outfilename, ".PPM"); break; case FMT_RLE: strcat(outfilename, ".RLE"); break; case FMT_TARGA: strcat(outfilename, ".TGA"); break; } if (! is_write_ok(outfilename)) { fclose(cinfo.input_file); return; } if ((cinfo.output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { fprintf(stderr, "djpeg: can't create %s\n", outfilename); fclose(cinfo.input_file); return; } /* Initialize the system-dependent method pointers. */ cinfo.methods = &dc_methods; cinfo.emethods = &e_methods; jselerror(&e_methods); /* error/trace message routines */ jselmemmgr(&e_methods); /* memory allocation routines */ dc_methods.d_ui_method_selection = d_ui_method_selection; /* Now OK to enable signal catcher. */ emethods = &e_methods; /* Set up JPEG parameters. */ j_d_defaults(&cinfo, TRUE); cinfo.do_block_smoothing = b_smooth; if (gray_scale) cinfo.out_color_space = CS_GRAYSCALE; if (num_colors > 0) { cinfo.desired_number_of_colors = num_colors; cinfo.quantize_colors = TRUE; } cinfo.two_pass_quantize = two_pass_q; cinfo.use_dithering = do_dither; e_methods.max_memory_to_use = cur_mem; e_methods.trace_level = cur_trace; /* Start up progress display, unless trace output is on */ fprintf(stderr, "Decompressing %s => %s\n", infilename, outfilename); if (cur_trace == 0) dc_methods.progress_monitor = progress_monitor; /* Set up to read a JFIF or baseline-JPEG file. */ /* A smarter UI would inspect the first few bytes of the input file */ /* to determine its type. */ #ifdef JFIF_SUPPORTED jselrjfif(&cinfo); #else You shoulda defined JFIF_SUPPORTED. /* deliberate syntax error */ #endif /* Do it to it! */ jpeg_decompress(&cinfo); /* Clear away progress display */ if (cur_trace == 0) fprintf(stderr, "\r \r"); fflush(stderr); /* All done. Close files, disable signal catcher */ fclose(cinfo.input_file); fclose(cinfo.output_file); emethods = NULL; } /* * Process one filespec; expand wildcards */ LOCAL void process_filespec (char * filespec) { FF_DATA ffblock; char infilename[MAX_FNAME_LEN]; int pathlen, i; boolean hasext; /* Determine whether there is a path and an extension */ pathlen = 0; hasext = FALSE; for (i = strlen(filespec)-1; i >= 0; i--) { switch (filespec[i]) { case ':': case '/': case '\\': pathlen = i+1; /* path part ends here */ i = 0; /* stop scanning */ break; case '.': hasext = TRUE; /* there is an extension */ break; default: break; /* keep scanning */ } } /* Make infilename be filespec; if no explicit extension, attach .JPG */ strcpy(infilename, filespec); if (! hasext) strcat(infilename, ".JPG"); /* Scan over matching files */ if (FINDFIRST(infilename, &ffblock) != 0) { fprintf(stderr, "No files matching %s\n", infilename); } else { do { strcpy(infilename, filespec); strcpy(infilename + pathlen, ffblock.FF_NAME); process_one_file(infilename); } while (FINDNEXT(&ffblock) == 0); } } /* * The main program. */ GLOBAL int main (int argc, char **argv) { int c; /* Set up signal catcher. */ emethods = NULL; signal(SIGINT, signal_catcher); #ifdef SIGTERM /* not all systems have SIGTERM */ signal(SIGTERM, signal_catcher); #endif /* Scan command line, process switches */ requested_fmt = DEFAULT_FMT; /* initialize default switch values */ b_smooth = FALSE; gray_scale = FALSE; num_colors = 0; two_pass_q = TRUE; do_dither = TRUE; cur_mem = 300000L; cur_trace = 0; while ((c = egetopt(argc, argv, "GPRTbgq:1Dm:d")) != EOF) switch (c) { case 'G': /* GIF output format. */ requested_fmt = FMT_GIF; break; case 'P': /* PPM output format. */ requested_fmt = FMT_PPM; break; case 'R': /* RLE output format. */ requested_fmt = FMT_RLE; break; case 'T': /* Targa output format. */ requested_fmt = FMT_TARGA; break; case 'b': /* Enable cross-block smoothing. */ b_smooth = TRUE; break; case 'g': /* Force grayscale output. */ gray_scale = TRUE; break; case 'q': /* Do color quantization. */ if (optarg == NULL) usage(); if (sscanf(optarg, "%d", &num_colors) != 1) usage(); break; case '1': /* Use fast one-pass quantization. */ two_pass_q = FALSE; break; case 'D': /* Suppress dithering in color quantization. */ do_dither = FALSE; break; case 'm': /* Maximum memory in Kb (or Mb with 'm'). */ { long lval; char ch = 'x'; if (optarg == NULL) usage(); if (sscanf(optarg, "%ld%c", &lval, &ch) < 1) usage(); if (ch == 'm' || ch == 'M') lval *= 1000L; cur_mem = lval * 1000L; } break; case 'd': /* Debugging. */ /* On first -d, print version identification */ if (cur_trace == 0) fprintf(stderr, "Independent JPEG Group's DJPEG, version %s\n%s\n", JVERSION, JCOPYRIGHT); cur_trace++; break; case '?': default: usage(); break; } /* Process file specifications */ if (optind >= argc) usage(); /* no filespecs?? */ while (optind < argc) process_filespec(argv[optind++]); /* All done. */ exit(EXIT_SUCCESS); return 0; /* suppress no-return-value warnings */ }