xref: /plan9/sys/src/cmd/gs/jpeg/jpegtran.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 /*
2  * jpegtran.c
3  *
4  * Copyright (C) 1995-1996, Thomas G. Lane.
5  * This file is part of the Independent JPEG Group's software.
6  * For conditions of distribution and use, see the accompanying README file.
7  *
8  * This file contains a command-line user interface for JPEG transcoding.
9  * It is very similar to cjpeg.c, but provides lossless transcoding between
10  * different JPEG file formats.
11  */
12 
13 #include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
14 #include "jversion.h"		/* for version message */
15 
16 #ifdef USE_CCOMMAND		/* command-line reader for Macintosh */
17 #ifdef __MWERKS__
18 #include <SIOUX.h>              /* Metrowerks needs this */
19 #include <console.h>		/* ... and this */
20 #endif
21 #ifdef THINK_C
22 #include <console.h>		/* Think declares it here */
23 #endif
24 #endif
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 * outfilename;	/* for -outfile switch */
38 
39 
40 LOCAL(void)
41 usage (void)
42 /* complain about bad command line */
43 {
44   fprintf(stderr, "usage: %s [switches] ", progname);
45 #ifdef TWO_FILE_COMMANDLINE
46   fprintf(stderr, "inputfile outputfile\n");
47 #else
48   fprintf(stderr, "[inputfile]\n");
49 #endif
50 
51   fprintf(stderr, "Switches (names may be abbreviated):\n");
52 #ifdef ENTROPY_OPT_SUPPORTED
53   fprintf(stderr, "  -optimize      Optimize Huffman table (smaller file, but slow compression)\n");
54 #endif
55 #ifdef C_PROGRESSIVE_SUPPORTED
56   fprintf(stderr, "  -progressive   Create progressive JPEG file\n");
57 #endif
58   fprintf(stderr, "Switches for advanced users:\n");
59   fprintf(stderr, "  -restart N     Set restart interval in rows, or in blocks with B\n");
60   fprintf(stderr, "  -maxmemory N   Maximum memory to use (in kbytes)\n");
61   fprintf(stderr, "  -outfile name  Specify name for output file\n");
62   fprintf(stderr, "  -verbose  or  -debug   Emit debug output\n");
63   fprintf(stderr, "Switches for wizards:\n");
64 #ifdef C_ARITH_CODING_SUPPORTED
65   fprintf(stderr, "  -arithmetic    Use arithmetic coding\n");
66 #endif
67 #ifdef C_MULTISCAN_FILES_SUPPORTED
68   fprintf(stderr, "  -scans file    Create multi-scan JPEG per script file\n");
69 #endif
70   exit(EXIT_FAILURE);
71 }
72 
73 
74 LOCAL(int)
75 parse_switches (j_compress_ptr cinfo, int argc, char **argv,
76 		int last_file_arg_seen, boolean for_real)
77 /* Parse optional switches.
78  * Returns argv[] index of first file-name argument (== argc if none).
79  * Any file names with indexes <= last_file_arg_seen are ignored;
80  * they have presumably been processed in a previous iteration.
81  * (Pass 0 for last_file_arg_seen on the first or only iteration.)
82  * for_real is FALSE on the first (dummy) pass; we may skip any expensive
83  * processing.
84  */
85 {
86   int argn;
87   char * arg;
88   boolean simple_progressive;
89   char * scansarg = NULL;	/* saves -scans parm if any */
90 
91   /* Set up default JPEG parameters. */
92   simple_progressive = FALSE;
93   outfilename = NULL;
94   cinfo->err->trace_level = 0;
95 
96   /* Scan command line options, adjust parameters */
97 
98   for (argn = 1; argn < argc; argn++) {
99     arg = argv[argn];
100     if (*arg != '-') {
101       /* Not a switch, must be a file name argument */
102       if (argn <= last_file_arg_seen) {
103 	outfilename = NULL;	/* -outfile applies to just one input file */
104 	continue;		/* ignore this name if previously processed */
105       }
106       break;			/* else done parsing switches */
107     }
108     arg++;			/* advance past switch marker character */
109 
110     if (keymatch(arg, "arithmetic", 1)) {
111       /* Use arithmetic coding. */
112 #ifdef C_ARITH_CODING_SUPPORTED
113       cinfo->arith_code = TRUE;
114 #else
115       fprintf(stderr, "%s: sorry, arithmetic coding not supported\n",
116 	      progname);
117       exit(EXIT_FAILURE);
118 #endif
119 
120     } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
121       /* Enable debug printouts. */
122       /* On first -d, print version identification */
123       static boolean printed_version = FALSE;
124 
125       if (! printed_version) {
126 	fprintf(stderr, "Independent JPEG Group's JPEGTRAN, version %s\n%s\n",
127 		JVERSION, JCOPYRIGHT);
128 	printed_version = TRUE;
129       }
130       cinfo->err->trace_level++;
131 
132     } else if (keymatch(arg, "maxmemory", 3)) {
133       /* Maximum memory in Kb (or Mb with 'm'). */
134       long lval;
135       char ch = 'x';
136 
137       if (++argn >= argc)	/* advance to next argument */
138 	usage();
139       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
140 	usage();
141       if (ch == 'm' || ch == 'M')
142 	lval *= 1000L;
143       cinfo->mem->max_memory_to_use = lval * 1000L;
144 
145     } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
146       /* Enable entropy parm optimization. */
147 #ifdef ENTROPY_OPT_SUPPORTED
148       cinfo->optimize_coding = TRUE;
149 #else
150       fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n",
151 	      progname);
152       exit(EXIT_FAILURE);
153 #endif
154 
155     } else if (keymatch(arg, "outfile", 4)) {
156       /* Set output file name. */
157       if (++argn >= argc)	/* advance to next argument */
158 	usage();
159       outfilename = argv[argn];	/* save it away for later use */
160 
161     } else if (keymatch(arg, "progressive", 1)) {
162       /* Select simple progressive mode. */
163 #ifdef C_PROGRESSIVE_SUPPORTED
164       simple_progressive = TRUE;
165       /* We must postpone execution until num_components is known. */
166 #else
167       fprintf(stderr, "%s: sorry, progressive output was not compiled\n",
168 	      progname);
169       exit(EXIT_FAILURE);
170 #endif
171 
172     } else if (keymatch(arg, "restart", 1)) {
173       /* Restart interval in MCU rows (or in MCUs with 'b'). */
174       long lval;
175       char ch = 'x';
176 
177       if (++argn >= argc)	/* advance to next argument */
178 	usage();
179       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
180 	usage();
181       if (lval < 0 || lval > 65535L)
182 	usage();
183       if (ch == 'b' || ch == 'B') {
184 	cinfo->restart_interval = (unsigned int) lval;
185 	cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */
186       } else {
187 	cinfo->restart_in_rows = (int) lval;
188 	/* restart_interval will be computed during startup */
189       }
190 
191     } else if (keymatch(arg, "scans", 2)) {
192       /* Set scan script. */
193 #ifdef C_MULTISCAN_FILES_SUPPORTED
194       if (++argn >= argc)	/* advance to next argument */
195 	usage();
196       scansarg = argv[argn];
197       /* We must postpone reading the file in case -progressive appears. */
198 #else
199       fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n",
200 	      progname);
201       exit(EXIT_FAILURE);
202 #endif
203 
204     } else {
205       usage();			/* bogus switch */
206     }
207   }
208 
209   /* Post-switch-scanning cleanup */
210 
211   if (for_real) {
212 
213 #ifdef C_PROGRESSIVE_SUPPORTED
214     if (simple_progressive)	/* process -progressive; -scans can override */
215       jpeg_simple_progression(cinfo);
216 #endif
217 
218 #ifdef C_MULTISCAN_FILES_SUPPORTED
219     if (scansarg != NULL)	/* process -scans if it was present */
220       if (! read_scan_script(cinfo, scansarg))
221 	usage();
222 #endif
223   }
224 
225   return argn;			/* return index of next arg (file name) */
226 }
227 
228 
229 /*
230  * The main program.
231  */
232 
233 int
234 main (int argc, char **argv)
235 {
236   struct jpeg_decompress_struct srcinfo;
237   struct jpeg_compress_struct dstinfo;
238   struct jpeg_error_mgr jsrcerr, jdsterr;
239 #ifdef PROGRESS_REPORT
240   struct cdjpeg_progress_mgr progress;
241 #endif
242   jvirt_barray_ptr * coef_arrays;
243   int file_index;
244   FILE * input_file;
245   FILE * output_file;
246 
247   /* On Mac, fetch a command line. */
248 #ifdef USE_CCOMMAND
249   argc = ccommand(&argv);
250 #endif
251 
252   progname = argv[0];
253   if (progname == NULL || progname[0] == 0)
254     progname = "jpegtran";	/* in case C library doesn't provide it */
255 
256   /* Initialize the JPEG decompression object with default error handling. */
257   srcinfo.err = jpeg_std_error(&jsrcerr);
258   jpeg_create_decompress(&srcinfo);
259   /* Initialize the JPEG compression object with default error handling. */
260   dstinfo.err = jpeg_std_error(&jdsterr);
261   jpeg_create_compress(&dstinfo);
262 
263   /* Now safe to enable signal catcher.
264    * Note: we assume only the decompression object will have virtual arrays.
265    */
266 #ifdef NEED_SIGNAL_CATCHER
267   enable_signal_catcher((j_common_ptr) &srcinfo);
268 #endif
269 
270   /* Scan command line to find file names.
271    * It is convenient to use just one switch-parsing routine, but the switch
272    * values read here are ignored; we will rescan the switches after opening
273    * the input file.
274    */
275 
276   file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE);
277   jsrcerr.trace_level = jdsterr.trace_level;
278   srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
279 
280 #ifdef TWO_FILE_COMMANDLINE
281   /* Must have either -outfile switch or explicit output file name */
282   if (outfilename == NULL) {
283     if (file_index != argc-2) {
284       fprintf(stderr, "%s: must name one input and one output file\n",
285 	      progname);
286       usage();
287     }
288     outfilename = argv[file_index+1];
289   } else {
290     if (file_index != argc-1) {
291       fprintf(stderr, "%s: must name one input and one output file\n",
292 	      progname);
293       usage();
294     }
295   }
296 #else
297   /* Unix style: expect zero or one file name */
298   if (file_index < argc-1) {
299     fprintf(stderr, "%s: only one input file\n", progname);
300     usage();
301   }
302 #endif /* TWO_FILE_COMMANDLINE */
303 
304   /* Open the input file. */
305   if (file_index < argc) {
306     if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) {
307       fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]);
308       exit(EXIT_FAILURE);
309     }
310   } else {
311     /* default input file is stdin */
312     input_file = read_stdin();
313   }
314 
315   /* Open the output file. */
316   if (outfilename != NULL) {
317     if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) {
318       fprintf(stderr, "%s: can't open %s\n", progname, outfilename);
319       exit(EXIT_FAILURE);
320     }
321   } else {
322     /* default output file is stdout */
323     output_file = write_stdout();
324   }
325 
326 #ifdef PROGRESS_REPORT
327   start_progress_monitor((j_common_ptr) &dstinfo, &progress);
328 #endif
329 
330   /* Specify data source for decompression */
331   jpeg_stdio_src(&srcinfo, input_file);
332 
333   /* Read file header */
334   (void) jpeg_read_header(&srcinfo, TRUE);
335 
336   /* Read source file as DCT coefficients */
337   coef_arrays = jpeg_read_coefficients(&srcinfo);
338 
339   /* Initialize destination compression parameters from source values */
340   jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
341 
342   /* Adjust default compression parameters by re-parsing the options */
343   file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE);
344 
345   /* Specify data destination for compression */
346   jpeg_stdio_dest(&dstinfo, output_file);
347 
348   /* Start compressor */
349   jpeg_write_coefficients(&dstinfo, coef_arrays);
350 
351   /* ought to copy source comments here... */
352 
353   /* Finish compression and release memory */
354   jpeg_finish_compress(&dstinfo);
355   jpeg_destroy_compress(&dstinfo);
356   (void) jpeg_finish_decompress(&srcinfo);
357   jpeg_destroy_decompress(&srcinfo);
358 
359   /* Close files, if we opened them */
360   if (input_file != stdin)
361     fclose(input_file);
362   if (output_file != stdout)
363     fclose(output_file);
364 
365 #ifdef PROGRESS_REPORT
366   end_progress_monitor((j_common_ptr) &dstinfo);
367 #endif
368 
369   /* All done. */
370   exit(jsrcerr.num_warnings + jdsterr.num_warnings ?EXIT_WARNING:EXIT_SUCCESS);
371   return 0;			/* suppress no-return-value warnings */
372 }
373