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) */ }
size_t Jpeg::Leanify(size_t size_leanified /*= 0*/) { struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; struct jpeg_error_mgr jsrcerr, jdsterr; srcinfo.err = jpeg_std_error(&jsrcerr); jsrcerr.error_exit = mozjpeg_error_handler; if (setjmp(setjmp_buffer)) { jpeg_destroy_compress(&dstinfo); jpeg_destroy_decompress(&srcinfo); return Format::Leanify(size_leanified); } jpeg_create_decompress(&srcinfo); dstinfo.err = jpeg_std_error(&jdsterr); jdsterr.error_exit = mozjpeg_error_handler; jpeg_create_compress(&dstinfo); if (is_verbose) { dstinfo.err->trace_level++; } if (is_fast) { jpeg_c_set_int_param(&dstinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST); } /* Specify data source for decompression */ jpeg_mem_src(&srcinfo, fp_, size_); // Always save exif to show warning if orientation might change. jpeg_save_markers(&srcinfo, JPEG_APP0 + 1, 0xFFFF); if (keep_icc_profile_ || keep_all_metadata_) { jpeg_save_markers(&srcinfo, JPEG_APP0 + 2, 0xFFFF); } if (keep_all_metadata_) { // Save the rest APPn markers. for (int i = 3; i < 16; i++) jpeg_save_markers(&srcinfo, JPEG_APP0 + i, 0xFFFF); // Save comments. jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF); } (void)jpeg_read_header(&srcinfo, true); /* Read source file as DCT coefficients */ auto coef_arrays = jpeg_read_coefficients(&srcinfo); /* Initialize destination compression parameters from source values */ jpeg_copy_critical_parameters(&srcinfo, &dstinfo); // use arithmetic coding if input file is arithmetic coded if (srcinfo.arith_code) { dstinfo.arith_code = true; dstinfo.optimize_coding = false; } else { dstinfo.optimize_coding = true; } uint8_t* outbuffer = nullptr; unsigned long outsize = 0; /* Specify data destination for compression */ jpeg_mem_dest(&dstinfo, &outbuffer, &outsize); /* Start compressor (note no image data is actually written here) */ jpeg_write_coefficients(&dstinfo, coef_arrays); for (auto marker = srcinfo.marker_list; marker; marker = marker->next) { if (marker->marker == JPEG_APP0 + 1 && !keep_exif_ && !keep_all_metadata_) { // Tag number: 0x0112, data format: unsigned short(3), number of components: 1 const uint8_t kExifOrientation[] = { 0x12, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00 }; const uint8_t kExifOrientationMotorola[] = { 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01 }; uint8_t* start = marker->data; uint8_t* end = start + marker->data_length; if (std::search(start, end, kExifOrientation, std::end(kExifOrientation)) != end || std::search(start, end, kExifOrientationMotorola, std::end(kExifOrientationMotorola)) != end) { std::cout << "Warning: The Exif being removed contains orientation data, result image might have wrong " "orientation, use --keep-exif to keep Exif." << std::endl; } continue; } jpeg_write_marker(&dstinfo, marker->marker, marker->data, marker->data_length); } /* Finish compression and release memory */ jpeg_finish_compress(&dstinfo); (void)jpeg_finish_decompress(&srcinfo); jpeg_destroy_decompress(&srcinfo); fp_ -= size_leanified; // use mozjpeg result if it's smaller than original if (outsize < size_) { memcpy(fp_, outbuffer, outsize); size_ = outsize; } else { memmove(fp_, fp_ + size_leanified, size_); } jpeg_destroy_compress(&dstinfo); return size_; }
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. */ #ifdef C_PROGRESSIVE_SUPPORTED simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE; #else simple_progressive = FALSE; #endif outfilename = NULL; copyoption = JCOPYOPT_DEFAULT; transformoption.transform = JXFORM_NONE; transformoption.perfect = FALSE; transformoption.trim = FALSE; transformoption.force_grayscale = FALSE; transformoption.crop = FALSE; transformoption.slow_hflip = 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, "copy", 2)) { /* Select which extra markers to copy. */ if (++argn >= argc) /* advance to next argument */ usage(); if (keymatch(argv[argn], "none", 1)) { copyoption = JCOPYOPT_NONE; } else if (keymatch(argv[argn], "comments", 1)) { copyoption = JCOPYOPT_COMMENTS; } else if (keymatch(argv[argn], "all", 1)) { copyoption = JCOPYOPT_ALL; } else usage(); } else if (keymatch(arg, "crop", 2)) { /* Perform lossless cropping. */ #if TRANSFORMS_SUPPORTED if (++argn >= argc) /* advance to next argument */ usage(); if (! jtransform_parse_crop_spec(&transformoption, argv[argn])) { fprintf(stderr, "%s: bogus -crop argument '%s'\n", progname, argv[argn]); exit(EXIT_FAILURE); } #else select_transform(JXFORM_NONE); /* force an error */ #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, "%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, "flip", 1)) { /* Mirror left-right or top-bottom. */ if (++argn >= argc) /* advance to next argument */ usage(); if (keymatch(argv[argn], "horizontal", 1)) select_transform(JXFORM_FLIP_H); else if (keymatch(argv[argn], "vertical", 1)) select_transform(JXFORM_FLIP_V); else usage(); } else if (keymatch(arg, "fastcrush", 4)) { jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE); } else if (keymatch(arg, "grayscale", 1) || keymatch(arg, "greyscale",1)) { /* Force to grayscale. */ #if TRANSFORMS_SUPPORTED transformoption.force_grayscale = TRUE; #else select_transform(JXFORM_NONE); /* force an error */ #endif } 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, "perfect", 2)) { /* Fail if there is any partial edge MCUs that the transform can't * handle. */ transformoption.perfect = TRUE; } else if (keymatch(arg, "progressive", 2)) { /* 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, "revert", 3)) { /* revert to old JPEG default */ jpeg_c_set_int_param(cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST); } else if (keymatch(arg, "rotate", 2)) { /* Rotate 90, 180, or 270 degrees (measured clockwise). */ if (++argn >= argc) /* advance to next argument */ usage(); if (keymatch(argv[argn], "90", 2)) select_transform(JXFORM_ROT_90); else if (keymatch(argv[argn], "180", 3)) select_transform(JXFORM_ROT_180); else if (keymatch(argv[argn], "270", 3)) select_transform(JXFORM_ROT_270); else usage(); } else if (keymatch(arg, "scans", 1)) { /* 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 if (keymatch(arg, "transpose", 1)) { /* Transpose (across UL-to-LR axis). */ select_transform(JXFORM_TRANSPOSE); } else if (keymatch(arg, "transverse", 6)) { /* Transverse transpose (across UR-to-LL axis). */ select_transform(JXFORM_TRANSVERSE); } else if (keymatch(arg, "trim", 3)) { /* Trim off any partial edge MCUs that the transform can't handle. */ transformoption.trim = TRUE; } 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) */ }
/* Write a VIPS image to a JPEG compress struct. */ static int write_vips( Write *write, int qfac, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean no_subsample, gboolean trellis_quant, gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) { VipsImage *in; J_COLOR_SPACE space; /* The image we'll be writing ... can change, see CMYK. */ in = write->in; /* Should have been converted for save. */ g_assert( in->BandFmt == VIPS_FORMAT_UCHAR ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 ); /* Check input image. */ if( vips_image_pio_input( in ) ) return( -1 ); /* Set compression parameters. */ write->cinfo.image_width = in->Xsize; write->cinfo.image_height = in->Ysize; write->cinfo.input_components = in->Bands; if( in->Bands == 4 && in->Type == VIPS_INTERPRETATION_CMYK ) { space = JCS_CMYK; /* IJG always sets an Adobe marker, so we should invert CMYK. */ if( vips_invert( in, &write->inverted, NULL ) ) return( -1 ); in = write->inverted; } else if( in->Bands == 3 ) space = JCS_RGB; else if( in->Bands == 1 ) space = JCS_GRAYSCALE; else /* Use luminance compression for all channels. */ space = JCS_UNKNOWN; write->cinfo.in_color_space = space; /* Build VIPS output stuff now we know the image we'll be writing. */ if( !(write->row_pointer = VIPS_ARRAY( NULL, in->Ysize, JSAMPROW )) ) return( -1 ); #ifdef HAVE_JPEG_EXT_PARAMS /* Reset compression profile to libjpeg defaults */ if( jpeg_c_int_param_supported( &write->cinfo, JINT_COMPRESS_PROFILE ) ) jpeg_c_set_int_param( &write->cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST ); #endif /* Reset to default. */ jpeg_set_defaults( &write->cinfo ); /* Compute optimal Huffman coding tables. */ write->cinfo.optimize_coding = optimize_coding; #ifdef HAVE_JPEG_EXT_PARAMS /* Apply trellis quantisation to each 8x8 block. Implies * "optimize_coding". */ if( trellis_quant ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_TRELLIS_QUANT ) ) { jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_TRELLIS_QUANT, TRUE ); write->cinfo.optimize_coding = TRUE; } else vips_warn( "vips2jpeg", "%s", _( "trellis_quant unsupported" ) ); } /* Apply overshooting to samples with extreme values e.g. 0 & 255 * for 8-bit. */ if( overshoot_deringing ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING ) ) jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING, TRUE ); else vips_warn( "vips2jpeg", "%s", _( "overshoot_deringing unsupported" ) ); } /* Split the spectrum of DCT coefficients into separate scans. * Requires progressive output. Must be set before * jpeg_simple_progression. */ if( optimize_scans ) { if( progressive ) { if( jpeg_c_bool_param_supported( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS ) ) jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS, TRUE ); else vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); } else vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans for baseline" ) ); } /* Use predefined quantization table. */ if( quant_table > 0 ) { if( jpeg_c_int_param_supported( &write->cinfo, JINT_BASE_QUANT_TBL_IDX ) ) jpeg_c_set_int_param( &write->cinfo, JINT_BASE_QUANT_TBL_IDX, quant_table ); else vips_warn( "vips2jpeg", "%s", _( "Setting quant_table unsupported" ) ); } #else /* Using jpeglib.h without extension parameters, warn of ignored * options. */ if( trellis_quant ) vips_warn( "vips2jpeg", "%s", _( "Ignoring trellis_quant" ) ); if( overshoot_deringing ) vips_warn( "vips2jpeg", "%s", _( "Ignoring overshoot_deringing" ) ); if( optimize_scans ) vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); if( quant_table > 0 ) vips_warn( "vips2jpeg", "%s", _( "Ignoring quant_table" ) ); #endif /* Set compression quality. Must be called after setting params above. */ jpeg_set_quality( &write->cinfo, qfac, TRUE ); /* Enable progressive write. */ if( progressive ) jpeg_simple_progression( &write->cinfo ); /* Turn off chroma subsampling. Follow IM and do it automatically for * high Q. */ if( no_subsample || qfac > 90 ) { int i; for( i = 0; i < in->Bands; i++ ) { write->cinfo.comp_info[i].h_samp_factor = 1; write->cinfo.comp_info[i].v_samp_factor = 1; } } /* Don't write the APP0 JFIF headers if we are stripping. */ if( strip ) write->cinfo.write_JFIF_header = FALSE; /* Build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); /* Write any APP markers we need. */ if( !strip ) { if( write_exif( write ) || write_blob( write, VIPS_META_XMP_NAME, JPEG_APP0 + 1 ) || write_blob( write, VIPS_META_IPCT_NAME, JPEG_APP0 + 13 ) ) return( -1 ); /* A profile supplied as an argument overrides an embedded * profile. "none" means don't attach a profile. */ if( profile && strcmp( profile, "none" ) != 0 && write_profile_file( write, profile ) ) return( -1 ); if( !profile && vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && write_profile_meta( write ) ) return( -1 ); } /* Write data. Note that the write function grabs the longjmp()! */ if( vips_sink_disc( in, write_jpeg_block, write ) ) return( -1 ); /* We have to reinstate the setjmp() before we jpeg_finish_compress(). */ if( setjmp( write->eman.jmp ) ) return( -1 ); jpeg_finish_compress( &write->cinfo ); return( 0 ); }