Home | History | Annotate | Download | only in libjpeg-turbo
      1 /*
      2  * jpegtran.c
      3  *
      4  * This file was part of the Independent JPEG Group's software:
      5  * Copyright (C) 1995-2010, Thomas G. Lane, Guido Vollbeding.
      6  * libjpeg-turbo Modifications:
      7  * Copyright (C) 2010, 2014, 2017, D. R. Commander.
      8  * For conditions of distribution and use, see the accompanying README.ijg
      9  * file.
     10  *
     11  * This file contains a command-line user interface for JPEG transcoding.
     12  * It is very similar to cjpeg.c, and partly to djpeg.c, but provides
     13  * lossless transcoding between different JPEG file formats.  It also
     14  * provides some lossless and sort-of-lossless transformations of JPEG data.
     15  */
     16 
     17 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
     18 #include "transupp.h"           /* Support routines for jpegtran */
     19 #include "jversion.h"           /* for version message */
     20 #include "jconfigint.h"
     21 
     22 #ifdef USE_CCOMMAND             /* command-line reader for Macintosh */
     23 #ifdef __MWERKS__
     24 #include <SIOUX.h>              /* Metrowerks needs this */
     25 #include <console.h>            /* ... and this */
     26 #endif
     27 #ifdef THINK_C
     28 #include <console.h>            /* Think declares it here */
     29 #endif
     30 #endif
     31 
     32 
     33 /*
     34  * Argument-parsing code.
     35  * The switch parser is designed to be useful with DOS-style command line
     36  * syntax, ie, intermixed switches and file names, where only the switches
     37  * to the left of a given file name affect processing of that file.
     38  * The main program in this file doesn't actually use this capability...
     39  */
     40 
     41 
     42 static const char *progname;    /* program name for error messages */
     43 static char *outfilename;       /* for -outfile switch */
     44 static JCOPY_OPTION copyoption; /* -copy switch */
     45 static jpeg_transform_info transformoption; /* image transformation options */
     46 
     47 
     48 LOCAL(void)
     49 usage (void)
     50 /* complain about bad command line */
     51 {
     52   fprintf(stderr, "usage: %s [switches] ", progname);
     53 #ifdef TWO_FILE_COMMANDLINE
     54   fprintf(stderr, "inputfile outputfile\n");
     55 #else
     56   fprintf(stderr, "[inputfile]\n");
     57 #endif
     58 
     59   fprintf(stderr, "Switches (names may be abbreviated):\n");
     60   fprintf(stderr, "  -copy none     Copy no extra markers from source file\n");
     61   fprintf(stderr, "  -copy comments Copy only comment markers (default)\n");
     62   fprintf(stderr, "  -copy all      Copy all extra markers\n");
     63 #ifdef ENTROPY_OPT_SUPPORTED
     64   fprintf(stderr, "  -optimize      Optimize Huffman table (smaller file, but slow compression)\n");
     65 #endif
     66 #ifdef C_PROGRESSIVE_SUPPORTED
     67   fprintf(stderr, "  -progressive   Create progressive JPEG file\n");
     68 #endif
     69   fprintf(stderr, "Switches for modifying the image:\n");
     70 #if TRANSFORMS_SUPPORTED
     71   fprintf(stderr, "  -crop WxH+X+Y  Crop to a rectangular subarea\n");
     72   fprintf(stderr, "  -grayscale     Reduce to grayscale (omit color data)\n");
     73   fprintf(stderr, "  -flip [horizontal|vertical]  Mirror image (left-right or top-bottom)\n");
     74   fprintf(stderr, "  -perfect       Fail if there is non-transformable edge blocks\n");
     75   fprintf(stderr, "  -rotate [90|180|270]         Rotate image (degrees clockwise)\n");
     76 #endif
     77 #if TRANSFORMS_SUPPORTED
     78   fprintf(stderr, "  -transpose     Transpose image\n");
     79   fprintf(stderr, "  -transverse    Transverse transpose image\n");
     80   fprintf(stderr, "  -trim          Drop non-transformable edge blocks\n");
     81 #endif
     82   fprintf(stderr, "Switches for advanced users:\n");
     83 #ifdef C_ARITH_CODING_SUPPORTED
     84   fprintf(stderr, "  -arithmetic    Use arithmetic coding\n");
     85 #endif
     86   fprintf(stderr, "  -restart N     Set restart interval in rows, or in blocks with B\n");
     87   fprintf(stderr, "  -maxmemory N   Maximum memory to use (in kbytes)\n");
     88   fprintf(stderr, "  -outfile name  Specify name for output file\n");
     89   fprintf(stderr, "  -verbose  or  -debug   Emit debug output\n");
     90   fprintf(stderr, "  -version       Print version information and exit\n");
     91   fprintf(stderr, "Switches for wizards:\n");
     92 #ifdef C_MULTISCAN_FILES_SUPPORTED
     93   fprintf(stderr, "  -scans FILE    Create multi-scan JPEG per script FILE\n");
     94 #endif
     95   exit(EXIT_FAILURE);
     96 }
     97 
     98 
     99 LOCAL(void)
    100 select_transform (JXFORM_CODE transform)
    101 /* Silly little routine to detect multiple transform options,
    102  * which we can't handle.
    103  */
    104 {
    105 #if TRANSFORMS_SUPPORTED
    106   if (transformoption.transform == JXFORM_NONE ||
    107       transformoption.transform == transform) {
    108     transformoption.transform = transform;
    109   } else {
    110     fprintf(stderr, "%s: can only do one image transformation at a time\n",
    111             progname);
    112     usage();
    113   }
    114 #else
    115   fprintf(stderr, "%s: sorry, image transformation was not compiled\n",
    116           progname);
    117   exit(EXIT_FAILURE);
    118 #endif
    119 }
    120 
    121 
    122 LOCAL(int)
    123 parse_switches (j_compress_ptr cinfo, int argc, char **argv,
    124                 int last_file_arg_seen, boolean for_real)
    125 /* Parse optional switches.
    126  * Returns argv[] index of first file-name argument (== argc if none).
    127  * Any file names with indexes <= last_file_arg_seen are ignored;
    128  * they have presumably been processed in a previous iteration.
    129  * (Pass 0 for last_file_arg_seen on the first or only iteration.)
    130  * for_real is FALSE on the first (dummy) pass; we may skip any expensive
    131  * processing.
    132  */
    133 {
    134   int argn;
    135   char *arg;
    136   boolean simple_progressive;
    137   char *scansarg = NULL;        /* saves -scans parm if any */
    138 
    139   /* Set up default JPEG parameters. */
    140   simple_progressive = FALSE;
    141   outfilename = NULL;
    142   copyoption = JCOPYOPT_DEFAULT;
    143   transformoption.transform = JXFORM_NONE;
    144   transformoption.perfect = FALSE;
    145   transformoption.trim = FALSE;
    146   transformoption.force_grayscale = FALSE;
    147   transformoption.crop = FALSE;
    148   transformoption.slow_hflip = FALSE;
    149   cinfo->err->trace_level = 0;
    150 
    151   /* Scan command line options, adjust parameters */
    152 
    153   for (argn = 1; argn < argc; argn++) {
    154     arg = argv[argn];
    155     if (*arg != '-') {
    156       /* Not a switch, must be a file name argument */
    157       if (argn <= last_file_arg_seen) {
    158         outfilename = NULL;     /* -outfile applies to just one input file */
    159         continue;               /* ignore this name if previously processed */
    160       }
    161       break;                    /* else done parsing switches */
    162     }
    163     arg++;                      /* advance past switch marker character */
    164 
    165     if (keymatch(arg, "arithmetic", 1)) {
    166       /* Use arithmetic coding. */
    167 #ifdef C_ARITH_CODING_SUPPORTED
    168       cinfo->arith_code = TRUE;
    169 #else
    170       fprintf(stderr, "%s: sorry, arithmetic coding not supported\n",
    171               progname);
    172       exit(EXIT_FAILURE);
    173 #endif
    174 
    175     } else if (keymatch(arg, "copy", 2)) {
    176       /* Select which extra markers to copy. */
    177       if (++argn >= argc)       /* advance to next argument */
    178         usage();
    179       if (keymatch(argv[argn], "none", 1)) {
    180         copyoption = JCOPYOPT_NONE;
    181       } else if (keymatch(argv[argn], "comments", 1)) {
    182         copyoption = JCOPYOPT_COMMENTS;
    183       } else if (keymatch(argv[argn], "all", 1)) {
    184         copyoption = JCOPYOPT_ALL;
    185       } else
    186         usage();
    187 
    188     } else if (keymatch(arg, "crop", 2)) {
    189       /* Perform lossless cropping. */
    190 #if TRANSFORMS_SUPPORTED
    191       if (++argn >= argc)       /* advance to next argument */
    192         usage();
    193       if (! jtransform_parse_crop_spec(&transformoption, argv[argn])) {
    194         fprintf(stderr, "%s: bogus -crop argument '%s'\n",
    195                 progname, argv[argn]);
    196         exit(EXIT_FAILURE);
    197       }
    198 #else
    199       select_transform(JXFORM_NONE);    /* force an error */
    200 #endif
    201 
    202     } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
    203       /* Enable debug printouts. */
    204       /* On first -d, print version identification */
    205       static boolean printed_version = FALSE;
    206 
    207       if (! printed_version) {
    208         fprintf(stderr, "%s version %s (build %s)\n",
    209                 PACKAGE_NAME, VERSION, BUILD);
    210         fprintf(stderr, "%s\n\n", JCOPYRIGHT);
    211         fprintf(stderr, "Emulating The Independent JPEG Group's software, version %s\n\n",
    212                 JVERSION);
    213         printed_version = TRUE;
    214       }
    215       cinfo->err->trace_level++;
    216 
    217     } else if (keymatch(arg, "version", 4)) {
    218       fprintf(stderr, "%s version %s (build %s)\n",
    219               PACKAGE_NAME, VERSION, BUILD);
    220       exit(EXIT_SUCCESS);
    221 
    222     } else if (keymatch(arg, "flip", 1)) {
    223       /* Mirror left-right or top-bottom. */
    224       if (++argn >= argc)       /* advance to next argument */
    225         usage();
    226       if (keymatch(argv[argn], "horizontal", 1))
    227         select_transform(JXFORM_FLIP_H);
    228       else if (keymatch(argv[argn], "vertical", 1))
    229         select_transform(JXFORM_FLIP_V);
    230       else
    231         usage();
    232 
    233     } else if (keymatch(arg, "grayscale", 1) || keymatch(arg, "greyscale",1)) {
    234       /* Force to grayscale. */
    235 #if TRANSFORMS_SUPPORTED
    236       transformoption.force_grayscale = TRUE;
    237 #else
    238       select_transform(JXFORM_NONE);    /* force an error */
    239 #endif
    240 
    241     } else if (keymatch(arg, "maxmemory", 3)) {
    242       /* Maximum memory in Kb (or Mb with 'm'). */
    243       long lval;
    244       char ch = 'x';
    245 
    246       if (++argn >= argc)       /* advance to next argument */
    247         usage();
    248       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
    249         usage();
    250       if (ch == 'm' || ch == 'M')
    251         lval *= 1000L;
    252       cinfo->mem->max_memory_to_use = lval * 1000L;
    253 
    254     } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
    255       /* Enable entropy parm optimization. */
    256 #ifdef ENTROPY_OPT_SUPPORTED
    257       cinfo->optimize_coding = TRUE;
    258 #else
    259       fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n",
    260               progname);
    261       exit(EXIT_FAILURE);
    262 #endif
    263 
    264     } else if (keymatch(arg, "outfile", 4)) {
    265       /* Set output file name. */
    266       if (++argn >= argc)       /* advance to next argument */
    267         usage();
    268       outfilename = argv[argn]; /* save it away for later use */
    269 
    270     } else if (keymatch(arg, "perfect", 2)) {
    271       /* Fail if there is any partial edge MCUs that the transform can't
    272        * handle. */
    273       transformoption.perfect = TRUE;
    274 
    275     } else if (keymatch(arg, "progressive", 2)) {
    276       /* Select simple progressive mode. */
    277 #ifdef C_PROGRESSIVE_SUPPORTED
    278       simple_progressive = TRUE;
    279       /* We must postpone execution until num_components is known. */
    280 #else
    281       fprintf(stderr, "%s: sorry, progressive output was not compiled\n",
    282               progname);
    283       exit(EXIT_FAILURE);
    284 #endif
    285 
    286     } else if (keymatch(arg, "restart", 1)) {
    287       /* Restart interval in MCU rows (or in MCUs with 'b'). */
    288       long lval;
    289       char ch = 'x';
    290 
    291       if (++argn >= argc)       /* advance to next argument */
    292         usage();
    293       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
    294         usage();
    295       if (lval < 0 || lval > 65535L)
    296         usage();
    297       if (ch == 'b' || ch == 'B') {
    298         cinfo->restart_interval = (unsigned int) lval;
    299         cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
    300       } else {
    301         cinfo->restart_in_rows = (int) lval;
    302         /* restart_interval will be computed during startup */
    303       }
    304 
    305     } else if (keymatch(arg, "rotate", 2)) {
    306       /* Rotate 90, 180, or 270 degrees (measured clockwise). */
    307       if (++argn >= argc)       /* advance to next argument */
    308         usage();
    309       if (keymatch(argv[argn], "90", 2))
    310         select_transform(JXFORM_ROT_90);
    311       else if (keymatch(argv[argn], "180", 3))
    312         select_transform(JXFORM_ROT_180);
    313       else if (keymatch(argv[argn], "270", 3))
    314         select_transform(JXFORM_ROT_270);
    315       else
    316         usage();
    317 
    318     } else if (keymatch(arg, "scans", 1)) {
    319       /* Set scan script. */
    320 #ifdef C_MULTISCAN_FILES_SUPPORTED
    321       if (++argn >= argc)       /* advance to next argument */
    322         usage();
    323       scansarg = argv[argn];
    324       /* We must postpone reading the file in case -progressive appears. */
    325 #else
    326       fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n",
    327               progname);
    328       exit(EXIT_FAILURE);
    329 #endif
    330 
    331     } else if (keymatch(arg, "transpose", 1)) {
    332       /* Transpose (across UL-to-LR axis). */
    333       select_transform(JXFORM_TRANSPOSE);
    334 
    335     } else if (keymatch(arg, "transverse", 6)) {
    336       /* Transverse transpose (across UR-to-LL axis). */
    337       select_transform(JXFORM_TRANSVERSE);
    338 
    339     } else if (keymatch(arg, "trim", 3)) {
    340       /* Trim off any partial edge MCUs that the transform can't handle. */
    341       transformoption.trim = TRUE;
    342 
    343     } else {
    344       usage();                  /* bogus switch */
    345     }
    346   }
    347 
    348   /* Post-switch-scanning cleanup */
    349 
    350   if (for_real) {
    351 
    352 #ifdef C_PROGRESSIVE_SUPPORTED
    353     if (simple_progressive)     /* process -progressive; -scans can override */
    354       jpeg_simple_progression(cinfo);
    355 #endif
    356 
    357 #ifdef C_MULTISCAN_FILES_SUPPORTED
    358     if (scansarg != NULL)       /* process -scans if it was present */
    359       if (! read_scan_script(cinfo, scansarg))
    360         usage();
    361 #endif
    362   }
    363 
    364   return argn;                  /* return index of next arg (file name) */
    365 }
    366 
    367 
    368 /*
    369  * The main program.
    370  */
    371 
    372 int
    373 main (int argc, char **argv)
    374 {
    375   struct jpeg_decompress_struct srcinfo;
    376   struct jpeg_compress_struct dstinfo;
    377   struct jpeg_error_mgr jsrcerr, jdsterr;
    378 #ifdef PROGRESS_REPORT
    379   struct cdjpeg_progress_mgr progress;
    380 #endif
    381   jvirt_barray_ptr *src_coef_arrays;
    382   jvirt_barray_ptr *dst_coef_arrays;
    383   int file_index;
    384   /* We assume all-in-memory processing and can therefore use only a
    385    * single file pointer for sequential input and output operation.
    386    */
    387   FILE *fp;
    388 
    389   /* On Mac, fetch a command line. */
    390 #ifdef USE_CCOMMAND
    391   argc = ccommand(&argv);
    392 #endif
    393 
    394   progname = argv[0];
    395   if (progname == NULL || progname[0] == 0)
    396     progname = "jpegtran";      /* in case C library doesn't provide it */
    397 
    398   /* Initialize the JPEG decompression object with default error handling. */
    399   srcinfo.err = jpeg_std_error(&jsrcerr);
    400   jpeg_create_decompress(&srcinfo);
    401   /* Initialize the JPEG compression object with default error handling. */
    402   dstinfo.err = jpeg_std_error(&jdsterr);
    403   jpeg_create_compress(&dstinfo);
    404 
    405   /* Scan command line to find file names.
    406    * It is convenient to use just one switch-parsing routine, but the switch
    407    * values read here are mostly ignored; we will rescan the switches after
    408    * opening the input file.  Also note that most of the switches affect the
    409    * destination JPEG object, so we parse into that and then copy over what
    410    * needs to affects the source too.
    411    */
    412 
    413   file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE);
    414   jsrcerr.trace_level = jdsterr.trace_level;
    415   srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
    416 
    417 #ifdef TWO_FILE_COMMANDLINE
    418   /* Must have either -outfile switch or explicit output file name */
    419   if (outfilename == NULL) {
    420     if (file_index != argc-2) {
    421       fprintf(stderr, "%s: must name one input and one output file\n",
    422               progname);
    423       usage();
    424     }
    425     outfilename = argv[file_index+1];
    426   } else {
    427     if (file_index != argc-1) {
    428       fprintf(stderr, "%s: must name one input and one output file\n",
    429               progname);
    430       usage();
    431     }
    432   }
    433 #else
    434   /* Unix style: expect zero or one file name */
    435   if (file_index < argc-1) {
    436     fprintf(stderr, "%s: only one input file\n", progname);
    437     usage();
    438   }
    439 #endif /* TWO_FILE_COMMANDLINE */
    440 
    441   /* Open the input file. */
    442   if (file_index < argc) {
    443     if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) {
    444       fprintf(stderr, "%s: can't open %s for reading\n", progname, argv[file_index]);
    445       exit(EXIT_FAILURE);
    446     }
    447   } else {
    448     /* default input file is stdin */
    449     fp = read_stdin();
    450   }
    451 
    452 #ifdef PROGRESS_REPORT
    453   start_progress_monitor((j_common_ptr) &dstinfo, &progress);
    454 #endif
    455 
    456   /* Specify data source for decompression */
    457   jpeg_stdio_src(&srcinfo, fp);
    458 
    459   /* Enable saving of extra markers that we want to copy */
    460   jcopy_markers_setup(&srcinfo, copyoption);
    461 
    462   /* Read file header */
    463   (void) jpeg_read_header(&srcinfo, TRUE);
    464 
    465   /* Any space needed by a transform option must be requested before
    466    * jpeg_read_coefficients so that memory allocation will be done right.
    467    */
    468 #if TRANSFORMS_SUPPORTED
    469   /* Fail right away if -perfect is given and transformation is not perfect.
    470    */
    471   if (!jtransform_request_workspace(&srcinfo, &transformoption)) {
    472     fprintf(stderr, "%s: transformation is not perfect\n", progname);
    473     exit(EXIT_FAILURE);
    474   }
    475 #endif
    476 
    477   /* Read source file as DCT coefficients */
    478   src_coef_arrays = jpeg_read_coefficients(&srcinfo);
    479 
    480   /* Initialize destination compression parameters from source values */
    481   jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
    482 
    483   /* Adjust destination parameters if required by transform options;
    484    * also find out which set of coefficient arrays will hold the output.
    485    */
    486 #if TRANSFORMS_SUPPORTED
    487   dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo,
    488                                                  src_coef_arrays,
    489                                                  &transformoption);
    490 #else
    491   dst_coef_arrays = src_coef_arrays;
    492 #endif
    493 
    494   /* Close input file, if we opened it.
    495    * Note: we assume that jpeg_read_coefficients consumed all input
    496    * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
    497    * only consume more while (! cinfo->inputctl->eoi_reached).
    498    * We cannot call jpeg_finish_decompress here since we still need the
    499    * virtual arrays allocated from the source object for processing.
    500    */
    501   if (fp != stdin)
    502     fclose(fp);
    503 
    504   /* Open the output file. */
    505   if (outfilename != NULL) {
    506     if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) {
    507       fprintf(stderr, "%s: can't open %s for writing\n", progname, outfilename);
    508       exit(EXIT_FAILURE);
    509     }
    510   } else {
    511     /* default output file is stdout */
    512     fp = write_stdout();
    513   }
    514 
    515   /* Adjust default compression parameters by re-parsing the options */
    516   file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE);
    517 
    518   /* Specify data destination for compression */
    519   jpeg_stdio_dest(&dstinfo, fp);
    520 
    521   /* Start compressor (note no image data is actually written here) */
    522   jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
    523 
    524   /* Copy to the output file any extra markers that we want to preserve */
    525   jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
    526 
    527   /* Execute image transformation, if any */
    528 #if TRANSFORMS_SUPPORTED
    529   jtransform_execute_transformation(&srcinfo, &dstinfo,
    530                                     src_coef_arrays,
    531                                     &transformoption);
    532 #endif
    533 
    534   /* Finish compression and release memory */
    535   jpeg_finish_compress(&dstinfo);
    536   jpeg_destroy_compress(&dstinfo);
    537   (void) jpeg_finish_decompress(&srcinfo);
    538   jpeg_destroy_decompress(&srcinfo);
    539 
    540   /* Close output file, if we opened it */
    541   if (fp != stdout)
    542     fclose(fp);
    543 
    544 #ifdef PROGRESS_REPORT
    545   end_progress_monitor((j_common_ptr) &dstinfo);
    546 #endif
    547 
    548   /* All done. */
    549   exit(jsrcerr.num_warnings + jdsterr.num_warnings ?EXIT_WARNING:EXIT_SUCCESS);
    550   return 0;                     /* suppress no-return-value warnings */
    551 }
    552