1 /*
2 * jpegtran.c
3 *
4 * This file was part of the Independent JPEG Group's software:
5 * Copyright (C) 1995-2019, Thomas G. Lane, Guido Vollbeding.
6 * libjpeg-turbo Modifications:
7 * Copyright (C) 2010, 2014, 2017, 2019-2020, 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 *icc_filename; /* for -icc switch */
44 static JDIMENSION max_scans; /* for -maxscans switch */
45 static char *outfilename; /* for -outfile switch */
46 static char *dropfilename; /* for -drop switch */
47 static boolean report; /* for -report switch */
48 static boolean strict; /* for -strict switch */
49 static JCOPY_OPTION copyoption; /* -copy switch */
50 static jpeg_transform_info transformoption; /* image transformation options */
51
52
53 LOCAL(void)
usage(void)54 usage(void)
55 /* 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
74 fprintf(stderr, "Switches for modifying the image:\n");
75 #if TRANSFORMS_SUPPORTED
76 fprintf(stderr, " -crop WxH+X+Y Crop to a rectangular region\n");
77 fprintf(stderr, " -drop +X+Y filename Drop (insert) another image\n");
78 fprintf(stderr, " -flip [horizontal|vertical] Mirror image (left-right or top-bottom)\n");
79 fprintf(stderr, " -grayscale Reduce to grayscale (omit color data)\n");
80 fprintf(stderr, " -perfect Fail if there is non-transformable edge blocks\n");
81 fprintf(stderr, " -rotate [90|180|270] Rotate image (degrees clockwise)\n");
82 #endif
83 #if TRANSFORMS_SUPPORTED
84 fprintf(stderr, " -transpose Transpose image\n");
85 fprintf(stderr, " -transverse Transverse transpose image\n");
86 fprintf(stderr, " -trim Drop non-transformable edge blocks\n");
87 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");
89 #endif
90 fprintf(stderr, "Switches for advanced users:\n");
91 #ifdef C_ARITH_CODING_SUPPORTED
92 fprintf(stderr, " -arithmetic Use arithmetic coding\n");
93 #endif
94 fprintf(stderr, " -icc FILE Embed ICC profile contained in FILE\n");
95 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");
97 fprintf(stderr, " -maxscans N Maximum number of scans to allow in input file\n");
98 fprintf(stderr, " -outfile name Specify name for output file\n");
99 fprintf(stderr, " -report Report transformation progress\n");
100 fprintf(stderr, " -strict Treat all warnings as fatal\n");
101 fprintf(stderr, " -verbose or -debug Emit debug output\n");
102 fprintf(stderr, " -version Print version information and exit\n");
103 fprintf(stderr, "Switches for wizards:\n");
104 #ifdef C_MULTISCAN_FILES_SUPPORTED
105 fprintf(stderr, " -scans FILE Create multi-scan JPEG per script FILE\n");
106 #endif
107 exit(EXIT_FAILURE);
108 }
109
110
111 LOCAL(void)
select_transform(JXFORM_CODE transform)112 select_transform(JXFORM_CODE transform)
113 /* 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",
123 progname);
124 usage();
125 }
126 #else
127 fprintf(stderr, "%s: sorry, image transformation was not compiled\n",
128 progname);
129 exit(EXIT_FAILURE);
130 #endif
131 }
132
133
134 LOCAL(int)
parse_switches(j_compress_ptr cinfo,int argc,char ** argv,int last_file_arg_seen,boolean for_real)135 parse_switches(j_compress_ptr cinfo, int argc, char **argv,
136 int last_file_arg_seen, boolean for_real)
137 /* 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;
147 char *arg;
148 boolean simple_progressive;
149 char *scansarg = NULL; /* saves -scans parm if any */
150
151 /* Set up default JPEG parameters. */
152 simple_progressive = FALSE;
153 icc_filename = NULL;
154 max_scans = 0;
155 outfilename = NULL;
156 report = FALSE;
157 strict = FALSE;
158 copyoption = JCOPYOPT_DEFAULT;
159 transformoption.transform = JXFORM_NONE;
160 transformoption.perfect = FALSE;
161 transformoption.trim = FALSE;
162 transformoption.force_grayscale = FALSE;
163 transformoption.crop = FALSE;
164 transformoption.slow_hflip = FALSE;
165 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) {
174 outfilename = NULL; /* -outfile applies to just one input file */
175 continue; /* ignore this name if previously processed */
176 }
177 break; /* else done parsing switches */
178 }
179 arg++; /* advance past switch marker character */
180
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",
187 progname);
188 exit(EXIT_FAILURE);
189 #endif
190
191 } else if (keymatch(arg, "copy", 2)) {
192 /* Select which extra markers to copy. */
193 if (++argn >= argc) /* advance to next argument */
194 usage();
195 if (keymatch(argv[argn], "none", 1)) {
196 copyoption = JCOPYOPT_NONE;
197 } else if (keymatch(argv[argn], "comments", 1)) {
198 copyoption = JCOPYOPT_COMMENTS;
199 } else if (keymatch(argv[argn], "all", 1)) {
200 copyoption = JCOPYOPT_ALL;
201 } else
202 usage();
203
204 } else if (keymatch(arg, "crop", 2)) {
205 /* Perform lossless cropping. */
206 #if TRANSFORMS_SUPPORTED
207 if (++argn >= argc) /* advance to next argument */
208 usage();
209 if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
210 !jtransform_parse_crop_spec(&transformoption, argv[argn])) {
211 fprintf(stderr, "%s: bogus -crop argument '%s'\n",
212 progname, argv[argn]);
213 exit(EXIT_FAILURE);
214 }
215 #else
216 select_transform(JXFORM_NONE); /* force an error */
217 #endif
218
219 } 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
239 } 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
244 if (!printed_version) {
245 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;
251 }
252 cinfo->err->trace_level++;
253
254 } 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
259 } else if (keymatch(arg, "flip", 1)) {
260 /* Mirror left-right or top-bottom. */
261 if (++argn >= argc) /* advance to next argument */
262 usage();
263 if (keymatch(argv[argn], "horizontal", 1))
264 select_transform(JXFORM_FLIP_H);
265 else if (keymatch(argv[argn], "vertical", 1))
266 select_transform(JXFORM_FLIP_V);
267 else
268 usage();
269
270 } else if (keymatch(arg, "grayscale", 1) ||
271 keymatch(arg, "greyscale", 1)) {
272 /* Force to grayscale. */
273 #if TRANSFORMS_SUPPORTED
274 transformoption.force_grayscale = TRUE;
275 #else
276 select_transform(JXFORM_NONE); /* force an error */
277 #endif
278
279 } 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
285 } else if (keymatch(arg, "maxmemory", 3)) {
286 /* Maximum memory in Kb (or Mb with 'm'). */
287 long lval;
288 char ch = 'x';
289
290 if (++argn >= argc) /* advance to next argument */
291 usage();
292 if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
293 usage();
294 if (ch == 'm' || ch == 'M')
295 lval *= 1000L;
296 cinfo->mem->max_memory_to_use = lval * 1000L;
297
298 } 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
304 } 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",
310 progname);
311 exit(EXIT_FAILURE);
312 #endif
313
314 } else if (keymatch(arg, "outfile", 4)) {
315 /* Set output file name. */
316 if (++argn >= argc) /* advance to next argument */
317 usage();
318 outfilename = argv[argn]; /* save it away for later use */
319
320 } 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)) {
326 /* 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",
332 progname);
333 exit(EXIT_FAILURE);
334 #endif
335
336 } else if (keymatch(arg, "report", 3)) {
337 report = TRUE;
338
339 } 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
344 if (++argn >= argc) /* advance to next argument */
345 usage();
346 if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
347 usage();
348 if (lval < 0 || lval > 65535L)
349 usage();
350 if (ch == 'b' || ch == 'B') {
351 cinfo->restart_interval = (unsigned int)lval;
352 cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
353 } else {
354 cinfo->restart_in_rows = (int)lval;
355 /* restart_interval will be computed during startup */
356 }
357
358 } else if (keymatch(arg, "rotate", 2)) {
359 /* Rotate 90, 180, or 270 degrees (measured clockwise). */
360 if (++argn >= argc) /* advance to next argument */
361 usage();
362 if (keymatch(argv[argn], "90", 2))
363 select_transform(JXFORM_ROT_90);
364 else if (keymatch(argv[argn], "180", 3))
365 select_transform(JXFORM_ROT_180);
366 else if (keymatch(argv[argn], "270", 3))
367 select_transform(JXFORM_ROT_270);
368 else
369 usage();
370
371 } else if (keymatch(arg, "scans", 1)) {
372 /* Set scan script. */
373 #ifdef C_MULTISCAN_FILES_SUPPORTED
374 if (++argn >= argc) /* advance to next argument */
375 usage();
376 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",
380 progname);
381 exit(EXIT_FAILURE);
382 #endif
383
384 } else if (keymatch(arg, "strict", 2)) {
385 strict = TRUE;
386
387 } 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
399 } 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
414 } else {
415 usage(); /* bogus switch */
416 }
417 }
418
419 /* Post-switch-scanning cleanup */
420
421 if (for_real) {
422
423 #ifdef C_PROGRESSIVE_SUPPORTED
424 if (simple_progressive) /* process -progressive; -scans can override */
425 jpeg_simple_progression(cinfo);
426 #endif
427
428 #ifdef C_MULTISCAN_FILES_SUPPORTED
429 if (scansarg != NULL) /* process -scans if it was present */
430 if (!read_scan_script(cinfo, scansarg))
431 usage();
432 #endif
433 }
434
435 return argn; /* return index of next arg (file name) */
436 }
437
438
439 METHODDEF(void)
my_emit_message(j_common_ptr cinfo,int msg_level)440 my_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
452 /*
453 * The main program.
454 */
455
456 int
457 #ifdef GTEST
jpegtran(int argc,char ** argv)458 jpegtran(int argc, char **argv)
459 #else
460 main(int argc, char **argv)
461 #endif
462 {
463 struct jpeg_decompress_struct srcinfo;
464 #if TRANSFORMS_SUPPORTED
465 struct jpeg_decompress_struct dropinfo;
466 struct jpeg_error_mgr jdroperr;
467 FILE *drop_file;
468 #endif
469 struct jpeg_compress_struct dstinfo;
470 struct jpeg_error_mgr jsrcerr, jdsterr;
471 struct cdjpeg_progress_mgr src_progress, dst_progress;
472 jvirt_barray_ptr *src_coef_arrays;
473 jvirt_barray_ptr *dst_coef_arrays;
474 int file_index;
475 /* We assume all-in-memory processing and can therefore use only a
476 * single file pointer for sequential input and output operation.
477 */
478 FILE *fp;
479 FILE *icc_file;
480 JOCTET *icc_profile = NULL;
481 long icc_len = 0;
482
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)
490 progname = "jpegtran"; /* in case C library doesn't provide it */
491
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
499 /* 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
504 * needs to affect the source too.
505 */
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
511 if (strict)
512 jsrcerr.emit_message = my_emit_message;
513
514 #ifdef TWO_FILE_COMMANDLINE
515 /* Must have either -outfile switch or explicit output file name */
516 if (outfilename == NULL) {
517 if (file_index != argc - 2) {
518 fprintf(stderr, "%s: must name one input and one output file\n",
519 progname);
520 usage();
521 }
522 outfilename = argv[file_index + 1];
523 } else {
524 if (file_index != argc - 1) {
525 fprintf(stderr, "%s: must name one input and one output file\n",
526 progname);
527 usage();
528 }
529 }
530 #else
531 /* Unix style: expect zero or one file name */
532 if (file_index < argc - 1) {
533 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) {
540 if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) {
541 fprintf(stderr, "%s: can't open %s for reading\n", progname,
542 argv[file_index]);
543 return EXIT_FAILURE;
544 }
545 } else {
546 /* default input file is stdin */
547 fp = read_stdin();
548 }
549
550 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);
553 return EXIT_FAILURE;
554 }
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);
560 return EXIT_FAILURE;
561 }
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);
565 return EXIT_FAILURE;
566 }
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);
572 return EXIT_FAILURE;
573 }
574 fclose(icc_file);
575 if (copyoption == JCOPYOPT_ALL)
576 copyoption = JCOPYOPT_ALL_EXCEPT_ICC;
577 }
578
579 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 }
602 #endif
603
604 /* Specify data source for decompression */
605 jpeg_stdio_src(&srcinfo, fp);
606
607 /* Enable saving of extra markers that we want to copy */
608 jcopy_markers_setup(&srcinfo, copyoption);
609
610 /* Read file header */
611 (void)jpeg_read_header(&srcinfo, TRUE);
612
613 #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
624 /* 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
628 /* 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);
632 return EXIT_FAILURE;
633 }
634 #endif
635
636 /* Read source file as DCT coefficients */
637 src_coef_arrays = jpeg_read_coefficients(&srcinfo);
638
639 #if TRANSFORMS_SUPPORTED
640 if (dropfilename != NULL) {
641 transformoption.drop_coef_arrays = jpeg_read_coefficients(&dropinfo);
642 }
643 #endif
644
645 /* 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,
653 src_coef_arrays,
654 &transformoption);
655 #else
656 dst_coef_arrays = src_coef_arrays;
657 #endif
658
659 /* 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
662 * only consume more while (!cinfo->inputctl->eoi_reached).
663 * 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) {
672 fprintf(stderr, "%s: can't open %s for writing\n", progname,
673 outfilename);
674 return EXIT_FAILURE;
675 }
676 } else {
677 /* default output file is stdout */
678 fp = write_stdout();
679 }
680
681 /* 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 */
685 jpeg_stdio_dest(&dstinfo, fp);
686
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
693 if (icc_profile != NULL)
694 jpeg_write_icc_profile(&dstinfo, icc_profile, (unsigned int)icc_len);
695
696 /* Execute image transformation, if any */
697 #if TRANSFORMS_SUPPORTED
698 jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays,
699 &transformoption);
700 #endif
701
702 /* Finish compression and release memory */
703 jpeg_finish_compress(&dstinfo);
704 jpeg_destroy_compress(&dstinfo);
705 #if TRANSFORMS_SUPPORTED
706 if (dropfilename != NULL) {
707 (void)jpeg_finish_decompress(&dropinfo);
708 jpeg_destroy_decompress(&dropinfo);
709 }
710 #endif
711 (void)jpeg_finish_decompress(&srcinfo);
712 jpeg_destroy_decompress(&srcinfo);
713
714 /* Close output file, if we opened it */
715 if (fp != stdout)
716 fclose(fp);
717 #if TRANSFORMS_SUPPORTED
718 if (drop_file != NULL)
719 fclose(drop_file);
720 #endif
721
722 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
727 free(icc_profile);
728
729 /* All done. */
730 #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
735 return (jsrcerr.num_warnings + jdsterr.num_warnings ?
736 EXIT_WARNING : EXIT_SUCCESS);
737 }
738