blob: 90fda7dab7487aa2f94f6ec6a1a055fb92064e6b [file] [log] [blame]
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +00001/*
2 * jpegtran.c
3 *
noel@chromium.org3395bcc2014-04-14 06:56:00 +00004 * This file was part of the Independent JPEG Group's software:
Jonathan Wrightbbb82822020-11-25 13:36:43 +00005 * Copyright (C) 1995-2019, Thomas G. Lane, Guido Vollbeding.
noel@chromium.org3395bcc2014-04-14 06:56:00 +00006 * libjpeg-turbo Modifications:
Jonathan Wrightbbb82822020-11-25 13:36:43 +00007 * Copyright (C) 2010, 2014, 2017, 2019-2020, D. R. Commander.
Tom Hudson0d47d2d2016-05-04 13:22:56 -04008 * For conditions of distribution and use, see the accompanying README.ijg
9 * file.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000010 *
11 * This file contains a command-line user interface for JPEG transcoding.
hbono@chromium.org98626972011-08-03 03:13:08 +000012 * 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.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000015 */
16
Tom Hudson0d47d2d2016-05-04 13:22:56 -040017#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"
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000021
Tom Hudson0d47d2d2016-05-04 13:22:56 -040022#ifdef USE_CCOMMAND /* command-line reader for Macintosh */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000023#ifdef __MWERKS__
24#include <SIOUX.h> /* Metrowerks needs this */
Tom Hudson0d47d2d2016-05-04 13:22:56 -040025#include <console.h> /* ... and this */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000026#endif
27#ifdef THINK_C
Tom Hudson0d47d2d2016-05-04 13:22:56 -040028#include <console.h> /* Think declares it here */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000029#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
Tom Hudson0d47d2d2016-05-04 13:22:56 -040042static const char *progname; /* program name for error messages */
Chris Blumecca8c4d2019-03-01 01:09:50 -080043static char *icc_filename; /* for -icc switch */
Jonathan Wrightbbb82822020-11-25 13:36:43 +000044static JDIMENSION max_scans; /* for -maxscans switch */
Tom Hudson0d47d2d2016-05-04 13:22:56 -040045static char *outfilename; /* for -outfile switch */
Jonathan Wrightbbb82822020-11-25 13:36:43 +000046static char *dropfilename; /* for -drop switch */
47static boolean report; /* for -report switch */
48static boolean strict; /* for -strict switch */
Tom Hudson0d47d2d2016-05-04 13:22:56 -040049static JCOPY_OPTION copyoption; /* -copy switch */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000050static jpeg_transform_info transformoption; /* image transformation options */
51
52
53LOCAL(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -080054usage(void)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000055/* complain about bad command line */
56{
57 fprintf(stderr, "usage: %s [switches] ", progname);
58#ifdef TWO_FILE_COMMANDLINE
59 fprintf(stderr, "inputfile outputfile\n");
60#else
61 fprintf(stderr, "[inputfile]\n");
62#endif
63
64 fprintf(stderr, "Switches (names may be abbreviated):\n");
65 fprintf(stderr, " -copy none Copy no extra markers from source file\n");
66 fprintf(stderr, " -copy comments Copy only comment markers (default)\n");
67 fprintf(stderr, " -copy all Copy all extra markers\n");
68#ifdef ENTROPY_OPT_SUPPORTED
69 fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n");
70#endif
71#ifdef C_PROGRESSIVE_SUPPORTED
72 fprintf(stderr, " -progressive Create progressive JPEG file\n");
73#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000074 fprintf(stderr, "Switches for modifying the image:\n");
hbono@chromium.org98626972011-08-03 03:13:08 +000075#if TRANSFORMS_SUPPORTED
Jonathan Wrightbbb82822020-11-25 13:36:43 +000076 fprintf(stderr, " -crop WxH+X+Y Crop to a rectangular region\n");
77 fprintf(stderr, " -drop +X+Y filename Drop (insert) another image\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000078 fprintf(stderr, " -flip [horizontal|vertical] Mirror image (left-right or top-bottom)\n");
Jonathan Wrightbbb82822020-11-25 13:36:43 +000079 fprintf(stderr, " -grayscale Reduce to grayscale (omit color data)\n");
hbono@chromium.org98626972011-08-03 03:13:08 +000080 fprintf(stderr, " -perfect Fail if there is non-transformable edge blocks\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000081 fprintf(stderr, " -rotate [90|180|270] Rotate image (degrees clockwise)\n");
hbono@chromium.org98626972011-08-03 03:13:08 +000082#endif
83#if TRANSFORMS_SUPPORTED
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000084 fprintf(stderr, " -transpose Transpose image\n");
85 fprintf(stderr, " -transverse Transverse transpose image\n");
86 fprintf(stderr, " -trim Drop non-transformable edge blocks\n");
Jonathan Wrightbbb82822020-11-25 13:36:43 +000087 fprintf(stderr, " with -drop: Requantize drop file to match source file\n");
88 fprintf(stderr, " -wipe WxH+X+Y Wipe (gray out) a rectangular region\n");
hbono@chromium.org98626972011-08-03 03:13:08 +000089#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000090 fprintf(stderr, "Switches for advanced users:\n");
hbono@chromium.orgdf5ffdd2012-05-11 07:46:03 +000091#ifdef C_ARITH_CODING_SUPPORTED
92 fprintf(stderr, " -arithmetic Use arithmetic coding\n");
93#endif
Chris Blumecca8c4d2019-03-01 01:09:50 -080094 fprintf(stderr, " -icc FILE Embed ICC profile contained in FILE\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000095 fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n");
96 fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n");
Jonathan Wrightbbb82822020-11-25 13:36:43 +000097 fprintf(stderr, " -maxscans N Maximum number of scans to allow in input file\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000098 fprintf(stderr, " -outfile name Specify name for output file\n");
Jonathan Wrightbbb82822020-11-25 13:36:43 +000099 fprintf(stderr, " -report Report transformation progress\n");
100 fprintf(stderr, " -strict Treat all warnings as fatal\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000101 fprintf(stderr, " -verbose or -debug Emit debug output\n");
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400102 fprintf(stderr, " -version Print version information and exit\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000103 fprintf(stderr, "Switches for wizards:\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000104#ifdef C_MULTISCAN_FILES_SUPPORTED
Chris Blumecca8c4d2019-03-01 01:09:50 -0800105 fprintf(stderr, " -scans FILE Create multi-scan JPEG per script FILE\n");
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000106#endif
107 exit(EXIT_FAILURE);
108}
109
110
111LOCAL(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800112select_transform(JXFORM_CODE transform)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000113/* Silly little routine to detect multiple transform options,
114 * which we can't handle.
115 */
116{
117#if TRANSFORMS_SUPPORTED
118 if (transformoption.transform == JXFORM_NONE ||
119 transformoption.transform == transform) {
120 transformoption.transform = transform;
121 } else {
122 fprintf(stderr, "%s: can only do one image transformation at a time\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400123 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000124 usage();
125 }
126#else
127 fprintf(stderr, "%s: sorry, image transformation was not compiled\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400128 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000129 exit(EXIT_FAILURE);
130#endif
131}
132
133
134LOCAL(int)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800135parse_switches(j_compress_ptr cinfo, int argc, char **argv,
136 int last_file_arg_seen, boolean for_real)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000137/* Parse optional switches.
138 * Returns argv[] index of first file-name argument (== argc if none).
139 * Any file names with indexes <= last_file_arg_seen are ignored;
140 * they have presumably been processed in a previous iteration.
141 * (Pass 0 for last_file_arg_seen on the first or only iteration.)
142 * for_real is FALSE on the first (dummy) pass; we may skip any expensive
143 * processing.
144 */
145{
146 int argn;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400147 char *arg;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000148 boolean simple_progressive;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400149 char *scansarg = NULL; /* saves -scans parm if any */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000150
151 /* Set up default JPEG parameters. */
152 simple_progressive = FALSE;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800153 icc_filename = NULL;
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000154 max_scans = 0;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000155 outfilename = NULL;
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000156 report = FALSE;
157 strict = FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000158 copyoption = JCOPYOPT_DEFAULT;
159 transformoption.transform = JXFORM_NONE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000160 transformoption.perfect = FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000161 transformoption.trim = FALSE;
162 transformoption.force_grayscale = FALSE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000163 transformoption.crop = FALSE;
164 transformoption.slow_hflip = FALSE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000165 cinfo->err->trace_level = 0;
166
167 /* Scan command line options, adjust parameters */
168
169 for (argn = 1; argn < argc; argn++) {
170 arg = argv[argn];
171 if (*arg != '-') {
172 /* Not a switch, must be a file name argument */
173 if (argn <= last_file_arg_seen) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400174 outfilename = NULL; /* -outfile applies to just one input file */
175 continue; /* ignore this name if previously processed */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000176 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400177 break; /* else done parsing switches */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000178 }
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400179 arg++; /* advance past switch marker character */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000180
181 if (keymatch(arg, "arithmetic", 1)) {
182 /* Use arithmetic coding. */
183#ifdef C_ARITH_CODING_SUPPORTED
184 cinfo->arith_code = TRUE;
185#else
186 fprintf(stderr, "%s: sorry, arithmetic coding not supported\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400187 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000188 exit(EXIT_FAILURE);
189#endif
190
hbono@chromium.org98626972011-08-03 03:13:08 +0000191 } else if (keymatch(arg, "copy", 2)) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000192 /* Select which extra markers to copy. */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400193 if (++argn >= argc) /* advance to next argument */
194 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000195 if (keymatch(argv[argn], "none", 1)) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400196 copyoption = JCOPYOPT_NONE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000197 } else if (keymatch(argv[argn], "comments", 1)) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400198 copyoption = JCOPYOPT_COMMENTS;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000199 } else if (keymatch(argv[argn], "all", 1)) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400200 copyoption = JCOPYOPT_ALL;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000201 } else
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400202 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000203
hbono@chromium.org98626972011-08-03 03:13:08 +0000204 } else if (keymatch(arg, "crop", 2)) {
205 /* Perform lossless cropping. */
206#if TRANSFORMS_SUPPORTED
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400207 if (++argn >= argc) /* advance to next argument */
208 usage();
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000209 if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
210 !jtransform_parse_crop_spec(&transformoption, argv[argn])) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400211 fprintf(stderr, "%s: bogus -crop argument '%s'\n",
212 progname, argv[argn]);
213 exit(EXIT_FAILURE);
hbono@chromium.org98626972011-08-03 03:13:08 +0000214 }
215#else
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400216 select_transform(JXFORM_NONE); /* force an error */
hbono@chromium.org98626972011-08-03 03:13:08 +0000217#endif
218
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000219 } else if (keymatch(arg, "drop", 2)) {
220#if TRANSFORMS_SUPPORTED
221 if (++argn >= argc) /* advance to next argument */
222 usage();
223 if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
224 !jtransform_parse_crop_spec(&transformoption, argv[argn]) ||
225 transformoption.crop_width_set != JCROP_UNSET ||
226 transformoption.crop_height_set != JCROP_UNSET) {
227 fprintf(stderr, "%s: bogus -drop argument '%s'\n",
228 progname, argv[argn]);
229 exit(EXIT_FAILURE);
230 }
231 if (++argn >= argc) /* advance to next argument */
232 usage();
233 dropfilename = argv[argn];
234 select_transform(JXFORM_DROP);
235#else
236 select_transform(JXFORM_NONE); /* force an error */
237#endif
238
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000239 } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
240 /* Enable debug printouts. */
241 /* On first -d, print version identification */
242 static boolean printed_version = FALSE;
243
Chris Blumecca8c4d2019-03-01 01:09:50 -0800244 if (!printed_version) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400245 fprintf(stderr, "%s version %s (build %s)\n",
246 PACKAGE_NAME, VERSION, BUILD);
247 fprintf(stderr, "%s\n\n", JCOPYRIGHT);
248 fprintf(stderr, "Emulating The Independent JPEG Group's software, version %s\n\n",
249 JVERSION);
250 printed_version = TRUE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000251 }
252 cinfo->err->trace_level++;
253
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400254 } else if (keymatch(arg, "version", 4)) {
255 fprintf(stderr, "%s version %s (build %s)\n",
256 PACKAGE_NAME, VERSION, BUILD);
257 exit(EXIT_SUCCESS);
258
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000259 } else if (keymatch(arg, "flip", 1)) {
260 /* Mirror left-right or top-bottom. */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400261 if (++argn >= argc) /* advance to next argument */
262 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000263 if (keymatch(argv[argn], "horizontal", 1))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400264 select_transform(JXFORM_FLIP_H);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000265 else if (keymatch(argv[argn], "vertical", 1))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400266 select_transform(JXFORM_FLIP_V);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000267 else
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400268 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000269
Chris Blumecca8c4d2019-03-01 01:09:50 -0800270 } else if (keymatch(arg, "grayscale", 1) ||
271 keymatch(arg, "greyscale", 1)) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000272 /* Force to grayscale. */
273#if TRANSFORMS_SUPPORTED
274 transformoption.force_grayscale = TRUE;
275#else
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400276 select_transform(JXFORM_NONE); /* force an error */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000277#endif
278
Chris Blumecca8c4d2019-03-01 01:09:50 -0800279 } else if (keymatch(arg, "icc", 1)) {
280 /* Set ICC filename. */
281 if (++argn >= argc) /* advance to next argument */
282 usage();
283 icc_filename = argv[argn];
284
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000285 } else if (keymatch(arg, "maxmemory", 3)) {
286 /* Maximum memory in Kb (or Mb with 'm'). */
287 long lval;
288 char ch = 'x';
289
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400290 if (++argn >= argc) /* advance to next argument */
291 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000292 if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400293 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000294 if (ch == 'm' || ch == 'M')
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400295 lval *= 1000L;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000296 cinfo->mem->max_memory_to_use = lval * 1000L;
297
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000298 } else if (keymatch(arg, "maxscans", 4)) {
299 if (++argn >= argc) /* advance to next argument */
300 usage();
301 if (sscanf(argv[argn], "%u", &max_scans) != 1)
302 usage();
303
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000304 } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
305 /* Enable entropy parm optimization. */
306#ifdef ENTROPY_OPT_SUPPORTED
307 cinfo->optimize_coding = TRUE;
308#else
309 fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400310 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000311 exit(EXIT_FAILURE);
312#endif
313
314 } else if (keymatch(arg, "outfile", 4)) {
315 /* Set output file name. */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400316 if (++argn >= argc) /* advance to next argument */
317 usage();
318 outfilename = argv[argn]; /* save it away for later use */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000319
hbono@chromium.org98626972011-08-03 03:13:08 +0000320 } else if (keymatch(arg, "perfect", 2)) {
321 /* Fail if there is any partial edge MCUs that the transform can't
322 * handle. */
323 transformoption.perfect = TRUE;
324
325 } else if (keymatch(arg, "progressive", 2)) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000326 /* Select simple progressive mode. */
327#ifdef C_PROGRESSIVE_SUPPORTED
328 simple_progressive = TRUE;
329 /* We must postpone execution until num_components is known. */
330#else
331 fprintf(stderr, "%s: sorry, progressive output was not compiled\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400332 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000333 exit(EXIT_FAILURE);
334#endif
335
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000336 } else if (keymatch(arg, "report", 3)) {
337 report = TRUE;
338
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000339 } else if (keymatch(arg, "restart", 1)) {
340 /* Restart interval in MCU rows (or in MCUs with 'b'). */
341 long lval;
342 char ch = 'x';
343
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400344 if (++argn >= argc) /* advance to next argument */
345 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000346 if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400347 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000348 if (lval < 0 || lval > 65535L)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400349 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000350 if (ch == 'b' || ch == 'B') {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800351 cinfo->restart_interval = (unsigned int)lval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400352 cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000353 } else {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800354 cinfo->restart_in_rows = (int)lval;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400355 /* restart_interval will be computed during startup */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000356 }
357
358 } else if (keymatch(arg, "rotate", 2)) {
359 /* Rotate 90, 180, or 270 degrees (measured clockwise). */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400360 if (++argn >= argc) /* advance to next argument */
361 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000362 if (keymatch(argv[argn], "90", 2))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400363 select_transform(JXFORM_ROT_90);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000364 else if (keymatch(argv[argn], "180", 3))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400365 select_transform(JXFORM_ROT_180);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000366 else if (keymatch(argv[argn], "270", 3))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400367 select_transform(JXFORM_ROT_270);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000368 else
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400369 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000370
371 } else if (keymatch(arg, "scans", 1)) {
372 /* Set scan script. */
373#ifdef C_MULTISCAN_FILES_SUPPORTED
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400374 if (++argn >= argc) /* advance to next argument */
375 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000376 scansarg = argv[argn];
377 /* We must postpone reading the file in case -progressive appears. */
378#else
379 fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400380 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000381 exit(EXIT_FAILURE);
382#endif
383
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000384 } else if (keymatch(arg, "strict", 2)) {
385 strict = TRUE;
386
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000387 } else if (keymatch(arg, "transpose", 1)) {
388 /* Transpose (across UL-to-LR axis). */
389 select_transform(JXFORM_TRANSPOSE);
390
391 } else if (keymatch(arg, "transverse", 6)) {
392 /* Transverse transpose (across UR-to-LL axis). */
393 select_transform(JXFORM_TRANSVERSE);
394
395 } else if (keymatch(arg, "trim", 3)) {
396 /* Trim off any partial edge MCUs that the transform can't handle. */
397 transformoption.trim = TRUE;
398
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000399 } else if (keymatch(arg, "wipe", 1)) {
400#if TRANSFORMS_SUPPORTED
401 if (++argn >= argc) /* advance to next argument */
402 usage();
403 if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
404 !jtransform_parse_crop_spec(&transformoption, argv[argn])) {
405 fprintf(stderr, "%s: bogus -wipe argument '%s'\n",
406 progname, argv[argn]);
407 exit(EXIT_FAILURE);
408 }
409 select_transform(JXFORM_WIPE);
410#else
411 select_transform(JXFORM_NONE); /* force an error */
412#endif
413
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000414 } else {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400415 usage(); /* bogus switch */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000416 }
417 }
418
419 /* Post-switch-scanning cleanup */
420
421 if (for_real) {
422
423#ifdef C_PROGRESSIVE_SUPPORTED
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400424 if (simple_progressive) /* process -progressive; -scans can override */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000425 jpeg_simple_progression(cinfo);
426#endif
427
428#ifdef C_MULTISCAN_FILES_SUPPORTED
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400429 if (scansarg != NULL) /* process -scans if it was present */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800430 if (!read_scan_script(cinfo, scansarg))
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400431 usage();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000432#endif
433 }
434
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400435 return argn; /* return index of next arg (file name) */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000436}
437
438
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000439METHODDEF(void)
440my_emit_message(j_common_ptr cinfo, int msg_level)
441{
442 if (msg_level < 0) {
443 /* Treat warning as fatal */
444 cinfo->err->error_exit(cinfo);
445 } else {
446 if (cinfo->err->trace_level >= msg_level)
447 cinfo->err->output_message(cinfo);
448 }
449}
450
451
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000452/*
453 * The main program.
454 */
455
456int
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100457#ifdef GTEST
458jpegtran(int argc, char **argv)
459#else
Chris Blumecca8c4d2019-03-01 01:09:50 -0800460main(int argc, char **argv)
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100461#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000462{
463 struct jpeg_decompress_struct srcinfo;
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000464#if TRANSFORMS_SUPPORTED
465 struct jpeg_decompress_struct dropinfo;
466 struct jpeg_error_mgr jdroperr;
467 FILE *drop_file;
468#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000469 struct jpeg_compress_struct dstinfo;
470 struct jpeg_error_mgr jsrcerr, jdsterr;
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000471 struct cdjpeg_progress_mgr src_progress, dst_progress;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400472 jvirt_barray_ptr *src_coef_arrays;
473 jvirt_barray_ptr *dst_coef_arrays;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000474 int file_index;
hbono@chromium.org98626972011-08-03 03:13:08 +0000475 /* We assume all-in-memory processing and can therefore use only a
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400476 * single file pointer for sequential input and output operation.
hbono@chromium.org98626972011-08-03 03:13:08 +0000477 */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400478 FILE *fp;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800479 FILE *icc_file;
480 JOCTET *icc_profile = NULL;
481 long icc_len = 0;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000482
483 /* On Mac, fetch a command line. */
484#ifdef USE_CCOMMAND
485 argc = ccommand(&argv);
486#endif
487
488 progname = argv[0];
489 if (progname == NULL || progname[0] == 0)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400490 progname = "jpegtran"; /* in case C library doesn't provide it */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000491
492 /* Initialize the JPEG decompression object with default error handling. */
493 srcinfo.err = jpeg_std_error(&jsrcerr);
494 jpeg_create_decompress(&srcinfo);
495 /* Initialize the JPEG compression object with default error handling. */
496 dstinfo.err = jpeg_std_error(&jdsterr);
497 jpeg_create_compress(&dstinfo);
498
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000499 /* Scan command line to find file names.
500 * It is convenient to use just one switch-parsing routine, but the switch
501 * values read here are mostly ignored; we will rescan the switches after
502 * opening the input file. Also note that most of the switches affect the
503 * destination JPEG object, so we parse into that and then copy over what
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000504 * needs to affect the source too.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000505 */
506
507 file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE);
508 jsrcerr.trace_level = jdsterr.trace_level;
509 srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
510
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000511 if (strict)
512 jsrcerr.emit_message = my_emit_message;
513
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000514#ifdef TWO_FILE_COMMANDLINE
515 /* Must have either -outfile switch or explicit output file name */
516 if (outfilename == NULL) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800517 if (file_index != argc - 2) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000518 fprintf(stderr, "%s: must name one input and one output file\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400519 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000520 usage();
521 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800522 outfilename = argv[file_index + 1];
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000523 } else {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800524 if (file_index != argc - 1) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000525 fprintf(stderr, "%s: must name one input and one output file\n",
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400526 progname);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000527 usage();
528 }
529 }
530#else
531 /* Unix style: expect zero or one file name */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800532 if (file_index < argc - 1) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000533 fprintf(stderr, "%s: only one input file\n", progname);
534 usage();
535 }
536#endif /* TWO_FILE_COMMANDLINE */
537
538 /* Open the input file. */
539 if (file_index < argc) {
hbono@chromium.org98626972011-08-03 03:13:08 +0000540 if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800541 fprintf(stderr, "%s: can't open %s for reading\n", progname,
542 argv[file_index]);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100543 return EXIT_FAILURE;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000544 }
545 } else {
546 /* default input file is stdin */
hbono@chromium.org98626972011-08-03 03:13:08 +0000547 fp = read_stdin();
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000548 }
549
Chris Blumecca8c4d2019-03-01 01:09:50 -0800550 if (icc_filename != NULL) {
551 if ((icc_file = fopen(icc_filename, READ_BINARY)) == NULL) {
552 fprintf(stderr, "%s: can't open %s\n", progname, icc_filename);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100553 return EXIT_FAILURE;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800554 }
555 if (fseek(icc_file, 0, SEEK_END) < 0 ||
556 (icc_len = ftell(icc_file)) < 1 ||
557 fseek(icc_file, 0, SEEK_SET) < 0) {
558 fprintf(stderr, "%s: can't determine size of %s\n", progname,
559 icc_filename);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100560 return EXIT_FAILURE;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800561 }
562 if ((icc_profile = (JOCTET *)malloc(icc_len)) == NULL) {
563 fprintf(stderr, "%s: can't allocate memory for ICC profile\n", progname);
564 fclose(icc_file);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100565 return EXIT_FAILURE;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800566 }
567 if (fread(icc_profile, icc_len, 1, icc_file) < 1) {
568 fprintf(stderr, "%s: can't read ICC profile from %s\n", progname,
569 icc_filename);
570 free(icc_profile);
571 fclose(icc_file);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100572 return EXIT_FAILURE;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800573 }
574 fclose(icc_file);
575 if (copyoption == JCOPYOPT_ALL)
576 copyoption = JCOPYOPT_ALL_EXCEPT_ICC;
577 }
578
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000579 if (report) {
580 start_progress_monitor((j_common_ptr)&dstinfo, &dst_progress);
581 dst_progress.report = report;
582 }
583 if (report || max_scans != 0) {
584 start_progress_monitor((j_common_ptr)&srcinfo, &src_progress);
585 src_progress.report = report;
586 src_progress.max_scans = max_scans;
587 }
588#if TRANSFORMS_SUPPORTED
589 /* Open the drop file. */
590 if (dropfilename != NULL) {
591 if ((drop_file = fopen(dropfilename, READ_BINARY)) == NULL) {
592 fprintf(stderr, "%s: can't open %s for reading\n", progname,
593 dropfilename);
594 return EXIT_FAILURE;
595 }
596 dropinfo.err = jpeg_std_error(&jdroperr);
597 jpeg_create_decompress(&dropinfo);
598 jpeg_stdio_src(&dropinfo, drop_file);
599 } else {
600 drop_file = NULL;
601 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000602#endif
603
604 /* Specify data source for decompression */
hbono@chromium.org98626972011-08-03 03:13:08 +0000605 jpeg_stdio_src(&srcinfo, fp);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000606
607 /* Enable saving of extra markers that we want to copy */
608 jcopy_markers_setup(&srcinfo, copyoption);
609
610 /* Read file header */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800611 (void)jpeg_read_header(&srcinfo, TRUE);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000612
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000613#if TRANSFORMS_SUPPORTED
614 if (dropfilename != NULL) {
615 (void)jpeg_read_header(&dropinfo, TRUE);
616 transformoption.crop_width = dropinfo.image_width;
617 transformoption.crop_width_set = JCROP_POS;
618 transformoption.crop_height = dropinfo.image_height;
619 transformoption.crop_height_set = JCROP_POS;
620 transformoption.drop_ptr = &dropinfo;
621 }
622#endif
623
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000624 /* Any space needed by a transform option must be requested before
625 * jpeg_read_coefficients so that memory allocation will be done right.
626 */
627#if TRANSFORMS_SUPPORTED
hbono@chromium.org98626972011-08-03 03:13:08 +0000628 /* Fail right away if -perfect is given and transformation is not perfect.
629 */
630 if (!jtransform_request_workspace(&srcinfo, &transformoption)) {
631 fprintf(stderr, "%s: transformation is not perfect\n", progname);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100632 return EXIT_FAILURE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000633 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000634#endif
635
636 /* Read source file as DCT coefficients */
637 src_coef_arrays = jpeg_read_coefficients(&srcinfo);
638
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000639#if TRANSFORMS_SUPPORTED
640 if (dropfilename != NULL) {
641 transformoption.drop_coef_arrays = jpeg_read_coefficients(&dropinfo);
642 }
643#endif
644
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000645 /* Initialize destination compression parameters from source values */
646 jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
647
648 /* Adjust destination parameters if required by transform options;
649 * also find out which set of coefficient arrays will hold the output.
650 */
651#if TRANSFORMS_SUPPORTED
652 dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo,
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400653 src_coef_arrays,
654 &transformoption);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000655#else
656 dst_coef_arrays = src_coef_arrays;
657#endif
658
hbono@chromium.org98626972011-08-03 03:13:08 +0000659 /* Close input file, if we opened it.
660 * Note: we assume that jpeg_read_coefficients consumed all input
661 * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
Chris Blumecca8c4d2019-03-01 01:09:50 -0800662 * only consume more while (!cinfo->inputctl->eoi_reached).
hbono@chromium.org98626972011-08-03 03:13:08 +0000663 * We cannot call jpeg_finish_decompress here since we still need the
664 * virtual arrays allocated from the source object for processing.
665 */
666 if (fp != stdin)
667 fclose(fp);
668
669 /* Open the output file. */
670 if (outfilename != NULL) {
671 if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800672 fprintf(stderr, "%s: can't open %s for writing\n", progname,
673 outfilename);
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100674 return EXIT_FAILURE;
hbono@chromium.org98626972011-08-03 03:13:08 +0000675 }
676 } else {
677 /* default output file is stdout */
678 fp = write_stdout();
679 }
680
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000681 /* Adjust default compression parameters by re-parsing the options */
682 file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE);
683
684 /* Specify data destination for compression */
hbono@chromium.org98626972011-08-03 03:13:08 +0000685 jpeg_stdio_dest(&dstinfo, fp);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000686
687 /* Start compressor (note no image data is actually written here) */
688 jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
689
690 /* Copy to the output file any extra markers that we want to preserve */
691 jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
692
Chris Blumecca8c4d2019-03-01 01:09:50 -0800693 if (icc_profile != NULL)
694 jpeg_write_icc_profile(&dstinfo, icc_profile, (unsigned int)icc_len);
695
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000696 /* Execute image transformation, if any */
697#if TRANSFORMS_SUPPORTED
Chris Blumecca8c4d2019-03-01 01:09:50 -0800698 jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays,
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400699 &transformoption);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000700#endif
701
702 /* Finish compression and release memory */
703 jpeg_finish_compress(&dstinfo);
704 jpeg_destroy_compress(&dstinfo);
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000705#if TRANSFORMS_SUPPORTED
706 if (dropfilename != NULL) {
707 (void)jpeg_finish_decompress(&dropinfo);
708 jpeg_destroy_decompress(&dropinfo);
709 }
710#endif
Chris Blumecca8c4d2019-03-01 01:09:50 -0800711 (void)jpeg_finish_decompress(&srcinfo);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000712 jpeg_destroy_decompress(&srcinfo);
713
hbono@chromium.org98626972011-08-03 03:13:08 +0000714 /* Close output file, if we opened it */
715 if (fp != stdout)
716 fclose(fp);
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000717#if TRANSFORMS_SUPPORTED
718 if (drop_file != NULL)
719 fclose(drop_file);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000720#endif
721
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000722 if (report)
723 end_progress_monitor((j_common_ptr)&dstinfo);
724 if (report || max_scans != 0)
725 end_progress_monitor((j_common_ptr)&srcinfo);
726
Jonathan Wrightdb870df2020-08-05 11:42:22 +0100727 free(icc_profile);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800728
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000729 /* All done. */
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000730#if TRANSFORMS_SUPPORTED
731 if (dropfilename != NULL)
732 return (jsrcerr.num_warnings + jdroperr.num_warnings +
733 jdsterr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
734#endif
Jonathan Wrightdc4d6f92020-06-23 12:35:48 +0100735 return (jsrcerr.num_warnings + jdsterr.num_warnings ?
736 EXIT_WARNING : EXIT_SUCCESS);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000737}