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