Coverage Report

Created: 2026-02-15 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
6.38k
#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
1.92k
{
203
1.92k
  if (*buf == NULL)
204
1.19k
    return;
205
206
4.21M
  for (unsigned int i = 0; i < lines; i++) {
207
4.21M
    if ((*buf)[i])
208
4.21M
      free((*buf)[i]);
209
4.21M
  }
210
731
  free(*buf);
211
731
  *buf = NULL;
212
731
}
213
214
215
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
216
955
{
217
955
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
218
219
955
  (*cinfo->err->output_message)(cinfo);
220
955
  if (myerr->jump_set)
221
955
    longjmp(myerr->setjmp_buffer, 1);
222
0
  else
223
0
    fatal("fatal error");
224
955
}
225
226
227
METHODDEF(void) my_output_message (j_common_ptr cinfo)
228
2.85k
{
229
2.85k
  char buffer[JMSG_LENGTH_MAX+1];
230
231
2.85k
  (*cinfo->err->format_message)((j_common_ptr)cinfo, buffer);
232
2.85k
  buffer[sizeof(buffer) - 1] = 0;
233
234
2.85k
  if (verbose_mode)
235
0
    fprintf(jpeg_log_fh, " (%s) ", buffer);
236
237
2.85k
  global_error_counter++;
238
2.85k
  strncopy(last_error, buffer, sizeof(last_error));
239
2.85k
}
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
8.38k
{
534
8.38k
  jpeg_saved_marker_ptr mrk;
535
8.38k
  int write_marker;
536
8.38k
  const char *s_name;
537
538
8.38k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
8.38k
  mrk=dinfo->marker_list;
542
11.3M
  while (mrk) {
543
11.2M
    write_marker = 0;
544
11.2M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
11.2M
    if (save_com && mrk->marker == JPEG_COM)
549
183k
      write_marker++;
550
551
11.2M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
39.7k
      write_marker++;
553
554
11.2M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
8.26k
      write_marker++;
556
557
11.2M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
17.9k
      write_marker++;
559
560
11.2M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
20.7k
      write_marker++;
562
563
11.2M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
11.2M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
11.2M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
11.2M
    if (!strncmp(s_name, "JFIF", 5))
576
461k
      write_marker=0;
577
578
579
11.2M
    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
11.2M
    if (write_marker)
583
269k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
11.2M
    mrk=mrk->next;
586
11.2M
  }
587
8.38k
}
write_markers
Line
Count
Source
533
2.79k
{
534
2.79k
  jpeg_saved_marker_ptr mrk;
535
2.79k
  int write_marker;
536
2.79k
  const char *s_name;
537
538
2.79k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
2.79k
  mrk=dinfo->marker_list;
542
3.76M
  while (mrk) {
543
3.76M
    write_marker = 0;
544
3.76M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
3.76M
    if (save_com && mrk->marker == JPEG_COM)
549
61.0k
      write_marker++;
550
551
3.76M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
13.2k
      write_marker++;
553
554
3.76M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
2.75k
      write_marker++;
556
557
3.76M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.98k
      write_marker++;
559
560
3.76M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
6.92k
      write_marker++;
562
563
3.76M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
3.76M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
3.76M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
3.76M
    if (!strncmp(s_name, "JFIF", 5))
576
153k
      write_marker=0;
577
578
579
3.76M
    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
3.76M
    if (write_marker)
583
89.9k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
3.76M
    mrk=mrk->next;
586
3.76M
  }
587
2.79k
}
write_markers
Line
Count
Source
533
2.79k
{
534
2.79k
  jpeg_saved_marker_ptr mrk;
535
2.79k
  int write_marker;
536
2.79k
  const char *s_name;
537
538
2.79k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
2.79k
  mrk=dinfo->marker_list;
542
3.76M
  while (mrk) {
543
3.76M
    write_marker = 0;
544
3.76M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
3.76M
    if (save_com && mrk->marker == JPEG_COM)
549
61.0k
      write_marker++;
550
551
3.76M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
13.2k
      write_marker++;
553
554
3.76M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
2.75k
      write_marker++;
556
557
3.76M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.98k
      write_marker++;
559
560
3.76M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
6.92k
      write_marker++;
562
563
3.76M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
3.76M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
3.76M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
3.76M
    if (!strncmp(s_name, "JFIF", 5))
576
153k
      write_marker=0;
577
578
579
3.76M
    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
3.76M
    if (write_marker)
583
89.9k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
3.76M
    mrk=mrk->next;
586
3.76M
  }
587
2.79k
}
write_markers
Line
Count
Source
533
2.79k
{
534
2.79k
  jpeg_saved_marker_ptr mrk;
535
2.79k
  int write_marker;
536
2.79k
  const char *s_name;
537
538
2.79k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
2.79k
  mrk=dinfo->marker_list;
542
3.76M
  while (mrk) {
543
3.76M
    write_marker = 0;
544
3.76M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
3.76M
    if (save_com && mrk->marker == JPEG_COM)
549
61.0k
      write_marker++;
550
551
3.76M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
13.2k
      write_marker++;
553
554
3.76M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
2.75k
      write_marker++;
556
557
3.76M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.98k
      write_marker++;
559
560
3.76M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
6.92k
      write_marker++;
562
563
3.76M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
3.76M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
3.76M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
3.76M
    if (!strncmp(s_name, "JFIF", 5))
576
153k
      write_marker=0;
577
578
579
3.76M
    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
3.76M
    if (write_marker)
583
89.9k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
3.76M
    mrk=mrk->next;
586
3.76M
  }
587
2.79k
}
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
5.37k
{
593
5.37k
  jpeg_saved_marker_ptr m;
594
5.37k
  unsigned int count = 0;
595
5.37k
  char *seen;
596
5.37k
  size_t marker_types = jpeg_special_marker_types_count();
597
5.37k
  int com_seen = 0;
598
5.37k
  int special;
599
600
5.37k
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
5.37k
  str[0] = 0;
604
5.37k
  *markers_total_size = 0;
605
606
5.37k
  m = dinfo->marker_list;
607
3.78M
  while (m) {
608
3.77M
    count++;
609
3.77M
    *markers_total_size += m->data_length;
610
611
3.77M
    if ((special = jpeg_special_marker(m)) >= 0) {
612
3.37M
      if (!seen[special])
613
18.6k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
3.37M
      seen[special]++;
615
3.37M
    }
616
617
3.77M
    if (m->marker == JPEG_COM && !com_seen) {
618
900
      str_add_list(str, str_size, "COM", ",");
619
900
      com_seen = 1;
620
900
    }
621
622
3.77M
    m = m->next;
623
3.77M
  }
624
625
5.37k
  free(seen);
626
627
5.37k
  return count;
628
5.37k
}
parse_markers
Line
Count
Source
592
1.79k
{
593
1.79k
  jpeg_saved_marker_ptr m;
594
1.79k
  unsigned int count = 0;
595
1.79k
  char *seen;
596
1.79k
  size_t marker_types = jpeg_special_marker_types_count();
597
1.79k
  int com_seen = 0;
598
1.79k
  int special;
599
600
1.79k
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
1.79k
  str[0] = 0;
604
1.79k
  *markers_total_size = 0;
605
606
1.79k
  m = dinfo->marker_list;
607
1.26M
  while (m) {
608
1.25M
    count++;
609
1.25M
    *markers_total_size += m->data_length;
610
611
1.25M
    if ((special = jpeg_special_marker(m)) >= 0) {
612
1.12M
      if (!seen[special])
613
6.20k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
1.12M
      seen[special]++;
615
1.12M
    }
616
617
1.25M
    if (m->marker == JPEG_COM && !com_seen) {
618
300
      str_add_list(str, str_size, "COM", ",");
619
300
      com_seen = 1;
620
300
    }
621
622
1.25M
    m = m->next;
623
1.25M
  }
624
625
1.79k
  free(seen);
626
627
1.79k
  return count;
628
1.79k
}
parse_markers
Line
Count
Source
592
1.79k
{
593
1.79k
  jpeg_saved_marker_ptr m;
594
1.79k
  unsigned int count = 0;
595
1.79k
  char *seen;
596
1.79k
  size_t marker_types = jpeg_special_marker_types_count();
597
1.79k
  int com_seen = 0;
598
1.79k
  int special;
599
600
1.79k
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
1.79k
  str[0] = 0;
604
1.79k
  *markers_total_size = 0;
605
606
1.79k
  m = dinfo->marker_list;
607
1.26M
  while (m) {
608
1.25M
    count++;
609
1.25M
    *markers_total_size += m->data_length;
610
611
1.25M
    if ((special = jpeg_special_marker(m)) >= 0) {
612
1.12M
      if (!seen[special])
613
6.20k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
1.12M
      seen[special]++;
615
1.12M
    }
616
617
1.25M
    if (m->marker == JPEG_COM && !com_seen) {
618
300
      str_add_list(str, str_size, "COM", ",");
619
300
      com_seen = 1;
620
300
    }
621
622
1.25M
    m = m->next;
623
1.25M
  }
624
625
1.79k
  free(seen);
626
627
1.79k
  return count;
628
1.79k
}
parse_markers
Line
Count
Source
592
1.79k
{
593
1.79k
  jpeg_saved_marker_ptr m;
594
1.79k
  unsigned int count = 0;
595
1.79k
  char *seen;
596
1.79k
  size_t marker_types = jpeg_special_marker_types_count();
597
1.79k
  int com_seen = 0;
598
1.79k
  int special;
599
600
1.79k
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
1.79k
  str[0] = 0;
604
1.79k
  *markers_total_size = 0;
605
606
1.79k
  m = dinfo->marker_list;
607
1.26M
  while (m) {
608
1.25M
    count++;
609
1.25M
    *markers_total_size += m->data_length;
610
611
1.25M
    if ((special = jpeg_special_marker(m)) >= 0) {
612
1.12M
      if (!seen[special])
613
6.20k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
1.12M
      seen[special]++;
615
1.12M
    }
616
617
1.25M
    if (m->marker == JPEG_COM && !com_seen) {
618
300
      str_add_list(str, str_size, "COM", ",");
619
300
      com_seen = 1;
620
300
    }
621
622
1.25M
    m = m->next;
623
1.25M
  }
624
625
1.79k
  free(seen);
626
627
1.79k
  return count;
628
1.79k
}
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
2.16k
{
636
2.16k
  FILE *infile = NULL;
637
2.16k
  FILE *outfile = NULL;
638
2.16k
  const char *outfname = NULL;
639
2.16k
  char tmpfilename[MAXPATHLEN];
640
2.16k
  struct jpeg_decompress_struct dinfo;
641
2.16k
  struct jpeg_compress_struct cinfo;
642
2.16k
  struct my_error_mgr jcerr, jderr;
643
2.16k
  JSAMPARRAY buf = NULL;
644
645
2.16k
  unsigned char *outbuffer = NULL;
646
2.16k
  size_t outbuffersize = 0;
647
2.16k
  unsigned char *inbuffer = NULL;
648
2.16k
  size_t inbuffersize = 0;
649
2.16k
  size_t inbufferused = 0;
650
2.16k
  unsigned char *tmpbuffer = NULL;
651
2.16k
  size_t tmpbuffersize = 0;
652
2.16k
  unsigned char *extrabuffer = NULL;
653
2.16k
  size_t extrabuffersize = 0;
654
655
2.16k
  jvirt_barray_ptr *coef_arrays = NULL;
656
2.16k
  char marker_str[256];
657
2.16k
  unsigned int marker_in_count, marker_in_size;
658
659
2.16k
  long in_image_size = 0;
660
2.16k
  long insize = 0, outsize = 0, lastsize = 0;
661
2.16k
  int oldquality, searchdone;
662
2.16k
  double ratio;
663
2.16k
  size_t last_retry_size = 0;
664
2.16k
  int retry_count = 0;
665
2.16k
  int retry = 0;
666
2.16k
  int res = -1;
667
668
2.16k
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
2.16k
  dinfo.err = jpeg_std_error(&jderr.pub);
672
2.16k
  jpeg_create_decompress(&dinfo);
673
2.16k
  jderr.pub.error_exit=my_error_exit;
674
2.16k
  jderr.pub.output_message=my_output_message;
675
2.16k
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
2.16k
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
2.16k
  jpeg_create_compress(&cinfo);
680
2.16k
  jcerr.pub.error_exit=my_error_exit;
681
2.16k
  jcerr.pub.output_message=my_output_message;
682
2.16k
  jcerr.jump_set = 0;
683
684
2.16k
  if (rate)
685
2.16k
    *rate = 0.0;
686
2.16k
  if (saved)
687
2.16k
    *saved = 0.0;
688
689
2.16k
  if (filename) {
690
2.16k
    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
2.16k
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
2.94k
retry_point:
701
702
2.94k
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
1.03k
  abort_decompress:
705
1.03k
    jpeg_abort_decompress(&dinfo);
706
1.03k
    fclose(infile);
707
1.03k
    free_line_buf(&buf, dinfo.output_height);
708
1.03k
    if (!quiet_mode || csv)
709
1.03k
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
1.03k
    jderr.jump_set=0;
711
1.03k
    res = 1;
712
1.03k
    goto exit_point;
713
1.91k
  } else {
714
1.91k
    jderr.jump_set=1;
715
1.91k
  }
716
717
718
  /* Prepare to decompress */
719
2.16k
  if (!retry) {
720
2.16k
    if (!quiet_mode || csv) {
721
2.16k
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
2.16k
      fflush(log_fh);
723
2.16k
    }
724
725
2.16k
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
2.16k
    } else {
728
2.16k
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
2.06k
        inbuffersize = IN_BUF_SIZE;
730
2.16k
    }
731
2.16k
    if (inbuffer)
732
0
      free(inbuffer);
733
2.16k
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
2.16k
  }
736
1.91k
  global_error_counter=0;
737
1.91k
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
49.0k
  for (int i = 0; i < 16; i++) {
739
47.1k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
47.1k
  }
741
2.16k
  if (!retry) {
742
2.16k
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
18.4E
  } else {
744
18.4E
    if (retry == 1)
745
788
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
18.4E
  }
749
1.91k
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
1.91k
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
1.91k
          &marker_in_size);
755
756
1.96k
  if (!retry) {
757
1.96k
    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
1.96k
    if (!quiet_mode || csv) {
764
1.96k
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
1.96k
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
1.96k
        (dinfo.progressive_mode?'P':'N'));
767
1.96k
      if (!csv)
768
1.96k
        fprintf(log_fh,"%s",marker_str);
769
1.96k
      fflush(log_fh);
770
1.96k
    }
771
1.96k
  }
772
773
  /* Decompress the image */
774
2.17k
  if (quality >= 0 && retry != 1) {
775
1.38k
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
1.38k
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
7.36M
    for (int i = 0; i < dinfo.output_height; i++) {
781
7.36M
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
7.36M
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
7.36M
    }
785
786
2.40M
    while (dinfo.output_scanline < dinfo.output_height) {
787
2.40M
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
2.40M
          dinfo.output_height-dinfo.output_scanline);
789
2.40M
    }
790
1.38k
  } else {
791
523
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
523
    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
523
  }
798
1.91k
  if (!retry) {
799
1.29k
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
1.29k
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
1.29k
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
1.29k
    } else {
806
1.29k
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
1.29k
      if (in_image_size > 0 && in_image_size < insize) {
809
519
        if (!quiet_mode)
810
519
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
519
            insize - in_image_size);
812
519
        if (nofix_mode)
813
0
          global_error_counter++;
814
519
        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
519
      }
826
1.29k
    }
827
1.29k
    if (!quiet_mode) {
828
1.29k
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
1.29k
      fflush(log_fh);
830
1.29k
    }
831
832
1.29k
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
1.29k
    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
1.29k
  }
845
846
847
  /* Prepare to compress... */
848
1.91k
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
419
    if (!quiet_mode)
851
419
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
419
    jpeg_abort_compress(&cinfo);
853
419
    jpeg_abort_decompress(&dinfo);
854
419
    fclose(infile);
855
419
    free_line_buf(&buf, dinfo.output_height);
856
419
    jcerr.jump_set=0;
857
419
    res = 2;
858
419
    goto exit_point;
859
1.49k
  } else {
860
1.49k
    jcerr.jump_set=1;
861
1.49k
  }
862
863
1.49k
  lastsize = 0;
864
1.49k
  searchdone = 0;
865
1.49k
  if (!retry) {
866
1.29k
    oldquality = 200;
867
1.29k
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
1.29k
      quality = 100;
870
1.29k
    }
871
1.29k
  }
872
873
874
4.56k
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
4.56k
  if (outbuffer)
878
3.27k
    free(outbuffer);
879
4.56k
  outbuffersize = insize + 32768;
880
4.56k
  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
4.56k
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
4.56k
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
3.78k
    cinfo.in_color_space=dinfo.out_color_space;
891
3.78k
    cinfo.input_components=dinfo.output_components;
892
3.78k
    cinfo.image_width=dinfo.image_width;
893
3.78k
    cinfo.image_height=dinfo.image_height;
894
3.78k
    jpeg_set_defaults(&cinfo);
895
3.78k
    jpeg_set_quality(&cinfo,quality,TRUE);
896
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
938
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
938
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
#endif
900
3.78k
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
3.01k
      cinfo.scan_info = NULL;
903
3.01k
      cinfo.num_scans = 0;
904
3.01k
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
771
      jpeg_simple_progression(&cinfo);
907
771
    }
908
3.78k
    cinfo.optimize_coding = TRUE;
909
3.78k
#ifdef HAVE_ARITH_CODE
910
3.78k
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
3.78k
#endif
913
3.78k
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
1.72k
      cinfo.write_JFIF_header = TRUE;
915
2.05k
    } else {
916
2.05k
      cinfo.write_JFIF_header = FALSE;
917
2.05k
    }
918
3.78k
    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
3.78k
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
3.78k
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
7.55k
    while (cinfo.next_scanline < cinfo.image_height) {
930
3.76k
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
3.76k
          dinfo.output_height);
932
3.76k
    }
933
934
3.78k
  } else {
935
    /* Lossless optimization ... */
936
937
783
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
257
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
191
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
#endif
942
783
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
586
      cinfo.scan_info = NULL;
945
586
      cinfo.num_scans = 0;
946
586
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
109
      jpeg_simple_progression(&cinfo);
949
109
    }
950
783
    cinfo.optimize_coding = TRUE;
951
783
#ifdef HAVE_ARITH_CODE
952
783
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
783
#endif
955
783
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
123
      cinfo.write_JFIF_header = TRUE;
957
660
    } else {
958
660
      cinfo.write_JFIF_header = FALSE;
959
660
    }
960
783
    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
783
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
783
    write_markers(&dinfo,&cinfo);
970
971
783
  }
972
973
4.56k
  jpeg_finish_compress(&cinfo);
974
4.56k
  outsize = outbuffersize + extrabuffersize;
975
4.56k
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
4.56k
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
3.75k
    long osize = outsize/1024;
982
3.75k
    long isize = insize/1024;
983
3.75k
    long tsize = target_size;
984
985
3.75k
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
3.75k
    if (tsize < 0) {
988
3.75k
      tsize=((-target_size)*insize/100)/1024;
989
3.75k
      if (tsize < 1)
990
1.18k
        tsize = 1;
991
3.75k
    }
992
993
3.75k
    if (osize == tsize || searchdone || tsize > isize) {
994
1.31k
      if (searchdone < 42 && lastsize > 0) {
995
605
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
52
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
52
          searchdone = 42;
999
52
          quality = oldquality;
1000
52
          goto binary_search_loop;
1001
52
        }
1002
605
      }
1003
1.26k
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
2.44k
    } else {
1007
2.44k
      int newquality;
1008
2.44k
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
2.44k
      if (osize > tsize)
1011
1.68k
        newquality = quality - dif;
1012
758
      else
1013
758
        newquality = quality + dif + 0.5;
1014
1015
2.44k
      if (dif < 1.0)
1016
73
        searchdone = 1;
1017
2.44k
      if (newquality < 0) {
1018
170
        newquality = 0;
1019
170
        searchdone = 1;
1020
170
      }
1021
2.44k
      if (newquality > 100) {
1022
225
        newquality = 100;
1023
225
        searchdone = 1;
1024
225
      }
1025
1026
2.44k
      oldquality = quality;
1027
2.44k
      quality = newquality;
1028
2.44k
      lastsize = osize;
1029
2.44k
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
2.44k
      goto binary_search_loop;
1032
2.44k
    }
1033
3.75k
  }
1034
1035
2.07k
  jpeg_finish_decompress(&dinfo);
1036
2.07k
  free_line_buf(&buf, dinfo.output_height);
1037
1038
2.07k
  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
2.07k
  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
2.07k
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
788
    retry = 1;
1111
788
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
788
    goto retry_point;
1114
788
  }
1115
1116
1.28k
  fclose(infile);
1117
1118
1.28k
  ratio = (insize - outsize) * 100.0 / insize;
1119
1.28k
  if (!quiet_mode || csv)
1120
705
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
1.28k
  if (rate) {
1122
705
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
705
  }
1124
1125
1.28k
  if ((outsize < insize && ratio >= threshold) || force) {
1126
321
    if (saved) {
1127
321
      *saved = (insize - outsize) / 1024.0;
1128
321
    }
1129
321
    if (!quiet_mode || csv)
1130
321
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
321
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
321
    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
321
    } else {
1142
321
      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
321
      } else {
1163
321
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
321
        outfname = tmpfilename;
1166
321
      }
1167
1168
321
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
321
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
321
      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
321
      fclose(outfile);
1180
321
    }
1181
1182
321
    if (outfname) {
1183
321
      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
321
      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
321
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
321
        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
321
        if (chown(outfname,
1219
321
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
321
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
321
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
321
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
321
      }
1228
321
    }
1229
964
  } else {
1230
964
    if (!quiet_mode || csv)
1231
384
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
964
    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
964
  }
1239
1240
1.28k
  res = 0;
1241
1242
2.16k
 exit_point:
1243
2.16k
  if (inbuffer)
1244
2.16k
    free(inbuffer);
1245
2.16k
  if (outbuffer)
1246
1.29k
    free(outbuffer);
1247
2.16k
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
2.16k
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
2.16k
  jpeg_destroy_compress(&cinfo);
1252
2.16k
  jpeg_destroy_decompress(&dinfo);
1253
1254
2.16k
  return res;
1255
1.28k
}
optimize
Line
Count
Source
635
760
{
636
760
  FILE *infile = NULL;
637
760
  FILE *outfile = NULL;
638
760
  const char *outfname = NULL;
639
760
  char tmpfilename[MAXPATHLEN];
640
760
  struct jpeg_decompress_struct dinfo;
641
760
  struct jpeg_compress_struct cinfo;
642
760
  struct my_error_mgr jcerr, jderr;
643
760
  JSAMPARRAY buf = NULL;
644
645
760
  unsigned char *outbuffer = NULL;
646
760
  size_t outbuffersize = 0;
647
760
  unsigned char *inbuffer = NULL;
648
760
  size_t inbuffersize = 0;
649
760
  size_t inbufferused = 0;
650
760
  unsigned char *tmpbuffer = NULL;
651
760
  size_t tmpbuffersize = 0;
652
760
  unsigned char *extrabuffer = NULL;
653
760
  size_t extrabuffersize = 0;
654
655
760
  jvirt_barray_ptr *coef_arrays = NULL;
656
760
  char marker_str[256];
657
760
  unsigned int marker_in_count, marker_in_size;
658
659
760
  long in_image_size = 0;
660
760
  long insize = 0, outsize = 0, lastsize = 0;
661
760
  int oldquality, searchdone;
662
760
  double ratio;
663
760
  size_t last_retry_size = 0;
664
760
  int retry_count = 0;
665
760
  int retry = 0;
666
760
  int res = -1;
667
668
760
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
760
  dinfo.err = jpeg_std_error(&jderr.pub);
672
760
  jpeg_create_decompress(&dinfo);
673
760
  jderr.pub.error_exit=my_error_exit;
674
760
  jderr.pub.output_message=my_output_message;
675
760
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
760
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
760
  jpeg_create_compress(&cinfo);
680
760
  jcerr.pub.error_exit=my_error_exit;
681
760
  jcerr.pub.output_message=my_output_message;
682
760
  jcerr.jump_set = 0;
683
684
760
  if (rate)
685
760
    *rate = 0.0;
686
760
  if (saved)
687
760
    *saved = 0.0;
688
689
760
  if (filename) {
690
760
    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
760
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
1.02k
retry_point:
701
702
1.02k
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
367
  abort_decompress:
705
367
    jpeg_abort_decompress(&dinfo);
706
367
    fclose(infile);
707
367
    free_line_buf(&buf, dinfo.output_height);
708
367
    if (!quiet_mode || csv)
709
367
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
367
    jderr.jump_set=0;
711
367
    res = 1;
712
367
    goto exit_point;
713
658
  } else {
714
658
    jderr.jump_set=1;
715
658
  }
716
717
718
  /* Prepare to decompress */
719
760
  if (!retry) {
720
760
    if (!quiet_mode || csv) {
721
760
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
760
      fflush(log_fh);
723
760
    }
724
725
760
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
760
    } else {
728
760
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
718
        inbuffersize = IN_BUF_SIZE;
730
760
    }
731
760
    if (inbuffer)
732
0
      free(inbuffer);
733
760
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
760
  }
736
658
  global_error_counter=0;
737
658
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
17.0k
  for (int i = 0; i < 16; i++) {
739
16.4k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
16.4k
  }
741
760
  if (!retry) {
742
760
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
18.4E
  } else {
744
18.4E
    if (retry == 1)
745
265
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
18.4E
  }
749
658
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
658
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
658
          &marker_in_size);
755
756
693
  if (!retry) {
757
693
    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
693
    if (!quiet_mode || csv) {
764
693
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
693
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
693
        (dinfo.progressive_mode?'P':'N'));
767
693
      if (!csv)
768
693
        fprintf(log_fh,"%s",marker_str);
769
693
      fflush(log_fh);
770
693
    }
771
693
  }
772
773
  /* Decompress the image */
774
764
  if (quality >= 0 && retry != 1) {
775
499
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
499
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
2.97M
    for (int i = 0; i < dinfo.output_height; i++) {
781
2.97M
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
2.97M
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
2.97M
    }
785
786
905k
    while (dinfo.output_scanline < dinfo.output_height) {
787
904k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
904k
          dinfo.output_height-dinfo.output_scanline);
789
904k
    }
790
499
  } else {
791
159
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
159
    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
159
  }
798
658
  if (!retry) {
799
464
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
464
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
464
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
464
    } else {
806
464
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
464
      if (in_image_size > 0 && in_image_size < insize) {
809
221
        if (!quiet_mode)
810
221
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
221
            insize - in_image_size);
812
221
        if (nofix_mode)
813
0
          global_error_counter++;
814
221
        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
221
      }
826
464
    }
827
464
    if (!quiet_mode) {
828
464
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
464
      fflush(log_fh);
830
464
    }
831
832
464
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
464
    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
464
  }
845
846
847
  /* Prepare to compress... */
848
658
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
134
    if (!quiet_mode)
851
134
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
134
    jpeg_abort_compress(&cinfo);
853
134
    jpeg_abort_decompress(&dinfo);
854
134
    fclose(infile);
855
134
    free_line_buf(&buf, dinfo.output_height);
856
134
    jcerr.jump_set=0;
857
134
    res = 2;
858
134
    goto exit_point;
859
524
  } else {
860
524
    jcerr.jump_set=1;
861
524
  }
862
863
524
  lastsize = 0;
864
524
  searchdone = 0;
865
524
  if (!retry) {
866
464
    oldquality = 200;
867
464
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
464
      quality = 100;
870
464
    }
871
464
  }
872
873
874
1.68k
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
1.68k
  if (outbuffer)
878
1.22k
    free(outbuffer);
879
1.68k
  outbuffersize = insize + 32768;
880
1.68k
  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
1.68k
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
1.68k
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
1.42k
    cinfo.in_color_space=dinfo.out_color_space;
891
1.42k
    cinfo.input_components=dinfo.output_components;
892
1.42k
    cinfo.image_width=dinfo.image_width;
893
1.42k
    cinfo.image_height=dinfo.image_height;
894
1.42k
    jpeg_set_defaults(&cinfo);
895
1.42k
    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
1.42k
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
1.24k
      cinfo.scan_info = NULL;
903
1.24k
      cinfo.num_scans = 0;
904
1.24k
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
175
      jpeg_simple_progression(&cinfo);
907
175
    }
908
1.42k
    cinfo.optimize_coding = TRUE;
909
1.42k
#ifdef HAVE_ARITH_CODE
910
1.42k
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
1.42k
#endif
913
1.42k
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
696
      cinfo.write_JFIF_header = TRUE;
915
726
    } else {
916
726
      cinfo.write_JFIF_header = FALSE;
917
726
    }
918
1.42k
    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
1.42k
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
1.42k
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
2.83k
    while (cinfo.next_scanline < cinfo.image_height) {
930
1.41k
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
1.41k
          dinfo.output_height);
932
1.41k
    }
933
934
1.42k
  } else {
935
    /* Lossless optimization ... */
936
937
263
    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
263
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
243
      cinfo.scan_info = NULL;
945
243
      cinfo.num_scans = 0;
946
243
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
9
      jpeg_simple_progression(&cinfo);
949
9
    }
950
263
    cinfo.optimize_coding = TRUE;
951
263
#ifdef HAVE_ARITH_CODE
952
263
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
263
#endif
955
263
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
44
      cinfo.write_JFIF_header = TRUE;
957
219
    } else {
958
219
      cinfo.write_JFIF_header = FALSE;
959
219
    }
960
263
    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
263
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
263
    write_markers(&dinfo,&cinfo);
970
971
263
  }
972
973
1.68k
  jpeg_finish_compress(&cinfo);
974
1.68k
  outsize = outbuffersize + extrabuffersize;
975
1.68k
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
1.68k
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
1.41k
    long osize = outsize/1024;
982
1.41k
    long isize = insize/1024;
983
1.41k
    long tsize = target_size;
984
985
1.41k
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
1.41k
    if (tsize < 0) {
988
1.41k
      tsize=((-target_size)*insize/100)/1024;
989
1.41k
      if (tsize < 1)
990
367
        tsize = 1;
991
1.41k
    }
992
993
1.41k
    if (osize == tsize || searchdone || tsize > isize) {
994
479
      if (searchdone < 42 && lastsize > 0) {
995
233
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
21
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
21
          searchdone = 42;
999
21
          quality = oldquality;
1000
21
          goto binary_search_loop;
1001
21
        }
1002
233
      }
1003
458
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
937
    } else {
1007
937
      int newquality;
1008
937
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
937
      if (osize > tsize)
1011
622
        newquality = quality - dif;
1012
315
      else
1013
315
        newquality = quality + dif + 0.5;
1014
1015
937
      if (dif < 1.0)
1016
32
        searchdone = 1;
1017
937
      if (newquality < 0) {
1018
59
        newquality = 0;
1019
59
        searchdone = 1;
1020
59
      }
1021
937
      if (newquality > 100) {
1022
86
        newquality = 100;
1023
86
        searchdone = 1;
1024
86
      }
1025
1026
937
      oldquality = quality;
1027
937
      quality = newquality;
1028
937
      lastsize = osize;
1029
937
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
937
      goto binary_search_loop;
1032
937
    }
1033
1.41k
  }
1034
1035
727
  jpeg_finish_decompress(&dinfo);
1036
727
  free_line_buf(&buf, dinfo.output_height);
1037
1038
727
  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
727
  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
727
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
265
    retry = 1;
1111
265
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
265
    goto retry_point;
1114
265
  }
1115
1116
462
  fclose(infile);
1117
1118
462
  ratio = (insize - outsize) * 100.0 / insize;
1119
462
  if (!quiet_mode || csv)
1120
259
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
462
  if (rate) {
1122
259
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
259
  }
1124
1125
462
  if ((outsize < insize && ratio >= threshold) || force) {
1126
124
    if (saved) {
1127
124
      *saved = (insize - outsize) / 1024.0;
1128
124
    }
1129
124
    if (!quiet_mode || csv)
1130
124
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
124
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
124
    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
124
    } else {
1142
124
      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
124
      } else {
1163
124
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
124
        outfname = tmpfilename;
1166
124
      }
1167
1168
124
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
124
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
124
      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
124
      fclose(outfile);
1180
124
    }
1181
1182
124
    if (outfname) {
1183
124
      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
124
      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
124
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
124
        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
124
        if (chown(outfname,
1219
124
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
124
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
124
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
124
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
124
      }
1228
124
    }
1229
338
  } else {
1230
338
    if (!quiet_mode || csv)
1231
135
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
338
    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
338
  }
1239
1240
462
  res = 0;
1241
1242
760
 exit_point:
1243
760
  if (inbuffer)
1244
760
    free(inbuffer);
1245
760
  if (outbuffer)
1246
464
    free(outbuffer);
1247
760
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
760
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
760
  jpeg_destroy_compress(&cinfo);
1252
760
  jpeg_destroy_decompress(&dinfo);
1253
1254
760
  return res;
1255
462
}
optimize
Line
Count
Source
635
760
{
636
760
  FILE *infile = NULL;
637
760
  FILE *outfile = NULL;
638
760
  const char *outfname = NULL;
639
760
  char tmpfilename[MAXPATHLEN];
640
760
  struct jpeg_decompress_struct dinfo;
641
760
  struct jpeg_compress_struct cinfo;
642
760
  struct my_error_mgr jcerr, jderr;
643
760
  JSAMPARRAY buf = NULL;
644
645
760
  unsigned char *outbuffer = NULL;
646
760
  size_t outbuffersize = 0;
647
760
  unsigned char *inbuffer = NULL;
648
760
  size_t inbuffersize = 0;
649
760
  size_t inbufferused = 0;
650
760
  unsigned char *tmpbuffer = NULL;
651
760
  size_t tmpbuffersize = 0;
652
760
  unsigned char *extrabuffer = NULL;
653
760
  size_t extrabuffersize = 0;
654
655
760
  jvirt_barray_ptr *coef_arrays = NULL;
656
760
  char marker_str[256];
657
760
  unsigned int marker_in_count, marker_in_size;
658
659
760
  long in_image_size = 0;
660
760
  long insize = 0, outsize = 0, lastsize = 0;
661
760
  int oldquality, searchdone;
662
760
  double ratio;
663
760
  size_t last_retry_size = 0;
664
760
  int retry_count = 0;
665
760
  int retry = 0;
666
760
  int res = -1;
667
668
760
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
760
  dinfo.err = jpeg_std_error(&jderr.pub);
672
760
  jpeg_create_decompress(&dinfo);
673
760
  jderr.pub.error_exit=my_error_exit;
674
760
  jderr.pub.output_message=my_output_message;
675
760
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
760
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
760
  jpeg_create_compress(&cinfo);
680
760
  jcerr.pub.error_exit=my_error_exit;
681
760
  jcerr.pub.output_message=my_output_message;
682
760
  jcerr.jump_set = 0;
683
684
760
  if (rate)
685
760
    *rate = 0.0;
686
760
  if (saved)
687
760
    *saved = 0.0;
688
689
760
  if (filename) {
690
760
    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
760
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
1.02k
retry_point:
701
702
1.02k
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
367
  abort_decompress:
705
367
    jpeg_abort_decompress(&dinfo);
706
367
    fclose(infile);
707
367
    free_line_buf(&buf, dinfo.output_height);
708
367
    if (!quiet_mode || csv)
709
367
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
367
    jderr.jump_set=0;
711
367
    res = 1;
712
367
    goto exit_point;
713
658
  } else {
714
658
    jderr.jump_set=1;
715
658
  }
716
717
718
  /* Prepare to decompress */
719
760
  if (!retry) {
720
760
    if (!quiet_mode || csv) {
721
760
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
760
      fflush(log_fh);
723
760
    }
724
725
760
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
760
    } else {
728
760
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
718
        inbuffersize = IN_BUF_SIZE;
730
760
    }
731
760
    if (inbuffer)
732
0
      free(inbuffer);
733
760
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
760
  }
736
658
  global_error_counter=0;
737
658
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
17.0k
  for (int i = 0; i < 16; i++) {
739
16.4k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
16.4k
  }
741
760
  if (!retry) {
742
760
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
18.4E
  } else {
744
18.4E
    if (retry == 1)
745
265
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
18.4E
  }
749
658
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
658
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
658
          &marker_in_size);
755
756
693
  if (!retry) {
757
693
    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
693
    if (!quiet_mode || csv) {
764
693
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
693
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
693
        (dinfo.progressive_mode?'P':'N'));
767
693
      if (!csv)
768
693
        fprintf(log_fh,"%s",marker_str);
769
693
      fflush(log_fh);
770
693
    }
771
693
  }
772
773
  /* Decompress the image */
774
764
  if (quality >= 0 && retry != 1) {
775
499
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
499
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
2.97M
    for (int i = 0; i < dinfo.output_height; i++) {
781
2.97M
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
2.97M
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
2.97M
    }
785
786
905k
    while (dinfo.output_scanline < dinfo.output_height) {
787
904k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
904k
          dinfo.output_height-dinfo.output_scanline);
789
904k
    }
790
499
  } else {
791
159
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
159
    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
159
  }
798
658
  if (!retry) {
799
464
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
464
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
464
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
464
    } else {
806
464
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
464
      if (in_image_size > 0 && in_image_size < insize) {
809
221
        if (!quiet_mode)
810
221
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
221
            insize - in_image_size);
812
221
        if (nofix_mode)
813
0
          global_error_counter++;
814
221
        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
221
      }
826
464
    }
827
464
    if (!quiet_mode) {
828
464
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
464
      fflush(log_fh);
830
464
    }
831
832
464
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
464
    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
464
  }
845
846
847
  /* Prepare to compress... */
848
658
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
134
    if (!quiet_mode)
851
134
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
134
    jpeg_abort_compress(&cinfo);
853
134
    jpeg_abort_decompress(&dinfo);
854
134
    fclose(infile);
855
134
    free_line_buf(&buf, dinfo.output_height);
856
134
    jcerr.jump_set=0;
857
134
    res = 2;
858
134
    goto exit_point;
859
524
  } else {
860
524
    jcerr.jump_set=1;
861
524
  }
862
863
524
  lastsize = 0;
864
524
  searchdone = 0;
865
524
  if (!retry) {
866
464
    oldquality = 200;
867
464
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
464
      quality = 100;
870
464
    }
871
464
  }
872
873
874
1.68k
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
1.68k
  if (outbuffer)
878
1.22k
    free(outbuffer);
879
1.68k
  outbuffersize = insize + 32768;
880
1.68k
  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
1.68k
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
1.68k
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
1.42k
    cinfo.in_color_space=dinfo.out_color_space;
891
1.42k
    cinfo.input_components=dinfo.output_components;
892
1.42k
    cinfo.image_width=dinfo.image_width;
893
1.42k
    cinfo.image_height=dinfo.image_height;
894
1.42k
    jpeg_set_defaults(&cinfo);
895
1.42k
    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
1.42k
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
1.24k
      cinfo.scan_info = NULL;
903
1.24k
      cinfo.num_scans = 0;
904
1.24k
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
175
      jpeg_simple_progression(&cinfo);
907
175
    }
908
1.42k
    cinfo.optimize_coding = TRUE;
909
1.42k
#ifdef HAVE_ARITH_CODE
910
1.42k
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
1.42k
#endif
913
1.42k
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
696
      cinfo.write_JFIF_header = TRUE;
915
726
    } else {
916
726
      cinfo.write_JFIF_header = FALSE;
917
726
    }
918
1.42k
    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
1.42k
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
1.42k
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
2.83k
    while (cinfo.next_scanline < cinfo.image_height) {
930
1.41k
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
1.41k
          dinfo.output_height);
932
1.41k
    }
933
934
1.42k
  } else {
935
    /* Lossless optimization ... */
936
937
263
    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
263
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
243
      cinfo.scan_info = NULL;
945
243
      cinfo.num_scans = 0;
946
243
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
9
      jpeg_simple_progression(&cinfo);
949
9
    }
950
263
    cinfo.optimize_coding = TRUE;
951
263
#ifdef HAVE_ARITH_CODE
952
263
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
263
#endif
955
263
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
44
      cinfo.write_JFIF_header = TRUE;
957
219
    } else {
958
219
      cinfo.write_JFIF_header = FALSE;
959
219
    }
960
263
    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
263
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
263
    write_markers(&dinfo,&cinfo);
970
971
263
  }
972
973
1.68k
  jpeg_finish_compress(&cinfo);
974
1.68k
  outsize = outbuffersize + extrabuffersize;
975
1.68k
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
1.68k
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
1.41k
    long osize = outsize/1024;
982
1.41k
    long isize = insize/1024;
983
1.41k
    long tsize = target_size;
984
985
1.41k
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
1.41k
    if (tsize < 0) {
988
1.41k
      tsize=((-target_size)*insize/100)/1024;
989
1.41k
      if (tsize < 1)
990
367
        tsize = 1;
991
1.41k
    }
992
993
1.41k
    if (osize == tsize || searchdone || tsize > isize) {
994
479
      if (searchdone < 42 && lastsize > 0) {
995
233
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
21
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
21
          searchdone = 42;
999
21
          quality = oldquality;
1000
21
          goto binary_search_loop;
1001
21
        }
1002
233
      }
1003
458
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
937
    } else {
1007
937
      int newquality;
1008
937
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
937
      if (osize > tsize)
1011
622
        newquality = quality - dif;
1012
315
      else
1013
315
        newquality = quality + dif + 0.5;
1014
1015
937
      if (dif < 1.0)
1016
32
        searchdone = 1;
1017
937
      if (newquality < 0) {
1018
59
        newquality = 0;
1019
59
        searchdone = 1;
1020
59
      }
1021
937
      if (newquality > 100) {
1022
86
        newquality = 100;
1023
86
        searchdone = 1;
1024
86
      }
1025
1026
937
      oldquality = quality;
1027
937
      quality = newquality;
1028
937
      lastsize = osize;
1029
937
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
937
      goto binary_search_loop;
1032
937
    }
1033
1.41k
  }
1034
1035
727
  jpeg_finish_decompress(&dinfo);
1036
727
  free_line_buf(&buf, dinfo.output_height);
1037
1038
727
  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
727
  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
727
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
265
    retry = 1;
1111
265
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
265
    goto retry_point;
1114
265
  }
1115
1116
462
  fclose(infile);
1117
1118
462
  ratio = (insize - outsize) * 100.0 / insize;
1119
462
  if (!quiet_mode || csv)
1120
259
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
462
  if (rate) {
1122
259
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
259
  }
1124
1125
462
  if ((outsize < insize && ratio >= threshold) || force) {
1126
124
    if (saved) {
1127
124
      *saved = (insize - outsize) / 1024.0;
1128
124
    }
1129
124
    if (!quiet_mode || csv)
1130
124
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
124
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
124
    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
124
    } else {
1142
124
      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
124
      } else {
1163
124
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
124
        outfname = tmpfilename;
1166
124
      }
1167
1168
124
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
124
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
124
      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
124
      fclose(outfile);
1180
124
    }
1181
1182
124
    if (outfname) {
1183
124
      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
124
      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
124
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
124
        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
124
        if (chown(outfname,
1219
124
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
124
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
124
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
124
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
124
      }
1228
124
    }
1229
338
  } else {
1230
338
    if (!quiet_mode || csv)
1231
135
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
338
    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
338
  }
1239
1240
462
  res = 0;
1241
1242
760
 exit_point:
1243
760
  if (inbuffer)
1244
760
    free(inbuffer);
1245
760
  if (outbuffer)
1246
464
    free(outbuffer);
1247
760
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
760
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
760
  jpeg_destroy_compress(&cinfo);
1252
760
  jpeg_destroy_decompress(&dinfo);
1253
1254
760
  return res;
1255
462
}
optimize
Line
Count
Source
635
641
{
636
641
  FILE *infile = NULL;
637
641
  FILE *outfile = NULL;
638
641
  const char *outfname = NULL;
639
641
  char tmpfilename[MAXPATHLEN];
640
641
  struct jpeg_decompress_struct dinfo;
641
641
  struct jpeg_compress_struct cinfo;
642
641
  struct my_error_mgr jcerr, jderr;
643
641
  JSAMPARRAY buf = NULL;
644
645
641
  unsigned char *outbuffer = NULL;
646
641
  size_t outbuffersize = 0;
647
641
  unsigned char *inbuffer = NULL;
648
641
  size_t inbuffersize = 0;
649
641
  size_t inbufferused = 0;
650
641
  unsigned char *tmpbuffer = NULL;
651
641
  size_t tmpbuffersize = 0;
652
641
  unsigned char *extrabuffer = NULL;
653
641
  size_t extrabuffersize = 0;
654
655
641
  jvirt_barray_ptr *coef_arrays = NULL;
656
641
  char marker_str[256];
657
641
  unsigned int marker_in_count, marker_in_size;
658
659
641
  long in_image_size = 0;
660
641
  long insize = 0, outsize = 0, lastsize = 0;
661
641
  int oldquality, searchdone;
662
641
  double ratio;
663
641
  size_t last_retry_size = 0;
664
641
  int retry_count = 0;
665
641
  int retry = 0;
666
641
  int res = -1;
667
668
641
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
641
  dinfo.err = jpeg_std_error(&jderr.pub);
672
641
  jpeg_create_decompress(&dinfo);
673
641
  jderr.pub.error_exit=my_error_exit;
674
641
  jderr.pub.output_message=my_output_message;
675
641
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
641
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
641
  jpeg_create_compress(&cinfo);
680
641
  jcerr.pub.error_exit=my_error_exit;
681
641
  jcerr.pub.output_message=my_output_message;
682
641
  jcerr.jump_set = 0;
683
684
641
  if (rate)
685
641
    *rate = 0.0;
686
641
  if (saved)
687
641
    *saved = 0.0;
688
689
641
  if (filename) {
690
641
    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
641
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
899
retry_point:
701
702
899
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
303
  abort_decompress:
705
303
    jpeg_abort_decompress(&dinfo);
706
303
    fclose(infile);
707
303
    free_line_buf(&buf, dinfo.output_height);
708
303
    if (!quiet_mode || csv)
709
303
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
303
    jderr.jump_set=0;
711
303
    res = 1;
712
303
    goto exit_point;
713
596
  } else {
714
596
    jderr.jump_set=1;
715
596
  }
716
717
718
  /* Prepare to decompress */
719
641
  if (!retry) {
720
641
    if (!quiet_mode || csv) {
721
641
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
641
      fflush(log_fh);
723
641
    }
724
725
641
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
641
    } else {
728
641
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
627
        inbuffersize = IN_BUF_SIZE;
730
641
    }
731
641
    if (inbuffer)
732
0
      free(inbuffer);
733
641
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
641
  }
736
596
  global_error_counter=0;
737
596
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
14.9k
  for (int i = 0; i < 16; i++) {
739
14.3k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
14.3k
  }
741
641
  if (!retry) {
742
641
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
18.4E
  } else {
744
18.4E
    if (retry == 1)
745
258
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
18.4E
  }
749
596
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
596
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
596
          &marker_in_size);
755
756
596
  if (!retry) {
757
574
    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
574
    if (!quiet_mode || csv) {
764
574
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
574
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
574
        (dinfo.progressive_mode?'P':'N'));
767
574
      if (!csv)
768
574
        fprintf(log_fh,"%s",marker_str);
769
574
      fflush(log_fh);
770
574
    }
771
574
  }
772
773
  /* Decompress the image */
774
649
  if (quality >= 0 && retry != 1) {
775
391
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
391
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
1.41M
    for (int i = 0; i < dinfo.output_height; i++) {
781
1.41M
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
1.41M
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
1.41M
    }
785
786
596k
    while (dinfo.output_scanline < dinfo.output_height) {
787
596k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
596k
          dinfo.output_height-dinfo.output_scanline);
789
596k
    }
790
391
  } else {
791
205
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
205
    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
205
  }
798
596
  if (!retry) {
799
362
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
362
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
362
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
362
    } else {
806
362
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
362
      if (in_image_size > 0 && in_image_size < insize) {
809
77
        if (!quiet_mode)
810
77
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
77
            insize - in_image_size);
812
77
        if (nofix_mode)
813
0
          global_error_counter++;
814
77
        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
77
      }
826
362
    }
827
362
    if (!quiet_mode) {
828
362
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
362
      fflush(log_fh);
830
362
    }
831
832
362
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
362
    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
362
  }
845
846
847
  /* Prepare to compress... */
848
596
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
151
    if (!quiet_mode)
851
151
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
151
    jpeg_abort_compress(&cinfo);
853
151
    jpeg_abort_decompress(&dinfo);
854
151
    fclose(infile);
855
151
    free_line_buf(&buf, dinfo.output_height);
856
151
    jcerr.jump_set=0;
857
151
    res = 2;
858
151
    goto exit_point;
859
445
  } else {
860
445
    jcerr.jump_set=1;
861
445
  }
862
863
445
  lastsize = 0;
864
445
  searchdone = 0;
865
445
  if (!retry) {
866
362
    oldquality = 200;
867
362
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
362
      quality = 100;
870
362
    }
871
362
  }
872
873
874
1.19k
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
1.19k
  if (outbuffer)
878
833
    free(outbuffer);
879
1.19k
  outbuffersize = insize + 32768;
880
1.19k
  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
1.19k
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
1.19k
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
938
    cinfo.in_color_space=dinfo.out_color_space;
891
938
    cinfo.input_components=dinfo.output_components;
892
938
    cinfo.image_width=dinfo.image_width;
893
938
    cinfo.image_height=dinfo.image_height;
894
938
    jpeg_set_defaults(&cinfo);
895
938
    jpeg_set_quality(&cinfo,quality,TRUE);
896
938
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
938
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
938
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
938
#endif
900
938
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
517
      cinfo.scan_info = NULL;
903
517
      cinfo.num_scans = 0;
904
517
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
421
      jpeg_simple_progression(&cinfo);
907
421
    }
908
938
    cinfo.optimize_coding = TRUE;
909
938
#ifdef HAVE_ARITH_CODE
910
938
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
938
#endif
913
938
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
333
      cinfo.write_JFIF_header = TRUE;
915
605
    } else {
916
605
      cinfo.write_JFIF_header = FALSE;
917
605
    }
918
938
    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
938
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
938
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
1.87k
    while (cinfo.next_scanline < cinfo.image_height) {
930
937
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
937
          dinfo.output_height);
932
937
    }
933
934
938
  } else {
935
    /* Lossless optimization ... */
936
937
257
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
257
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
257
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
191
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
257
#endif
942
257
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
100
      cinfo.scan_info = NULL;
945
100
      cinfo.num_scans = 0;
946
157
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
91
      jpeg_simple_progression(&cinfo);
949
91
    }
950
257
    cinfo.optimize_coding = TRUE;
951
257
#ifdef HAVE_ARITH_CODE
952
257
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
257
#endif
955
257
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
35
      cinfo.write_JFIF_header = TRUE;
957
222
    } else {
958
222
      cinfo.write_JFIF_header = FALSE;
959
222
    }
960
257
    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
257
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
257
    write_markers(&dinfo,&cinfo);
970
971
257
  }
972
973
1.19k
  jpeg_finish_compress(&cinfo);
974
1.19k
  outsize = outbuffersize + extrabuffersize;
975
1.19k
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
1.19k
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
925
    long osize = outsize/1024;
982
925
    long isize = insize/1024;
983
925
    long tsize = target_size;
984
985
925
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
925
    if (tsize < 0) {
988
925
      tsize=((-target_size)*insize/100)/1024;
989
925
      if (tsize < 1)
990
455
        tsize = 1;
991
925
    }
992
993
925
    if (osize == tsize || searchdone || tsize > isize) {
994
359
      if (searchdone < 42 && lastsize > 0) {
995
139
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
10
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
10
          searchdone = 42;
999
10
          quality = oldquality;
1000
10
          goto binary_search_loop;
1001
10
        }
1002
139
      }
1003
349
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
566
    } else {
1007
566
      int newquality;
1008
566
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
566
      if (osize > tsize)
1011
438
        newquality = quality - dif;
1012
128
      else
1013
128
        newquality = quality + dif + 0.5;
1014
1015
566
      if (dif < 1.0)
1016
9
        searchdone = 1;
1017
566
      if (newquality < 0) {
1018
52
        newquality = 0;
1019
52
        searchdone = 1;
1020
52
      }
1021
566
      if (newquality > 100) {
1022
53
        newquality = 100;
1023
53
        searchdone = 1;
1024
53
      }
1025
1026
566
      oldquality = quality;
1027
566
      quality = newquality;
1028
566
      lastsize = osize;
1029
566
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
566
      goto binary_search_loop;
1032
566
    }
1033
925
  }
1034
1035
619
  jpeg_finish_decompress(&dinfo);
1036
619
  free_line_buf(&buf, dinfo.output_height);
1037
1038
619
  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
619
  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
619
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
258
    retry = 1;
1111
258
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
258
    goto retry_point;
1114
258
  }
1115
1116
361
  fclose(infile);
1117
1118
361
  ratio = (insize - outsize) * 100.0 / insize;
1119
361
  if (!quiet_mode || csv)
1120
187
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
361
  if (rate) {
1122
187
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
187
  }
1124
1125
361
  if ((outsize < insize && ratio >= threshold) || force) {
1126
73
    if (saved) {
1127
73
      *saved = (insize - outsize) / 1024.0;
1128
73
    }
1129
73
    if (!quiet_mode || csv)
1130
73
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
73
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
73
    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
73
    } else {
1142
73
      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
73
      } else {
1163
73
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
73
        outfname = tmpfilename;
1166
73
      }
1167
1168
73
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
73
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
73
      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
73
      fclose(outfile);
1180
73
    }
1181
1182
73
    if (outfname) {
1183
73
      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
73
      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
73
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
73
        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
73
        if (chown(outfname,
1219
73
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
73
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
73
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
73
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
73
      }
1228
73
    }
1229
288
  } else {
1230
288
    if (!quiet_mode || csv)
1231
114
      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
361
  res = 0;
1241
1242
641
 exit_point:
1243
641
  if (inbuffer)
1244
641
    free(inbuffer);
1245
641
  if (outbuffer)
1246
362
    free(outbuffer);
1247
641
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
641
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
641
  jpeg_destroy_compress(&cinfo);
1252
641
  jpeg_destroy_decompress(&dinfo);
1253
1254
641
  return res;
1255
361
}
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 :-) */