Esempio n. 1
0
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) */
}
Esempio n. 2
0
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_;
}
Esempio n. 3
0
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) */
}
Esempio n. 4
0
/* 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 );
}