/src/jpegoptim/jpegoptim.c
Line | Count | Source |
1 | | /******************************************************************* |
2 | | * JPEGoptim |
3 | | * Copyright (c) Timo Kokkonen, 1996-2025. |
4 | | * All Rights Reserved. |
5 | | * |
6 | | * requires libjpeg (Independent JPEG Group's JPEG software |
7 | | * release 6a or later...) |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-3.0-or-later |
10 | | * |
11 | | * This file is part of JPEGoptim. |
12 | | * |
13 | | * JPEGoptim is free software: you can redistribute it and/or modify |
14 | | * it under the terms of the GNU General Public License as published by |
15 | | * the Free Software Foundation, either version 3 of the License, or |
16 | | * (at your option) any later version. |
17 | | * |
18 | | * JPEGoptim is distributed in the hope that it will be useful, |
19 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 | | * GNU General Public License for more details. |
22 | | * |
23 | | * You should have received a copy of the GNU General Public License |
24 | | * along with JPEGoptim. If not, see <https://www.gnu.org/licenses/>. |
25 | | */ |
26 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | #include "config.h" |
29 | | #endif |
30 | | #include <stdio.h> |
31 | | #include <stdlib.h> |
32 | | #ifdef HAVE_UNISTD_H |
33 | | #include <unistd.h> |
34 | | #endif |
35 | | #if HAVE_DIRENT_H |
36 | | #include <dirent.h> |
37 | | #endif |
38 | | #if HAVE_FCNTL_H |
39 | | #include <fcntl.h> |
40 | | #endif |
41 | | #if HAVE_SYS_STAT_H |
42 | | #include <sys/stat.h> |
43 | | #endif |
44 | | #if HAVE_SYS_TYPES_H |
45 | | #include <sys/types.h> |
46 | | #endif |
47 | | #if HAVE_SYS_WAIT_H |
48 | | #include <sys/wait.h> |
49 | | #endif |
50 | | #if HAVE_GETOPT_H && HAVE_GETOPT_LONG |
51 | | #include <getopt.h> |
52 | | #else |
53 | | #include "getopt.h" |
54 | | #endif |
55 | | #include <signal.h> |
56 | | #include <string.h> |
57 | | #include <jpeglib.h> |
58 | | #include <jerror.h> |
59 | | #include <setjmp.h> |
60 | | #include <time.h> |
61 | | #include <math.h> |
62 | | |
63 | | #include "jpegmarker.h" |
64 | | #include "jpegoptim.h" |
65 | | |
66 | | |
67 | 0 | #define VERSION "1.5.7beta" |
68 | 0 | #define COPYRIGHT "Copyright (C) 1996-2025, Timo Kokkonen" |
69 | | |
70 | | #if HAVE_WAIT && HAVE_FORK |
71 | | #define PARALLEL_PROCESSING 1 |
72 | 0 | #define MAX_WORKERS 256 |
73 | | #endif |
74 | | |
75 | 1.73k | #define IN_BUF_SIZE (256 * 1024) |
76 | | |
77 | | |
78 | | struct my_error_mgr { |
79 | | struct jpeg_error_mgr pub; |
80 | | jmp_buf setjmp_buffer; |
81 | | int jump_set; |
82 | | }; |
83 | | typedef struct my_error_mgr * my_error_ptr; |
84 | | |
85 | | |
86 | | #ifdef PARALLEL_PROCESSING |
87 | | struct worker { |
88 | | pid_t pid; |
89 | | int read_pipe; |
90 | | }; |
91 | | struct worker *workers; |
92 | | int worker_count = 0; |
93 | | #endif |
94 | | |
95 | | |
96 | | int verbose_mode = 0; |
97 | | int quiet_mode = 0; |
98 | | int preserve_mode = 0; |
99 | | int preserve_perms = 0; |
100 | | int overwrite_mode = 0; |
101 | | int retry_mode = 0; |
102 | | int totals_mode = 0; |
103 | | int stdin_mode = 0; |
104 | | int stdout_mode = 0; |
105 | | int noaction = 0; |
106 | | int quality = -1; |
107 | | int dest = 0; |
108 | | int force = 0; |
109 | | int save_extra = 0; |
110 | | int save_exif = 1; |
111 | | int save_iptc = 1; |
112 | | int save_com = 1; |
113 | | int save_icc = 1; |
114 | | int save_xmp = 1; |
115 | | int save_adobe = 0; |
116 | | int save_jfxx = 0; |
117 | | int save_jfif = 1; |
118 | | int strip_none = 0; |
119 | | double threshold = -1.0; |
120 | | int csv = 0; |
121 | | int auto_mode = 0; |
122 | | int all_normal = 0; |
123 | | int all_progressive = 0; |
124 | | int target_size = 0; |
125 | | #ifdef HAVE_ARITH_CODE |
126 | | int arith_mode = -1; |
127 | | #endif |
128 | | int max_workers = 1; |
129 | | int nofix_mode = 0; |
130 | | int files_stdin = 0; |
131 | | FILE *files_from = NULL; |
132 | | |
133 | | int compress_err_count = 0; |
134 | | int decompress_err_count = 0; |
135 | | int global_error_counter = 0; |
136 | | char last_error[JMSG_LENGTH_MAX+1]; |
137 | | FILE *jpeg_log_fh; |
138 | | long average_count = 0; |
139 | | double average_rate = 0.0; |
140 | | double total_save = 0.0; |
141 | | |
142 | | const struct option long_options[] = { |
143 | | #ifdef HAVE_ARITH_CODE |
144 | | { "all-arith", 0, &arith_mode, 1 }, |
145 | | { "all-huffman", 0, &arith_mode, 0 }, |
146 | | #endif |
147 | | { "auto-mode", 0, &auto_mode, 1 }, |
148 | | { "all-normal", 0, &all_normal, 1 }, |
149 | | { "all-progressive", 0, &all_progressive, 1 }, |
150 | | { "csv", 0, 0, 'b' }, |
151 | | { "dest", 1, 0, 'd' }, |
152 | | { "files-stdin", 0, &files_stdin, 1 }, |
153 | | { "files-from", 1, 0, 'F' }, |
154 | | { "force", 0, 0, 'f' }, |
155 | | { "help", 0, 0, 'h' }, |
156 | | { "keep-adobe", 0, &save_adobe, 1 }, |
157 | | { "keep-all", 0, &strip_none, 1 }, |
158 | | { "keep-com", 0, &save_com, 1 }, |
159 | | { "keep-exif", 0, &save_exif, 1 }, |
160 | | { "keep-iptc", 0, &save_iptc, 1 }, |
161 | | { "keep-icc", 0, &save_icc, 1 }, |
162 | | { "keep-jfif", 0, &save_jfif, 1 }, |
163 | | { "keep-jfxx", 0, &save_jfxx, 1 }, |
164 | | { "keep-xmp", 0, &save_xmp, 1 }, |
165 | | { "max", 1, 0, 'm' }, |
166 | | { "noaction", 0, 0, 'n' }, |
167 | | { "nofix", 0, &nofix_mode, 1 }, |
168 | | { "overwrite", 0, 0, 'o' }, |
169 | | { "preserve", 0, 0, 'p' }, |
170 | | { "preserve-perms", 0, 0, 'P' }, |
171 | | { "quiet", 0, 0, 'q' }, |
172 | | { "retry", 0, &retry_mode, 'r' }, |
173 | | { "save-extra", 0, &save_extra, 1 }, |
174 | | { "size", 1, 0, 'S' }, |
175 | | { "stdin", 0, &stdin_mode, 1 }, |
176 | | { "stdout", 0, &stdout_mode, 1 }, |
177 | | { "strip-all", 0, 0, 's' }, |
178 | | { "strip-none", 0, &strip_none, 1 }, |
179 | | { "strip-com", 0, &save_com, 0 }, |
180 | | { "strip-exif", 0, &save_exif, 0 }, |
181 | | { "strip-iptc", 0, &save_iptc, 0 }, |
182 | | { "strip-icc", 0, &save_icc, 0 }, |
183 | | { "strip-xmp", 0, &save_xmp, 0 }, |
184 | | { "strip-jfif", 0, &save_jfif, 0 }, |
185 | | { "strip-jfxx", 0, &save_jfxx, 0 }, |
186 | | { "strip-adobe", 0, &save_adobe, 0 }, |
187 | | { "threshold", 1, 0, 'T' }, |
188 | | { "totals", 0, 0, 't' }, |
189 | | { "verbose", 0, 0, 'v' }, |
190 | | { "version", 0, 0, 'V' }, |
191 | | #ifdef PARALLEL_PROCESSING |
192 | | { "workers", 1, &max_workers, 'w' }, |
193 | | #endif |
194 | | { 0, 0, 0, 0 } |
195 | | }; |
196 | | |
197 | | |
198 | | /*****************************************************************/ |
199 | | |
200 | | |
201 | | void free_line_buf(JSAMPARRAY *buf, unsigned int lines) |
202 | 532 | { |
203 | 532 | if (*buf == NULL) |
204 | 239 | return; |
205 | | |
206 | 861k | for (unsigned int i = 0; i < lines; i++) { |
207 | 861k | if ((*buf)[i]) |
208 | 861k | free((*buf)[i]); |
209 | 861k | } |
210 | 293 | free(*buf); |
211 | 293 | *buf = NULL; |
212 | 293 | } |
213 | | |
214 | | |
215 | | METHODDEF(void) my_error_exit (j_common_ptr cinfo) |
216 | 173 | { |
217 | 173 | my_error_ptr myerr = (my_error_ptr)cinfo->err; |
218 | | |
219 | 173 | (*cinfo->err->output_message)(cinfo); |
220 | 173 | if (myerr->jump_set) |
221 | 173 | longjmp(myerr->setjmp_buffer, 1); |
222 | 0 | else |
223 | 0 | fatal("fatal error"); |
224 | 173 | } |
225 | | |
226 | | |
227 | | METHODDEF(void) my_output_message (j_common_ptr cinfo) |
228 | 690 | { |
229 | 690 | char buffer[JMSG_LENGTH_MAX+1]; |
230 | | |
231 | 690 | (*cinfo->err->format_message)((j_common_ptr)cinfo, buffer); |
232 | 690 | buffer[sizeof(buffer) - 1] = 0; |
233 | | |
234 | 690 | if (verbose_mode) |
235 | 0 | fprintf(jpeg_log_fh, " (%s) ", buffer); |
236 | | |
237 | 690 | global_error_counter++; |
238 | 690 | strncopy(last_error, buffer, sizeof(last_error)); |
239 | 690 | } |
240 | | |
241 | | |
242 | | void print_usage(void) |
243 | 0 | { |
244 | 0 | fprintf(stdout,PROGRAMNAME " v" VERSION " " COPYRIGHT "\n"); |
245 | |
|
246 | 0 | fprintf(stdout, |
247 | 0 | "Usage: " PROGRAMNAME " [options] <filenames> \n\n" |
248 | 0 | " -d<path>, --dest=<path>\n" |
249 | 0 | " specify alternative destination directory for \n" |
250 | 0 | " optimized files (default is to overwrite originals)\n" |
251 | 0 | " -f, --force force optimization\n" |
252 | 0 | " -h, --help display this help and exit\n" |
253 | 0 | " -m<quality>, --max=<quality>\n" |
254 | 0 | " set maximum image quality factor (disables lossless\n" |
255 | 0 | " optimization mode, which is by default on)\n" |
256 | 0 | " Valid quality values: 0 - 100\n" |
257 | 0 | " -n, --noaction don't really optimize files, just print results\n" |
258 | 0 | " -S<size>, --size=<size>\n" |
259 | 0 | " Try to optimize file to given size (disables lossless\n" |
260 | 0 | " optimization mode). Target size is specified either in\n" |
261 | 0 | " kilo bytes (1 - n) or as percentage (1%% - 99%%)\n" |
262 | 0 | " -T<threshold>, --threshold=<threshold>\n" |
263 | 0 | " keep old file if the gain is below a threshold (%%)\n" |
264 | 0 | #ifdef PARALLEL_PROCESSING |
265 | 0 | " -w<max>, --workers=<max>\n" |
266 | 0 | " set maximum number of parallel threads (default is 1)\n" |
267 | 0 | #endif |
268 | 0 | " -b, --csv print progress info in CSV format\n" |
269 | 0 | " -o, --overwrite overwrite target file even if it exists (meaningful\n" |
270 | 0 | " only when used with -d, --dest option)\n" |
271 | 0 | " -p, --preserve preserve file timestamps\n" |
272 | 0 | " -P, --preserve-perms\n" |
273 | 0 | " preserve original file permissions by overwriting it\n" |
274 | 0 | " -q, --quiet quiet mode\n" |
275 | 0 | " -r, --retry try (recursively) optimize until file size does not change anymore\n" |
276 | 0 | " -t, --totals print totals after processing all files\n" |
277 | 0 | " -v, --verbose enable verbose mode (positively chatty)\n" |
278 | 0 | " -V, --version print program version\n\n" |
279 | 0 | " -s, --strip-all strip all markers from output file\n" |
280 | 0 | " --strip-none do not strip any markers\n" |
281 | 0 | " --strip-adobe strip Adobe (APP14) markers from output file\n" |
282 | 0 | " --strip-com strip Comment markers from output file\n" |
283 | 0 | " --strip-exif strip Exif markers from output file\n" |
284 | 0 | " --strip-iptc strip IPTC/Photoshop (APP13) markers from output file\n" |
285 | 0 | " --strip-icc strip ICC profile markers from output file\n" |
286 | 0 | " --strip-jfif strip JFIF markers from output file\n" |
287 | 0 | " --strip-jfxx strip JFXX (JFIF Extension) markers from output file\n" |
288 | 0 | " --strip-xmp strip XMP markers markers from output file\n" |
289 | 0 | "\n" |
290 | 0 | " --keep-all do not strip any markers (same as --strip-none)\n" |
291 | 0 | " --keep-adobe preserve any Adobe (APP14) markers\n" |
292 | 0 | " --keep-com preserve any Comment markers\n" |
293 | 0 | " --keep-exif preserve any Exif markers\n" |
294 | 0 | " --keep-iptc preserve any IPTC/Photoshop (APP13) markers\n" |
295 | 0 | " --keep-icc preserve any ICC profile markers\n" |
296 | 0 | " --keep-jfif preserve any JFIF markers\n" |
297 | 0 | " --keep-jfxx preserve any JFXX (JFIF Extension) markers\n" |
298 | 0 | " --keep-xmp preserve any XMP markers markers\n" |
299 | 0 | "\n" |
300 | 0 | " --all-normal force all output files to be non-progressive\n" |
301 | 0 | " --all-progressive force all output files to be progressive\n" |
302 | 0 | " --auto-mode select normal or progressive based on which produces\n" |
303 | 0 | " smaller output file\n" |
304 | 0 | #ifdef HAVE_ARITH_CODE |
305 | 0 | " --all-arith force all output files to use arithmetic coding\n" |
306 | 0 | " --all-huffman force all output files to use Huffman coding\n" |
307 | 0 | #endif |
308 | 0 | " --stdout send output to standard output (instead of a file)\n" |
309 | 0 | " --stdin read input from standard input (instead of a file)\n" |
310 | 0 | " --files-stdin Read names of files to process from stdin\n" |
311 | 0 | " --files-from=FILE Read names of files to process from a file\n" |
312 | 0 | " --nofix skip processing of input files if they contain any errors\n" |
313 | 0 | " --save-extra preserve extraneous data after the end of image\n" |
314 | 0 | "\n\n"); |
315 | 0 | } |
316 | | |
317 | | |
318 | | void print_version() |
319 | 0 | { |
320 | 0 | struct jpeg_error_mgr jerr; |
321 | |
|
322 | 0 | #ifdef __DATE__ |
323 | 0 | printf(PROGRAMNAME " v%s %s (%s)\n",VERSION, HOST_TYPE, __DATE__); |
324 | | #else |
325 | | printf(PROGRAMNAME " v%s %s\n", VERSION, HOST_TYPE); |
326 | | #endif |
327 | 0 | printf(COPYRIGHT "\n\n"); |
328 | 0 | printf("This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n" |
329 | 0 | "and you are welcome to redistribute it under certain conditions.\n" |
330 | 0 | "See the GNU General Public License for more details.\n\n"); |
331 | |
|
332 | 0 | if (!jpeg_std_error(&jerr)) |
333 | 0 | fatal("jpeg_std_error() failed"); |
334 | |
|
335 | 0 | printf("\nlibjpeg version: %s\n%s\n", |
336 | 0 | jerr.jpeg_message_table[JMSG_VERSION], |
337 | 0 | jerr.jpeg_message_table[JMSG_COPYRIGHT]); |
338 | 0 | } |
339 | | |
340 | | |
341 | | void parse_arguments(int argc, char **argv, char *dest_path, size_t dest_path_len) |
342 | 0 | { |
343 | 0 | int opt_index; |
344 | 0 | int i, c; |
345 | |
|
346 | 0 | while(1) { |
347 | 0 | opt_index=0; |
348 | 0 | if ((c = getopt_long(argc,argv,"d:hm:nstqvfVpProT:S:bw:", |
349 | 0 | long_options, &opt_index)) == -1) |
350 | 0 | break; |
351 | | |
352 | 0 | switch (c) { |
353 | | |
354 | 0 | case 'm': |
355 | 0 | { |
356 | 0 | int tmpvar; |
357 | |
|
358 | 0 | if (sscanf(optarg,"%d",&tmpvar) == 1) { |
359 | 0 | quality=tmpvar; |
360 | 0 | if (quality < 0) quality=0; |
361 | 0 | if (quality > 100) quality=100; |
362 | 0 | } |
363 | 0 | else |
364 | 0 | fatal("invalid argument for -m, --max"); |
365 | 0 | } |
366 | 0 | break; |
367 | | |
368 | 0 | case 'd': |
369 | 0 | if (realpath(optarg,dest_path)==NULL) |
370 | 0 | fatal("invalid destination directory: %s", optarg); |
371 | 0 | if (!is_directory(dest_path)) |
372 | 0 | fatal("destination not a directory: %s", dest_path); |
373 | 0 | strncatenate(dest_path, DIR_SEPARATOR_S, dest_path_len); |
374 | 0 | if (verbose_mode) |
375 | 0 | fprintf(stderr,"Destination directory: %s\n",dest_path); |
376 | 0 | dest=1; |
377 | 0 | break; |
378 | | |
379 | 0 | case 'v': |
380 | 0 | verbose_mode++; |
381 | 0 | break; |
382 | | |
383 | 0 | case 'h': |
384 | 0 | print_usage(); |
385 | 0 | exit(0); |
386 | 0 | break; |
387 | | |
388 | 0 | case 'q': |
389 | 0 | quiet_mode=1; |
390 | 0 | break; |
391 | | |
392 | 0 | case 'r': |
393 | 0 | retry_mode=1; |
394 | 0 | break; |
395 | | |
396 | 0 | case 't': |
397 | 0 | totals_mode=1; |
398 | 0 | break; |
399 | | |
400 | 0 | case 'n': |
401 | 0 | noaction=1; |
402 | 0 | break; |
403 | | |
404 | 0 | case 'f': |
405 | 0 | force=1; |
406 | 0 | break; |
407 | | |
408 | 0 | case 'b': |
409 | 0 | csv=1; |
410 | 0 | quiet_mode=1; |
411 | 0 | break; |
412 | | |
413 | 0 | case 'V': |
414 | 0 | print_version(); |
415 | 0 | exit(0); |
416 | 0 | break; |
417 | | |
418 | 0 | case 'o': |
419 | 0 | overwrite_mode=1; |
420 | 0 | break; |
421 | | |
422 | 0 | case 'p': |
423 | 0 | preserve_mode=1; |
424 | 0 | break; |
425 | | |
426 | 0 | case 'P': |
427 | 0 | preserve_perms=1; |
428 | 0 | break; |
429 | | |
430 | 0 | case 's': |
431 | 0 | save_exif=0; |
432 | 0 | save_iptc=0; |
433 | 0 | save_com=0; |
434 | 0 | save_icc=0; |
435 | 0 | save_xmp=0; |
436 | 0 | save_adobe=0; |
437 | 0 | save_jfif=0; |
438 | 0 | save_jfxx=0; |
439 | 0 | break; |
440 | | |
441 | 0 | case 'T': |
442 | 0 | { |
443 | 0 | double tmpvar; |
444 | 0 | if (sscanf(optarg,"%lf", &tmpvar) == 1) { |
445 | 0 | threshold=tmpvar; |
446 | 0 | if (threshold < 0) threshold=0; |
447 | 0 | if (threshold > 100) threshold=100; |
448 | 0 | } |
449 | 0 | else fatal("invalid argument for -T, --threshold"); |
450 | 0 | } |
451 | 0 | break; |
452 | | |
453 | 0 | case 'S': |
454 | 0 | { |
455 | 0 | unsigned int tmpvar; |
456 | 0 | if (sscanf(optarg,"%u", &tmpvar) == 1) { |
457 | 0 | if (tmpvar > 0 && tmpvar < 100 && |
458 | 0 | optarg[strlen(optarg)-1] == '%' ) { |
459 | 0 | target_size=-tmpvar; |
460 | 0 | } else { |
461 | 0 | target_size=tmpvar; |
462 | 0 | } |
463 | 0 | quality=100; |
464 | 0 | } |
465 | 0 | else fatal("invalid argument for -S, --size"); |
466 | 0 | } |
467 | 0 | break; |
468 | | |
469 | 0 | #ifdef PARALLEL_PROCESSING |
470 | 0 | case 'w': |
471 | 0 | { |
472 | 0 | int tmpvar; |
473 | 0 | if (sscanf(optarg, "%d", &tmpvar) == 1) { |
474 | 0 | if (tmpvar > 0 && tmpvar <= MAX_WORKERS) |
475 | 0 | max_workers = tmpvar; |
476 | 0 | } |
477 | 0 | else fatal("invalid argument for -w, --workers"); |
478 | 0 | } |
479 | 0 | break; |
480 | 0 | #endif |
481 | | |
482 | 0 | case 'F': |
483 | 0 | { |
484 | 0 | if (optarg[0] == '-' && optarg[1] == 0) { |
485 | 0 | files_stdin = 1; |
486 | 0 | break; |
487 | 0 | } |
488 | 0 | if (!is_file(optarg, NULL)) |
489 | 0 | fatal("argument for --files-from must be a file"); |
490 | 0 | if ((files_from = fopen(optarg, "r")) == NULL) |
491 | 0 | fatal("cannot open file: '%s'", optarg); |
492 | 0 | } |
493 | 0 | break; |
494 | | |
495 | 0 | case '?': |
496 | 0 | exit(1); |
497 | |
|
498 | 0 | } |
499 | 0 | } |
500 | | |
501 | | |
502 | | /* check for '-' option indicating input is from stdin... */ |
503 | 0 | i = optind; |
504 | 0 | while (argv[i]) { |
505 | 0 | if (argv[i][0]=='-' && argv[i][1]==0) |
506 | 0 | stdin_mode=1; |
507 | 0 | i++; |
508 | 0 | } |
509 | |
|
510 | 0 | if (stdin_mode) |
511 | 0 | stdout_mode=1; |
512 | 0 | if (files_stdin) |
513 | 0 | files_from = stdin; |
514 | 0 | if (stdin_mode && files_from == stdin) |
515 | 0 | fatal("cannot specify both --stdin and --files-stdin"); |
516 | 0 | if (all_normal && all_progressive) |
517 | 0 | fatal("cannot specify both --all-normal and --all-progressive"); |
518 | 0 | if (auto_mode && (all_normal || all_progressive)) |
519 | 0 | fatal("cannot specify --all-normal or --all-progressive if using --auto-mode"); |
520 | 0 | } |
521 | | |
522 | | |
523 | | void own_signal_handler(int a) |
524 | 0 | { |
525 | 0 | if (verbose_mode > 1) |
526 | 0 | fprintf(stderr,PROGRAMNAME ": died from signal: %d\n",a); |
527 | 0 | exit(2); |
528 | 0 | } |
529 | | |
530 | | |
531 | | void write_markers(struct jpeg_decompress_struct *dinfo, |
532 | | struct jpeg_compress_struct *cinfo) |
533 | 5.23k | { |
534 | 5.23k | jpeg_saved_marker_ptr mrk; |
535 | 5.23k | int write_marker; |
536 | 5.23k | const char *s_name; |
537 | | |
538 | 5.23k | if (!cinfo || !dinfo) |
539 | 0 | fatal("invalid call to write_markers()"); |
540 | | |
541 | 5.23k | mrk=dinfo->marker_list; |
542 | 6.93M | while (mrk) { |
543 | 6.92M | write_marker = 0; |
544 | 6.92M | s_name = jpeg_special_marker_name(mrk); |
545 | | |
546 | | /* Check for markers to save... */ |
547 | | |
548 | 6.92M | if (save_com && mrk->marker == JPEG_COM) |
549 | 73.0k | write_marker++; |
550 | | |
551 | 6.92M | if (save_iptc && !strncmp(s_name, "IPTC", 5)) |
552 | 29.0k | write_marker++; |
553 | | |
554 | 6.92M | if (save_exif && !strncmp(s_name, "Exif", 5)) |
555 | 5.97k | write_marker++; |
556 | | |
557 | 6.92M | if (save_icc && !strncmp(s_name, "ICC", 4)) |
558 | 17.5k | write_marker++; |
559 | | |
560 | 6.92M | if (save_xmp && !strncmp(s_name, "XMP", 4)) |
561 | 13.0k | write_marker++; |
562 | | |
563 | 6.92M | if (save_jfxx && !strncmp(s_name, "JFXX", 5)) |
564 | 0 | write_marker++; |
565 | | |
566 | 6.92M | if (save_adobe && !strncmp(s_name, "Adobe", 6)) |
567 | 0 | write_marker++; |
568 | | |
569 | 6.92M | if (strip_none) |
570 | 0 | write_marker++; |
571 | | |
572 | | |
573 | | /* libjpeg emits some markers automatically so skip these to avoid duplicates... */ |
574 | | |
575 | 6.92M | if (!strncmp(s_name, "JFIF", 5)) |
576 | 699k | write_marker=0; |
577 | | |
578 | | |
579 | 6.92M | if (verbose_mode > 2) |
580 | 0 | fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker), |
581 | 0 | s_name, (write_marker ? "Keep" : "Discard")); |
582 | 6.92M | if (write_marker) |
583 | 138k | jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length); |
584 | | |
585 | 6.92M | mrk=mrk->next; |
586 | 6.92M | } |
587 | 5.23k | } Line | Count | Source | 533 | 1.74k | { | 534 | 1.74k | jpeg_saved_marker_ptr mrk; | 535 | 1.74k | int write_marker; | 536 | 1.74k | const char *s_name; | 537 | | | 538 | 1.74k | if (!cinfo || !dinfo) | 539 | 0 | fatal("invalid call to write_markers()"); | 540 | | | 541 | 1.74k | mrk=dinfo->marker_list; | 542 | 2.31M | while (mrk) { | 543 | 2.30M | write_marker = 0; | 544 | 2.30M | s_name = jpeg_special_marker_name(mrk); | 545 | | | 546 | | /* Check for markers to save... */ | 547 | | | 548 | 2.30M | if (save_com && mrk->marker == JPEG_COM) | 549 | 24.3k | write_marker++; | 550 | | | 551 | 2.30M | if (save_iptc && !strncmp(s_name, "IPTC", 5)) | 552 | 9.67k | write_marker++; | 553 | | | 554 | 2.30M | if (save_exif && !strncmp(s_name, "Exif", 5)) | 555 | 1.99k | write_marker++; | 556 | | | 557 | 2.30M | if (save_icc && !strncmp(s_name, "ICC", 4)) | 558 | 5.85k | write_marker++; | 559 | | | 560 | 2.30M | if (save_xmp && !strncmp(s_name, "XMP", 4)) | 561 | 4.35k | write_marker++; | 562 | | | 563 | 2.30M | if (save_jfxx && !strncmp(s_name, "JFXX", 5)) | 564 | 0 | write_marker++; | 565 | | | 566 | 2.30M | if (save_adobe && !strncmp(s_name, "Adobe", 6)) | 567 | 0 | write_marker++; | 568 | | | 569 | 2.30M | if (strip_none) | 570 | 0 | write_marker++; | 571 | | | 572 | | | 573 | | /* libjpeg emits some markers automatically so skip these to avoid duplicates... */ | 574 | | | 575 | 2.30M | if (!strncmp(s_name, "JFIF", 5)) | 576 | 233k | write_marker=0; | 577 | | | 578 | | | 579 | 2.30M | if (verbose_mode > 2) | 580 | 0 | fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker), | 581 | 0 | s_name, (write_marker ? "Keep" : "Discard")); | 582 | 2.30M | if (write_marker) | 583 | 46.2k | jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length); | 584 | | | 585 | 2.30M | mrk=mrk->next; | 586 | 2.30M | } | 587 | 1.74k | } |
Line | Count | Source | 533 | 1.74k | { | 534 | 1.74k | jpeg_saved_marker_ptr mrk; | 535 | 1.74k | int write_marker; | 536 | 1.74k | const char *s_name; | 537 | | | 538 | 1.74k | if (!cinfo || !dinfo) | 539 | 0 | fatal("invalid call to write_markers()"); | 540 | | | 541 | 1.74k | mrk=dinfo->marker_list; | 542 | 2.31M | while (mrk) { | 543 | 2.30M | write_marker = 0; | 544 | 2.30M | s_name = jpeg_special_marker_name(mrk); | 545 | | | 546 | | /* Check for markers to save... */ | 547 | | | 548 | 2.30M | if (save_com && mrk->marker == JPEG_COM) | 549 | 24.3k | write_marker++; | 550 | | | 551 | 2.30M | if (save_iptc && !strncmp(s_name, "IPTC", 5)) | 552 | 9.67k | write_marker++; | 553 | | | 554 | 2.30M | if (save_exif && !strncmp(s_name, "Exif", 5)) | 555 | 1.99k | write_marker++; | 556 | | | 557 | 2.30M | if (save_icc && !strncmp(s_name, "ICC", 4)) | 558 | 5.85k | write_marker++; | 559 | | | 560 | 2.30M | if (save_xmp && !strncmp(s_name, "XMP", 4)) | 561 | 4.35k | write_marker++; | 562 | | | 563 | 2.30M | if (save_jfxx && !strncmp(s_name, "JFXX", 5)) | 564 | 0 | write_marker++; | 565 | | | 566 | 2.30M | if (save_adobe && !strncmp(s_name, "Adobe", 6)) | 567 | 0 | write_marker++; | 568 | | | 569 | 2.30M | if (strip_none) | 570 | 0 | write_marker++; | 571 | | | 572 | | | 573 | | /* libjpeg emits some markers automatically so skip these to avoid duplicates... */ | 574 | | | 575 | 2.30M | if (!strncmp(s_name, "JFIF", 5)) | 576 | 233k | write_marker=0; | 577 | | | 578 | | | 579 | 2.30M | if (verbose_mode > 2) | 580 | 0 | fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker), | 581 | 0 | s_name, (write_marker ? "Keep" : "Discard")); | 582 | 2.30M | if (write_marker) | 583 | 46.2k | jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length); | 584 | | | 585 | 2.30M | mrk=mrk->next; | 586 | 2.30M | } | 587 | 1.74k | } |
Line | Count | Source | 533 | 1.74k | { | 534 | 1.74k | jpeg_saved_marker_ptr mrk; | 535 | 1.74k | int write_marker; | 536 | 1.74k | const char *s_name; | 537 | | | 538 | 1.74k | if (!cinfo || !dinfo) | 539 | 0 | fatal("invalid call to write_markers()"); | 540 | | | 541 | 1.74k | mrk=dinfo->marker_list; | 542 | 2.31M | while (mrk) { | 543 | 2.30M | write_marker = 0; | 544 | 2.30M | s_name = jpeg_special_marker_name(mrk); | 545 | | | 546 | | /* Check for markers to save... */ | 547 | | | 548 | 2.30M | if (save_com && mrk->marker == JPEG_COM) | 549 | 24.3k | write_marker++; | 550 | | | 551 | 2.30M | if (save_iptc && !strncmp(s_name, "IPTC", 5)) | 552 | 9.67k | write_marker++; | 553 | | | 554 | 2.30M | if (save_exif && !strncmp(s_name, "Exif", 5)) | 555 | 1.99k | write_marker++; | 556 | | | 557 | 2.30M | if (save_icc && !strncmp(s_name, "ICC", 4)) | 558 | 5.85k | write_marker++; | 559 | | | 560 | 2.30M | if (save_xmp && !strncmp(s_name, "XMP", 4)) | 561 | 4.35k | write_marker++; | 562 | | | 563 | 2.30M | if (save_jfxx && !strncmp(s_name, "JFXX", 5)) | 564 | 0 | write_marker++; | 565 | | | 566 | 2.30M | if (save_adobe && !strncmp(s_name, "Adobe", 6)) | 567 | 0 | write_marker++; | 568 | | | 569 | 2.30M | if (strip_none) | 570 | 0 | write_marker++; | 571 | | | 572 | | | 573 | | /* libjpeg emits some markers automatically so skip these to avoid duplicates... */ | 574 | | | 575 | 2.30M | if (!strncmp(s_name, "JFIF", 5)) | 576 | 233k | write_marker=0; | 577 | | | 578 | | | 579 | 2.30M | if (verbose_mode > 2) | 580 | 0 | fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker), | 581 | 0 | s_name, (write_marker ? "Keep" : "Discard")); | 582 | 2.30M | if (write_marker) | 583 | 46.2k | jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length); | 584 | | | 585 | 2.30M | mrk=mrk->next; | 586 | 2.30M | } | 587 | 1.74k | } |
|
588 | | |
589 | | |
590 | | unsigned int parse_markers(const struct jpeg_decompress_struct *dinfo, |
591 | | char *str, unsigned int str_size, unsigned int *markers_total_size) |
592 | 1.51k | { |
593 | 1.51k | jpeg_saved_marker_ptr m; |
594 | 1.51k | unsigned int count = 0; |
595 | 1.51k | char *seen; |
596 | 1.51k | size_t marker_types = jpeg_special_marker_types_count(); |
597 | 1.51k | int com_seen = 0; |
598 | 1.51k | int special; |
599 | | |
600 | 1.51k | if ((seen = calloc(marker_types, 1)) == NULL) |
601 | 0 | fatal("not enough of memory"); |
602 | | |
603 | 1.51k | str[0] = 0; |
604 | 1.51k | *markers_total_size = 0; |
605 | | |
606 | 1.51k | m = dinfo->marker_list; |
607 | 2.23M | while (m) { |
608 | 2.23M | count++; |
609 | 2.23M | *markers_total_size += m->data_length; |
610 | | |
611 | 2.23M | if ((special = jpeg_special_marker(m)) >= 0) { |
612 | 1.88M | if (!seen[special]) |
613 | 11.1k | str_add_list(str, str_size, jpeg_special_marker_types[special].name, ","); |
614 | 1.88M | seen[special]++; |
615 | 1.88M | } |
616 | | |
617 | 2.23M | if (m->marker == JPEG_COM && !com_seen) { |
618 | 423 | str_add_list(str, str_size, "COM", ","); |
619 | 423 | com_seen = 1; |
620 | 423 | } |
621 | | |
622 | 2.23M | m = m->next; |
623 | 2.23M | } |
624 | | |
625 | 1.51k | free(seen); |
626 | | |
627 | 1.51k | return count; |
628 | 1.51k | } Line | Count | Source | 592 | 505 | { | 593 | 505 | jpeg_saved_marker_ptr m; | 594 | 505 | unsigned int count = 0; | 595 | 505 | char *seen; | 596 | 505 | size_t marker_types = jpeg_special_marker_types_count(); | 597 | 505 | int com_seen = 0; | 598 | 505 | int special; | 599 | | | 600 | 505 | if ((seen = calloc(marker_types, 1)) == NULL) | 601 | 0 | fatal("not enough of memory"); | 602 | | | 603 | 505 | str[0] = 0; | 604 | 505 | *markers_total_size = 0; | 605 | | | 606 | 505 | m = dinfo->marker_list; | 607 | 744k | while (m) { | 608 | 743k | count++; | 609 | 743k | *markers_total_size += m->data_length; | 610 | | | 611 | 743k | if ((special = jpeg_special_marker(m)) >= 0) { | 612 | 629k | if (!seen[special]) | 613 | 3.71k | str_add_list(str, str_size, jpeg_special_marker_types[special].name, ","); | 614 | 629k | seen[special]++; | 615 | 629k | } | 616 | | | 617 | 743k | if (m->marker == JPEG_COM && !com_seen) { | 618 | 141 | str_add_list(str, str_size, "COM", ","); | 619 | 141 | com_seen = 1; | 620 | 141 | } | 621 | | | 622 | 743k | m = m->next; | 623 | 743k | } | 624 | | | 625 | 505 | free(seen); | 626 | | | 627 | 505 | return count; | 628 | 505 | } |
Line | Count | Source | 592 | 505 | { | 593 | 505 | jpeg_saved_marker_ptr m; | 594 | 505 | unsigned int count = 0; | 595 | 505 | char *seen; | 596 | 505 | size_t marker_types = jpeg_special_marker_types_count(); | 597 | 505 | int com_seen = 0; | 598 | 505 | int special; | 599 | | | 600 | 505 | if ((seen = calloc(marker_types, 1)) == NULL) | 601 | 0 | fatal("not enough of memory"); | 602 | | | 603 | 505 | str[0] = 0; | 604 | 505 | *markers_total_size = 0; | 605 | | | 606 | 505 | m = dinfo->marker_list; | 607 | 744k | while (m) { | 608 | 743k | count++; | 609 | 743k | *markers_total_size += m->data_length; | 610 | | | 611 | 743k | if ((special = jpeg_special_marker(m)) >= 0) { | 612 | 629k | if (!seen[special]) | 613 | 3.71k | str_add_list(str, str_size, jpeg_special_marker_types[special].name, ","); | 614 | 629k | seen[special]++; | 615 | 629k | } | 616 | | | 617 | 743k | if (m->marker == JPEG_COM && !com_seen) { | 618 | 141 | str_add_list(str, str_size, "COM", ","); | 619 | 141 | com_seen = 1; | 620 | 141 | } | 621 | | | 622 | 743k | m = m->next; | 623 | 743k | } | 624 | | | 625 | 505 | free(seen); | 626 | | | 627 | 505 | return count; | 628 | 505 | } |
Line | Count | Source | 592 | 505 | { | 593 | 505 | jpeg_saved_marker_ptr m; | 594 | 505 | unsigned int count = 0; | 595 | 505 | char *seen; | 596 | 505 | size_t marker_types = jpeg_special_marker_types_count(); | 597 | 505 | int com_seen = 0; | 598 | 505 | int special; | 599 | | | 600 | 505 | if ((seen = calloc(marker_types, 1)) == NULL) | 601 | 0 | fatal("not enough of memory"); | 602 | | | 603 | 505 | str[0] = 0; | 604 | 505 | *markers_total_size = 0; | 605 | | | 606 | 505 | m = dinfo->marker_list; | 607 | 744k | while (m) { | 608 | 743k | count++; | 609 | 743k | *markers_total_size += m->data_length; | 610 | | | 611 | 743k | if ((special = jpeg_special_marker(m)) >= 0) { | 612 | 629k | if (!seen[special]) | 613 | 3.71k | str_add_list(str, str_size, jpeg_special_marker_types[special].name, ","); | 614 | 629k | seen[special]++; | 615 | 629k | } | 616 | | | 617 | 743k | if (m->marker == JPEG_COM && !com_seen) { | 618 | 141 | str_add_list(str, str_size, "COM", ","); | 619 | 141 | com_seen = 1; | 620 | 141 | } | 621 | | | 622 | 743k | m = m->next; | 623 | 743k | } | 624 | | | 625 | 505 | free(seen); | 626 | | | 627 | 505 | return count; | 628 | 505 | } |
|
629 | | |
630 | | |
631 | | |
632 | | int optimize(FILE *log_fh, const char *filename, const char *newname, |
633 | | const char *tmpdir, struct stat *file_stat, |
634 | | double *rate, double *saved) |
635 | 603 | { |
636 | 603 | FILE *infile = NULL; |
637 | 603 | FILE *outfile = NULL; |
638 | 603 | const char *outfname = NULL; |
639 | 603 | char tmpfilename[MAXPATHLEN]; |
640 | 603 | struct jpeg_decompress_struct dinfo; |
641 | 603 | struct jpeg_compress_struct cinfo; |
642 | 603 | struct my_error_mgr jcerr, jderr; |
643 | 603 | JSAMPARRAY buf = NULL; |
644 | | |
645 | 603 | unsigned char *outbuffer = NULL; |
646 | 603 | size_t outbuffersize = 0; |
647 | 603 | unsigned char *inbuffer = NULL; |
648 | 603 | size_t inbuffersize = 0; |
649 | 603 | size_t inbufferused = 0; |
650 | 603 | unsigned char *tmpbuffer = NULL; |
651 | 603 | size_t tmpbuffersize = 0; |
652 | 603 | unsigned char *extrabuffer = NULL; |
653 | 603 | size_t extrabuffersize = 0; |
654 | | |
655 | 603 | jvirt_barray_ptr *coef_arrays = NULL; |
656 | 603 | char marker_str[256]; |
657 | 603 | unsigned int marker_in_count, marker_in_size; |
658 | | |
659 | 603 | long in_image_size = 0; |
660 | 603 | long insize = 0, outsize = 0, lastsize = 0; |
661 | 603 | int oldquality, searchdone; |
662 | 603 | double ratio; |
663 | 603 | size_t last_retry_size = 0; |
664 | 603 | int retry_count = 0; |
665 | 603 | int retry = 0; |
666 | 603 | int res = -1; |
667 | | |
668 | 603 | jpeg_log_fh = log_fh; |
669 | | |
670 | | /* Initialize decompression object */ |
671 | 603 | dinfo.err = jpeg_std_error(&jderr.pub); |
672 | 603 | jpeg_create_decompress(&dinfo); |
673 | 603 | jderr.pub.error_exit=my_error_exit; |
674 | 603 | jderr.pub.output_message=my_output_message; |
675 | 603 | jderr.jump_set = 0; |
676 | | |
677 | | /* Initialize compression object */ |
678 | 603 | cinfo.err = jpeg_std_error(&jcerr.pub); |
679 | 603 | jpeg_create_compress(&cinfo); |
680 | 603 | jcerr.pub.error_exit=my_error_exit; |
681 | 603 | jcerr.pub.output_message=my_output_message; |
682 | 603 | jcerr.jump_set = 0; |
683 | | |
684 | 603 | if (rate) |
685 | 603 | *rate = 0.0; |
686 | 603 | if (saved) |
687 | 603 | *saved = 0.0; |
688 | | |
689 | 603 | if (filename) { |
690 | 603 | if ((infile = fopen(filename, "rb")) == NULL) { |
691 | 0 | warn("cannot open file: %s", filename); |
692 | 0 | res = 1; |
693 | 0 | goto exit_point; |
694 | 0 | } |
695 | 603 | } else { |
696 | 0 | infile = stdin; |
697 | 0 | set_filemode_binary(infile); |
698 | 0 | } |
699 | | |
700 | 806 | retry_point: |
701 | | |
702 | 806 | if (setjmp(jderr.setjmp_buffer)) { |
703 | | /* Error handler for decompress */ |
704 | 161 | abort_decompress: |
705 | 161 | jpeg_abort_decompress(&dinfo); |
706 | 161 | fclose(infile); |
707 | 161 | free_line_buf(&buf, dinfo.output_height); |
708 | 161 | if (!quiet_mode || csv) |
709 | 161 | fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n"); |
710 | 161 | jderr.jump_set=0; |
711 | 161 | res = 1; |
712 | 161 | goto exit_point; |
713 | 645 | } else { |
714 | 645 | jderr.jump_set=1; |
715 | 645 | } |
716 | | |
717 | | |
718 | | /* Prepare to decompress */ |
719 | 645 | if (!retry) { |
720 | 603 | if (!quiet_mode || csv) { |
721 | 603 | fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin")); |
722 | 603 | fflush(log_fh); |
723 | 603 | } |
724 | | |
725 | 603 | if (stdin_mode || stdout_mode) { |
726 | 0 | inbuffersize = IN_BUF_SIZE; |
727 | 603 | } else { |
728 | 603 | if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE) |
729 | 524 | inbuffersize = IN_BUF_SIZE; |
730 | 603 | } |
731 | 603 | if (inbuffer) |
732 | 0 | free(inbuffer); |
733 | 603 | if (!(inbuffer=calloc(inbuffersize, 1))) |
734 | 0 | fatal("not enough memory"); |
735 | 603 | } |
736 | 645 | global_error_counter=0; |
737 | 645 | jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); |
738 | 13.5k | for (int i = 0; i < 16; i++) { |
739 | 12.8k | jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff); |
740 | 12.8k | } |
741 | 645 | if (!retry) { |
742 | 603 | jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE); |
743 | 603 | } else { |
744 | 42 | if (retry == 1) |
745 | 203 | jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused); |
746 | 18.4E | else |
747 | 18.4E | jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize); |
748 | 42 | } |
749 | 645 | jpeg_read_header(&dinfo, TRUE); |
750 | | |
751 | | |
752 | | /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */ |
753 | 645 | marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str), |
754 | 645 | &marker_in_size); |
755 | | |
756 | 645 | if (!retry) { |
757 | 563 | if (verbose_mode > 1) { |
758 | 0 | fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n", |
759 | 0 | marker_in_count,marker_in_size); |
760 | 0 | fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman")); |
761 | 0 | } |
762 | | |
763 | 563 | if (!quiet_mode || csv) { |
764 | 563 | fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, |
765 | 563 | (int)dinfo.image_height,(int)dinfo.num_components*8, |
766 | 563 | (dinfo.progressive_mode?'P':'N')); |
767 | 563 | if (!csv) |
768 | 563 | fprintf(log_fh,"%s",marker_str); |
769 | 563 | fflush(log_fh); |
770 | 563 | } |
771 | 563 | } |
772 | | |
773 | | /* Decompress the image */ |
774 | 761 | if (quality >= 0 && retry != 1) { |
775 | 558 | jpeg_start_decompress(&dinfo); |
776 | | |
777 | | /* Allocate line buffer to store the decompressed image */ |
778 | 558 | if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW)))) |
779 | 0 | fatal("not enough memory"); |
780 | 1.70M | for (int i = 0; i < dinfo.output_height; i++) { |
781 | 1.70M | if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components, |
782 | 1.70M | sizeof(JSAMPLE)))) |
783 | 0 | fatal("not enough memory"); |
784 | 1.70M | } |
785 | | |
786 | 658k | while (dinfo.output_scanline < dinfo.output_height) { |
787 | 657k | jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], |
788 | 657k | dinfo.output_height-dinfo.output_scanline); |
789 | 657k | } |
790 | 558 | } else { |
791 | 87 | coef_arrays = jpeg_read_coefficients(&dinfo); |
792 | 87 | if (!coef_arrays) { |
793 | 0 | if (!quiet_mode) |
794 | 0 | fprintf(log_fh, " (failed to read coefficients) "); |
795 | 0 | goto abort_decompress; |
796 | 0 | } |
797 | 87 | } |
798 | 645 | if (!retry) { |
799 | 535 | in_image_size = inbufferused - dinfo.src->bytes_in_buffer; |
800 | 535 | if(verbose_mode > 2) |
801 | 0 | fprintf(log_fh, " (input image size: %lu (%lu))", |
802 | 0 | in_image_size, inbufferused); |
803 | 535 | if (stdin_mode) { |
804 | 0 | insize = in_image_size; |
805 | 535 | } else { |
806 | 535 | if ((insize = file_size(infile)) < 0) |
807 | 0 | fatal("failed to stat() input file"); |
808 | 535 | if (in_image_size > 0 && in_image_size < insize) { |
809 | 263 | if (!quiet_mode) |
810 | 263 | fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ", |
811 | 263 | insize - in_image_size); |
812 | 263 | if (nofix_mode) |
813 | 0 | global_error_counter++; |
814 | 263 | if (save_extra) { |
815 | 0 | extrabuffersize = insize - in_image_size; |
816 | 0 | if (extrabuffer) |
817 | 0 | free(extrabuffer); |
818 | 0 | if (!(extrabuffer = calloc(extrabuffersize, 1))) |
819 | 0 | fatal("not enough memory"); |
820 | 0 | if (fseek(infile, in_image_size, SEEK_SET)) |
821 | 0 | fatal("failed to seek input file"); |
822 | 0 | if (fread(extrabuffer, extrabuffersize, 1, infile) != 1) |
823 | 0 | fatal("failed to read inputfile"); |
824 | 0 | } |
825 | 263 | } |
826 | 535 | } |
827 | 535 | if (!quiet_mode) { |
828 | 535 | fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] ")); |
829 | 535 | fflush(log_fh); |
830 | 535 | } |
831 | | |
832 | 535 | if (nofix_mode && global_error_counter != 0) { |
833 | | /* Skip files containing any errors (or warnings) */ |
834 | 0 | goto abort_decompress; |
835 | 0 | } |
836 | | |
837 | 535 | if (dest && !noaction) { |
838 | 0 | if (file_exists(newname) && !overwrite_mode) { |
839 | 0 | if (!quiet_mode) |
840 | 0 | fprintf(log_fh, " (target file already exists) "); |
841 | 0 | goto abort_decompress; |
842 | 0 | } |
843 | 0 | } |
844 | 535 | } |
845 | | |
846 | | |
847 | | /* Prepare to compress... */ |
848 | 645 | if (setjmp(jcerr.setjmp_buffer)) { |
849 | | /* Error handler for compress failures */ |
850 | 104 | if (!quiet_mode) |
851 | 104 | fprintf(log_fh," [Compress ERROR: %s]\n",last_error); |
852 | 104 | jpeg_abort_compress(&cinfo); |
853 | 104 | jpeg_abort_decompress(&dinfo); |
854 | 104 | fclose(infile); |
855 | 104 | free_line_buf(&buf, dinfo.output_height); |
856 | 104 | jcerr.jump_set=0; |
857 | 104 | res = 2; |
858 | 104 | goto exit_point; |
859 | 541 | } else { |
860 | 541 | jcerr.jump_set=1; |
861 | 541 | } |
862 | | |
863 | 541 | lastsize = 0; |
864 | 541 | searchdone = 0; |
865 | 541 | if (!retry) { |
866 | 535 | oldquality = 200; |
867 | 535 | if (target_size != 0) { |
868 | | /* Always start with quality 100 if -S option specified... */ |
869 | 535 | quality = 100; |
870 | 535 | } |
871 | 535 | } |
872 | | |
873 | | |
874 | 2.71k | binary_search_loop: |
875 | | |
876 | | /* Allocate memory buffer that should be large enough to store the output JPEG... */ |
877 | 2.71k | if (outbuffer) |
878 | 2.18k | free(outbuffer); |
879 | 2.71k | outbuffersize = insize + 32768; |
880 | 2.71k | if (!(outbuffer=calloc(outbuffersize, 1))) |
881 | 0 | fatal("not enough memory"); |
882 | | |
883 | | /* setup custom "destination manager" for libjpeg to write to our buffer */ |
884 | 2.71k | jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); |
885 | | |
886 | | |
887 | 2.71k | if (quality >= 0 && retry != 1) { |
888 | | /* Lossy "optimization" ... */ |
889 | | |
890 | 2.51k | cinfo.in_color_space=dinfo.out_color_space; |
891 | 2.51k | cinfo.input_components=dinfo.output_components; |
892 | 2.51k | cinfo.image_width=dinfo.image_width; |
893 | 2.51k | cinfo.image_height=dinfo.image_height; |
894 | 2.51k | jpeg_set_defaults(&cinfo); |
895 | 2.51k | jpeg_set_quality(&cinfo,quality,TRUE); |
896 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE |
897 | 742 | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) |
898 | 742 | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); |
899 | | #endif |
900 | 2.51k | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { |
901 | | /* Explicitly disable progressive mode. */ |
902 | 1.92k | cinfo.scan_info = NULL; |
903 | 1.92k | cinfo.num_scans = 0; |
904 | 1.92k | } else if (all_progressive || dinfo.progressive_mode) { |
905 | | /* Enable progressive mode. */ |
906 | 595 | jpeg_simple_progression(&cinfo); |
907 | 595 | } |
908 | 2.51k | cinfo.optimize_coding = TRUE; |
909 | 2.51k | #ifdef HAVE_ARITH_CODE |
910 | 2.51k | if (arith_mode >= 0) |
911 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); |
912 | 2.51k | #endif |
913 | 2.51k | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { |
914 | 1.34k | cinfo.write_JFIF_header = TRUE; |
915 | 1.34k | } else { |
916 | 1.17k | cinfo.write_JFIF_header = FALSE; |
917 | 1.17k | } |
918 | 2.51k | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { |
919 | | /* If outputting Adobe marker, don't write JFIF marker... */ |
920 | 0 | cinfo.write_JFIF_header = FALSE; |
921 | 0 | } |
922 | | |
923 | 2.51k | jpeg_start_compress(&cinfo,TRUE); |
924 | | |
925 | | /* Write markers */ |
926 | 2.51k | write_markers(&dinfo,&cinfo); |
927 | | |
928 | | /* Write image */ |
929 | 5.02k | while (cinfo.next_scanline < cinfo.image_height) { |
930 | 2.51k | jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], |
931 | 2.51k | dinfo.output_height); |
932 | 2.51k | } |
933 | | |
934 | 2.51k | } else { |
935 | | /* Lossless optimization ... */ |
936 | | |
937 | 200 | jpeg_copy_critical_parameters(&dinfo, &cinfo); |
938 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE |
939 | 72 | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) |
940 | 61 | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); |
941 | | #endif |
942 | 200 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { |
943 | | /* Explicitly disable progressive mode. */ |
944 | 136 | cinfo.scan_info = NULL; |
945 | 136 | cinfo.num_scans = 0; |
946 | 136 | } else if (all_progressive || dinfo.progressive_mode) { |
947 | | /* Enable progressive mode. */ |
948 | 39 | jpeg_simple_progression(&cinfo); |
949 | 39 | } |
950 | 200 | cinfo.optimize_coding = TRUE; |
951 | 200 | #ifdef HAVE_ARITH_CODE |
952 | 200 | if (arith_mode >= 0) |
953 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); |
954 | 200 | #endif |
955 | 200 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { |
956 | 88 | cinfo.write_JFIF_header = TRUE; |
957 | 112 | } else { |
958 | 112 | cinfo.write_JFIF_header = FALSE; |
959 | 112 | } |
960 | 200 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { |
961 | | /* If outputting Adobe marker, don't write JFIF marker... */ |
962 | 0 | cinfo.write_JFIF_header = FALSE; |
963 | 0 | } |
964 | | |
965 | | /* Write image */ |
966 | 200 | jpeg_write_coefficients(&cinfo, coef_arrays); |
967 | | |
968 | | /* Write markers */ |
969 | 200 | write_markers(&dinfo,&cinfo); |
970 | | |
971 | 200 | } |
972 | | |
973 | 2.71k | jpeg_finish_compress(&cinfo); |
974 | 2.71k | outsize = outbuffersize + extrabuffersize; |
975 | 2.71k | if (verbose_mode > 2) |
976 | 0 | fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize); |
977 | | |
978 | 2.71k | if (target_size != 0 && !retry) { |
979 | | /* Perform (binary) search to try to reach target file size... */ |
980 | | |
981 | 2.51k | long osize = outsize/1024; |
982 | 2.51k | long isize = insize/1024; |
983 | 2.51k | long tsize = target_size; |
984 | | |
985 | 2.51k | if (verbose_mode > 1) |
986 | 0 | fprintf(log_fh, "(size=%ld)",outsize); |
987 | 2.51k | if (tsize < 0) { |
988 | 2.51k | tsize=((-target_size)*insize/100)/1024; |
989 | 2.51k | if (tsize < 1) |
990 | 586 | tsize = 1; |
991 | 2.51k | } |
992 | | |
993 | 2.51k | if (osize == tsize || searchdone || tsize > isize) { |
994 | 570 | if (searchdone < 42 && lastsize > 0) { |
995 | 458 | if (labs(osize-tsize) > labs(lastsize-tsize)) { |
996 | 40 | if (verbose_mode) |
997 | 0 | fprintf(log_fh,"(revert to %d)",oldquality); |
998 | 40 | searchdone = 42; |
999 | 40 | quality = oldquality; |
1000 | 40 | goto binary_search_loop; |
1001 | 40 | } |
1002 | 458 | } |
1003 | 530 | if (verbose_mode) |
1004 | 0 | fprintf(log_fh," "); |
1005 | | |
1006 | 1.94k | } else { |
1007 | 1.94k | int newquality; |
1008 | 1.94k | double dif = abs(oldquality-quality) / 2.0; |
1009 | | |
1010 | 1.94k | if (osize > tsize) |
1011 | 1.34k | newquality = quality - dif; |
1012 | 596 | else |
1013 | 596 | newquality = quality + dif + 0.5; |
1014 | | |
1015 | 1.94k | if (dif < 1.0) |
1016 | 61 | searchdone = 1; |
1017 | 1.94k | if (newquality < 0) { |
1018 | 128 | newquality = 0; |
1019 | 128 | searchdone = 1; |
1020 | 128 | } |
1021 | 1.94k | if (newquality > 100) { |
1022 | 156 | newquality = 100; |
1023 | 156 | searchdone = 1; |
1024 | 156 | } |
1025 | | |
1026 | 1.94k | oldquality = quality; |
1027 | 1.94k | quality = newquality; |
1028 | 1.94k | lastsize = osize; |
1029 | 1.94k | if (verbose_mode) |
1030 | 0 | fprintf(log_fh,"(try %d)",quality); |
1031 | 1.94k | goto binary_search_loop; |
1032 | 1.94k | } |
1033 | 2.51k | } |
1034 | | |
1035 | 735 | jpeg_finish_decompress(&dinfo); |
1036 | 735 | free_line_buf(&buf, dinfo.output_height); |
1037 | | |
1038 | 735 | if (retry_mode) { |
1039 | 0 | if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) { |
1040 | | /* Retry compression until output file stops getting smaller |
1041 | | or we hit max limit of iterations (10)... */ |
1042 | 0 | if (retry_count == 0) |
1043 | 0 | last_retry_size = outsize + 1; |
1044 | 0 | if (++retry_count < 10 && outsize < last_retry_size) { |
1045 | 0 | if (tmpbuffer) |
1046 | 0 | free(tmpbuffer); |
1047 | 0 | tmpbuffer = outbuffer; |
1048 | 0 | tmpbuffersize = outbuffersize; |
1049 | 0 | outbuffer = NULL; |
1050 | 0 | last_retry_size = outsize; |
1051 | 0 | retry = 2; |
1052 | 0 | if (verbose_mode) |
1053 | 0 | fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize); |
1054 | 0 | goto retry_point; |
1055 | 0 | } |
1056 | 0 | } |
1057 | 0 | if (retry == 2) { |
1058 | 0 | if (verbose_mode) |
1059 | 0 | fprintf(log_fh, "(retry done: %lu) ", outsize); |
1060 | 0 | if (outsize > last_retry_size) { |
1061 | 0 | if (outbuffer) |
1062 | 0 | free(outbuffer); |
1063 | 0 | outbuffer = tmpbuffer; |
1064 | 0 | outbuffersize = tmpbuffersize; |
1065 | 0 | outsize = outbuffersize + extrabuffersize; |
1066 | 0 | tmpbuffer = NULL; |
1067 | 0 | } |
1068 | 0 | } |
1069 | 0 | } |
1070 | | |
1071 | | /* If auto_mode, try both progressive and non-progressive... */ |
1072 | 735 | if (auto_mode) { |
1073 | 0 | int newmode = (dinfo.progressive_mode ? 0 : 1); |
1074 | 0 | if (retry != 3) { |
1075 | 0 | if (newmode) |
1076 | 0 | all_progressive = 1; |
1077 | 0 | else |
1078 | 0 | all_normal = 1; |
1079 | 0 | if (tmpbuffer) |
1080 | 0 | free(tmpbuffer); |
1081 | 0 | tmpbuffer = outbuffer; |
1082 | 0 | tmpbuffersize = outbuffersize; |
1083 | 0 | outbuffer = NULL; |
1084 | 0 | last_retry_size = outsize; |
1085 | 0 | retry = 3; |
1086 | 0 | if (verbose_mode) |
1087 | 0 | fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal")); |
1088 | 0 | goto retry_point; |
1089 | 0 | } else { |
1090 | 0 | if (verbose_mode > 1) |
1091 | 0 | fprintf(log_fh, "(automode done: %lu) ", outsize); |
1092 | 0 | auto_mode = 0; |
1093 | 0 | if (outsize > last_retry_size) { |
1094 | 0 | if (verbose_mode) |
1095 | 0 | fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal")); |
1096 | 0 | all_progressive = 0; |
1097 | 0 | all_normal = 0; |
1098 | 0 | if (outbuffer) |
1099 | 0 | free(outbuffer); |
1100 | 0 | outbuffer = tmpbuffer; |
1101 | 0 | outbuffersize = tmpbuffersize; |
1102 | 0 | outsize = outbuffersize + extrabuffersize; |
1103 | 0 | tmpbuffer = NULL; |
1104 | 0 | } |
1105 | 0 | } |
1106 | 0 | } |
1107 | | |
1108 | | /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */ |
1109 | 735 | if (quality >= 0 && outsize >= insize && retry != 1) { |
1110 | 203 | retry = 1; |
1111 | 203 | if (verbose_mode) |
1112 | 0 | fprintf(log_fh, "(retry w/lossless) "); |
1113 | 203 | goto retry_point; |
1114 | 203 | } |
1115 | | |
1116 | 532 | fclose(infile); |
1117 | | |
1118 | 532 | ratio = (insize - outsize) * 100.0 / insize; |
1119 | 532 | if (!quiet_mode || csv) |
1120 | 338 | fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); |
1121 | 532 | if (rate) { |
1122 | 338 | *rate = (ratio < 0 ? 0.0 : ratio); |
1123 | 338 | } |
1124 | | |
1125 | 532 | if ((outsize < insize && ratio >= threshold) || force) { |
1126 | 244 | if (saved) { |
1127 | 244 | *saved = (insize - outsize) / 1024.0; |
1128 | 244 | } |
1129 | 244 | if (!quiet_mode || csv) |
1130 | 244 | fprintf(log_fh,csv ? "optimized\n" : "optimized.\n"); |
1131 | 244 | if (noaction) { |
1132 | 0 | res = 0; |
1133 | 0 | goto exit_point; |
1134 | 0 | } |
1135 | | |
1136 | 244 | if (stdout_mode) { |
1137 | 0 | outfname=NULL; |
1138 | 0 | set_filemode_binary(stdout); |
1139 | 0 | if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) |
1140 | 0 | fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename)); |
1141 | 244 | } else { |
1142 | 244 | if (preserve_perms && !dest) { |
1143 | | /* make backup of the original file */ |
1144 | 0 | int newlen = snprintf(tmpfilename, sizeof(tmpfilename), |
1145 | 0 | "%s.jpegoptim.bak", newname); |
1146 | 0 | if (newlen >= sizeof(tmpfilename)) |
1147 | 0 | fatal("temp filename too long: %s", tmpfilename); |
1148 | |
|
1149 | 0 | if (verbose_mode > 1) |
1150 | 0 | fprintf(log_fh,"%s, creating backup as: %s\n", |
1151 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); |
1152 | 0 | if (file_exists(tmpfilename)) |
1153 | 0 | fatal("%s, backup file already exists: %s", |
1154 | 0 | (stdin_mode ?" stdin" : filename), tmpfilename); |
1155 | 0 | if (copy_file(newname,tmpfilename)) |
1156 | 0 | fatal("%s, failed to create backup: %s", |
1157 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); |
1158 | 0 | if ((outfile=create_file(newname))==NULL) |
1159 | 0 | fatal("%s, error opening output file: %s", |
1160 | 0 | (stdin_mode ? "stdin" : filename), newname); |
1161 | 0 | outfname = newname; |
1162 | 244 | } else { |
1163 | 244 | if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename)))) |
1164 | 0 | fatal("error creating temporary file: %s", tmpfilename); |
1165 | 244 | outfname = tmpfilename; |
1166 | 244 | } |
1167 | | |
1168 | 244 | if (verbose_mode > 1) |
1169 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", |
1170 | 0 | (long unsigned int)outbuffersize, outfname); |
1171 | 244 | if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1) |
1172 | 0 | fatal("write failed to file: %s", outfname); |
1173 | 244 | if (save_extra && extrabuffersize > 0) { |
1174 | 0 | if (verbose_mode > 1) |
1175 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname); |
1176 | 0 | if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1) |
1177 | 0 | fatal("write failed to file: %s", outfname); |
1178 | 0 | } |
1179 | 244 | fclose(outfile); |
1180 | 244 | } |
1181 | | |
1182 | 244 | if (outfname) { |
1183 | 244 | if (preserve_mode) { |
1184 | | /* preserve file modification time */ |
1185 | 0 | if (verbose_mode > 1) |
1186 | 0 | fprintf(log_fh,"set file modification time same as in original: %s\n", |
1187 | 0 | outfname); |
1188 | 0 | #if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM) |
1189 | 0 | struct timespec time_save[2]; |
1190 | 0 | time_save[0].tv_sec = 0; |
1191 | 0 | time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */ |
1192 | 0 | time_save[1] = file_stat->st_mtim; |
1193 | 0 | if (utimensat(AT_FDCWD,outfname,time_save,0) != 0) |
1194 | 0 | warn("failed to reset output file time/date"); |
1195 | | #else |
1196 | | struct utimbuf time_save; |
1197 | | time_save.actime=file_stat->st_atime; |
1198 | | time_save.modtime=file_stat->st_mtime; |
1199 | | if (utime(outfname,&time_save) != 0) |
1200 | | warn("failed to reset output file time/date"); |
1201 | | #endif |
1202 | 0 | } |
1203 | | |
1204 | 244 | if (preserve_perms && !dest) { |
1205 | | /* original file was already replaced, remove backup... */ |
1206 | 0 | if (verbose_mode > 1) |
1207 | 0 | fprintf(log_fh,"removing backup file: %s\n", tmpfilename); |
1208 | 0 | if (delete_file(tmpfilename)) |
1209 | 0 | warn("failed to remove backup file: %s", tmpfilename); |
1210 | 244 | } else { |
1211 | | /* make temp file to be the original file... */ |
1212 | | |
1213 | | /* preserve file mode */ |
1214 | 244 | if (chmod(outfname,(file_stat->st_mode & 0777)) != 0) |
1215 | 0 | warn("failed to set output file mode"); |
1216 | | |
1217 | | /* preserve file group (and owner if run by root) */ |
1218 | 244 | if (chown(outfname, |
1219 | 244 | (geteuid()==0 ? file_stat->st_uid : -1), |
1220 | 244 | file_stat->st_gid) != 0) |
1221 | 0 | warn("failed to reset output file group/owner"); |
1222 | | |
1223 | 244 | if (verbose_mode > 1) |
1224 | 0 | fprintf(log_fh,"renaming: %s to %s\n", outfname, newname); |
1225 | 244 | if (rename_file(outfname, newname)) |
1226 | 0 | fatal("cannot rename temp file"); |
1227 | 244 | } |
1228 | 244 | } |
1229 | 288 | } else { |
1230 | 288 | if (!quiet_mode || csv) |
1231 | 94 | fprintf(log_fh,csv ? "skipped\n" : "skipped.\n"); |
1232 | 288 | if (stdout_mode) { |
1233 | 0 | set_filemode_binary(stdout); |
1234 | 0 | if (fwrite(inbuffer, in_image_size, 1, stdout) != 1) |
1235 | 0 | fatal("%s, write failed to stdout", |
1236 | 0 | (stdin_mode ? "stdin" : filename)); |
1237 | 0 | } |
1238 | 288 | } |
1239 | | |
1240 | 532 | res = 0; |
1241 | | |
1242 | 603 | exit_point: |
1243 | 603 | if (inbuffer) |
1244 | 603 | free(inbuffer); |
1245 | 603 | if (outbuffer) |
1246 | 535 | free(outbuffer); |
1247 | 603 | if (tmpbuffer) |
1248 | 0 | free(tmpbuffer); |
1249 | 603 | if (extrabuffer) |
1250 | 0 | free(extrabuffer); |
1251 | 603 | jpeg_destroy_compress(&cinfo); |
1252 | 603 | jpeg_destroy_decompress(&dinfo); |
1253 | | |
1254 | 603 | return res; |
1255 | 532 | } Line | Count | Source | 635 | 209 | { | 636 | 209 | FILE *infile = NULL; | 637 | 209 | FILE *outfile = NULL; | 638 | 209 | const char *outfname = NULL; | 639 | 209 | char tmpfilename[MAXPATHLEN]; | 640 | 209 | struct jpeg_decompress_struct dinfo; | 641 | 209 | struct jpeg_compress_struct cinfo; | 642 | 209 | struct my_error_mgr jcerr, jderr; | 643 | 209 | JSAMPARRAY buf = NULL; | 644 | | | 645 | 209 | unsigned char *outbuffer = NULL; | 646 | 209 | size_t outbuffersize = 0; | 647 | 209 | unsigned char *inbuffer = NULL; | 648 | 209 | size_t inbuffersize = 0; | 649 | 209 | size_t inbufferused = 0; | 650 | 209 | unsigned char *tmpbuffer = NULL; | 651 | 209 | size_t tmpbuffersize = 0; | 652 | 209 | unsigned char *extrabuffer = NULL; | 653 | 209 | size_t extrabuffersize = 0; | 654 | | | 655 | 209 | jvirt_barray_ptr *coef_arrays = NULL; | 656 | 209 | char marker_str[256]; | 657 | 209 | unsigned int marker_in_count, marker_in_size; | 658 | | | 659 | 209 | long in_image_size = 0; | 660 | 209 | long insize = 0, outsize = 0, lastsize = 0; | 661 | 209 | int oldquality, searchdone; | 662 | 209 | double ratio; | 663 | 209 | size_t last_retry_size = 0; | 664 | 209 | int retry_count = 0; | 665 | 209 | int retry = 0; | 666 | 209 | int res = -1; | 667 | | | 668 | 209 | jpeg_log_fh = log_fh; | 669 | | | 670 | | /* Initialize decompression object */ | 671 | 209 | dinfo.err = jpeg_std_error(&jderr.pub); | 672 | 209 | jpeg_create_decompress(&dinfo); | 673 | 209 | jderr.pub.error_exit=my_error_exit; | 674 | 209 | jderr.pub.output_message=my_output_message; | 675 | 209 | jderr.jump_set = 0; | 676 | | | 677 | | /* Initialize compression object */ | 678 | 209 | cinfo.err = jpeg_std_error(&jcerr.pub); | 679 | 209 | jpeg_create_compress(&cinfo); | 680 | 209 | jcerr.pub.error_exit=my_error_exit; | 681 | 209 | jcerr.pub.output_message=my_output_message; | 682 | 209 | jcerr.jump_set = 0; | 683 | | | 684 | 209 | if (rate) | 685 | 209 | *rate = 0.0; | 686 | 209 | if (saved) | 687 | 209 | *saved = 0.0; | 688 | | | 689 | 209 | if (filename) { | 690 | 209 | if ((infile = fopen(filename, "rb")) == NULL) { | 691 | 0 | warn("cannot open file: %s", filename); | 692 | 0 | res = 1; | 693 | 0 | goto exit_point; | 694 | 0 | } | 695 | 209 | } else { | 696 | 0 | infile = stdin; | 697 | 0 | set_filemode_binary(infile); | 698 | 0 | } | 699 | | | 700 | 274 | retry_point: | 701 | | | 702 | 274 | if (setjmp(jderr.setjmp_buffer)) { | 703 | | /* Error handler for decompress */ | 704 | 61 | abort_decompress: | 705 | 61 | jpeg_abort_decompress(&dinfo); | 706 | 61 | fclose(infile); | 707 | 61 | free_line_buf(&buf, dinfo.output_height); | 708 | 61 | if (!quiet_mode || csv) | 709 | 61 | fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n"); | 710 | 61 | jderr.jump_set=0; | 711 | 61 | res = 1; | 712 | 61 | goto exit_point; | 713 | 213 | } else { | 714 | 213 | jderr.jump_set=1; | 715 | 213 | } | 716 | | | 717 | | | 718 | | /* Prepare to decompress */ | 719 | 213 | if (!retry) { | 720 | 209 | if (!quiet_mode || csv) { | 721 | 209 | fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin")); | 722 | 209 | fflush(log_fh); | 723 | 209 | } | 724 | | | 725 | 209 | if (stdin_mode || stdout_mode) { | 726 | 0 | inbuffersize = IN_BUF_SIZE; | 727 | 209 | } else { | 728 | 209 | if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE) | 729 | 176 | inbuffersize = IN_BUF_SIZE; | 730 | 209 | } | 731 | 209 | if (inbuffer) | 732 | 0 | free(inbuffer); | 733 | 209 | if (!(inbuffer=calloc(inbuffersize, 1))) | 734 | 0 | fatal("not enough memory"); | 735 | 209 | } | 736 | 213 | global_error_counter=0; | 737 | 213 | jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); | 738 | 4.59k | for (int i = 0; i < 16; i++) { | 739 | 4.38k | jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff); | 740 | 4.38k | } | 741 | 213 | if (!retry) { | 742 | 209 | jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE); | 743 | 209 | } else { | 744 | 4 | if (retry == 1) | 745 | 65 | jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused); | 746 | 18.4E | else | 747 | 18.4E | jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize); | 748 | 4 | } | 749 | 213 | jpeg_read_header(&dinfo, TRUE); | 750 | | | 751 | | | 752 | | /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */ | 753 | 213 | marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str), | 754 | 213 | &marker_in_size); | 755 | | | 756 | 213 | if (!retry) { | 757 | 196 | if (verbose_mode > 1) { | 758 | 0 | fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n", | 759 | 0 | marker_in_count,marker_in_size); | 760 | 0 | fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman")); | 761 | 0 | } | 762 | | | 763 | 196 | if (!quiet_mode || csv) { | 764 | 196 | fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, | 765 | 196 | (int)dinfo.image_height,(int)dinfo.num_components*8, | 766 | 196 | (dinfo.progressive_mode?'P':'N')); | 767 | 196 | if (!csv) | 768 | 196 | fprintf(log_fh,"%s",marker_str); | 769 | 196 | fflush(log_fh); | 770 | 196 | } | 771 | 196 | } | 772 | | | 773 | | /* Decompress the image */ | 774 | 259 | if (quality >= 0 && retry != 1) { | 775 | 194 | jpeg_start_decompress(&dinfo); | 776 | | | 777 | | /* Allocate line buffer to store the decompressed image */ | 778 | 194 | if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW)))) | 779 | 0 | fatal("not enough memory"); | 780 | 661k | for (int i = 0; i < dinfo.output_height; i++) { | 781 | 661k | if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components, | 782 | 661k | sizeof(JSAMPLE)))) | 783 | 0 | fatal("not enough memory"); | 784 | 661k | } | 785 | | | 786 | 241k | while (dinfo.output_scanline < dinfo.output_height) { | 787 | 240k | jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], | 788 | 240k | dinfo.output_height-dinfo.output_scanline); | 789 | 240k | } | 790 | 194 | } else { | 791 | 19 | coef_arrays = jpeg_read_coefficients(&dinfo); | 792 | 19 | if (!coef_arrays) { | 793 | 0 | if (!quiet_mode) | 794 | 0 | fprintf(log_fh, " (failed to read coefficients) "); | 795 | 0 | goto abort_decompress; | 796 | 0 | } | 797 | 19 | } | 798 | 213 | if (!retry) { | 799 | 186 | in_image_size = inbufferused - dinfo.src->bytes_in_buffer; | 800 | 186 | if(verbose_mode > 2) | 801 | 0 | fprintf(log_fh, " (input image size: %lu (%lu))", | 802 | 0 | in_image_size, inbufferused); | 803 | 186 | if (stdin_mode) { | 804 | 0 | insize = in_image_size; | 805 | 186 | } else { | 806 | 186 | if ((insize = file_size(infile)) < 0) | 807 | 0 | fatal("failed to stat() input file"); | 808 | 186 | if (in_image_size > 0 && in_image_size < insize) { | 809 | 106 | if (!quiet_mode) | 810 | 106 | fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ", | 811 | 106 | insize - in_image_size); | 812 | 106 | if (nofix_mode) | 813 | 0 | global_error_counter++; | 814 | 106 | if (save_extra) { | 815 | 0 | extrabuffersize = insize - in_image_size; | 816 | 0 | if (extrabuffer) | 817 | 0 | free(extrabuffer); | 818 | 0 | if (!(extrabuffer = calloc(extrabuffersize, 1))) | 819 | 0 | fatal("not enough memory"); | 820 | 0 | if (fseek(infile, in_image_size, SEEK_SET)) | 821 | 0 | fatal("failed to seek input file"); | 822 | 0 | if (fread(extrabuffer, extrabuffersize, 1, infile) != 1) | 823 | 0 | fatal("failed to read inputfile"); | 824 | 0 | } | 825 | 106 | } | 826 | 186 | } | 827 | 186 | if (!quiet_mode) { | 828 | 186 | fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] ")); | 829 | 186 | fflush(log_fh); | 830 | 186 | } | 831 | | | 832 | 186 | if (nofix_mode && global_error_counter != 0) { | 833 | | /* Skip files containing any errors (or warnings) */ | 834 | 0 | goto abort_decompress; | 835 | 0 | } | 836 | | | 837 | 186 | if (dest && !noaction) { | 838 | 0 | if (file_exists(newname) && !overwrite_mode) { | 839 | 0 | if (!quiet_mode) | 840 | 0 | fprintf(log_fh, " (target file already exists) "); | 841 | 0 | goto abort_decompress; | 842 | 0 | } | 843 | 0 | } | 844 | 186 | } | 845 | | | 846 | | | 847 | | /* Prepare to compress... */ | 848 | 213 | if (setjmp(jcerr.setjmp_buffer)) { | 849 | | /* Error handler for compress failures */ | 850 | 31 | if (!quiet_mode) | 851 | 31 | fprintf(log_fh," [Compress ERROR: %s]\n",last_error); | 852 | 31 | jpeg_abort_compress(&cinfo); | 853 | 31 | jpeg_abort_decompress(&dinfo); | 854 | 31 | fclose(infile); | 855 | 31 | free_line_buf(&buf, dinfo.output_height); | 856 | 31 | jcerr.jump_set=0; | 857 | 31 | res = 2; | 858 | 31 | goto exit_point; | 859 | 182 | } else { | 860 | 182 | jcerr.jump_set=1; | 861 | 182 | } | 862 | | | 863 | 182 | lastsize = 0; | 864 | 182 | searchdone = 0; | 865 | 186 | if (!retry) { | 866 | 186 | oldquality = 200; | 867 | 186 | if (target_size != 0) { | 868 | | /* Always start with quality 100 if -S option specified... */ | 869 | 186 | quality = 100; | 870 | 186 | } | 871 | 186 | } | 872 | | | 873 | | | 874 | 951 | binary_search_loop: | 875 | | | 876 | | /* Allocate memory buffer that should be large enough to store the output JPEG... */ | 877 | 951 | if (outbuffer) | 878 | 765 | free(outbuffer); | 879 | 951 | outbuffersize = insize + 32768; | 880 | 951 | if (!(outbuffer=calloc(outbuffersize, 1))) | 881 | 0 | fatal("not enough memory"); | 882 | | | 883 | | /* setup custom "destination manager" for libjpeg to write to our buffer */ | 884 | 951 | jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); | 885 | | | 886 | | | 887 | 951 | if (quality >= 0 && retry != 1) { | 888 | | /* Lossy "optimization" ... */ | 889 | | | 890 | 887 | cinfo.in_color_space=dinfo.out_color_space; | 891 | 887 | cinfo.input_components=dinfo.output_components; | 892 | 887 | cinfo.image_width=dinfo.image_width; | 893 | 887 | cinfo.image_height=dinfo.image_height; | 894 | 887 | jpeg_set_defaults(&cinfo); | 895 | 887 | jpeg_set_quality(&cinfo,quality,TRUE); | 896 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 897 | | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 898 | | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 899 | | #endif | 900 | 887 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 901 | | /* Explicitly disable progressive mode. */ | 902 | 768 | cinfo.scan_info = NULL; | 903 | 768 | cinfo.num_scans = 0; | 904 | 768 | } else if (all_progressive || dinfo.progressive_mode) { | 905 | | /* Enable progressive mode. */ | 906 | 119 | jpeg_simple_progression(&cinfo); | 907 | 119 | } | 908 | 887 | cinfo.optimize_coding = TRUE; | 909 | 887 | #ifdef HAVE_ARITH_CODE | 910 | 887 | if (arith_mode >= 0) | 911 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 912 | 887 | #endif | 913 | 887 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 914 | 490 | cinfo.write_JFIF_header = TRUE; | 915 | 490 | } else { | 916 | 397 | cinfo.write_JFIF_header = FALSE; | 917 | 397 | } | 918 | 887 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 919 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 920 | 0 | cinfo.write_JFIF_header = FALSE; | 921 | 0 | } | 922 | | | 923 | 887 | jpeg_start_compress(&cinfo,TRUE); | 924 | | | 925 | | /* Write markers */ | 926 | 887 | write_markers(&dinfo,&cinfo); | 927 | | | 928 | | /* Write image */ | 929 | 1.77k | while (cinfo.next_scanline < cinfo.image_height) { | 930 | 885 | jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], | 931 | 885 | dinfo.output_height); | 932 | 885 | } | 933 | | | 934 | 887 | } else { | 935 | | /* Lossless optimization ... */ | 936 | | | 937 | 64 | jpeg_copy_critical_parameters(&dinfo, &cinfo); | 938 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 939 | | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 940 | | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 941 | | #endif | 942 | 64 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 943 | | /* Explicitly disable progressive mode. */ | 944 | 54 | cinfo.scan_info = NULL; | 945 | 54 | cinfo.num_scans = 0; | 946 | 54 | } else if (all_progressive || dinfo.progressive_mode) { | 947 | | /* Enable progressive mode. */ | 948 | 3 | jpeg_simple_progression(&cinfo); | 949 | 3 | } | 950 | 64 | cinfo.optimize_coding = TRUE; | 951 | 64 | #ifdef HAVE_ARITH_CODE | 952 | 64 | if (arith_mode >= 0) | 953 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 954 | 64 | #endif | 955 | 64 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 956 | 31 | cinfo.write_JFIF_header = TRUE; | 957 | 33 | } else { | 958 | 33 | cinfo.write_JFIF_header = FALSE; | 959 | 33 | } | 960 | 64 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 961 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 962 | 0 | cinfo.write_JFIF_header = FALSE; | 963 | 0 | } | 964 | | | 965 | | /* Write image */ | 966 | 64 | jpeg_write_coefficients(&cinfo, coef_arrays); | 967 | | | 968 | | /* Write markers */ | 969 | 64 | write_markers(&dinfo,&cinfo); | 970 | | | 971 | 64 | } | 972 | | | 973 | 951 | jpeg_finish_compress(&cinfo); | 974 | 951 | outsize = outbuffersize + extrabuffersize; | 975 | 951 | if (verbose_mode > 2) | 976 | 0 | fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize); | 977 | | | 978 | 951 | if (target_size != 0 && !retry) { | 979 | | /* Perform (binary) search to try to reach target file size... */ | 980 | | | 981 | 885 | long osize = outsize/1024; | 982 | 885 | long isize = insize/1024; | 983 | 885 | long tsize = target_size; | 984 | | | 985 | 885 | if (verbose_mode > 1) | 986 | 0 | fprintf(log_fh, "(size=%ld)",outsize); | 987 | 885 | if (tsize < 0) { | 988 | 885 | tsize=((-target_size)*insize/100)/1024; | 989 | 885 | if (tsize < 1) | 990 | 182 | tsize = 1; | 991 | 885 | } | 992 | | | 993 | 885 | if (osize == tsize || searchdone || tsize > isize) { | 994 | 198 | if (searchdone < 42 && lastsize > 0) { | 995 | 161 | if (labs(osize-tsize) > labs(lastsize-tsize)) { | 996 | 14 | if (verbose_mode) | 997 | 0 | fprintf(log_fh,"(revert to %d)",oldquality); | 998 | 14 | searchdone = 42; | 999 | 14 | quality = oldquality; | 1000 | 14 | goto binary_search_loop; | 1001 | 14 | } | 1002 | 161 | } | 1003 | 184 | if (verbose_mode) | 1004 | 0 | fprintf(log_fh," "); | 1005 | | | 1006 | 687 | } else { | 1007 | 687 | int newquality; | 1008 | 687 | double dif = abs(oldquality-quality) / 2.0; | 1009 | | | 1010 | 687 | if (osize > tsize) | 1011 | 459 | newquality = quality - dif; | 1012 | 228 | else | 1013 | 228 | newquality = quality + dif + 0.5; | 1014 | | | 1015 | 687 | if (dif < 1.0) | 1016 | 26 | searchdone = 1; | 1017 | 687 | if (newquality < 0) { | 1018 | 38 | newquality = 0; | 1019 | 38 | searchdone = 1; | 1020 | 38 | } | 1021 | 687 | if (newquality > 100) { | 1022 | 48 | newquality = 100; | 1023 | 48 | searchdone = 1; | 1024 | 48 | } | 1025 | | | 1026 | 687 | oldquality = quality; | 1027 | 687 | quality = newquality; | 1028 | 687 | lastsize = osize; | 1029 | 687 | if (verbose_mode) | 1030 | 0 | fprintf(log_fh,"(try %d)",quality); | 1031 | 687 | goto binary_search_loop; | 1032 | 687 | } | 1033 | 885 | } | 1034 | | | 1035 | 250 | jpeg_finish_decompress(&dinfo); | 1036 | 250 | free_line_buf(&buf, dinfo.output_height); | 1037 | | | 1038 | 250 | if (retry_mode) { | 1039 | 0 | if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) { | 1040 | | /* Retry compression until output file stops getting smaller | 1041 | | or we hit max limit of iterations (10)... */ | 1042 | 0 | if (retry_count == 0) | 1043 | 0 | last_retry_size = outsize + 1; | 1044 | 0 | if (++retry_count < 10 && outsize < last_retry_size) { | 1045 | 0 | if (tmpbuffer) | 1046 | 0 | free(tmpbuffer); | 1047 | 0 | tmpbuffer = outbuffer; | 1048 | 0 | tmpbuffersize = outbuffersize; | 1049 | 0 | outbuffer = NULL; | 1050 | 0 | last_retry_size = outsize; | 1051 | 0 | retry = 2; | 1052 | 0 | if (verbose_mode) | 1053 | 0 | fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize); | 1054 | 0 | goto retry_point; | 1055 | 0 | } | 1056 | 0 | } | 1057 | 0 | if (retry == 2) { | 1058 | 0 | if (verbose_mode) | 1059 | 0 | fprintf(log_fh, "(retry done: %lu) ", outsize); | 1060 | 0 | if (outsize > last_retry_size) { | 1061 | 0 | if (outbuffer) | 1062 | 0 | free(outbuffer); | 1063 | 0 | outbuffer = tmpbuffer; | 1064 | 0 | outbuffersize = tmpbuffersize; | 1065 | 0 | outsize = outbuffersize + extrabuffersize; | 1066 | 0 | tmpbuffer = NULL; | 1067 | 0 | } | 1068 | 0 | } | 1069 | 0 | } | 1070 | | | 1071 | | /* If auto_mode, try both progressive and non-progressive... */ | 1072 | 250 | if (auto_mode) { | 1073 | 0 | int newmode = (dinfo.progressive_mode ? 0 : 1); | 1074 | 0 | if (retry != 3) { | 1075 | 0 | if (newmode) | 1076 | 0 | all_progressive = 1; | 1077 | 0 | else | 1078 | 0 | all_normal = 1; | 1079 | 0 | if (tmpbuffer) | 1080 | 0 | free(tmpbuffer); | 1081 | 0 | tmpbuffer = outbuffer; | 1082 | 0 | tmpbuffersize = outbuffersize; | 1083 | 0 | outbuffer = NULL; | 1084 | 0 | last_retry_size = outsize; | 1085 | 0 | retry = 3; | 1086 | 0 | if (verbose_mode) | 1087 | 0 | fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal")); | 1088 | 0 | goto retry_point; | 1089 | 0 | } else { | 1090 | 0 | if (verbose_mode > 1) | 1091 | 0 | fprintf(log_fh, "(automode done: %lu) ", outsize); | 1092 | 0 | auto_mode = 0; | 1093 | 0 | if (outsize > last_retry_size) { | 1094 | 0 | if (verbose_mode) | 1095 | 0 | fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal")); | 1096 | 0 | all_progressive = 0; | 1097 | 0 | all_normal = 0; | 1098 | 0 | if (outbuffer) | 1099 | 0 | free(outbuffer); | 1100 | 0 | outbuffer = tmpbuffer; | 1101 | 0 | outbuffersize = tmpbuffersize; | 1102 | 0 | outsize = outbuffersize + extrabuffersize; | 1103 | 0 | tmpbuffer = NULL; | 1104 | 0 | } | 1105 | 0 | } | 1106 | 0 | } | 1107 | | | 1108 | | /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */ | 1109 | 250 | if (quality >= 0 && outsize >= insize && retry != 1) { | 1110 | 65 | retry = 1; | 1111 | 65 | if (verbose_mode) | 1112 | 0 | fprintf(log_fh, "(retry w/lossless) "); | 1113 | 65 | goto retry_point; | 1114 | 65 | } | 1115 | | | 1116 | 185 | fclose(infile); | 1117 | | | 1118 | 185 | ratio = (insize - outsize) * 100.0 / insize; | 1119 | 185 | if (!quiet_mode || csv) | 1120 | 117 | fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); | 1121 | 185 | if (rate) { | 1122 | 117 | *rate = (ratio < 0 ? 0.0 : ratio); | 1123 | 117 | } | 1124 | | | 1125 | 185 | if ((outsize < insize && ratio >= threshold) || force) { | 1126 | 84 | if (saved) { | 1127 | 84 | *saved = (insize - outsize) / 1024.0; | 1128 | 84 | } | 1129 | 84 | if (!quiet_mode || csv) | 1130 | 84 | fprintf(log_fh,csv ? "optimized\n" : "optimized.\n"); | 1131 | 84 | if (noaction) { | 1132 | 0 | res = 0; | 1133 | 0 | goto exit_point; | 1134 | 0 | } | 1135 | | | 1136 | 84 | if (stdout_mode) { | 1137 | 0 | outfname=NULL; | 1138 | 0 | set_filemode_binary(stdout); | 1139 | 0 | if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) | 1140 | 0 | fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename)); | 1141 | 84 | } else { | 1142 | 84 | if (preserve_perms && !dest) { | 1143 | | /* make backup of the original file */ | 1144 | 0 | int newlen = snprintf(tmpfilename, sizeof(tmpfilename), | 1145 | 0 | "%s.jpegoptim.bak", newname); | 1146 | 0 | if (newlen >= sizeof(tmpfilename)) | 1147 | 0 | fatal("temp filename too long: %s", tmpfilename); | 1148 | |
| 1149 | 0 | if (verbose_mode > 1) | 1150 | 0 | fprintf(log_fh,"%s, creating backup as: %s\n", | 1151 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1152 | 0 | if (file_exists(tmpfilename)) | 1153 | 0 | fatal("%s, backup file already exists: %s", | 1154 | 0 | (stdin_mode ?" stdin" : filename), tmpfilename); | 1155 | 0 | if (copy_file(newname,tmpfilename)) | 1156 | 0 | fatal("%s, failed to create backup: %s", | 1157 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1158 | 0 | if ((outfile=create_file(newname))==NULL) | 1159 | 0 | fatal("%s, error opening output file: %s", | 1160 | 0 | (stdin_mode ? "stdin" : filename), newname); | 1161 | 0 | outfname = newname; | 1162 | 84 | } else { | 1163 | 84 | if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename)))) | 1164 | 0 | fatal("error creating temporary file: %s", tmpfilename); | 1165 | 84 | outfname = tmpfilename; | 1166 | 84 | } | 1167 | | | 1168 | 84 | if (verbose_mode > 1) | 1169 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", | 1170 | 0 | (long unsigned int)outbuffersize, outfname); | 1171 | 84 | if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1) | 1172 | 0 | fatal("write failed to file: %s", outfname); | 1173 | 84 | if (save_extra && extrabuffersize > 0) { | 1174 | 0 | if (verbose_mode > 1) | 1175 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname); | 1176 | 0 | if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1) | 1177 | 0 | fatal("write failed to file: %s", outfname); | 1178 | 0 | } | 1179 | 84 | fclose(outfile); | 1180 | 84 | } | 1181 | | | 1182 | 84 | if (outfname) { | 1183 | 84 | if (preserve_mode) { | 1184 | | /* preserve file modification time */ | 1185 | 0 | if (verbose_mode > 1) | 1186 | 0 | fprintf(log_fh,"set file modification time same as in original: %s\n", | 1187 | 0 | outfname); | 1188 | 0 | #if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM) | 1189 | 0 | struct timespec time_save[2]; | 1190 | 0 | time_save[0].tv_sec = 0; | 1191 | 0 | time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */ | 1192 | 0 | time_save[1] = file_stat->st_mtim; | 1193 | 0 | if (utimensat(AT_FDCWD,outfname,time_save,0) != 0) | 1194 | 0 | warn("failed to reset output file time/date"); | 1195 | | #else | 1196 | | struct utimbuf time_save; | 1197 | | time_save.actime=file_stat->st_atime; | 1198 | | time_save.modtime=file_stat->st_mtime; | 1199 | | if (utime(outfname,&time_save) != 0) | 1200 | | warn("failed to reset output file time/date"); | 1201 | | #endif | 1202 | 0 | } | 1203 | | | 1204 | 84 | if (preserve_perms && !dest) { | 1205 | | /* original file was already replaced, remove backup... */ | 1206 | 0 | if (verbose_mode > 1) | 1207 | 0 | fprintf(log_fh,"removing backup file: %s\n", tmpfilename); | 1208 | 0 | if (delete_file(tmpfilename)) | 1209 | 0 | warn("failed to remove backup file: %s", tmpfilename); | 1210 | 84 | } else { | 1211 | | /* make temp file to be the original file... */ | 1212 | | | 1213 | | /* preserve file mode */ | 1214 | 84 | if (chmod(outfname,(file_stat->st_mode & 0777)) != 0) | 1215 | 0 | warn("failed to set output file mode"); | 1216 | | | 1217 | | /* preserve file group (and owner if run by root) */ | 1218 | 84 | if (chown(outfname, | 1219 | 84 | (geteuid()==0 ? file_stat->st_uid : -1), | 1220 | 84 | file_stat->st_gid) != 0) | 1221 | 0 | warn("failed to reset output file group/owner"); | 1222 | | | 1223 | 84 | if (verbose_mode > 1) | 1224 | 0 | fprintf(log_fh,"renaming: %s to %s\n", outfname, newname); | 1225 | 84 | if (rename_file(outfname, newname)) | 1226 | 0 | fatal("cannot rename temp file"); | 1227 | 84 | } | 1228 | 84 | } | 1229 | 101 | } else { | 1230 | 101 | if (!quiet_mode || csv) | 1231 | 33 | fprintf(log_fh,csv ? "skipped\n" : "skipped.\n"); | 1232 | 101 | if (stdout_mode) { | 1233 | 0 | set_filemode_binary(stdout); | 1234 | 0 | if (fwrite(inbuffer, in_image_size, 1, stdout) != 1) | 1235 | 0 | fatal("%s, write failed to stdout", | 1236 | 0 | (stdin_mode ? "stdin" : filename)); | 1237 | 0 | } | 1238 | 101 | } | 1239 | | | 1240 | 185 | res = 0; | 1241 | | | 1242 | 209 | exit_point: | 1243 | 209 | if (inbuffer) | 1244 | 209 | free(inbuffer); | 1245 | 209 | if (outbuffer) | 1246 | 186 | free(outbuffer); | 1247 | 209 | if (tmpbuffer) | 1248 | 0 | free(tmpbuffer); | 1249 | 209 | if (extrabuffer) | 1250 | 0 | free(extrabuffer); | 1251 | 209 | jpeg_destroy_compress(&cinfo); | 1252 | 209 | jpeg_destroy_decompress(&dinfo); | 1253 | | | 1254 | 209 | return res; | 1255 | 185 | } |
Line | Count | Source | 635 | 209 | { | 636 | 209 | FILE *infile = NULL; | 637 | 209 | FILE *outfile = NULL; | 638 | 209 | const char *outfname = NULL; | 639 | 209 | char tmpfilename[MAXPATHLEN]; | 640 | 209 | struct jpeg_decompress_struct dinfo; | 641 | 209 | struct jpeg_compress_struct cinfo; | 642 | 209 | struct my_error_mgr jcerr, jderr; | 643 | 209 | JSAMPARRAY buf = NULL; | 644 | | | 645 | 209 | unsigned char *outbuffer = NULL; | 646 | 209 | size_t outbuffersize = 0; | 647 | 209 | unsigned char *inbuffer = NULL; | 648 | 209 | size_t inbuffersize = 0; | 649 | 209 | size_t inbufferused = 0; | 650 | 209 | unsigned char *tmpbuffer = NULL; | 651 | 209 | size_t tmpbuffersize = 0; | 652 | 209 | unsigned char *extrabuffer = NULL; | 653 | 209 | size_t extrabuffersize = 0; | 654 | | | 655 | 209 | jvirt_barray_ptr *coef_arrays = NULL; | 656 | 209 | char marker_str[256]; | 657 | 209 | unsigned int marker_in_count, marker_in_size; | 658 | | | 659 | 209 | long in_image_size = 0; | 660 | 209 | long insize = 0, outsize = 0, lastsize = 0; | 661 | 209 | int oldquality, searchdone; | 662 | 209 | double ratio; | 663 | 209 | size_t last_retry_size = 0; | 664 | 209 | int retry_count = 0; | 665 | 209 | int retry = 0; | 666 | 209 | int res = -1; | 667 | | | 668 | 209 | jpeg_log_fh = log_fh; | 669 | | | 670 | | /* Initialize decompression object */ | 671 | 209 | dinfo.err = jpeg_std_error(&jderr.pub); | 672 | 209 | jpeg_create_decompress(&dinfo); | 673 | 209 | jderr.pub.error_exit=my_error_exit; | 674 | 209 | jderr.pub.output_message=my_output_message; | 675 | 209 | jderr.jump_set = 0; | 676 | | | 677 | | /* Initialize compression object */ | 678 | 209 | cinfo.err = jpeg_std_error(&jcerr.pub); | 679 | 209 | jpeg_create_compress(&cinfo); | 680 | 209 | jcerr.pub.error_exit=my_error_exit; | 681 | 209 | jcerr.pub.output_message=my_output_message; | 682 | 209 | jcerr.jump_set = 0; | 683 | | | 684 | 209 | if (rate) | 685 | 209 | *rate = 0.0; | 686 | 209 | if (saved) | 687 | 209 | *saved = 0.0; | 688 | | | 689 | 209 | if (filename) { | 690 | 209 | if ((infile = fopen(filename, "rb")) == NULL) { | 691 | 0 | warn("cannot open file: %s", filename); | 692 | 0 | res = 1; | 693 | 0 | goto exit_point; | 694 | 0 | } | 695 | 209 | } else { | 696 | 0 | infile = stdin; | 697 | 0 | set_filemode_binary(infile); | 698 | 0 | } | 699 | | | 700 | 274 | retry_point: | 701 | | | 702 | 274 | if (setjmp(jderr.setjmp_buffer)) { | 703 | | /* Error handler for decompress */ | 704 | 61 | abort_decompress: | 705 | 61 | jpeg_abort_decompress(&dinfo); | 706 | 61 | fclose(infile); | 707 | 61 | free_line_buf(&buf, dinfo.output_height); | 708 | 61 | if (!quiet_mode || csv) | 709 | 61 | fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n"); | 710 | 61 | jderr.jump_set=0; | 711 | 61 | res = 1; | 712 | 61 | goto exit_point; | 713 | 213 | } else { | 714 | 213 | jderr.jump_set=1; | 715 | 213 | } | 716 | | | 717 | | | 718 | | /* Prepare to decompress */ | 719 | 213 | if (!retry) { | 720 | 209 | if (!quiet_mode || csv) { | 721 | 209 | fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin")); | 722 | 209 | fflush(log_fh); | 723 | 209 | } | 724 | | | 725 | 209 | if (stdin_mode || stdout_mode) { | 726 | 0 | inbuffersize = IN_BUF_SIZE; | 727 | 209 | } else { | 728 | 209 | if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE) | 729 | 176 | inbuffersize = IN_BUF_SIZE; | 730 | 209 | } | 731 | 209 | if (inbuffer) | 732 | 0 | free(inbuffer); | 733 | 209 | if (!(inbuffer=calloc(inbuffersize, 1))) | 734 | 0 | fatal("not enough memory"); | 735 | 209 | } | 736 | 213 | global_error_counter=0; | 737 | 213 | jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); | 738 | 4.59k | for (int i = 0; i < 16; i++) { | 739 | 4.38k | jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff); | 740 | 4.38k | } | 741 | 213 | if (!retry) { | 742 | 209 | jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE); | 743 | 209 | } else { | 744 | 4 | if (retry == 1) | 745 | 65 | jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused); | 746 | 18.4E | else | 747 | 18.4E | jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize); | 748 | 4 | } | 749 | 213 | jpeg_read_header(&dinfo, TRUE); | 750 | | | 751 | | | 752 | | /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */ | 753 | 213 | marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str), | 754 | 213 | &marker_in_size); | 755 | | | 756 | 213 | if (!retry) { | 757 | 196 | if (verbose_mode > 1) { | 758 | 0 | fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n", | 759 | 0 | marker_in_count,marker_in_size); | 760 | 0 | fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman")); | 761 | 0 | } | 762 | | | 763 | 196 | if (!quiet_mode || csv) { | 764 | 196 | fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, | 765 | 196 | (int)dinfo.image_height,(int)dinfo.num_components*8, | 766 | 196 | (dinfo.progressive_mode?'P':'N')); | 767 | 196 | if (!csv) | 768 | 196 | fprintf(log_fh,"%s",marker_str); | 769 | 196 | fflush(log_fh); | 770 | 196 | } | 771 | 196 | } | 772 | | | 773 | | /* Decompress the image */ | 774 | 259 | if (quality >= 0 && retry != 1) { | 775 | 194 | jpeg_start_decompress(&dinfo); | 776 | | | 777 | | /* Allocate line buffer to store the decompressed image */ | 778 | 194 | if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW)))) | 779 | 0 | fatal("not enough memory"); | 780 | 661k | for (int i = 0; i < dinfo.output_height; i++) { | 781 | 661k | if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components, | 782 | 661k | sizeof(JSAMPLE)))) | 783 | 0 | fatal("not enough memory"); | 784 | 661k | } | 785 | | | 786 | 241k | while (dinfo.output_scanline < dinfo.output_height) { | 787 | 240k | jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], | 788 | 240k | dinfo.output_height-dinfo.output_scanline); | 789 | 240k | } | 790 | 194 | } else { | 791 | 19 | coef_arrays = jpeg_read_coefficients(&dinfo); | 792 | 19 | if (!coef_arrays) { | 793 | 0 | if (!quiet_mode) | 794 | 0 | fprintf(log_fh, " (failed to read coefficients) "); | 795 | 0 | goto abort_decompress; | 796 | 0 | } | 797 | 19 | } | 798 | 213 | if (!retry) { | 799 | 186 | in_image_size = inbufferused - dinfo.src->bytes_in_buffer; | 800 | 186 | if(verbose_mode > 2) | 801 | 0 | fprintf(log_fh, " (input image size: %lu (%lu))", | 802 | 0 | in_image_size, inbufferused); | 803 | 186 | if (stdin_mode) { | 804 | 0 | insize = in_image_size; | 805 | 186 | } else { | 806 | 186 | if ((insize = file_size(infile)) < 0) | 807 | 0 | fatal("failed to stat() input file"); | 808 | 186 | if (in_image_size > 0 && in_image_size < insize) { | 809 | 106 | if (!quiet_mode) | 810 | 106 | fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ", | 811 | 106 | insize - in_image_size); | 812 | 106 | if (nofix_mode) | 813 | 0 | global_error_counter++; | 814 | 106 | if (save_extra) { | 815 | 0 | extrabuffersize = insize - in_image_size; | 816 | 0 | if (extrabuffer) | 817 | 0 | free(extrabuffer); | 818 | 0 | if (!(extrabuffer = calloc(extrabuffersize, 1))) | 819 | 0 | fatal("not enough memory"); | 820 | 0 | if (fseek(infile, in_image_size, SEEK_SET)) | 821 | 0 | fatal("failed to seek input file"); | 822 | 0 | if (fread(extrabuffer, extrabuffersize, 1, infile) != 1) | 823 | 0 | fatal("failed to read inputfile"); | 824 | 0 | } | 825 | 106 | } | 826 | 186 | } | 827 | 186 | if (!quiet_mode) { | 828 | 186 | fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] ")); | 829 | 186 | fflush(log_fh); | 830 | 186 | } | 831 | | | 832 | 186 | if (nofix_mode && global_error_counter != 0) { | 833 | | /* Skip files containing any errors (or warnings) */ | 834 | 0 | goto abort_decompress; | 835 | 0 | } | 836 | | | 837 | 186 | if (dest && !noaction) { | 838 | 0 | if (file_exists(newname) && !overwrite_mode) { | 839 | 0 | if (!quiet_mode) | 840 | 0 | fprintf(log_fh, " (target file already exists) "); | 841 | 0 | goto abort_decompress; | 842 | 0 | } | 843 | 0 | } | 844 | 186 | } | 845 | | | 846 | | | 847 | | /* Prepare to compress... */ | 848 | 213 | if (setjmp(jcerr.setjmp_buffer)) { | 849 | | /* Error handler for compress failures */ | 850 | 31 | if (!quiet_mode) | 851 | 31 | fprintf(log_fh," [Compress ERROR: %s]\n",last_error); | 852 | 31 | jpeg_abort_compress(&cinfo); | 853 | 31 | jpeg_abort_decompress(&dinfo); | 854 | 31 | fclose(infile); | 855 | 31 | free_line_buf(&buf, dinfo.output_height); | 856 | 31 | jcerr.jump_set=0; | 857 | 31 | res = 2; | 858 | 31 | goto exit_point; | 859 | 182 | } else { | 860 | 182 | jcerr.jump_set=1; | 861 | 182 | } | 862 | | | 863 | 182 | lastsize = 0; | 864 | 182 | searchdone = 0; | 865 | 186 | if (!retry) { | 866 | 186 | oldquality = 200; | 867 | 186 | if (target_size != 0) { | 868 | | /* Always start with quality 100 if -S option specified... */ | 869 | 186 | quality = 100; | 870 | 186 | } | 871 | 186 | } | 872 | | | 873 | | | 874 | 951 | binary_search_loop: | 875 | | | 876 | | /* Allocate memory buffer that should be large enough to store the output JPEG... */ | 877 | 951 | if (outbuffer) | 878 | 765 | free(outbuffer); | 879 | 951 | outbuffersize = insize + 32768; | 880 | 951 | if (!(outbuffer=calloc(outbuffersize, 1))) | 881 | 0 | fatal("not enough memory"); | 882 | | | 883 | | /* setup custom "destination manager" for libjpeg to write to our buffer */ | 884 | 951 | jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); | 885 | | | 886 | | | 887 | 951 | if (quality >= 0 && retry != 1) { | 888 | | /* Lossy "optimization" ... */ | 889 | | | 890 | 887 | cinfo.in_color_space=dinfo.out_color_space; | 891 | 887 | cinfo.input_components=dinfo.output_components; | 892 | 887 | cinfo.image_width=dinfo.image_width; | 893 | 887 | cinfo.image_height=dinfo.image_height; | 894 | 887 | jpeg_set_defaults(&cinfo); | 895 | 887 | jpeg_set_quality(&cinfo,quality,TRUE); | 896 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 897 | | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 898 | | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 899 | | #endif | 900 | 887 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 901 | | /* Explicitly disable progressive mode. */ | 902 | 768 | cinfo.scan_info = NULL; | 903 | 768 | cinfo.num_scans = 0; | 904 | 768 | } else if (all_progressive || dinfo.progressive_mode) { | 905 | | /* Enable progressive mode. */ | 906 | 119 | jpeg_simple_progression(&cinfo); | 907 | 119 | } | 908 | 887 | cinfo.optimize_coding = TRUE; | 909 | 887 | #ifdef HAVE_ARITH_CODE | 910 | 887 | if (arith_mode >= 0) | 911 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 912 | 887 | #endif | 913 | 887 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 914 | 490 | cinfo.write_JFIF_header = TRUE; | 915 | 490 | } else { | 916 | 397 | cinfo.write_JFIF_header = FALSE; | 917 | 397 | } | 918 | 887 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 919 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 920 | 0 | cinfo.write_JFIF_header = FALSE; | 921 | 0 | } | 922 | | | 923 | 887 | jpeg_start_compress(&cinfo,TRUE); | 924 | | | 925 | | /* Write markers */ | 926 | 887 | write_markers(&dinfo,&cinfo); | 927 | | | 928 | | /* Write image */ | 929 | 1.77k | while (cinfo.next_scanline < cinfo.image_height) { | 930 | 885 | jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], | 931 | 885 | dinfo.output_height); | 932 | 885 | } | 933 | | | 934 | 887 | } else { | 935 | | /* Lossless optimization ... */ | 936 | | | 937 | 64 | jpeg_copy_critical_parameters(&dinfo, &cinfo); | 938 | | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 939 | | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 940 | | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 941 | | #endif | 942 | 64 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 943 | | /* Explicitly disable progressive mode. */ | 944 | 54 | cinfo.scan_info = NULL; | 945 | 54 | cinfo.num_scans = 0; | 946 | 54 | } else if (all_progressive || dinfo.progressive_mode) { | 947 | | /* Enable progressive mode. */ | 948 | 3 | jpeg_simple_progression(&cinfo); | 949 | 3 | } | 950 | 64 | cinfo.optimize_coding = TRUE; | 951 | 64 | #ifdef HAVE_ARITH_CODE | 952 | 64 | if (arith_mode >= 0) | 953 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 954 | 64 | #endif | 955 | 64 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 956 | 31 | cinfo.write_JFIF_header = TRUE; | 957 | 33 | } else { | 958 | 33 | cinfo.write_JFIF_header = FALSE; | 959 | 33 | } | 960 | 64 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 961 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 962 | 0 | cinfo.write_JFIF_header = FALSE; | 963 | 0 | } | 964 | | | 965 | | /* Write image */ | 966 | 64 | jpeg_write_coefficients(&cinfo, coef_arrays); | 967 | | | 968 | | /* Write markers */ | 969 | 64 | write_markers(&dinfo,&cinfo); | 970 | | | 971 | 64 | } | 972 | | | 973 | 951 | jpeg_finish_compress(&cinfo); | 974 | 951 | outsize = outbuffersize + extrabuffersize; | 975 | 951 | if (verbose_mode > 2) | 976 | 0 | fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize); | 977 | | | 978 | 951 | if (target_size != 0 && !retry) { | 979 | | /* Perform (binary) search to try to reach target file size... */ | 980 | | | 981 | 885 | long osize = outsize/1024; | 982 | 885 | long isize = insize/1024; | 983 | 885 | long tsize = target_size; | 984 | | | 985 | 885 | if (verbose_mode > 1) | 986 | 0 | fprintf(log_fh, "(size=%ld)",outsize); | 987 | 885 | if (tsize < 0) { | 988 | 885 | tsize=((-target_size)*insize/100)/1024; | 989 | 885 | if (tsize < 1) | 990 | 182 | tsize = 1; | 991 | 885 | } | 992 | | | 993 | 885 | if (osize == tsize || searchdone || tsize > isize) { | 994 | 198 | if (searchdone < 42 && lastsize > 0) { | 995 | 161 | if (labs(osize-tsize) > labs(lastsize-tsize)) { | 996 | 14 | if (verbose_mode) | 997 | 0 | fprintf(log_fh,"(revert to %d)",oldquality); | 998 | 14 | searchdone = 42; | 999 | 14 | quality = oldquality; | 1000 | 14 | goto binary_search_loop; | 1001 | 14 | } | 1002 | 161 | } | 1003 | 184 | if (verbose_mode) | 1004 | 0 | fprintf(log_fh," "); | 1005 | | | 1006 | 687 | } else { | 1007 | 687 | int newquality; | 1008 | 687 | double dif = abs(oldquality-quality) / 2.0; | 1009 | | | 1010 | 687 | if (osize > tsize) | 1011 | 459 | newquality = quality - dif; | 1012 | 228 | else | 1013 | 228 | newquality = quality + dif + 0.5; | 1014 | | | 1015 | 687 | if (dif < 1.0) | 1016 | 26 | searchdone = 1; | 1017 | 687 | if (newquality < 0) { | 1018 | 38 | newquality = 0; | 1019 | 38 | searchdone = 1; | 1020 | 38 | } | 1021 | 687 | if (newquality > 100) { | 1022 | 48 | newquality = 100; | 1023 | 48 | searchdone = 1; | 1024 | 48 | } | 1025 | | | 1026 | 687 | oldquality = quality; | 1027 | 687 | quality = newquality; | 1028 | 687 | lastsize = osize; | 1029 | 687 | if (verbose_mode) | 1030 | 0 | fprintf(log_fh,"(try %d)",quality); | 1031 | 687 | goto binary_search_loop; | 1032 | 687 | } | 1033 | 885 | } | 1034 | | | 1035 | 250 | jpeg_finish_decompress(&dinfo); | 1036 | 250 | free_line_buf(&buf, dinfo.output_height); | 1037 | | | 1038 | 250 | if (retry_mode) { | 1039 | 0 | if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) { | 1040 | | /* Retry compression until output file stops getting smaller | 1041 | | or we hit max limit of iterations (10)... */ | 1042 | 0 | if (retry_count == 0) | 1043 | 0 | last_retry_size = outsize + 1; | 1044 | 0 | if (++retry_count < 10 && outsize < last_retry_size) { | 1045 | 0 | if (tmpbuffer) | 1046 | 0 | free(tmpbuffer); | 1047 | 0 | tmpbuffer = outbuffer; | 1048 | 0 | tmpbuffersize = outbuffersize; | 1049 | 0 | outbuffer = NULL; | 1050 | 0 | last_retry_size = outsize; | 1051 | 0 | retry = 2; | 1052 | 0 | if (verbose_mode) | 1053 | 0 | fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize); | 1054 | 0 | goto retry_point; | 1055 | 0 | } | 1056 | 0 | } | 1057 | 0 | if (retry == 2) { | 1058 | 0 | if (verbose_mode) | 1059 | 0 | fprintf(log_fh, "(retry done: %lu) ", outsize); | 1060 | 0 | if (outsize > last_retry_size) { | 1061 | 0 | if (outbuffer) | 1062 | 0 | free(outbuffer); | 1063 | 0 | outbuffer = tmpbuffer; | 1064 | 0 | outbuffersize = tmpbuffersize; | 1065 | 0 | outsize = outbuffersize + extrabuffersize; | 1066 | 0 | tmpbuffer = NULL; | 1067 | 0 | } | 1068 | 0 | } | 1069 | 0 | } | 1070 | | | 1071 | | /* If auto_mode, try both progressive and non-progressive... */ | 1072 | 250 | if (auto_mode) { | 1073 | 0 | int newmode = (dinfo.progressive_mode ? 0 : 1); | 1074 | 0 | if (retry != 3) { | 1075 | 0 | if (newmode) | 1076 | 0 | all_progressive = 1; | 1077 | 0 | else | 1078 | 0 | all_normal = 1; | 1079 | 0 | if (tmpbuffer) | 1080 | 0 | free(tmpbuffer); | 1081 | 0 | tmpbuffer = outbuffer; | 1082 | 0 | tmpbuffersize = outbuffersize; | 1083 | 0 | outbuffer = NULL; | 1084 | 0 | last_retry_size = outsize; | 1085 | 0 | retry = 3; | 1086 | 0 | if (verbose_mode) | 1087 | 0 | fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal")); | 1088 | 0 | goto retry_point; | 1089 | 0 | } else { | 1090 | 0 | if (verbose_mode > 1) | 1091 | 0 | fprintf(log_fh, "(automode done: %lu) ", outsize); | 1092 | 0 | auto_mode = 0; | 1093 | 0 | if (outsize > last_retry_size) { | 1094 | 0 | if (verbose_mode) | 1095 | 0 | fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal")); | 1096 | 0 | all_progressive = 0; | 1097 | 0 | all_normal = 0; | 1098 | 0 | if (outbuffer) | 1099 | 0 | free(outbuffer); | 1100 | 0 | outbuffer = tmpbuffer; | 1101 | 0 | outbuffersize = tmpbuffersize; | 1102 | 0 | outsize = outbuffersize + extrabuffersize; | 1103 | 0 | tmpbuffer = NULL; | 1104 | 0 | } | 1105 | 0 | } | 1106 | 0 | } | 1107 | | | 1108 | | /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */ | 1109 | 250 | if (quality >= 0 && outsize >= insize && retry != 1) { | 1110 | 65 | retry = 1; | 1111 | 65 | if (verbose_mode) | 1112 | 0 | fprintf(log_fh, "(retry w/lossless) "); | 1113 | 65 | goto retry_point; | 1114 | 65 | } | 1115 | | | 1116 | 185 | fclose(infile); | 1117 | | | 1118 | 185 | ratio = (insize - outsize) * 100.0 / insize; | 1119 | 185 | if (!quiet_mode || csv) | 1120 | 117 | fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); | 1121 | 185 | if (rate) { | 1122 | 117 | *rate = (ratio < 0 ? 0.0 : ratio); | 1123 | 117 | } | 1124 | | | 1125 | 185 | if ((outsize < insize && ratio >= threshold) || force) { | 1126 | 84 | if (saved) { | 1127 | 84 | *saved = (insize - outsize) / 1024.0; | 1128 | 84 | } | 1129 | 84 | if (!quiet_mode || csv) | 1130 | 84 | fprintf(log_fh,csv ? "optimized\n" : "optimized.\n"); | 1131 | 84 | if (noaction) { | 1132 | 0 | res = 0; | 1133 | 0 | goto exit_point; | 1134 | 0 | } | 1135 | | | 1136 | 84 | if (stdout_mode) { | 1137 | 0 | outfname=NULL; | 1138 | 0 | set_filemode_binary(stdout); | 1139 | 0 | if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) | 1140 | 0 | fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename)); | 1141 | 84 | } else { | 1142 | 84 | if (preserve_perms && !dest) { | 1143 | | /* make backup of the original file */ | 1144 | 0 | int newlen = snprintf(tmpfilename, sizeof(tmpfilename), | 1145 | 0 | "%s.jpegoptim.bak", newname); | 1146 | 0 | if (newlen >= sizeof(tmpfilename)) | 1147 | 0 | fatal("temp filename too long: %s", tmpfilename); | 1148 | |
| 1149 | 0 | if (verbose_mode > 1) | 1150 | 0 | fprintf(log_fh,"%s, creating backup as: %s\n", | 1151 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1152 | 0 | if (file_exists(tmpfilename)) | 1153 | 0 | fatal("%s, backup file already exists: %s", | 1154 | 0 | (stdin_mode ?" stdin" : filename), tmpfilename); | 1155 | 0 | if (copy_file(newname,tmpfilename)) | 1156 | 0 | fatal("%s, failed to create backup: %s", | 1157 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1158 | 0 | if ((outfile=create_file(newname))==NULL) | 1159 | 0 | fatal("%s, error opening output file: %s", | 1160 | 0 | (stdin_mode ? "stdin" : filename), newname); | 1161 | 0 | outfname = newname; | 1162 | 84 | } else { | 1163 | 84 | if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename)))) | 1164 | 0 | fatal("error creating temporary file: %s", tmpfilename); | 1165 | 84 | outfname = tmpfilename; | 1166 | 84 | } | 1167 | | | 1168 | 84 | if (verbose_mode > 1) | 1169 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", | 1170 | 0 | (long unsigned int)outbuffersize, outfname); | 1171 | 84 | if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1) | 1172 | 0 | fatal("write failed to file: %s", outfname); | 1173 | 84 | if (save_extra && extrabuffersize > 0) { | 1174 | 0 | if (verbose_mode > 1) | 1175 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname); | 1176 | 0 | if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1) | 1177 | 0 | fatal("write failed to file: %s", outfname); | 1178 | 0 | } | 1179 | 84 | fclose(outfile); | 1180 | 84 | } | 1181 | | | 1182 | 84 | if (outfname) { | 1183 | 84 | if (preserve_mode) { | 1184 | | /* preserve file modification time */ | 1185 | 0 | if (verbose_mode > 1) | 1186 | 0 | fprintf(log_fh,"set file modification time same as in original: %s\n", | 1187 | 0 | outfname); | 1188 | 0 | #if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM) | 1189 | 0 | struct timespec time_save[2]; | 1190 | 0 | time_save[0].tv_sec = 0; | 1191 | 0 | time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */ | 1192 | 0 | time_save[1] = file_stat->st_mtim; | 1193 | 0 | if (utimensat(AT_FDCWD,outfname,time_save,0) != 0) | 1194 | 0 | warn("failed to reset output file time/date"); | 1195 | | #else | 1196 | | struct utimbuf time_save; | 1197 | | time_save.actime=file_stat->st_atime; | 1198 | | time_save.modtime=file_stat->st_mtime; | 1199 | | if (utime(outfname,&time_save) != 0) | 1200 | | warn("failed to reset output file time/date"); | 1201 | | #endif | 1202 | 0 | } | 1203 | | | 1204 | 84 | if (preserve_perms && !dest) { | 1205 | | /* original file was already replaced, remove backup... */ | 1206 | 0 | if (verbose_mode > 1) | 1207 | 0 | fprintf(log_fh,"removing backup file: %s\n", tmpfilename); | 1208 | 0 | if (delete_file(tmpfilename)) | 1209 | 0 | warn("failed to remove backup file: %s", tmpfilename); | 1210 | 84 | } else { | 1211 | | /* make temp file to be the original file... */ | 1212 | | | 1213 | | /* preserve file mode */ | 1214 | 84 | if (chmod(outfname,(file_stat->st_mode & 0777)) != 0) | 1215 | 0 | warn("failed to set output file mode"); | 1216 | | | 1217 | | /* preserve file group (and owner if run by root) */ | 1218 | 84 | if (chown(outfname, | 1219 | 84 | (geteuid()==0 ? file_stat->st_uid : -1), | 1220 | 84 | file_stat->st_gid) != 0) | 1221 | 0 | warn("failed to reset output file group/owner"); | 1222 | | | 1223 | 84 | if (verbose_mode > 1) | 1224 | 0 | fprintf(log_fh,"renaming: %s to %s\n", outfname, newname); | 1225 | 84 | if (rename_file(outfname, newname)) | 1226 | 0 | fatal("cannot rename temp file"); | 1227 | 84 | } | 1228 | 84 | } | 1229 | 101 | } else { | 1230 | 101 | if (!quiet_mode || csv) | 1231 | 33 | fprintf(log_fh,csv ? "skipped\n" : "skipped.\n"); | 1232 | 101 | if (stdout_mode) { | 1233 | 0 | set_filemode_binary(stdout); | 1234 | 0 | if (fwrite(inbuffer, in_image_size, 1, stdout) != 1) | 1235 | 0 | fatal("%s, write failed to stdout", | 1236 | 0 | (stdin_mode ? "stdin" : filename)); | 1237 | 0 | } | 1238 | 101 | } | 1239 | | | 1240 | 185 | res = 0; | 1241 | | | 1242 | 209 | exit_point: | 1243 | 209 | if (inbuffer) | 1244 | 209 | free(inbuffer); | 1245 | 209 | if (outbuffer) | 1246 | 186 | free(outbuffer); | 1247 | 209 | if (tmpbuffer) | 1248 | 0 | free(tmpbuffer); | 1249 | 209 | if (extrabuffer) | 1250 | 0 | free(extrabuffer); | 1251 | 209 | jpeg_destroy_compress(&cinfo); | 1252 | 209 | jpeg_destroy_decompress(&dinfo); | 1253 | | | 1254 | 209 | return res; | 1255 | 185 | } |
Line | Count | Source | 635 | 185 | { | 636 | 185 | FILE *infile = NULL; | 637 | 185 | FILE *outfile = NULL; | 638 | 185 | const char *outfname = NULL; | 639 | 185 | char tmpfilename[MAXPATHLEN]; | 640 | 185 | struct jpeg_decompress_struct dinfo; | 641 | 185 | struct jpeg_compress_struct cinfo; | 642 | 185 | struct my_error_mgr jcerr, jderr; | 643 | 185 | JSAMPARRAY buf = NULL; | 644 | | | 645 | 185 | unsigned char *outbuffer = NULL; | 646 | 185 | size_t outbuffersize = 0; | 647 | 185 | unsigned char *inbuffer = NULL; | 648 | 185 | size_t inbuffersize = 0; | 649 | 185 | size_t inbufferused = 0; | 650 | 185 | unsigned char *tmpbuffer = NULL; | 651 | 185 | size_t tmpbuffersize = 0; | 652 | 185 | unsigned char *extrabuffer = NULL; | 653 | 185 | size_t extrabuffersize = 0; | 654 | | | 655 | 185 | jvirt_barray_ptr *coef_arrays = NULL; | 656 | 185 | char marker_str[256]; | 657 | 185 | unsigned int marker_in_count, marker_in_size; | 658 | | | 659 | 185 | long in_image_size = 0; | 660 | 185 | long insize = 0, outsize = 0, lastsize = 0; | 661 | 185 | int oldquality, searchdone; | 662 | 185 | double ratio; | 663 | 185 | size_t last_retry_size = 0; | 664 | 185 | int retry_count = 0; | 665 | 185 | int retry = 0; | 666 | 185 | int res = -1; | 667 | | | 668 | 185 | jpeg_log_fh = log_fh; | 669 | | | 670 | | /* Initialize decompression object */ | 671 | 185 | dinfo.err = jpeg_std_error(&jderr.pub); | 672 | 185 | jpeg_create_decompress(&dinfo); | 673 | 185 | jderr.pub.error_exit=my_error_exit; | 674 | 185 | jderr.pub.output_message=my_output_message; | 675 | 185 | jderr.jump_set = 0; | 676 | | | 677 | | /* Initialize compression object */ | 678 | 185 | cinfo.err = jpeg_std_error(&jcerr.pub); | 679 | 185 | jpeg_create_compress(&cinfo); | 680 | 185 | jcerr.pub.error_exit=my_error_exit; | 681 | 185 | jcerr.pub.output_message=my_output_message; | 682 | 185 | jcerr.jump_set = 0; | 683 | | | 684 | 185 | if (rate) | 685 | 185 | *rate = 0.0; | 686 | 185 | if (saved) | 687 | 185 | *saved = 0.0; | 688 | | | 689 | 185 | if (filename) { | 690 | 185 | if ((infile = fopen(filename, "rb")) == NULL) { | 691 | 0 | warn("cannot open file: %s", filename); | 692 | 0 | res = 1; | 693 | 0 | goto exit_point; | 694 | 0 | } | 695 | 185 | } else { | 696 | 0 | infile = stdin; | 697 | 0 | set_filemode_binary(infile); | 698 | 0 | } | 699 | | | 700 | 258 | retry_point: | 701 | | | 702 | 258 | if (setjmp(jderr.setjmp_buffer)) { | 703 | | /* Error handler for decompress */ | 704 | 39 | abort_decompress: | 705 | 39 | jpeg_abort_decompress(&dinfo); | 706 | 39 | fclose(infile); | 707 | 39 | free_line_buf(&buf, dinfo.output_height); | 708 | 39 | if (!quiet_mode || csv) | 709 | 39 | fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n"); | 710 | 39 | jderr.jump_set=0; | 711 | 39 | res = 1; | 712 | 39 | goto exit_point; | 713 | 219 | } else { | 714 | 219 | jderr.jump_set=1; | 715 | 219 | } | 716 | | | 717 | | | 718 | | /* Prepare to decompress */ | 719 | 219 | if (!retry) { | 720 | 185 | if (!quiet_mode || csv) { | 721 | 185 | fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin")); | 722 | 185 | fflush(log_fh); | 723 | 185 | } | 724 | | | 725 | 185 | if (stdin_mode || stdout_mode) { | 726 | 0 | inbuffersize = IN_BUF_SIZE; | 727 | 185 | } else { | 728 | 185 | if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE) | 729 | 172 | inbuffersize = IN_BUF_SIZE; | 730 | 185 | } | 731 | 185 | if (inbuffer) | 732 | 0 | free(inbuffer); | 733 | 185 | if (!(inbuffer=calloc(inbuffersize, 1))) | 734 | 0 | fatal("not enough memory"); | 735 | 185 | } | 736 | 219 | global_error_counter=0; | 737 | 219 | jpeg_save_markers(&dinfo, JPEG_COM, 0xffff); | 738 | 4.34k | for (int i = 0; i < 16; i++) { | 739 | 4.12k | jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff); | 740 | 4.12k | } | 741 | 219 | if (!retry) { | 742 | 185 | jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE); | 743 | 185 | } else { | 744 | 34 | if (retry == 1) | 745 | 73 | jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused); | 746 | 18.4E | else | 747 | 18.4E | jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize); | 748 | 34 | } | 749 | 219 | jpeg_read_header(&dinfo, TRUE); | 750 | | | 751 | | | 752 | | /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */ | 753 | 219 | marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str), | 754 | 219 | &marker_in_size); | 755 | | | 756 | 219 | if (!retry) { | 757 | 171 | if (verbose_mode > 1) { | 758 | 0 | fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n", | 759 | 0 | marker_in_count,marker_in_size); | 760 | 0 | fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman")); | 761 | 0 | } | 762 | | | 763 | 171 | if (!quiet_mode || csv) { | 764 | 171 | fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width, | 765 | 171 | (int)dinfo.image_height,(int)dinfo.num_components*8, | 766 | 171 | (dinfo.progressive_mode?'P':'N')); | 767 | 171 | if (!csv) | 768 | 171 | fprintf(log_fh,"%s",marker_str); | 769 | 171 | fflush(log_fh); | 770 | 171 | } | 771 | 171 | } | 772 | | | 773 | | /* Decompress the image */ | 774 | 243 | if (quality >= 0 && retry != 1) { | 775 | 170 | jpeg_start_decompress(&dinfo); | 776 | | | 777 | | /* Allocate line buffer to store the decompressed image */ | 778 | 170 | if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW)))) | 779 | 0 | fatal("not enough memory"); | 780 | 380k | for (int i = 0; i < dinfo.output_height; i++) { | 781 | 380k | if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components, | 782 | 380k | sizeof(JSAMPLE)))) | 783 | 0 | fatal("not enough memory"); | 784 | 380k | } | 785 | | | 786 | 175k | while (dinfo.output_scanline < dinfo.output_height) { | 787 | 175k | jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline], | 788 | 175k | dinfo.output_height-dinfo.output_scanline); | 789 | 175k | } | 790 | 170 | } else { | 791 | 49 | coef_arrays = jpeg_read_coefficients(&dinfo); | 792 | 49 | if (!coef_arrays) { | 793 | 0 | if (!quiet_mode) | 794 | 0 | fprintf(log_fh, " (failed to read coefficients) "); | 795 | 0 | goto abort_decompress; | 796 | 0 | } | 797 | 49 | } | 798 | 219 | if (!retry) { | 799 | 163 | in_image_size = inbufferused - dinfo.src->bytes_in_buffer; | 800 | 163 | if(verbose_mode > 2) | 801 | 0 | fprintf(log_fh, " (input image size: %lu (%lu))", | 802 | 0 | in_image_size, inbufferused); | 803 | 163 | if (stdin_mode) { | 804 | 0 | insize = in_image_size; | 805 | 163 | } else { | 806 | 163 | if ((insize = file_size(infile)) < 0) | 807 | 0 | fatal("failed to stat() input file"); | 808 | 163 | if (in_image_size > 0 && in_image_size < insize) { | 809 | 51 | if (!quiet_mode) | 810 | 51 | fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ", | 811 | 51 | insize - in_image_size); | 812 | 51 | if (nofix_mode) | 813 | 0 | global_error_counter++; | 814 | 51 | if (save_extra) { | 815 | 0 | extrabuffersize = insize - in_image_size; | 816 | 0 | if (extrabuffer) | 817 | 0 | free(extrabuffer); | 818 | 0 | if (!(extrabuffer = calloc(extrabuffersize, 1))) | 819 | 0 | fatal("not enough memory"); | 820 | 0 | if (fseek(infile, in_image_size, SEEK_SET)) | 821 | 0 | fatal("failed to seek input file"); | 822 | 0 | if (fread(extrabuffer, extrabuffersize, 1, infile) != 1) | 823 | 0 | fatal("failed to read inputfile"); | 824 | 0 | } | 825 | 51 | } | 826 | 163 | } | 827 | 163 | if (!quiet_mode) { | 828 | 163 | fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] ")); | 829 | 163 | fflush(log_fh); | 830 | 163 | } | 831 | | | 832 | 163 | if (nofix_mode && global_error_counter != 0) { | 833 | | /* Skip files containing any errors (or warnings) */ | 834 | 0 | goto abort_decompress; | 835 | 0 | } | 836 | | | 837 | 163 | if (dest && !noaction) { | 838 | 0 | if (file_exists(newname) && !overwrite_mode) { | 839 | 0 | if (!quiet_mode) | 840 | 0 | fprintf(log_fh, " (target file already exists) "); | 841 | 0 | goto abort_decompress; | 842 | 0 | } | 843 | 0 | } | 844 | 163 | } | 845 | | | 846 | | | 847 | | /* Prepare to compress... */ | 848 | 219 | if (setjmp(jcerr.setjmp_buffer)) { | 849 | | /* Error handler for compress failures */ | 850 | 42 | if (!quiet_mode) | 851 | 42 | fprintf(log_fh," [Compress ERROR: %s]\n",last_error); | 852 | 42 | jpeg_abort_compress(&cinfo); | 853 | 42 | jpeg_abort_decompress(&dinfo); | 854 | 42 | fclose(infile); | 855 | 42 | free_line_buf(&buf, dinfo.output_height); | 856 | 42 | jcerr.jump_set=0; | 857 | 42 | res = 2; | 858 | 42 | goto exit_point; | 859 | 177 | } else { | 860 | 177 | jcerr.jump_set=1; | 861 | 177 | } | 862 | | | 863 | 177 | lastsize = 0; | 864 | 177 | searchdone = 0; | 865 | 177 | if (!retry) { | 866 | 163 | oldquality = 200; | 867 | 163 | if (target_size != 0) { | 868 | | /* Always start with quality 100 if -S option specified... */ | 869 | 163 | quality = 100; | 870 | 163 | } | 871 | 163 | } | 872 | | | 873 | | | 874 | 814 | binary_search_loop: | 875 | | | 876 | | /* Allocate memory buffer that should be large enough to store the output JPEG... */ | 877 | 814 | if (outbuffer) | 878 | 651 | free(outbuffer); | 879 | 814 | outbuffersize = insize + 32768; | 880 | 814 | if (!(outbuffer=calloc(outbuffersize, 1))) | 881 | 0 | fatal("not enough memory"); | 882 | | | 883 | | /* setup custom "destination manager" for libjpeg to write to our buffer */ | 884 | 814 | jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536); | 885 | | | 886 | | | 887 | 814 | if (quality >= 0 && retry != 1) { | 888 | | /* Lossy "optimization" ... */ | 889 | | | 890 | 742 | cinfo.in_color_space=dinfo.out_color_space; | 891 | 742 | cinfo.input_components=dinfo.output_components; | 892 | 742 | cinfo.image_width=dinfo.image_width; | 893 | 742 | cinfo.image_height=dinfo.image_height; | 894 | 742 | jpeg_set_defaults(&cinfo); | 895 | 742 | jpeg_set_quality(&cinfo,quality,TRUE); | 896 | 742 | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 897 | 742 | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 898 | 742 | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 899 | 742 | #endif | 900 | 742 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 901 | | /* Explicitly disable progressive mode. */ | 902 | 385 | cinfo.scan_info = NULL; | 903 | 385 | cinfo.num_scans = 0; | 904 | 385 | } else if (all_progressive || dinfo.progressive_mode) { | 905 | | /* Enable progressive mode. */ | 906 | 357 | jpeg_simple_progression(&cinfo); | 907 | 357 | } | 908 | 742 | cinfo.optimize_coding = TRUE; | 909 | 742 | #ifdef HAVE_ARITH_CODE | 910 | 742 | if (arith_mode >= 0) | 911 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 912 | 742 | #endif | 913 | 742 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 914 | 362 | cinfo.write_JFIF_header = TRUE; | 915 | 380 | } else { | 916 | 380 | cinfo.write_JFIF_header = FALSE; | 917 | 380 | } | 918 | 742 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 919 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 920 | 0 | cinfo.write_JFIF_header = FALSE; | 921 | 0 | } | 922 | | | 923 | 742 | jpeg_start_compress(&cinfo,TRUE); | 924 | | | 925 | | /* Write markers */ | 926 | 742 | write_markers(&dinfo,&cinfo); | 927 | | | 928 | | /* Write image */ | 929 | 1.48k | while (cinfo.next_scanline < cinfo.image_height) { | 930 | 741 | jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline], | 931 | 741 | dinfo.output_height); | 932 | 741 | } | 933 | | | 934 | 742 | } else { | 935 | | /* Lossless optimization ... */ | 936 | | | 937 | 72 | jpeg_copy_critical_parameters(&dinfo, &cinfo); | 938 | 72 | #ifdef HAVE_JINT_DC_SCAN_OPT_MODE | 939 | 72 | if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE)) | 940 | 61 | jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1); | 941 | 72 | #endif | 942 | 72 | if (all_normal || (!dinfo.progressive_mode && !all_progressive)) { | 943 | | /* Explicitly disable progressive mode. */ | 944 | 28 | cinfo.scan_info = NULL; | 945 | 28 | cinfo.num_scans = 0; | 946 | 44 | } else if (all_progressive || dinfo.progressive_mode) { | 947 | | /* Enable progressive mode. */ | 948 | 33 | jpeg_simple_progression(&cinfo); | 949 | 33 | } | 950 | 72 | cinfo.optimize_coding = TRUE; | 951 | 72 | #ifdef HAVE_ARITH_CODE | 952 | 72 | if (arith_mode >= 0) | 953 | 0 | cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE); | 954 | 72 | #endif | 955 | 72 | if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) { | 956 | 26 | cinfo.write_JFIF_header = TRUE; | 957 | 46 | } else { | 958 | 46 | cinfo.write_JFIF_header = FALSE; | 959 | 46 | } | 960 | 72 | if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) { | 961 | | /* If outputting Adobe marker, don't write JFIF marker... */ | 962 | 0 | cinfo.write_JFIF_header = FALSE; | 963 | 0 | } | 964 | | | 965 | | /* Write image */ | 966 | 72 | jpeg_write_coefficients(&cinfo, coef_arrays); | 967 | | | 968 | | /* Write markers */ | 969 | 72 | write_markers(&dinfo,&cinfo); | 970 | | | 971 | 72 | } | 972 | | | 973 | 814 | jpeg_finish_compress(&cinfo); | 974 | 814 | outsize = outbuffersize + extrabuffersize; | 975 | 814 | if (verbose_mode > 2) | 976 | 0 | fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize); | 977 | | | 978 | 814 | if (target_size != 0 && !retry) { | 979 | | /* Perform (binary) search to try to reach target file size... */ | 980 | | | 981 | 741 | long osize = outsize/1024; | 982 | 741 | long isize = insize/1024; | 983 | 741 | long tsize = target_size; | 984 | | | 985 | 741 | if (verbose_mode > 1) | 986 | 0 | fprintf(log_fh, "(size=%ld)",outsize); | 987 | 741 | if (tsize < 0) { | 988 | 741 | tsize=((-target_size)*insize/100)/1024; | 989 | 741 | if (tsize < 1) | 990 | 222 | tsize = 1; | 991 | 741 | } | 992 | | | 993 | 741 | if (osize == tsize || searchdone || tsize > isize) { | 994 | 174 | if (searchdone < 42 && lastsize > 0) { | 995 | 136 | if (labs(osize-tsize) > labs(lastsize-tsize)) { | 996 | 12 | if (verbose_mode) | 997 | 0 | fprintf(log_fh,"(revert to %d)",oldquality); | 998 | 12 | searchdone = 42; | 999 | 12 | quality = oldquality; | 1000 | 12 | goto binary_search_loop; | 1001 | 12 | } | 1002 | 136 | } | 1003 | 162 | if (verbose_mode) | 1004 | 0 | fprintf(log_fh," "); | 1005 | | | 1006 | 567 | } else { | 1007 | 567 | int newquality; | 1008 | 567 | double dif = abs(oldquality-quality) / 2.0; | 1009 | | | 1010 | 567 | if (osize > tsize) | 1011 | 427 | newquality = quality - dif; | 1012 | 140 | else | 1013 | 140 | newquality = quality + dif + 0.5; | 1014 | | | 1015 | 567 | if (dif < 1.0) | 1016 | 9 | searchdone = 1; | 1017 | 567 | if (newquality < 0) { | 1018 | 52 | newquality = 0; | 1019 | 52 | searchdone = 1; | 1020 | 52 | } | 1021 | 567 | if (newquality > 100) { | 1022 | 60 | newquality = 100; | 1023 | 60 | searchdone = 1; | 1024 | 60 | } | 1025 | | | 1026 | 567 | oldquality = quality; | 1027 | 567 | quality = newquality; | 1028 | 567 | lastsize = osize; | 1029 | 567 | if (verbose_mode) | 1030 | 0 | fprintf(log_fh,"(try %d)",quality); | 1031 | 567 | goto binary_search_loop; | 1032 | 567 | } | 1033 | 741 | } | 1034 | | | 1035 | 235 | jpeg_finish_decompress(&dinfo); | 1036 | 235 | free_line_buf(&buf, dinfo.output_height); | 1037 | | | 1038 | 235 | if (retry_mode) { | 1039 | 0 | if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) { | 1040 | | /* Retry compression until output file stops getting smaller | 1041 | | or we hit max limit of iterations (10)... */ | 1042 | 0 | if (retry_count == 0) | 1043 | 0 | last_retry_size = outsize + 1; | 1044 | 0 | if (++retry_count < 10 && outsize < last_retry_size) { | 1045 | 0 | if (tmpbuffer) | 1046 | 0 | free(tmpbuffer); | 1047 | 0 | tmpbuffer = outbuffer; | 1048 | 0 | tmpbuffersize = outbuffersize; | 1049 | 0 | outbuffer = NULL; | 1050 | 0 | last_retry_size = outsize; | 1051 | 0 | retry = 2; | 1052 | 0 | if (verbose_mode) | 1053 | 0 | fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize); | 1054 | 0 | goto retry_point; | 1055 | 0 | } | 1056 | 0 | } | 1057 | 0 | if (retry == 2) { | 1058 | 0 | if (verbose_mode) | 1059 | 0 | fprintf(log_fh, "(retry done: %lu) ", outsize); | 1060 | 0 | if (outsize > last_retry_size) { | 1061 | 0 | if (outbuffer) | 1062 | 0 | free(outbuffer); | 1063 | 0 | outbuffer = tmpbuffer; | 1064 | 0 | outbuffersize = tmpbuffersize; | 1065 | 0 | outsize = outbuffersize + extrabuffersize; | 1066 | 0 | tmpbuffer = NULL; | 1067 | 0 | } | 1068 | 0 | } | 1069 | 0 | } | 1070 | | | 1071 | | /* If auto_mode, try both progressive and non-progressive... */ | 1072 | 235 | if (auto_mode) { | 1073 | 0 | int newmode = (dinfo.progressive_mode ? 0 : 1); | 1074 | 0 | if (retry != 3) { | 1075 | 0 | if (newmode) | 1076 | 0 | all_progressive = 1; | 1077 | 0 | else | 1078 | 0 | all_normal = 1; | 1079 | 0 | if (tmpbuffer) | 1080 | 0 | free(tmpbuffer); | 1081 | 0 | tmpbuffer = outbuffer; | 1082 | 0 | tmpbuffersize = outbuffersize; | 1083 | 0 | outbuffer = NULL; | 1084 | 0 | last_retry_size = outsize; | 1085 | 0 | retry = 3; | 1086 | 0 | if (verbose_mode) | 1087 | 0 | fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal")); | 1088 | 0 | goto retry_point; | 1089 | 0 | } else { | 1090 | 0 | if (verbose_mode > 1) | 1091 | 0 | fprintf(log_fh, "(automode done: %lu) ", outsize); | 1092 | 0 | auto_mode = 0; | 1093 | 0 | if (outsize > last_retry_size) { | 1094 | 0 | if (verbose_mode) | 1095 | 0 | fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal")); | 1096 | 0 | all_progressive = 0; | 1097 | 0 | all_normal = 0; | 1098 | 0 | if (outbuffer) | 1099 | 0 | free(outbuffer); | 1100 | 0 | outbuffer = tmpbuffer; | 1101 | 0 | outbuffersize = tmpbuffersize; | 1102 | 0 | outsize = outbuffersize + extrabuffersize; | 1103 | 0 | tmpbuffer = NULL; | 1104 | 0 | } | 1105 | 0 | } | 1106 | 0 | } | 1107 | | | 1108 | | /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */ | 1109 | 235 | if (quality >= 0 && outsize >= insize && retry != 1) { | 1110 | 73 | retry = 1; | 1111 | 73 | if (verbose_mode) | 1112 | 0 | fprintf(log_fh, "(retry w/lossless) "); | 1113 | 73 | goto retry_point; | 1114 | 73 | } | 1115 | | | 1116 | 162 | fclose(infile); | 1117 | | | 1118 | 162 | ratio = (insize - outsize) * 100.0 / insize; | 1119 | 162 | if (!quiet_mode || csv) | 1120 | 104 | fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio); | 1121 | 162 | if (rate) { | 1122 | 104 | *rate = (ratio < 0 ? 0.0 : ratio); | 1123 | 104 | } | 1124 | | | 1125 | 162 | if ((outsize < insize && ratio >= threshold) || force) { | 1126 | 76 | if (saved) { | 1127 | 76 | *saved = (insize - outsize) / 1024.0; | 1128 | 76 | } | 1129 | 76 | if (!quiet_mode || csv) | 1130 | 76 | fprintf(log_fh,csv ? "optimized\n" : "optimized.\n"); | 1131 | 76 | if (noaction) { | 1132 | 0 | res = 0; | 1133 | 0 | goto exit_point; | 1134 | 0 | } | 1135 | | | 1136 | 76 | if (stdout_mode) { | 1137 | 0 | outfname=NULL; | 1138 | 0 | set_filemode_binary(stdout); | 1139 | 0 | if (fwrite(outbuffer,outbuffersize,1,stdout) != 1) | 1140 | 0 | fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename)); | 1141 | 76 | } else { | 1142 | 76 | if (preserve_perms && !dest) { | 1143 | | /* make backup of the original file */ | 1144 | 0 | int newlen = snprintf(tmpfilename, sizeof(tmpfilename), | 1145 | 0 | "%s.jpegoptim.bak", newname); | 1146 | 0 | if (newlen >= sizeof(tmpfilename)) | 1147 | 0 | fatal("temp filename too long: %s", tmpfilename); | 1148 | |
| 1149 | 0 | if (verbose_mode > 1) | 1150 | 0 | fprintf(log_fh,"%s, creating backup as: %s\n", | 1151 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1152 | 0 | if (file_exists(tmpfilename)) | 1153 | 0 | fatal("%s, backup file already exists: %s", | 1154 | 0 | (stdin_mode ?" stdin" : filename), tmpfilename); | 1155 | 0 | if (copy_file(newname,tmpfilename)) | 1156 | 0 | fatal("%s, failed to create backup: %s", | 1157 | 0 | (stdin_mode ? "stdin" : filename), tmpfilename); | 1158 | 0 | if ((outfile=create_file(newname))==NULL) | 1159 | 0 | fatal("%s, error opening output file: %s", | 1160 | 0 | (stdin_mode ? "stdin" : filename), newname); | 1161 | 0 | outfname = newname; | 1162 | 76 | } else { | 1163 | 76 | if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename)))) | 1164 | 0 | fatal("error creating temporary file: %s", tmpfilename); | 1165 | 76 | outfname = tmpfilename; | 1166 | 76 | } | 1167 | | | 1168 | 76 | if (verbose_mode > 1) | 1169 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", | 1170 | 0 | (long unsigned int)outbuffersize, outfname); | 1171 | 76 | if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1) | 1172 | 0 | fatal("write failed to file: %s", outfname); | 1173 | 76 | if (save_extra && extrabuffersize > 0) { | 1174 | 0 | if (verbose_mode > 1) | 1175 | 0 | fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname); | 1176 | 0 | if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1) | 1177 | 0 | fatal("write failed to file: %s", outfname); | 1178 | 0 | } | 1179 | 76 | fclose(outfile); | 1180 | 76 | } | 1181 | | | 1182 | 76 | if (outfname) { | 1183 | 76 | if (preserve_mode) { | 1184 | | /* preserve file modification time */ | 1185 | 0 | if (verbose_mode > 1) | 1186 | 0 | fprintf(log_fh,"set file modification time same as in original: %s\n", | 1187 | 0 | outfname); | 1188 | 0 | #if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM) | 1189 | 0 | struct timespec time_save[2]; | 1190 | 0 | time_save[0].tv_sec = 0; | 1191 | 0 | time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */ | 1192 | 0 | time_save[1] = file_stat->st_mtim; | 1193 | 0 | if (utimensat(AT_FDCWD,outfname,time_save,0) != 0) | 1194 | 0 | warn("failed to reset output file time/date"); | 1195 | | #else | 1196 | | struct utimbuf time_save; | 1197 | | time_save.actime=file_stat->st_atime; | 1198 | | time_save.modtime=file_stat->st_mtime; | 1199 | | if (utime(outfname,&time_save) != 0) | 1200 | | warn("failed to reset output file time/date"); | 1201 | | #endif | 1202 | 0 | } | 1203 | | | 1204 | 76 | if (preserve_perms && !dest) { | 1205 | | /* original file was already replaced, remove backup... */ | 1206 | 0 | if (verbose_mode > 1) | 1207 | 0 | fprintf(log_fh,"removing backup file: %s\n", tmpfilename); | 1208 | 0 | if (delete_file(tmpfilename)) | 1209 | 0 | warn("failed to remove backup file: %s", tmpfilename); | 1210 | 76 | } else { | 1211 | | /* make temp file to be the original file... */ | 1212 | | | 1213 | | /* preserve file mode */ | 1214 | 76 | if (chmod(outfname,(file_stat->st_mode & 0777)) != 0) | 1215 | 0 | warn("failed to set output file mode"); | 1216 | | | 1217 | | /* preserve file group (and owner if run by root) */ | 1218 | 76 | if (chown(outfname, | 1219 | 76 | (geteuid()==0 ? file_stat->st_uid : -1), | 1220 | 76 | file_stat->st_gid) != 0) | 1221 | 0 | warn("failed to reset output file group/owner"); | 1222 | | | 1223 | 76 | if (verbose_mode > 1) | 1224 | 0 | fprintf(log_fh,"renaming: %s to %s\n", outfname, newname); | 1225 | 76 | if (rename_file(outfname, newname)) | 1226 | 0 | fatal("cannot rename temp file"); | 1227 | 76 | } | 1228 | 76 | } | 1229 | 86 | } else { | 1230 | 86 | if (!quiet_mode || csv) | 1231 | 28 | fprintf(log_fh,csv ? "skipped\n" : "skipped.\n"); | 1232 | 86 | if (stdout_mode) { | 1233 | 0 | set_filemode_binary(stdout); | 1234 | 0 | if (fwrite(inbuffer, in_image_size, 1, stdout) != 1) | 1235 | 0 | fatal("%s, write failed to stdout", | 1236 | 0 | (stdin_mode ? "stdin" : filename)); | 1237 | 0 | } | 1238 | 86 | } | 1239 | | | 1240 | 162 | res = 0; | 1241 | | | 1242 | 185 | exit_point: | 1243 | 185 | if (inbuffer) | 1244 | 185 | free(inbuffer); | 1245 | 185 | if (outbuffer) | 1246 | 163 | free(outbuffer); | 1247 | 185 | if (tmpbuffer) | 1248 | 0 | free(tmpbuffer); | 1249 | 185 | if (extrabuffer) | 1250 | 0 | free(extrabuffer); | 1251 | 185 | jpeg_destroy_compress(&cinfo); | 1252 | 185 | jpeg_destroy_decompress(&dinfo); | 1253 | | | 1254 | 185 | return res; | 1255 | 162 | } |
|
1256 | | |
1257 | | |
1258 | | #ifdef PARALLEL_PROCESSING |
1259 | | int wait_for_worker(FILE *log_fh) |
1260 | 0 | { |
1261 | 0 | FILE *p; |
1262 | 0 | struct worker *w; |
1263 | 0 | char buf[1024]; |
1264 | 0 | int wstatus; |
1265 | 0 | pid_t pid; |
1266 | 0 | int j, e; |
1267 | 0 | int state = 0; |
1268 | 0 | double val; |
1269 | 0 | double rate = 0.0; |
1270 | 0 | double saved = 0.0; |
1271 | | |
1272 | |
|
1273 | 0 | if ((pid = wait(&wstatus)) < 0) |
1274 | 0 | return pid; |
1275 | | |
1276 | 0 | w = NULL; |
1277 | 0 | for (j = 0; j < MAX_WORKERS; j++) { |
1278 | 0 | if (workers[j].pid == pid) { |
1279 | 0 | w = &workers[j]; |
1280 | 0 | break; |
1281 | 0 | } |
1282 | 0 | } |
1283 | 0 | if (!w) |
1284 | 0 | fatal("Unknown worker[%d] process found\n", pid); |
1285 | |
|
1286 | 0 | if (WIFEXITED(wstatus)) { |
1287 | 0 | e = WEXITSTATUS(wstatus); |
1288 | 0 | if (verbose_mode) |
1289 | 0 | fprintf(log_fh, "worker[%d] [slot=%d] exited: %d\n", |
1290 | 0 | pid, j, e); |
1291 | 0 | if (e == 0) { |
1292 | | //average_count++; |
1293 | | //average_rate += rate; |
1294 | | //total_save += saved; |
1295 | 0 | } else if (e == 1) { |
1296 | 0 | decompress_err_count++; |
1297 | 0 | } else if (e == 2) { |
1298 | 0 | compress_err_count++; |
1299 | 0 | } |
1300 | 0 | } else { |
1301 | 0 | fatal("worker[%d] killed", pid); |
1302 | 0 | } |
1303 | |
|
1304 | 0 | p = fdopen(w->read_pipe, "r"); |
1305 | 0 | if (!p) fatal("fdopen failed()"); |
1306 | 0 | while (fgets(buf, sizeof(buf), p)) { |
1307 | 0 | if (verbose_mode > 2) |
1308 | 0 | fprintf(log_fh, "worker[%d] PIPE: %s", pid, buf); |
1309 | 0 | if (state == 0 && buf[0] == '\n') { |
1310 | 0 | state=1; |
1311 | 0 | continue; |
1312 | 0 | } |
1313 | 0 | if (state == 1 && !strncmp(buf, "STAT", 4)) { |
1314 | 0 | state=2; |
1315 | 0 | continue; |
1316 | 0 | } |
1317 | 0 | if (state >= 2) { |
1318 | 0 | if (sscanf(buf, "%lf", &val) == 1) { |
1319 | 0 | if (state == 2) { |
1320 | 0 | rate = val; |
1321 | 0 | } |
1322 | 0 | else if (state == 3) { |
1323 | 0 | saved = val; |
1324 | 0 | average_count++; |
1325 | 0 | average_rate += rate; |
1326 | 0 | total_save += saved; |
1327 | 0 | } |
1328 | 0 | } |
1329 | 0 | state++; |
1330 | 0 | continue; |
1331 | 0 | } |
1332 | 0 | if (state == 0) |
1333 | 0 | fprintf(log_fh, "%s", buf); |
1334 | 0 | } |
1335 | 0 | close(w->read_pipe); |
1336 | 0 | w->pid = -1; |
1337 | 0 | w->read_pipe = -1; |
1338 | 0 | worker_count --; |
1339 | |
|
1340 | 0 | return pid; |
1341 | 0 | } |
1342 | | #endif |
1343 | | |
1344 | | |
1345 | | #ifndef BUILD_FOR_OSS_FUZZ // Libfuzzer provides its own fuzzer |
1346 | | /****************************************************************************/ |
1347 | | int main(int argc, char **argv) |
1348 | | { |
1349 | | struct stat file_stat; |
1350 | | char tmpfilename[MAXPATHLEN + 1],tmpdir[MAXPATHLEN + 1]; |
1351 | | char newname[MAXPATHLEN + 1], dest_path[MAXPATHLEN + 1]; |
1352 | | char namebuf[MAXPATHLEN + 2]; |
1353 | | const char *filename; |
1354 | | int arg_idx; |
1355 | | int res; |
1356 | | double rate, saved; |
1357 | | FILE *log_fh; |
1358 | | #ifdef PARALLEL_PROCESSING |
1359 | | struct worker *w; |
1360 | | int pipe_fd[2]; |
1361 | | pid_t pid; |
1362 | | |
1363 | | |
1364 | | /* Allocate table to keep track of child processes... */ |
1365 | | if (!(workers = calloc(MAX_WORKERS, sizeof(struct worker)))) |
1366 | | fatal("not enough memory"); |
1367 | | for (int i = 0; i < MAX_WORKERS; i++) { |
1368 | | workers[i].pid = -1; |
1369 | | workers[i].read_pipe = -1; |
1370 | | } |
1371 | | #endif |
1372 | | |
1373 | | umask(077); |
1374 | | signal(SIGINT,own_signal_handler); |
1375 | | signal(SIGTERM,own_signal_handler); |
1376 | | |
1377 | | /* Parse command line parameters */ |
1378 | | parse_arguments(argc, argv, dest_path, sizeof(dest_path)); |
1379 | | log_fh = (stdout_mode ? stderr : stdout); |
1380 | | if (quiet_mode) |
1381 | | verbose_mode = 0; |
1382 | | |
1383 | | if (verbose_mode) { |
1384 | | if (quality >= 0 && target_size == 0) |
1385 | | fprintf(log_fh, "Image quality limit set to: %d\n", quality); |
1386 | | if (threshold >= 0) |
1387 | | fprintf(log_fh, "Compression threshold (%%) set to: %0.1lf\n", threshold); |
1388 | | if (all_normal) |
1389 | | fprintf(log_fh, "All output files will be non-progressive\n"); |
1390 | | if (all_progressive) |
1391 | | fprintf(log_fh, "All output files will be progressive\n"); |
1392 | | if (target_size > 0) |
1393 | | fprintf(log_fh, "Target size for output files set to: %d Kbytes.\n", |
1394 | | target_size); |
1395 | | if (target_size < 0) |
1396 | | fprintf(log_fh, "Target size for output files set to: %d%%\n", |
1397 | | -target_size); |
1398 | | #ifdef PARALLEL_PROCESSING |
1399 | | if (max_workers > 0) |
1400 | | fprintf(log_fh, "Using maximum of %d parallel threads\n", max_workers); |
1401 | | #endif |
1402 | | } |
1403 | | |
1404 | | |
1405 | | if (stdin_mode) { |
1406 | | /* Process just one file, if source is stdin... */ |
1407 | | res = optimize(stderr, NULL, NULL, NULL, &file_stat, NULL, NULL); |
1408 | | return (res == 0 ? 0 : 1); |
1409 | | } |
1410 | | |
1411 | | arg_idx = (optind > 0 ? optind : 1); |
1412 | | if (files_from == NULL && argc <= arg_idx) { |
1413 | | if (!quiet_mode) |
1414 | | fprintf(stderr, PROGRAMNAME ": file argument(s) missing\n" |
1415 | | "Try '" PROGRAMNAME " --help' for more information.\n"); |
1416 | | exit(1); |
1417 | | } |
1418 | | |
1419 | | |
1420 | | /* Main loop to process input files */ |
1421 | | do { |
1422 | | if (files_from) { |
1423 | | if (!fgetstr(namebuf, sizeof(namebuf), files_from)) |
1424 | | break; |
1425 | | filename = namebuf; |
1426 | | } else { |
1427 | | filename = argv[arg_idx]; |
1428 | | } |
1429 | | |
1430 | | if (*filename == 0) |
1431 | | continue; |
1432 | | if (verbose_mode > 1) |
1433 | | fprintf(log_fh, "processing file: %s\n", filename); |
1434 | | if (strnlen(filename, MAXPATHLEN + 1) > MAXPATHLEN) { |
1435 | | warn("skipping too long filename: %s", filename); |
1436 | | continue; |
1437 | | } |
1438 | | |
1439 | | if (!noaction) { |
1440 | | /* generate tmp dir & new filename */ |
1441 | | if (dest) { |
1442 | | strncopy(tmpdir, dest_path, sizeof(tmpdir)); |
1443 | | strncopy(newname, dest_path, sizeof(newname)); |
1444 | | if (!splitname(filename, tmpfilename, sizeof(tmpfilename))) |
1445 | | fatal("splitname() failed for: %s", filename); |
1446 | | strncatenate(newname, tmpfilename, sizeof(newname)); |
1447 | | } else { |
1448 | | if (!splitdir(filename, tmpdir, sizeof(tmpdir))) |
1449 | | fatal("splitdir() failed for: %s", filename); |
1450 | | strncopy(newname, filename, sizeof(newname)); |
1451 | | } |
1452 | | } |
1453 | | |
1454 | | if (file_exists(filename)) { |
1455 | | if (!is_file(filename, &file_stat)) { |
1456 | | if (is_directory(filename)) |
1457 | | warn("skipping directory: %s", filename); |
1458 | | else |
1459 | | warn("skipping special file: %s", filename); |
1460 | | continue; |
1461 | | } |
1462 | | } else { |
1463 | | warn("file not found: %s", filename); |
1464 | | continue; |
1465 | | } |
1466 | | |
1467 | | #ifdef PARALLEL_PROCESSING |
1468 | | if (max_workers > 1) { |
1469 | | /* Multi process mode, run up to max_workers processes simultaneously... */ |
1470 | | |
1471 | | if (worker_count >= max_workers) { |
1472 | | // wait for a worker to exit... |
1473 | | wait_for_worker(log_fh); |
1474 | | } |
1475 | | if (pipe(pipe_fd) < 0) |
1476 | | fatal("failed to open pipe"); |
1477 | | pid = fork(); |
1478 | | if (pid < 0) |
1479 | | fatal("fork() failed"); |
1480 | | if (pid == 0) { |
1481 | | /* Child process starts here... */ |
1482 | | if (files_from) |
1483 | | fclose(files_from); |
1484 | | close(pipe_fd[0]); |
1485 | | FILE *p; |
1486 | | |
1487 | | if (!(p = fdopen(pipe_fd[1],"w"))) |
1488 | | fatal("worker: fdopen failed"); |
1489 | | |
1490 | | res = optimize(p, filename, newname, tmpdir, &file_stat, &rate, &saved); |
1491 | | if (res == 0) |
1492 | | fprintf(p, "\n\nSTATS\n%lf\n%lf\n", rate, saved); |
1493 | | exit(res); |
1494 | | } else { |
1495 | | /* Parent continues here... */ |
1496 | | int j; |
1497 | | |
1498 | | close(pipe_fd[1]); |
1499 | | |
1500 | | w = NULL; |
1501 | | for (j = 0; j < MAX_WORKERS; j++) { |
1502 | | if (workers[j].pid < 0) { |
1503 | | w = &workers[j]; |
1504 | | break; |
1505 | | } |
1506 | | } |
1507 | | if (!w) |
1508 | | fatal("no space to start a new worker (%d)", worker_count); |
1509 | | w->pid = pid; |
1510 | | w->read_pipe = pipe_fd[0]; |
1511 | | worker_count++; |
1512 | | if (verbose_mode > 0) |
1513 | | fprintf(log_fh, "worker[%d] [slot=%d] started\n", pid, j);; |
1514 | | } |
1515 | | |
1516 | | } else |
1517 | | #endif |
1518 | | { |
1519 | | /* Single process mode, process one file at a time... */ |
1520 | | |
1521 | | res = optimize(log_fh, filename, newname, tmpdir, &file_stat, &rate, &saved); |
1522 | | if (res == 0) { |
1523 | | average_count++; |
1524 | | average_rate += rate; |
1525 | | total_save += saved; |
1526 | | } else if (res == 1) { |
1527 | | decompress_err_count++; |
1528 | | } else if (res == 2) { |
1529 | | compress_err_count++; |
1530 | | } |
1531 | | } |
1532 | | |
1533 | | } while (files_from || ++arg_idx < argc); |
1534 | | |
1535 | | |
1536 | | #ifdef PARALLEL_PROCESSING |
1537 | | /* Wait for any child processes to exit... */ |
1538 | | if (max_workers > 1) { |
1539 | | if (verbose_mode) { |
1540 | | fprintf(log_fh, "Waiting for %d workers to finish...\n", worker_count); |
1541 | | } |
1542 | | while ((pid = wait_for_worker(log_fh)) > 0) { |
1543 | | if (verbose_mode > 2) |
1544 | | fprintf(log_fh, "worker[%d] done\n", pid); |
1545 | | } |
1546 | | } |
1547 | | #endif |
1548 | | |
1549 | | if (totals_mode && !quiet_mode) |
1550 | | fprintf(log_fh, "Average ""compression"" (%ld files): %0.2f%% (total saved %0.0fk)\n", |
1551 | | average_count, average_rate/average_count, total_save); |
1552 | | |
1553 | | |
1554 | | return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);; |
1555 | | } |
1556 | | #else |
1557 | | void fuzz_set_target_size(const int new_target_size) |
1558 | 3 | { |
1559 | 3 | target_size = new_target_size; |
1560 | 3 | } |
1561 | | #endif /* BUILD_FOR_OSS_FUZZ */ |
1562 | | /* eof :-) */ |