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 force_baseline; boolean simple_progressive; char * qualityarg = NULL; /* saves -quality parm if any */ char * qtablefile = NULL; /* saves -qtables filename if any */ char * qslotsarg = NULL; /* saves -qslots parm if any */ char * samplearg = NULL; /* saves -sample parm if any */ char * scansarg = NULL; /* saves -scans parm if any */ /* Set up default JPEG parameters. */ force_baseline = FALSE; /* by default, allow 16-bit quantizers */ #ifdef C_PROGRESSIVE_SUPPORTED simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE; #else simple_progressive = FALSE; #endif is_targa = FALSE; outfilename = NULL; memdst = FALSE; 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; /* No table optimization required for AC */ cinfo->optimize_coding = FALSE; #else fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", progname); exit(EXIT_FAILURE); #endif } else if (keymatch(arg, "baseline", 1)) { /* Force baseline-compatible output (8-bit quantizer values). */ force_baseline = TRUE; /* Disable multiple scans */ simple_progressive = FALSE; cinfo->num_scans = 0; cinfo->scan_info = NULL; } else if (keymatch(arg, "dct", 2)) { /* Select DCT algorithm. */ if (++argn >= argc) { /* advance to next argument */ fprintf(stderr, "%s: missing argument for dct\n", progname); usage(); } if (keymatch(argv[argn], "int", 1)) { cinfo->dct_method = JDCT_ISLOW; } else if (keymatch(argv[argn], "fast", 2)) { cinfo->dct_method = JDCT_IFAST; } else if (keymatch(argv[argn], "float", 2)) { cinfo->dct_method = JDCT_FLOAT; } else { fprintf(stderr, "%s: invalid argument for dct\n", progname); usage(); } } 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, "%s version %s (build %s)\n", PACKAGE_NAME, VERSION, BUILD); fprintf(stderr, "%s\n\n", JCOPYRIGHT); fprintf(stderr, "Emulating The Independent JPEG Group's software, version %s\n\n", JVERSION); printed_version = TRUE; } cinfo->err->trace_level++; } else if (keymatch(arg, "version", 4)) { fprintf(stderr, "%s version %s (build %s)\n", PACKAGE_NAME, VERSION, BUILD); exit(EXIT_SUCCESS); } else if (keymatch(arg, "fastcrush", 4)) { jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE); } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) { /* Force a monochrome JPEG file to be generated. */ jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); } else if (keymatch(arg, "rgb", 3)) { /* Force an RGB JPEG file to be generated. */ jpeg_set_colorspace(cinfo, JCS_RGB); } else if (keymatch(arg, "lambda1", 7)) { if (++argn >= argc) /* advance to next argument */ usage(); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, atof(argv[argn])); } else if (keymatch(arg, "lambda2", 7)) { if (++argn >= argc) /* advance to next argument */ usage(); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, atof(argv[argn])); } 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, "dc-scan-opt", 3)) { if (++argn >= argc) { /* advance to next argument */ fprintf(stderr, "%s: missing argument for dc-scan-opt\n", progname); usage(); } jpeg_c_set_int_param(cinfo, JINT_DC_SCAN_OPT_MODE, atoi(argv[argn])); } 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 in\n", progname); exit(EXIT_FAILURE); #endif } else if (keymatch(arg, "outfile", 4)) { /* Set output file name. */ if (++argn >= argc) { /* advance to next argument */ fprintf(stderr, "%s: missing argument for outfile\n", progname); 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 in\n", progname); exit(EXIT_FAILURE); #endif } else if (keymatch(arg, "memdst", 2)) { /* Use in-memory destination manager */ #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) memdst = TRUE; #else fprintf(stderr, "%s: sorry, in-memory destination manager was not compiled in\n", progname); exit(EXIT_FAILURE); #endif } else if (keymatch(arg, "quality", 1)) { /* Quality ratings (quantization table scaling factors). */ if (++argn >= argc) { /* advance to next argument */ fprintf(stderr, "%s: missing argument for quality\n", progname); usage(); } qualityarg = argv[argn]; } else if (keymatch(arg, "qslots", 2)) { /* Quantization table slot numbers. */ if (++argn >= argc) /* advance to next argument */ usage(); qslotsarg = argv[argn]; /* Must delay setting qslots until after we have processed any * colorspace-determining switches, since jpeg_set_colorspace sets * default quant table numbers. */ } else if (keymatch(arg, "qtables", 2)) { /* Quantization tables fetched from file. */ if (++argn >= argc) /* advance to next argument */ usage(); qtablefile = argv[argn]; /* We postpone actually reading the file in case -quality comes later. */ } else if (keymatch(arg, "quant-table", 7)) { int val; if (++argn >= argc) /* advance to next argument */ usage(); val = atoi(argv[argn]); jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, val); if (jpeg_c_get_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX) != val) { fprintf(stderr, "%s: %d is invalid argument for quant-table\n", progname, val); usage(); } jpeg_set_quality(cinfo, 75, TRUE); } else if (keymatch(arg, "quant-baseline", 7)) { /* Force quantization table to meet baseline requirements */ force_baseline = TRUE; } 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, "revert", 3)) { /* revert to old JPEG default */ jpeg_c_set_int_param(cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST); jpeg_set_defaults(cinfo); } else if (keymatch(arg, "sample", 2)) { /* Set sampling factors. */ if (++argn >= argc) /* advance to next argument */ usage(); samplearg = argv[argn]; /* Must delay setting sample factors until after we have processed any * colorspace-determining switches, since jpeg_set_colorspace sets * default sampling factors. */ } else if (keymatch(arg, "scans", 4)) { /* 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 in\n", progname); exit(EXIT_FAILURE); #endif } else if (keymatch(arg, "smooth", 2)) { /* Set input smoothing factor. */ int val; if (++argn >= argc) /* advance to next argument */ usage(); if (sscanf(argv[argn], "%d", &val) != 1) usage(); if (val < 0 || val > 100) usage(); cinfo->smoothing_factor = val; } else if (keymatch(arg, "targa", 1)) { /* Input file is Targa format. */ is_targa = TRUE; } else if (keymatch(arg, "notrellis-dc", 11)) { /* disable trellis quantization */ jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, FALSE); } else if (keymatch(arg, "notrellis", 1)) { /* disable trellis quantization */ jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT, FALSE); } else if (keymatch(arg, "trellis-dc-ver-weight", 12)) { if (++argn >= argc) { /* advance to next argument */ fprintf(stderr, "%s: missing argument for trellis-dc-ver-weight\n", progname); usage(); } jpeg_c_set_float_param(cinfo, JFLOAT_TRELLIS_DELTA_DC_WEIGHT, atof(argv[argn])); } else if (keymatch(arg, "trellis-dc", 9)) { /* enable DC trellis quantization */ jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, TRUE); } else if (keymatch(arg, "tune-psnr", 6)) { jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, 1); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, 9.0); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, 0.0); jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, FALSE); jpeg_set_quality(cinfo, 75, TRUE); } else if (keymatch(arg, "tune-ssim", 6)) { jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, 1); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, 11.5); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, 12.75); jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, FALSE); jpeg_set_quality(cinfo, 75, TRUE); } else if (keymatch(arg, "tune-ms-ssim", 6)) { jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, 3); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, 12.0); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, 13.0); jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, TRUE); jpeg_set_quality(cinfo, 75, TRUE); } else if (keymatch(arg, "tune-hvs-psnr", 6)) { jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, 3); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, 14.75); jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, 16.5); jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, TRUE); jpeg_set_quality(cinfo, 75, TRUE); } else if (keymatch(arg, "noovershoot", 11)) { jpeg_c_set_bool_param(cinfo, JBOOLEAN_OVERSHOOT_DERINGING, FALSE); } else { fprintf(stderr, "%s: unknown option '%s'\n", progname, arg); usage(); /* bogus switch */ } } /* Post-switch-scanning cleanup */ if (for_real) { /* Set quantization tables for selected quality. */ /* Some or all may be overridden if -qtables is present. */ if (qualityarg != NULL) /* process -quality if it was present */ if (! set_quality_ratings(cinfo, qualityarg, force_baseline)) { fprintf(stderr, "%s: can't set quality ratings\n", progname); usage(); } if (qtablefile != NULL) /* process -qtables if it was present */ if (! read_quant_tables(cinfo, qtablefile, force_baseline)) { fprintf(stderr, "%s: can't read qtable file\n", progname); usage(); } if (qslotsarg != NULL) /* process -qslots if it was present */ if (! set_quant_slots(cinfo, qslotsarg)) usage(); if (samplearg != NULL) /* process -sample if it was present */ if (! set_sample_factors(cinfo, samplearg)) { fprintf(stderr, "%s: can't set sample factors\n", progname); usage(); } #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) */ }
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 * src_coef_arrays; jvirt_barray_ptr * dst_coef_arrays; int file_index; /* We assume all-in-memory processing and can therefore use only a * single file pointer for sequential input and output operation. */ FILE * fp; unsigned char *inbuffer = NULL; unsigned long insize = 0; unsigned char *outbuffer = NULL; unsigned long outsize = 0; /* 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); /* Scan command line to find file names. * It is convenient to use just one switch-parsing routine, but the switch * values read here are mostly ignored; we will rescan the switches after * opening the input file. Also note that most of the switches affect the * destination JPEG object, so we parse into that and then copy over what * needs to affects the source too. */ file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE); jsrcerr.trace_level = jdsterr.trace_level; srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use; #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 ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) { fprintf(stderr, "%s: can't open %s for reading\n", progname, argv[file_index]); exit(EXIT_FAILURE); } } else { /* default input file is stdin */ fp = read_stdin(); } #ifdef PROGRESS_REPORT start_progress_monitor((j_common_ptr) &dstinfo, &progress); #endif /* Specify data source for decompression */ if (jpeg_c_int_param_supported(&dstinfo, JINT_COMPRESS_PROFILE) && jpeg_c_get_int_param(&dstinfo, JINT_COMPRESS_PROFILE) == JCP_MAX_COMPRESSION) memsrc = TRUE; /* needed to revert to original */ #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) if (memsrc) { size_t nbytes; do { inbuffer = (unsigned char *)realloc(inbuffer, insize + INPUT_BUF_SIZE); if (inbuffer == NULL) { fprintf(stderr, "%s: memory allocation failure\n", progname); exit(EXIT_FAILURE); } nbytes = JFREAD(fp, &inbuffer[insize], INPUT_BUF_SIZE); if (nbytes < INPUT_BUF_SIZE && ferror(fp)) { if (file_index < argc) fprintf(stderr, "%s: can't read from %s\n", progname, argv[file_index]); else fprintf(stderr, "%s: can't read from stdin\n", progname); } insize += (unsigned long)nbytes; } while (nbytes == INPUT_BUF_SIZE); jpeg_mem_src(&srcinfo, inbuffer, insize); } else #endif jpeg_stdio_src(&srcinfo, fp); /* Enable saving of extra markers that we want to copy */ jcopy_markers_setup(&srcinfo, copyoption); /* Read file header */ (void) jpeg_read_header(&srcinfo, TRUE); /* Any space needed by a transform option must be requested before * jpeg_read_coefficients so that memory allocation will be done right. */ #if TRANSFORMS_SUPPORTED /* Fail right away if -perfect is given and transformation is not perfect. */ if (!jtransform_request_workspace(&srcinfo, &transformoption)) { fprintf(stderr, "%s: transformation is not perfect\n", progname); exit(EXIT_FAILURE); } #endif /* Read source file as DCT coefficients */ src_coef_arrays = jpeg_read_coefficients(&srcinfo); /* Initialize destination compression parameters from source values */ jpeg_copy_critical_parameters(&srcinfo, &dstinfo); /* Adjust destination parameters if required by transform options; * also find out which set of coefficient arrays will hold the output. */ #if TRANSFORMS_SUPPORTED dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); #else dst_coef_arrays = src_coef_arrays; #endif /* Close input file, if we opened it. * Note: we assume that jpeg_read_coefficients consumed all input * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will * only consume more while (! cinfo->inputctl->eoi_reached). * We cannot call jpeg_finish_decompress here since we still need the * virtual arrays allocated from the source object for processing. */ if (fp != stdin) fclose(fp); /* Open the output file. */ if (outfilename != NULL) { if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) { fprintf(stderr, "%s: can't open %s for writing\n", progname, outfilename); exit(EXIT_FAILURE); } } else { /* default output file is stdout */ fp = write_stdout(); } /* Adjust default compression parameters by re-parsing the options */ file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); /* Specify data destination for compression */ #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) if (jpeg_c_int_param_supported(&dstinfo, JINT_COMPRESS_PROFILE) && jpeg_c_get_int_param(&dstinfo, JINT_COMPRESS_PROFILE) == JCP_MAX_COMPRESSION) jpeg_mem_dest(&dstinfo, &outbuffer, &outsize); else #endif jpeg_stdio_dest(&dstinfo, fp); /* Start compressor (note no image data is actually written here) */ jpeg_write_coefficients(&dstinfo, dst_coef_arrays); /* Copy to the output file any extra markers that we want to preserve */ jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); /* Execute image transformation, if any */ #if TRANSFORMS_SUPPORTED jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); #endif /* Finish compression and release memory */ jpeg_finish_compress(&dstinfo); if (jpeg_c_int_param_supported(&dstinfo, JINT_COMPRESS_PROFILE) && jpeg_c_get_int_param(&dstinfo, JINT_COMPRESS_PROFILE) == JCP_MAX_COMPRESSION) { size_t nbytes; unsigned char *buffer = outbuffer; unsigned long size = outsize; if (insize < size) { size = insize; buffer = inbuffer; } nbytes = JFWRITE(fp, buffer, size); if (nbytes < size && ferror(fp)) { if (file_index < argc) fprintf(stderr, "%s: can't write to %s\n", progname, argv[file_index]); else fprintf(stderr, "%s: can't write to stdout\n", progname); } } jpeg_destroy_compress(&dstinfo); (void) jpeg_finish_decompress(&srcinfo); jpeg_destroy_decompress(&srcinfo); /* Close output file, if we opened it */ if (fp != stdout) fclose(fp); #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 */ }