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