xref: /aosp_15_r20/external/libjpeg-turbo/jpegtran.c (revision dfc6aa5c1cfd4bc4e2018dc74aa96e29ee49c6da)
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-2022, 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 #ifdef _MSC_VER
18 #define _CRT_SECURE_NO_DEPRECATE
19 #endif
20 
21 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
22 #include "transupp.h"           /* Support routines for jpegtran */
23 #include "jversion.h"           /* for version message */
24 #include "jconfigint.h"
25 
26 
27 /*
28  * Argument-parsing code.
29  * The switch parser is designed to be useful with DOS-style command line
30  * syntax, ie, intermixed switches and file names, where only the switches
31  * to the left of a given file name affect processing of that file.
32  * The main program in this file doesn't actually use this capability...
33  */
34 
35 
36 static const char *progname;    /* program name for error messages */
37 static char *icc_filename;      /* for -icc switch */
38 static JDIMENSION max_scans;    /* for -maxscans switch */
39 static char *outfilename;       /* for -outfile switch */
40 static char *dropfilename;      /* for -drop switch */
41 static boolean report;          /* for -report switch */
42 static boolean strict;          /* for -strict switch */
43 static JCOPY_OPTION copyoption; /* -copy switch */
44 static jpeg_transform_info transformoption; /* image transformation options */
45 
46 
47 LOCAL(void)
usage(void)48 usage(void)
49 /* complain about bad command line */
50 {
51   fprintf(stderr, "usage: %s [switches] ", progname);
52 #ifdef TWO_FILE_COMMANDLINE
53   fprintf(stderr, "inputfile outputfile\n");
54 #else
55   fprintf(stderr, "[inputfile]\n");
56 #endif
57 
58   fprintf(stderr, "Switches (names may be abbreviated):\n");
59   fprintf(stderr, "  -copy none     Copy no extra markers from source file\n");
60   fprintf(stderr, "  -copy comments Copy only comment markers (default)\n");
61   fprintf(stderr, "  -copy icc      Copy only ICC profile markers\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 region\n");
72   fprintf(stderr, "  -drop +X+Y filename          Drop (insert) another image\n");
73   fprintf(stderr, "  -flip [horizontal|vertical]  Mirror image (left-right or top-bottom)\n");
74   fprintf(stderr, "  -grayscale     Reduce to grayscale (omit color data)\n");
75   fprintf(stderr, "  -perfect       Fail if there is non-transformable edge blocks\n");
76   fprintf(stderr, "  -rotate [90|180|270]         Rotate image (degrees clockwise)\n");
77 #endif
78 #if TRANSFORMS_SUPPORTED
79   fprintf(stderr, "  -transpose     Transpose image\n");
80   fprintf(stderr, "  -transverse    Transverse transpose image\n");
81   fprintf(stderr, "  -trim          Drop non-transformable edge blocks\n");
82   fprintf(stderr, "                 with -drop: Requantize drop file to match source file\n");
83   fprintf(stderr, "  -wipe WxH+X+Y  Wipe (gray out) a rectangular region\n");
84 #endif
85   fprintf(stderr, "Switches for advanced users:\n");
86 #ifdef C_ARITH_CODING_SUPPORTED
87   fprintf(stderr, "  -arithmetic    Use arithmetic coding\n");
88 #endif
89   fprintf(stderr, "  -icc FILE      Embed ICC profile contained in FILE\n");
90   fprintf(stderr, "  -restart N     Set restart interval in rows, or in blocks with B\n");
91   fprintf(stderr, "  -maxmemory N   Maximum memory to use (in kbytes)\n");
92   fprintf(stderr, "  -maxscans N    Maximum number of scans to allow in input file\n");
93   fprintf(stderr, "  -outfile name  Specify name for output file\n");
94   fprintf(stderr, "  -report        Report transformation progress\n");
95   fprintf(stderr, "  -strict        Treat all warnings as fatal\n");
96   fprintf(stderr, "  -verbose  or  -debug   Emit debug output\n");
97   fprintf(stderr, "  -version       Print version information and exit\n");
98   fprintf(stderr, "Switches for wizards:\n");
99 #ifdef C_MULTISCAN_FILES_SUPPORTED
100   fprintf(stderr, "  -scans FILE    Create multi-scan JPEG per script FILE\n");
101 #endif
102   exit(EXIT_FAILURE);
103 }
104 
105 
106 LOCAL(void)
select_transform(JXFORM_CODE transform)107 select_transform(JXFORM_CODE transform)
108 /* Silly little routine to detect multiple transform options,
109  * which we can't handle.
110  */
111 {
112 #if TRANSFORMS_SUPPORTED
113   if (transformoption.transform == JXFORM_NONE ||
114       transformoption.transform == transform) {
115     transformoption.transform = transform;
116   } else {
117     fprintf(stderr, "%s: can only do one image transformation at a time\n",
118             progname);
119     usage();
120   }
121 #else
122   fprintf(stderr, "%s: sorry, image transformation was not compiled\n",
123           progname);
124   exit(EXIT_FAILURE);
125 #endif
126 }
127 
128 
129 LOCAL(int)
parse_switches(j_compress_ptr cinfo,int argc,char ** argv,int last_file_arg_seen,boolean for_real)130 parse_switches(j_compress_ptr cinfo, int argc, char **argv,
131                int last_file_arg_seen, boolean for_real)
132 /* Parse optional switches.
133  * Returns argv[] index of first file-name argument (== argc if none).
134  * Any file names with indexes <= last_file_arg_seen are ignored;
135  * they have presumably been processed in a previous iteration.
136  * (Pass 0 for last_file_arg_seen on the first or only iteration.)
137  * for_real is FALSE on the first (dummy) pass; we may skip any expensive
138  * processing.
139  */
140 {
141   int argn;
142   char *arg;
143   boolean simple_progressive;
144   char *scansarg = NULL;        /* saves -scans parm if any */
145 
146   /* Set up default JPEG parameters. */
147   simple_progressive = FALSE;
148   icc_filename = NULL;
149   max_scans = 0;
150   outfilename = NULL;
151   report = FALSE;
152   strict = FALSE;
153   copyoption = JCOPYOPT_DEFAULT;
154   transformoption.transform = JXFORM_NONE;
155   transformoption.perfect = FALSE;
156   transformoption.trim = FALSE;
157   transformoption.force_grayscale = FALSE;
158   transformoption.crop = FALSE;
159   transformoption.slow_hflip = FALSE;
160   cinfo->err->trace_level = 0;
161 
162   /* Scan command line options, adjust parameters */
163 
164   for (argn = 1; argn < argc; argn++) {
165     arg = argv[argn];
166     if (*arg != '-') {
167       /* Not a switch, must be a file name argument */
168       if (argn <= last_file_arg_seen) {
169         outfilename = NULL;     /* -outfile applies to just one input file */
170         continue;               /* ignore this name if previously processed */
171       }
172       break;                    /* else done parsing switches */
173     }
174     arg++;                      /* advance past switch marker character */
175 
176     if (keymatch(arg, "arithmetic", 1)) {
177       /* Use arithmetic coding. */
178 #ifdef C_ARITH_CODING_SUPPORTED
179       cinfo->arith_code = TRUE;
180 #else
181       fprintf(stderr, "%s: sorry, arithmetic coding not supported\n",
182               progname);
183       exit(EXIT_FAILURE);
184 #endif
185 
186     } else if (keymatch(arg, "copy", 2)) {
187       /* Select which extra markers to copy. */
188       if (++argn >= argc)       /* advance to next argument */
189         usage();
190       if (keymatch(argv[argn], "none", 1)) {
191         copyoption = JCOPYOPT_NONE;
192       } else if (keymatch(argv[argn], "comments", 1)) {
193         copyoption = JCOPYOPT_COMMENTS;
194       } else if (keymatch(argv[argn], "icc", 1)) {
195         copyoption = JCOPYOPT_ICC;
196       } else if (keymatch(argv[argn], "all", 1)) {
197         copyoption = JCOPYOPT_ALL;
198       } else
199         usage();
200 
201     } else if (keymatch(arg, "crop", 2)) {
202       /* Perform lossless cropping. */
203 #if TRANSFORMS_SUPPORTED
204       if (++argn >= argc)       /* advance to next argument */
205         usage();
206       if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
207           !jtransform_parse_crop_spec(&transformoption, argv[argn])) {
208         fprintf(stderr, "%s: bogus -crop argument '%s'\n",
209                 progname, argv[argn]);
210         exit(EXIT_FAILURE);
211       }
212 #else
213       select_transform(JXFORM_NONE);    /* force an error */
214 #endif
215 
216     } else if (keymatch(arg, "drop", 2)) {
217 #if TRANSFORMS_SUPPORTED
218       if (++argn >= argc)       /* advance to next argument */
219         usage();
220       if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
221           !jtransform_parse_crop_spec(&transformoption, argv[argn]) ||
222           transformoption.crop_width_set != JCROP_UNSET ||
223           transformoption.crop_height_set != JCROP_UNSET) {
224         fprintf(stderr, "%s: bogus -drop argument '%s'\n",
225                 progname, argv[argn]);
226         exit(EXIT_FAILURE);
227       }
228       if (++argn >= argc)       /* advance to next argument */
229         usage();
230       dropfilename = argv[argn];
231       select_transform(JXFORM_DROP);
232 #else
233       select_transform(JXFORM_NONE);    /* force an error */
234 #endif
235 
236     } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
237       /* Enable debug printouts. */
238       /* On first -d, print version identification */
239       static boolean printed_version = FALSE;
240 
241       if (!printed_version) {
242         fprintf(stderr, "%s version %s (build %s)\n",
243                 PACKAGE_NAME, VERSION, BUILD);
244         fprintf(stderr, "%s\n\n", JCOPYRIGHT);
245         fprintf(stderr, "Emulating The Independent JPEG Group's software, version %s\n\n",
246                 JVERSION);
247         printed_version = TRUE;
248       }
249       cinfo->err->trace_level++;
250 
251     } else if (keymatch(arg, "version", 4)) {
252       fprintf(stderr, "%s version %s (build %s)\n",
253               PACKAGE_NAME, VERSION, BUILD);
254       exit(EXIT_SUCCESS);
255 
256     } else if (keymatch(arg, "flip", 1)) {
257       /* Mirror left-right or top-bottom. */
258       if (++argn >= argc)       /* advance to next argument */
259         usage();
260       if (keymatch(argv[argn], "horizontal", 1))
261         select_transform(JXFORM_FLIP_H);
262       else if (keymatch(argv[argn], "vertical", 1))
263         select_transform(JXFORM_FLIP_V);
264       else
265         usage();
266 
267     } else if (keymatch(arg, "grayscale", 1) ||
268                keymatch(arg, "greyscale", 1)) {
269       /* Force to grayscale. */
270 #if TRANSFORMS_SUPPORTED
271       transformoption.force_grayscale = TRUE;
272 #else
273       select_transform(JXFORM_NONE);    /* force an error */
274 #endif
275 
276     } else if (keymatch(arg, "icc", 1)) {
277       /* Set ICC filename. */
278       if (++argn >= argc)       /* advance to next argument */
279         usage();
280       icc_filename = argv[argn];
281 
282     } else if (keymatch(arg, "maxmemory", 3)) {
283       /* Maximum memory in Kb (or Mb with 'm'). */
284       long lval;
285       char ch = 'x';
286 
287       if (++argn >= argc)       /* advance to next argument */
288         usage();
289       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
290         usage();
291       if (ch == 'm' || ch == 'M')
292         lval *= 1000L;
293       cinfo->mem->max_memory_to_use = lval * 1000L;
294 
295     } else if (keymatch(arg, "maxscans", 4)) {
296       if (++argn >= argc)       /* advance to next argument */
297         usage();
298       if (sscanf(argv[argn], "%u", &max_scans) != 1)
299         usage();
300 
301     } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
302       /* Enable entropy parm optimization. */
303 #ifdef ENTROPY_OPT_SUPPORTED
304       cinfo->optimize_coding = TRUE;
305 #else
306       fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n",
307               progname);
308       exit(EXIT_FAILURE);
309 #endif
310 
311     } else if (keymatch(arg, "outfile", 4)) {
312       /* Set output file name. */
313       if (++argn >= argc)       /* advance to next argument */
314         usage();
315       outfilename = argv[argn]; /* save it away for later use */
316 
317     } else if (keymatch(arg, "perfect", 2)) {
318       /* Fail if there is any partial edge MCUs that the transform can't
319        * handle. */
320       transformoption.perfect = TRUE;
321 
322     } else if (keymatch(arg, "progressive", 2)) {
323       /* Select simple progressive mode. */
324 #ifdef C_PROGRESSIVE_SUPPORTED
325       simple_progressive = TRUE;
326       /* We must postpone execution until num_components is known. */
327 #else
328       fprintf(stderr, "%s: sorry, progressive output was not compiled\n",
329               progname);
330       exit(EXIT_FAILURE);
331 #endif
332 
333     } else if (keymatch(arg, "report", 3)) {
334       report = TRUE;
335 
336     } else if (keymatch(arg, "restart", 1)) {
337       /* Restart interval in MCU rows (or in MCUs with 'b'). */
338       long lval;
339       char ch = 'x';
340 
341       if (++argn >= argc)       /* advance to next argument */
342         usage();
343       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
344         usage();
345       if (lval < 0 || lval > 65535L)
346         usage();
347       if (ch == 'b' || ch == 'B') {
348         cinfo->restart_interval = (unsigned int)lval;
349         cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
350       } else {
351         cinfo->restart_in_rows = (int)lval;
352         /* restart_interval will be computed during startup */
353       }
354 
355     } else if (keymatch(arg, "rotate", 2)) {
356       /* Rotate 90, 180, or 270 degrees (measured clockwise). */
357       if (++argn >= argc)       /* advance to next argument */
358         usage();
359       if (keymatch(argv[argn], "90", 2))
360         select_transform(JXFORM_ROT_90);
361       else if (keymatch(argv[argn], "180", 3))
362         select_transform(JXFORM_ROT_180);
363       else if (keymatch(argv[argn], "270", 3))
364         select_transform(JXFORM_ROT_270);
365       else
366         usage();
367 
368     } else if (keymatch(arg, "scans", 1)) {
369       /* Set scan script. */
370 #ifdef C_MULTISCAN_FILES_SUPPORTED
371       if (++argn >= argc)       /* advance to next argument */
372         usage();
373       scansarg = argv[argn];
374       /* We must postpone reading the file in case -progressive appears. */
375 #else
376       fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n",
377               progname);
378       exit(EXIT_FAILURE);
379 #endif
380 
381     } else if (keymatch(arg, "strict", 2)) {
382       strict = TRUE;
383 
384     } else if (keymatch(arg, "transpose", 1)) {
385       /* Transpose (across UL-to-LR axis). */
386       select_transform(JXFORM_TRANSPOSE);
387 
388     } else if (keymatch(arg, "transverse", 6)) {
389       /* Transverse transpose (across UR-to-LL axis). */
390       select_transform(JXFORM_TRANSVERSE);
391 
392     } else if (keymatch(arg, "trim", 3)) {
393       /* Trim off any partial edge MCUs that the transform can't handle. */
394       transformoption.trim = TRUE;
395 
396     } else if (keymatch(arg, "wipe", 1)) {
397 #if TRANSFORMS_SUPPORTED
398       if (++argn >= argc)       /* advance to next argument */
399         usage();
400       if (transformoption.crop /* reject multiple crop/drop/wipe requests */ ||
401           !jtransform_parse_crop_spec(&transformoption, argv[argn])) {
402         fprintf(stderr, "%s: bogus -wipe argument '%s'\n",
403                 progname, argv[argn]);
404         exit(EXIT_FAILURE);
405       }
406       select_transform(JXFORM_WIPE);
407 #else
408       select_transform(JXFORM_NONE);    /* force an error */
409 #endif
410 
411     } else {
412       usage();                  /* bogus switch */
413     }
414   }
415 
416   /* Post-switch-scanning cleanup */
417 
418   if (for_real) {
419 
420 #ifdef C_PROGRESSIVE_SUPPORTED
421     if (simple_progressive)     /* process -progressive; -scans can override */
422       jpeg_simple_progression(cinfo);
423 #endif
424 
425 #ifdef C_MULTISCAN_FILES_SUPPORTED
426     if (scansarg != NULL)       /* process -scans if it was present */
427       if (!read_scan_script(cinfo, scansarg))
428         usage();
429 #endif
430   }
431 
432   return argn;                  /* return index of next arg (file name) */
433 }
434 
435 
436 METHODDEF(void)
my_emit_message(j_common_ptr cinfo,int msg_level)437 my_emit_message(j_common_ptr cinfo, int msg_level)
438 {
439   if (msg_level < 0) {
440     /* Treat warning as fatal */
441     cinfo->err->error_exit(cinfo);
442   } else {
443     if (cinfo->err->trace_level >= msg_level)
444       cinfo->err->output_message(cinfo);
445   }
446 }
447 
448 
449 /*
450  * The main program.
451  */
452 
453 int
454 #ifdef GTEST
jpegtran(int argc,char ** argv)455 jpegtran(int argc, char **argv)
456 #else
457 main(int argc, char **argv)
458 #endif
459 {
460   struct jpeg_decompress_struct srcinfo;
461 #if TRANSFORMS_SUPPORTED
462   struct jpeg_decompress_struct dropinfo;
463   struct jpeg_error_mgr jdroperr;
464   FILE *drop_file;
465 #endif
466   struct jpeg_compress_struct dstinfo;
467   struct jpeg_error_mgr jsrcerr, jdsterr;
468   struct cdjpeg_progress_mgr src_progress, dst_progress;
469   jvirt_barray_ptr *src_coef_arrays;
470   jvirt_barray_ptr *dst_coef_arrays;
471   int file_index;
472   /* We assume all-in-memory processing and can therefore use only a
473    * single file pointer for sequential input and output operation.
474    */
475   FILE *fp;
476   FILE *icc_file;
477   JOCTET *icc_profile = NULL;
478   long icc_len = 0;
479 
480   progname = argv[0];
481   if (progname == NULL || progname[0] == 0)
482     progname = "jpegtran";      /* in case C library doesn't provide it */
483 
484   /* Initialize the JPEG decompression object with default error handling. */
485   srcinfo.err = jpeg_std_error(&jsrcerr);
486   jpeg_create_decompress(&srcinfo);
487   /* Initialize the JPEG compression object with default error handling. */
488   dstinfo.err = jpeg_std_error(&jdsterr);
489   jpeg_create_compress(&dstinfo);
490 
491   /* Scan command line to find file names.
492    * It is convenient to use just one switch-parsing routine, but the switch
493    * values read here are mostly ignored; we will rescan the switches after
494    * opening the input file.  Also note that most of the switches affect the
495    * destination JPEG object, so we parse into that and then copy over what
496    * needs to affect the source too.
497    */
498 
499   file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE);
500   jsrcerr.trace_level = jdsterr.trace_level;
501   srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
502 
503   if (strict)
504     jsrcerr.emit_message = my_emit_message;
505 
506 #ifdef TWO_FILE_COMMANDLINE
507   /* Must have either -outfile switch or explicit output file name */
508   if (outfilename == NULL) {
509     if (file_index != argc - 2) {
510       fprintf(stderr, "%s: must name one input and one output file\n",
511               progname);
512       usage();
513     }
514     outfilename = argv[file_index + 1];
515   } else {
516     if (file_index != argc - 1) {
517       fprintf(stderr, "%s: must name one input and one output file\n",
518               progname);
519       usage();
520     }
521   }
522 #else
523   /* Unix style: expect zero or one file name */
524   if (file_index < argc - 1) {
525     fprintf(stderr, "%s: only one input file\n", progname);
526     usage();
527   }
528 #endif /* TWO_FILE_COMMANDLINE */
529 
530   /* Open the input file. */
531   if (file_index < argc) {
532     if ((fp = fopen(argv[file_index], READ_BINARY)) == NULL) {
533       fprintf(stderr, "%s: can't open %s for reading\n", progname,
534               argv[file_index]);
535       return EXIT_FAILURE;
536     }
537   } else {
538     /* default input file is stdin */
539     fp = read_stdin();
540   }
541 
542   if (icc_filename != NULL) {
543     if ((icc_file = fopen(icc_filename, READ_BINARY)) == NULL) {
544       fprintf(stderr, "%s: can't open %s\n", progname, icc_filename);
545       return EXIT_FAILURE;
546     }
547     if (fseek(icc_file, 0, SEEK_END) < 0 ||
548         (icc_len = ftell(icc_file)) < 1 ||
549         fseek(icc_file, 0, SEEK_SET) < 0) {
550       fprintf(stderr, "%s: can't determine size of %s\n", progname,
551               icc_filename);
552       return EXIT_FAILURE;
553     }
554     if ((icc_profile = (JOCTET *)malloc(icc_len)) == NULL) {
555       fprintf(stderr, "%s: can't allocate memory for ICC profile\n", progname);
556       fclose(icc_file);
557       return EXIT_FAILURE;
558     }
559     if (fread(icc_profile, icc_len, 1, icc_file) < 1) {
560       fprintf(stderr, "%s: can't read ICC profile from %s\n", progname,
561               icc_filename);
562       free(icc_profile);
563       fclose(icc_file);
564       return EXIT_FAILURE;
565     }
566     fclose(icc_file);
567     if (copyoption == JCOPYOPT_ALL)
568       copyoption = JCOPYOPT_ALL_EXCEPT_ICC;
569     if (copyoption == JCOPYOPT_ICC)
570       copyoption = JCOPYOPT_NONE;
571   }
572 
573   if (report) {
574     start_progress_monitor((j_common_ptr)&dstinfo, &dst_progress);
575     dst_progress.report = report;
576   }
577   if (report || max_scans != 0) {
578     start_progress_monitor((j_common_ptr)&srcinfo, &src_progress);
579     src_progress.report = report;
580     src_progress.max_scans = max_scans;
581   }
582 #if TRANSFORMS_SUPPORTED
583   /* Open the drop file. */
584   if (dropfilename != NULL) {
585     if ((drop_file = fopen(dropfilename, READ_BINARY)) == NULL) {
586       fprintf(stderr, "%s: can't open %s for reading\n", progname,
587               dropfilename);
588       return EXIT_FAILURE;
589     }
590     dropinfo.err = jpeg_std_error(&jdroperr);
591     jpeg_create_decompress(&dropinfo);
592     jpeg_stdio_src(&dropinfo, drop_file);
593   } else {
594     drop_file = NULL;
595   }
596 #endif
597 
598   /* Specify data source for decompression */
599   jpeg_stdio_src(&srcinfo, fp);
600 
601   /* Enable saving of extra markers that we want to copy */
602   jcopy_markers_setup(&srcinfo, copyoption);
603 
604   /* Read file header */
605   (void)jpeg_read_header(&srcinfo, TRUE);
606 
607 #if TRANSFORMS_SUPPORTED
608   if (dropfilename != NULL) {
609     (void)jpeg_read_header(&dropinfo, TRUE);
610     transformoption.crop_width = dropinfo.image_width;
611     transformoption.crop_width_set = JCROP_POS;
612     transformoption.crop_height = dropinfo.image_height;
613     transformoption.crop_height_set = JCROP_POS;
614     transformoption.drop_ptr = &dropinfo;
615   }
616 #endif
617 
618   /* Any space needed by a transform option must be requested before
619    * jpeg_read_coefficients so that memory allocation will be done right.
620    */
621 #if TRANSFORMS_SUPPORTED
622   /* Fail right away if -perfect is given and transformation is not perfect.
623    */
624   if (!jtransform_request_workspace(&srcinfo, &transformoption)) {
625     fprintf(stderr, "%s: transformation is not perfect\n", progname);
626     return EXIT_FAILURE;
627   }
628 #endif
629 
630   /* Read source file as DCT coefficients */
631   src_coef_arrays = jpeg_read_coefficients(&srcinfo);
632 
633 #if TRANSFORMS_SUPPORTED
634   if (dropfilename != NULL) {
635     transformoption.drop_coef_arrays = jpeg_read_coefficients(&dropinfo);
636   }
637 #endif
638 
639   /* Initialize destination compression parameters from source values */
640   jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
641 
642   /* Adjust destination parameters if required by transform options;
643    * also find out which set of coefficient arrays will hold the output.
644    */
645 #if TRANSFORMS_SUPPORTED
646   dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo,
647                                                  src_coef_arrays,
648                                                  &transformoption);
649 #else
650   dst_coef_arrays = src_coef_arrays;
651 #endif
652 
653   /* Close input file, if we opened it.
654    * Note: we assume that jpeg_read_coefficients consumed all input
655    * until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
656    * only consume more while (!cinfo->inputctl->eoi_reached).
657    * We cannot call jpeg_finish_decompress here since we still need the
658    * virtual arrays allocated from the source object for processing.
659    */
660   if (fp != stdin)
661     fclose(fp);
662 
663   /* Open the output file. */
664   if (outfilename != NULL) {
665     if ((fp = fopen(outfilename, WRITE_BINARY)) == NULL) {
666       fprintf(stderr, "%s: can't open %s for writing\n", progname,
667               outfilename);
668       return EXIT_FAILURE;
669     }
670   } else {
671     /* default output file is stdout */
672     fp = write_stdout();
673   }
674 
675   /* Adjust default compression parameters by re-parsing the options */
676   file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE);
677 
678   /* Specify data destination for compression */
679   jpeg_stdio_dest(&dstinfo, fp);
680 
681   /* Start compressor (note no image data is actually written here) */
682   jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
683 
684   /* Copy to the output file any extra markers that we want to preserve */
685   jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
686 
687   if (icc_profile != NULL)
688     jpeg_write_icc_profile(&dstinfo, icc_profile, (unsigned int)icc_len);
689 
690   /* Execute image transformation, if any */
691 #if TRANSFORMS_SUPPORTED
692   jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays,
693                                     &transformoption);
694 #endif
695 
696   /* Finish compression and release memory */
697   jpeg_finish_compress(&dstinfo);
698   jpeg_destroy_compress(&dstinfo);
699 #if TRANSFORMS_SUPPORTED
700   if (dropfilename != NULL) {
701     (void)jpeg_finish_decompress(&dropinfo);
702     jpeg_destroy_decompress(&dropinfo);
703   }
704 #endif
705   (void)jpeg_finish_decompress(&srcinfo);
706   jpeg_destroy_decompress(&srcinfo);
707 
708   /* Close output file, if we opened it */
709   if (fp != stdout)
710     fclose(fp);
711 #if TRANSFORMS_SUPPORTED
712   if (drop_file != NULL)
713     fclose(drop_file);
714 #endif
715 
716   if (report)
717     end_progress_monitor((j_common_ptr)&dstinfo);
718   if (report || max_scans != 0)
719     end_progress_monitor((j_common_ptr)&srcinfo);
720 
721   free(icc_profile);
722 
723   /* All done. */
724 #if TRANSFORMS_SUPPORTED
725   if (dropfilename != NULL)
726     return (jsrcerr.num_warnings + jdroperr.num_warnings +
727             jdsterr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
728 #endif
729   return (jsrcerr.num_warnings + jdsterr.num_warnings ?
730           EXIT_WARNING : EXIT_SUCCESS);
731 }
732