/* * jpegtran.c * * Copyright (C) 1995, 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 a command-line user interface for JPEG transcoding. * It is very similar to cjpeg.c, but provides lossless transcoding between * different JPEG file formats. */ #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ #include "jversion.h" /* for version message */ #ifdef USE_CCOMMAND /* command-line reader for Macintosh */ #ifdef __MWERKS__ #include /* Metrowerks declares it here */ #endif #ifdef THINK_C #include /* Think declares it here */ #endif #endif /* * Argument-parsing code. * The switch parser is designed to be useful with DOS-style command line * syntax, ie, intermixed switches and file names, where only the switches * to the left of a given file name affect processing of that file. * The main program in this file doesn't actually use this capability... */ static const char * progname; /* program name for error messages */ static char * outfilename; /* for -outfile switch */ LOCAL void usage( void ) { /* complain about bad command line */ fprintf( stderr, "usage: %s [switches] ", progname ); #ifdef TWO_FILE_COMMANDLINE fprintf( stderr, "inputfile outputfile\n" ); #else fprintf( stderr, "[inputfile]\n" ); #endif fprintf( stderr, "Switches (names may be abbreviated):\n" ); #ifdef ENTROPY_OPT_SUPPORTED fprintf( stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n" ); #endif #ifdef C_PROGRESSIVE_SUPPORTED fprintf( stderr, " -progressive Create progressive JPEG file\n" ); #endif fprintf( stderr, "Switches for advanced users:\n" ); fprintf( stderr, " -restart N Set restart interval in rows, or in blocks with B\n" ); fprintf( stderr, " -maxmemory N Maximum memory to use (in kbytes)\n" ); fprintf( stderr, " -outfile name Specify name for output file\n" ); fprintf( stderr, " -verbose or -debug Emit debug output\n" ); fprintf( stderr, "Switches for wizards:\n" ); #ifdef C_ARITH_CODING_SUPPORTED fprintf( stderr, " -arithmetic Use arithmetic coding\n" ); #endif #ifdef C_MULTISCAN_FILES_SUPPORTED fprintf( stderr, " -scans file Create multi-scan JPEG per script file\n" ); #endif exit( EXIT_FAILURE ); } LOCAL int parse_switches( j_compress_ptr cinfo, int argc, char ** argv, int last_file_arg_seen, boolean for_real ) { /* Parse optional switches. * Returns argv[] index of first file-name argument (== argc if none). * Any file names with indexes <= last_file_arg_seen are ignored; * they have presumably been processed in a previous iteration. * (Pass 0 for last_file_arg_seen on the first or only iteration.) * for_real is FALSE on the first (dummy) pass; we may skip any expensive * processing. */ int argn; char * arg; boolean simple_progressive; char * scansarg = NULL; /* saves -scans parm if any */ /* Set up default JPEG parameters. */ simple_progressive = FALSE; outfilename = NULL; cinfo->err->trace_level = 0; /* Scan command line options, adjust parameters */ for ( argn = 1; argn < argc; argn++ ) { arg = argv[argn]; if ( *arg != '-' ) { /* Not a switch, must be a file name argument */ if ( argn <= last_file_arg_seen ) { outfilename = NULL;/* -outfile applies to just one input file */ continue; /* ignore this name if previously processed */ } break; /* else done parsing switches */ } arg++; /* advance past switch marker character */ if ( keymatch( arg, "arithmetic", 1 ) ) { /* Use arithmetic coding. */ #ifdef C_ARITH_CODING_SUPPORTED cinfo->arith_code = TRUE; #else fprintf( stderr, "%s: sorry, arithmetic coding not supported\n", progname ); exit( EXIT_FAILURE ); #endif } else if ( keymatch( arg, "debug", 1 ) || keymatch( arg, "verbose", 1 ) ) { /* Enable debug printouts. */ /* On first -d, print version identification */ static boolean printed_version = FALSE; if ( !printed_version ) { fprintf( stderr, "Independent JPEG Group's JPEGTRAN, version %s\n%s\n", JVERSION, JCOPYRIGHT ); printed_version = TRUE; } cinfo->err->trace_level++; } else if ( keymatch( arg, "maxmemory", 3 ) ) { /* Maximum memory in Kb (or Mb with 'm'). */ long lval; char ch = 'x'; if ( ++argn >= argc ) {/* advance to next argument */ usage(); } if ( sscanf( argv[argn], "%ld%c", &lval, &ch ) < 1 ) { usage(); } if ( ( ch == 'm' ) || ( ch == 'M' ) ) { lval *= 1000L; } cinfo->mem->max_memory_to_use = lval * 1000L; } else if ( keymatch( arg, "optimize", 1 ) || keymatch( arg, "optimise", 1 ) ) { /* Enable entropy parm optimization. */ #ifdef ENTROPY_OPT_SUPPORTED cinfo->optimize_coding = TRUE; #else fprintf( stderr, "%s: sorry, entropy optimization was not compiled\n", progname ); exit( EXIT_FAILURE ); #endif } else if ( keymatch( arg, "outfile", 4 ) ) { /* Set output file name. */ if ( ++argn >= argc ) {/* advance to next argument */ usage(); } outfilename = argv[argn];/* save it away for later use */ } else if ( keymatch( arg, "progressive", 1 ) ) { /* Select simple progressive mode. */ #ifdef C_PROGRESSIVE_SUPPORTED simple_progressive = TRUE; /* We must postpone execution until num_components is known. */ #else fprintf( stderr, "%s: sorry, progressive output was not compiled\n", progname ); exit( EXIT_FAILURE ); #endif } else if ( keymatch( arg, "restart", 1 ) ) { /* Restart interval in MCU rows (or in MCUs with 'b'). */ long lval; char ch = 'x'; if ( ++argn >= argc ) {/* advance to next argument */ usage(); } if ( sscanf( argv[argn], "%ld%c", &lval, &ch ) < 1 ) { usage(); } if ( ( lval < 0 ) || ( lval > 65535L ) ) { usage(); } if ( ( ch == 'b' ) || ( ch == 'B' ) ) { cinfo->restart_interval = (unsigned int) lval; cinfo->restart_in_rows = 0;/* else prior '-restart n' overrides me */ } else { cinfo->restart_in_rows = (int) lval; /* restart_interval will be computed during startup */ } } else if ( keymatch( arg, "scans", 2 ) ) { /* Set scan script. */ #ifdef C_MULTISCAN_FILES_SUPPORTED if ( ++argn >= argc ) {/* advance to next argument */ usage(); } scansarg = argv[argn]; /* We must postpone reading the file in case -progressive appears. */ #else fprintf( stderr, "%s: sorry, multi-scan output was not compiled\n", progname ); exit( EXIT_FAILURE ); #endif } else { usage(); /* bogus switch */ } } /* Post-switch-scanning cleanup */ if ( for_real ) { #ifdef C_PROGRESSIVE_SUPPORTED if ( simple_progressive ) {/* process -progressive; -scans can override */ jpeg_simple_progression( cinfo ); } #endif #ifdef C_MULTISCAN_FILES_SUPPORTED if ( scansarg != NULL ) {/* process -scans if it was present */ if ( !read_scan_script( cinfo, scansarg ) ) { usage(); } } #endif } return argn; /* return index of next arg (file name) */ } /* * The main program. */ GLOBAL int main( int argc, char ** argv ) { struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; struct jpeg_error_mgr jsrcerr, jdsterr; #ifdef PROGRESS_REPORT struct cdjpeg_progress_mgr progress; #endif jvirt_barray_ptr * coef_arrays; int file_index; FILE * input_file; FILE * output_file; /* On Mac, fetch a command line. */ #ifdef USE_CCOMMAND argc = ccommand( &argv ); #endif progname = argv[0]; if ( ( progname == NULL ) || ( progname[0] == 0 ) ) { progname = "jpegtran"; } /* in case C library doesn't provide it */ /* Initialize the JPEG decompression object with default error handling. */ srcinfo.err = jpeg_std_error( &jsrcerr ); jpeg_create_decompress( &srcinfo ); /* Initialize the JPEG compression object with default error handling. */ dstinfo.err = jpeg_std_error( &jdsterr ); jpeg_create_compress( &dstinfo ); /* Now safe to enable signal catcher. * Note: we assume only the decompression object will have virtual arrays. */ #ifdef NEED_SIGNAL_CATCHER enable_signal_catcher( ( j_common_ptr ) & srcinfo ); #endif /* Scan command line to find file names. * It is convenient to use just one switch-parsing routine, but the switch * values read here are ignored; we will rescan the switches after opening * the input file. */ file_index = parse_switches( &dstinfo, argc, argv, 0, FALSE ); jsrcerr.trace_level = jdsterr.trace_level; #ifdef TWO_FILE_COMMANDLINE /* Must have either -outfile switch or explicit output file name */ if ( outfilename == NULL ) { if ( file_index != argc - 2 ) { fprintf( stderr, "%s: must name one input and one output file\n", progname ); usage(); } outfilename = argv[file_index + 1]; } else { if ( file_index != argc - 1 ) { fprintf( stderr, "%s: must name one input and one output file\n", progname ); usage(); } } #else /* Unix style: expect zero or one file name */ if ( file_index < argc - 1 ) { fprintf( stderr, "%s: only one input file\n", progname ); usage(); } #endif /* TWO_FILE_COMMANDLINE */ /* Open the input file. */ if ( file_index < argc ) { if ( ( input_file = fopen( argv[file_index], READ_BINARY ) ) == NULL ) { fprintf( stderr, "%s: can't open %s\n", progname, argv[file_index] ); exit( EXIT_FAILURE ); } } else { /* default input file is stdin */ input_file = read_stdin(); } /* Open the output file. */ if ( outfilename != NULL ) { if ( ( output_file = fopen( outfilename, WRITE_BINARY ) ) == NULL ) { fprintf( stderr, "%s: can't open %s\n", progname, outfilename ); exit( EXIT_FAILURE ); } } else { /* default output file is stdout */ output_file = write_stdout(); } #ifdef PROGRESS_REPORT start_progress_monitor( ( j_common_ptr ) & dstinfo, &progress ); #endif /* Specify data source for decompression */ jpeg_stdio_src( &srcinfo, input_file ); /* Read file header */ (void) jpeg_read_header( &srcinfo, TRUE ); /* Read source file as DCT coefficients */ coef_arrays = jpeg_read_coefficients( &srcinfo ); /* Initialize destination compression parameters from source values */ jpeg_copy_critical_parameters( &srcinfo, &dstinfo ); /* Adjust default compression parameters by re-parsing the options */ file_index = parse_switches( &dstinfo, argc, argv, 0, TRUE ); /* Specify data destination for compression */ jpeg_stdio_dest( &dstinfo, output_file ); /* Start compressor */ jpeg_write_coefficients( &dstinfo, coef_arrays ); /* ought to copy source comments here... */ /* Finish compression and release memory */ jpeg_finish_compress( &dstinfo ); jpeg_destroy_compress( &dstinfo ); (void) jpeg_finish_decompress( &srcinfo ); jpeg_destroy_decompress( &srcinfo ); /* Close files, if we opened them */ if ( input_file != stdin ) { fclose( input_file ); } if ( output_file != stdout ) { fclose( output_file ); } #ifdef PROGRESS_REPORT end_progress_monitor( ( j_common_ptr ) & dstinfo ); #endif /* All done. */ exit( jsrcerr.num_warnings + jdsterr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS ); return 0; /* suppress no-return-value warnings */ }