Add fault tolerance features to djpeg and jpegtran
- Enable progress reporting at run time using a new -report argument (cjpeg now supports that argument as well) - Limit the allowable number of scans using a new -maxscans argument - Treat warnings as fatal using a new -strict argument This mainly demonstrates how to work around the two issues with the JPEG standard described here: https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf since those and similar issues continue to be erroneously reported as libjpeg-turbo bugs.
This commit is contained in:
10
ChangeLog.md
10
ChangeLog.md
@@ -101,6 +101,16 @@ modern ARMv8 CPUs.
|
||||
instructions, so that the Loongson MMI SIMD extensions can be included in any
|
||||
MIPS64 libjpeg-turbo build.
|
||||
|
||||
14. Added fault tolerance features to djpeg and jpegtran, mainly to demonstrate
|
||||
methods by which applications can guard against the exploits of the JPEG format
|
||||
described in the report
|
||||
["Two Issues with the JPEG Standard"](https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf).
|
||||
|
||||
- Both programs now accept a `-maxscans` argument, which can be used to
|
||||
limit the number of allowable scans in the input file.
|
||||
- Both programs now accept a `-strict` argument, which can be used to
|
||||
treat all warnings as fatal.
|
||||
|
||||
|
||||
2.0.3
|
||||
=====
|
||||
|
||||
47
cdjpeg.c
47
cdjpeg.c
@@ -3,8 +3,8 @@
|
||||
*
|
||||
* This file was part of the Independent JPEG Group's software:
|
||||
* Copyright (C) 1991-1997, Thomas G. Lane.
|
||||
* It was modified by The libjpeg-turbo Project to include only code relevant
|
||||
* to libjpeg-turbo.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2019, D. R. Commander.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
*
|
||||
@@ -25,26 +25,37 @@
|
||||
* Optional progress monitor: display a percent-done figure on stderr.
|
||||
*/
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
|
||||
METHODDEF(void)
|
||||
progress_monitor(j_common_ptr cinfo)
|
||||
{
|
||||
cd_progress_ptr prog = (cd_progress_ptr)cinfo->progress;
|
||||
int total_passes = prog->pub.total_passes + prog->total_extra_passes;
|
||||
int percent_done =
|
||||
(int)(prog->pub.pass_counter * 100L / prog->pub.pass_limit);
|
||||
|
||||
if (percent_done != prog->percent_done) {
|
||||
prog->percent_done = percent_done;
|
||||
if (total_passes > 1) {
|
||||
fprintf(stderr, "\rPass %d/%d: %3d%% ",
|
||||
prog->pub.completed_passes + prog->completed_extra_passes + 1,
|
||||
total_passes, percent_done);
|
||||
} else {
|
||||
fprintf(stderr, "\r %3d%% ", percent_done);
|
||||
if (prog->max_scans != 0 && cinfo->is_decompressor) {
|
||||
int scan_no = ((j_decompress_ptr)cinfo)->input_scan_number;
|
||||
|
||||
if (scan_no > prog->max_scans) {
|
||||
fprintf(stderr, "Scan number %d exceeds maximum scans (%d)\n", scan_no,
|
||||
prog->max_scans);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (prog->report) {
|
||||
int total_passes = prog->pub.total_passes + prog->total_extra_passes;
|
||||
int percent_done =
|
||||
(int)(prog->pub.pass_counter * 100L / prog->pub.pass_limit);
|
||||
|
||||
if (percent_done != prog->percent_done) {
|
||||
prog->percent_done = percent_done;
|
||||
if (total_passes > 1) {
|
||||
fprintf(stderr, "\rPass %d/%d: %3d%% ",
|
||||
prog->pub.completed_passes + prog->completed_extra_passes + 1,
|
||||
total_passes, percent_done);
|
||||
} else {
|
||||
fprintf(stderr, "\r %3d%% ", percent_done);
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +68,8 @@ start_progress_monitor(j_common_ptr cinfo, cd_progress_ptr progress)
|
||||
progress->pub.progress_monitor = progress_monitor;
|
||||
progress->completed_extra_passes = 0;
|
||||
progress->total_extra_passes = 0;
|
||||
progress->max_scans = 0;
|
||||
progress->report = FALSE;
|
||||
progress->percent_done = -1;
|
||||
cinfo->progress = &progress->pub;
|
||||
}
|
||||
@@ -73,8 +86,6 @@ end_progress_monitor(j_common_ptr cinfo)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Case-insensitive matching of possibly-abbreviated keyword switches.
|
||||
|
||||
5
cdjpeg.h
5
cdjpeg.h
@@ -4,7 +4,7 @@
|
||||
* This file was part of the Independent JPEG Group's software:
|
||||
* Copyright (C) 1994-1997, Thomas G. Lane.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2017, D. R. Commander.
|
||||
* Copyright (C) 2017, 2019, D. R. Commander.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
*
|
||||
@@ -87,6 +87,9 @@ struct cdjpeg_progress_mgr {
|
||||
struct jpeg_progress_mgr pub; /* fields known to JPEG library */
|
||||
int completed_extra_passes; /* extra passes completed */
|
||||
int total_extra_passes; /* total extra */
|
||||
JDIMENSION max_scans; /* abort if the number of scans exceeds this
|
||||
value and the value is non-zero */
|
||||
boolean report; /* whether or not to report progress */
|
||||
/* last printed percentage stored here to avoid multiple printouts */
|
||||
int percent_done;
|
||||
};
|
||||
|
||||
5
cjpeg.1
5
cjpeg.1
@@ -1,4 +1,4 @@
|
||||
.TH CJPEG 1 "18 March 2017"
|
||||
.TH CJPEG 1 "18 December 2019"
|
||||
.SH NAME
|
||||
cjpeg \- compress an image file to a JPEG file
|
||||
.SH SYNOPSIS
|
||||
@@ -215,6 +215,9 @@ Compress to memory instead of a file. This feature was implemented mainly as a
|
||||
way of testing the in-memory destination manager (jpeg_mem_dest()), but it is
|
||||
also useful for benchmarking, since it reduces the I/O overhead.
|
||||
.TP
|
||||
.BI \-report
|
||||
Report compression progress.
|
||||
.TP
|
||||
.B \-verbose
|
||||
Enable debug printout. More
|
||||
.BR \-v 's
|
||||
|
||||
22
cjpeg.c
22
cjpeg.c
@@ -5,7 +5,7 @@
|
||||
* Copyright (C) 1991-1998, Thomas G. Lane.
|
||||
* Modified 2003-2011 by Guido Vollbeding.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2010, 2013-2014, 2017, D. R. Commander.
|
||||
* Copyright (C) 2010, 2013-2014, 2017, 2019, D. R. Commander.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
*
|
||||
@@ -147,6 +147,7 @@ static const char *progname; /* program name for error messages */
|
||||
static char *icc_filename; /* for -icc switch */
|
||||
static char *outfilename; /* for -outfile switch */
|
||||
boolean memdst; /* for -memdst switch */
|
||||
boolean report; /* for -report switch */
|
||||
|
||||
|
||||
LOCAL(void)
|
||||
@@ -200,6 +201,7 @@ usage(void)
|
||||
#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
|
||||
fprintf(stderr, " -memdst Compress to memory instead of file (useful for benchmarking)\n");
|
||||
#endif
|
||||
fprintf(stderr, " -report Report compression progress\n");
|
||||
fprintf(stderr, " -verbose or -debug Emit debug output\n");
|
||||
fprintf(stderr, " -version Print version information and exit\n");
|
||||
fprintf(stderr, "Switches for wizards:\n");
|
||||
@@ -244,6 +246,7 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
icc_filename = NULL;
|
||||
outfilename = NULL;
|
||||
memdst = FALSE;
|
||||
report = FALSE;
|
||||
cinfo->err->trace_level = 0;
|
||||
|
||||
/* Scan command line options, adjust parameters */
|
||||
@@ -395,6 +398,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
qtablefile = argv[argn];
|
||||
/* We postpone actually reading the file in case -quality comes later. */
|
||||
|
||||
} else if (keymatch(arg, "report", 3)) {
|
||||
report = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "restart", 1)) {
|
||||
/* Restart interval in MCU rows (or in MCUs with 'b'). */
|
||||
long lval;
|
||||
@@ -505,9 +511,7 @@ main(int argc, char **argv)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
#ifdef PROGRESS_REPORT
|
||||
struct cdjpeg_progress_mgr progress;
|
||||
#endif
|
||||
int file_index;
|
||||
cjpeg_source_ptr src_mgr;
|
||||
FILE *input_file;
|
||||
@@ -628,9 +632,10 @@ main(int argc, char **argv)
|
||||
fclose(icc_file);
|
||||
}
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
start_progress_monitor((j_common_ptr)&cinfo, &progress);
|
||||
#endif
|
||||
if (report) {
|
||||
start_progress_monitor((j_common_ptr)&cinfo, &progress);
|
||||
progress.report = report;
|
||||
}
|
||||
|
||||
/* Figure out the input file format, and set up to read it. */
|
||||
src_mgr = select_file_type(&cinfo, input_file);
|
||||
@@ -676,9 +681,8 @@ main(int argc, char **argv)
|
||||
if (output_file != stdout && output_file != NULL)
|
||||
fclose(output_file);
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
end_progress_monitor((j_common_ptr)&cinfo);
|
||||
#endif
|
||||
if (report)
|
||||
end_progress_monitor((j_common_ptr)&cinfo);
|
||||
|
||||
if (memdst) {
|
||||
fprintf(stderr, "Compressed size: %lu bytes\n", outsize);
|
||||
|
||||
24
djpeg.1
24
djpeg.1
@@ -1,4 +1,4 @@
|
||||
.TH DJPEG 1 "13 November 2017"
|
||||
.TH DJPEG 1 "18 December 2019"
|
||||
.SH NAME
|
||||
djpeg \- decompress a JPEG file to an image file
|
||||
.SH SYNOPSIS
|
||||
@@ -190,6 +190,19 @@ number. For example,
|
||||
.B \-max 4m
|
||||
selects 4000000 bytes. If more space is needed, an error will occur.
|
||||
.TP
|
||||
.BI \-maxscans " N"
|
||||
Abort if the JPEG image contains more than
|
||||
.I N
|
||||
scans. This feature demonstrates a method by which applications can guard
|
||||
against denial-of-service attacks instigated by specially-crafted malformed
|
||||
JPEG images containing numerous scans with missing image data or image data
|
||||
consisting only of "EOB runs" (a feature of progressive JPEG images that allows
|
||||
potentially hundreds of thousands of adjoining zero-value pixels to be
|
||||
represented using only a few bytes.) Attempting to decompress such malformed
|
||||
JPEG images can cause excessive CPU activity, since the decompressor must fully
|
||||
process each scan (even if the scan is corrupt) before it can proceed to the
|
||||
next scan.
|
||||
.TP
|
||||
.BI \-outfile " name"
|
||||
Send output image to the named file, not to standard output.
|
||||
.TP
|
||||
@@ -197,6 +210,9 @@ Send output image to the named file, not to standard output.
|
||||
Load input file into memory before decompressing. This feature was implemented
|
||||
mainly as a way of testing the in-memory source manager (jpeg_mem_src().)
|
||||
.TP
|
||||
.BI \-report
|
||||
Report decompression progress.
|
||||
.TP
|
||||
.BI \-skip " Y0,Y1"
|
||||
Decompress all rows of the JPEG image except those between Y0 and Y1
|
||||
(inclusive.) Note that if decompression scaling is being used, then Y0 and Y1
|
||||
@@ -210,6 +226,12 @@ decompression scaling is being used, then X, Y, W, and H are relative to the
|
||||
scaled image dimensions. Currently this option only works with the
|
||||
PBMPLUS (PPM/PGM), GIF, and Targa output formats.
|
||||
.TP
|
||||
.BI \-strict
|
||||
Treat all warnings as fatal. This feature also demonstrates a method by which
|
||||
applications can guard against attacks instigated by specially-crafted
|
||||
malformed JPEG images. Enabling this option will cause the decompressor to
|
||||
abort if the JPEG image contains incomplete or corrupt image data.
|
||||
.TP
|
||||
.B \-verbose
|
||||
Enable debug printout. More
|
||||
.BR \-v 's
|
||||
|
||||
60
djpeg.c
60
djpeg.c
@@ -5,7 +5,7 @@
|
||||
* Copyright (C) 1991-1997, Thomas G. Lane.
|
||||
* Modified 2013 by Guido Vollbeding.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2010-2011, 2013-2017, D. R. Commander.
|
||||
* Copyright (C) 2010-2011, 2013-2017, 2019, D. R. Commander.
|
||||
* Copyright (C) 2015, Google, Inc.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
@@ -94,11 +94,14 @@ static IMAGE_FORMATS requested_fmt;
|
||||
|
||||
static const char *progname; /* program name for error messages */
|
||||
static char *icc_filename; /* for -icc switch */
|
||||
JDIMENSION max_scans; /* for -maxscans switch */
|
||||
static char *outfilename; /* for -outfile switch */
|
||||
boolean memsrc; /* for -memsrc switch */
|
||||
boolean report; /* for -report switch */
|
||||
boolean skip, crop;
|
||||
JDIMENSION skip_start, skip_end;
|
||||
JDIMENSION crop_x, crop_y, crop_width, crop_height;
|
||||
boolean strict; /* for -strict switch */
|
||||
#define INPUT_BUF_SIZE 4096
|
||||
|
||||
|
||||
@@ -171,14 +174,16 @@ usage(void)
|
||||
fprintf(stderr, " -onepass Use 1-pass quantization (fast, low quality)\n");
|
||||
#endif
|
||||
fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n");
|
||||
fprintf(stderr, " -maxscans N Maximum number of scans to allow in input file\n");
|
||||
fprintf(stderr, " -outfile name Specify name for output file\n");
|
||||
#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
|
||||
fprintf(stderr, " -memsrc Load input file into memory before decompressing\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr, " -report Report decompression progress\n");
|
||||
fprintf(stderr, " -skip Y0,Y1 Decompress all rows except those between Y0 and Y1 (inclusive)\n");
|
||||
fprintf(stderr, " -crop WxH+X+Y Decompress only a rectangular subregion of the image\n");
|
||||
fprintf(stderr, " [requires PBMPLUS (PPM/PGM), GIF, or Targa output format]\n");
|
||||
fprintf(stderr, " -strict Treat all warnings as fatal\n");
|
||||
fprintf(stderr, " -verbose or -debug Emit debug output\n");
|
||||
fprintf(stderr, " -version Print version information and exit\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -203,10 +208,13 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv,
|
||||
/* Set up default JPEG parameters. */
|
||||
requested_fmt = DEFAULT_FMT; /* set default output file format */
|
||||
icc_filename = NULL;
|
||||
max_scans = 0;
|
||||
outfilename = NULL;
|
||||
memsrc = FALSE;
|
||||
report = FALSE;
|
||||
skip = FALSE;
|
||||
crop = FALSE;
|
||||
strict = FALSE;
|
||||
cinfo->err->trace_level = 0;
|
||||
|
||||
/* Scan command line options, adjust parameters */
|
||||
@@ -351,6 +359,12 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv,
|
||||
lval *= 1000L;
|
||||
cinfo->mem->max_memory_to_use = lval * 1000L;
|
||||
|
||||
} else if (keymatch(arg, "maxscans", 4)) {
|
||||
if (++argn >= argc) /* advance to next argument */
|
||||
usage();
|
||||
if (sscanf(argv[argn], "%u", &max_scans) != 1)
|
||||
usage();
|
||||
|
||||
} else if (keymatch(arg, "nosmooth", 3)) {
|
||||
/* Suppress fancy upsampling */
|
||||
cinfo->do_fancy_upsampling = FALSE;
|
||||
@@ -383,6 +397,9 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv,
|
||||
/* PPM/PGM output format. */
|
||||
requested_fmt = FMT_PPM;
|
||||
|
||||
} else if (keymatch(arg, "report", 2)) {
|
||||
report = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "rle", 1)) {
|
||||
/* RLE output format. */
|
||||
requested_fmt = FMT_RLE;
|
||||
@@ -413,6 +430,9 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv,
|
||||
usage();
|
||||
crop = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "strict", 2)) {
|
||||
strict = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "targa", 1)) {
|
||||
/* Targa output format. */
|
||||
requested_fmt = FMT_TARGA;
|
||||
@@ -499,6 +519,19 @@ print_text_marker(j_decompress_ptr cinfo)
|
||||
}
|
||||
|
||||
|
||||
METHODDEF(void)
|
||||
my_emit_message(j_common_ptr cinfo, int msg_level)
|
||||
{
|
||||
if (msg_level < 0) {
|
||||
/* Treat warning as fatal */
|
||||
cinfo->err->error_exit(cinfo);
|
||||
} else {
|
||||
if (cinfo->err->trace_level >= msg_level)
|
||||
cinfo->err->output_message(cinfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The main program.
|
||||
*/
|
||||
@@ -508,9 +541,7 @@ main(int argc, char **argv)
|
||||
{
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
#ifdef PROGRESS_REPORT
|
||||
struct cdjpeg_progress_mgr progress;
|
||||
#endif
|
||||
int file_index;
|
||||
djpeg_dest_ptr dest_mgr = NULL;
|
||||
FILE *input_file;
|
||||
@@ -555,6 +586,9 @@ main(int argc, char **argv)
|
||||
|
||||
file_index = parse_switches(&cinfo, argc, argv, 0, FALSE);
|
||||
|
||||
if (strict)
|
||||
jerr.emit_message = my_emit_message;
|
||||
|
||||
#ifdef TWO_FILE_COMMANDLINE
|
||||
/* Must have either -outfile switch or explicit output file name */
|
||||
if (outfilename == NULL) {
|
||||
@@ -601,9 +635,11 @@ main(int argc, char **argv)
|
||||
output_file = write_stdout();
|
||||
}
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
start_progress_monitor((j_common_ptr)&cinfo, &progress);
|
||||
#endif
|
||||
if (report || max_scans != 0) {
|
||||
start_progress_monitor((j_common_ptr)&cinfo, &progress);
|
||||
progress.report = report;
|
||||
progress.max_scans = max_scans;
|
||||
}
|
||||
|
||||
/* Specify data source for decompression */
|
||||
#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
|
||||
@@ -763,12 +799,11 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
/* Hack: count final pass as done in case finish_output does an extra pass.
|
||||
* The library won't have updated completed_passes.
|
||||
*/
|
||||
progress.pub.completed_passes = progress.pub.total_passes;
|
||||
#endif
|
||||
if (report || max_scans != 0)
|
||||
progress.pub.completed_passes = progress.pub.total_passes;
|
||||
|
||||
if (icc_filename != NULL) {
|
||||
FILE *icc_file;
|
||||
@@ -807,9 +842,8 @@ main(int argc, char **argv)
|
||||
if (output_file != stdout)
|
||||
fclose(output_file);
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
end_progress_monitor((j_common_ptr)&cinfo);
|
||||
#endif
|
||||
if (report || max_scans != 0)
|
||||
end_progress_monitor((j_common_ptr)&cinfo);
|
||||
|
||||
if (memsrc && inbuffer != NULL)
|
||||
free(inbuffer);
|
||||
|
||||
24
jpegtran.1
24
jpegtran.1
@@ -1,4 +1,4 @@
|
||||
.TH JPEGTRAN 1 "18 March 2017"
|
||||
.TH JPEGTRAN 1 "18 December 2019"
|
||||
.SH NAME
|
||||
jpegtran \- lossless transformation of JPEG files
|
||||
.SH SYNOPSIS
|
||||
@@ -229,9 +229,31 @@ number. For example,
|
||||
.B \-max 4m
|
||||
selects 4000000 bytes. If more space is needed, an error will occur.
|
||||
.TP
|
||||
.BI \-maxscans " N"
|
||||
Abort if the input image contains more than
|
||||
.I N
|
||||
scans. This feature demonstrates a method by which applications can guard
|
||||
against denial-of-service attacks instigated by specially-crafted malformed
|
||||
JPEG images containing numerous scans with missing image data or image data
|
||||
consisting only of "EOB runs" (a feature of progressive JPEG images that allows
|
||||
potentially hundreds of thousands of adjoining zero-value pixels to be
|
||||
represented using only a few bytes.) Attempting to transform such malformed
|
||||
JPEG images can cause excessive CPU activity, since the decompressor must fully
|
||||
process each scan (even if the scan is corrupt) before it can proceed to the
|
||||
next scan.
|
||||
.TP
|
||||
.BI \-outfile " name"
|
||||
Send output image to the named file, not to standard output.
|
||||
.TP
|
||||
.BI \-report
|
||||
Report transformation progress.
|
||||
.TP
|
||||
.BI \-strict
|
||||
Treat all warnings as fatal. This feature also demonstrates a method by which
|
||||
applications can guard against attacks instigated by specially-crafted
|
||||
malformed JPEG images. Enabling this option will cause the decompressor to
|
||||
abort if the input image contains incomplete or corrupt image data.
|
||||
.TP
|
||||
.B \-verbose
|
||||
Enable debug printout. More
|
||||
.BR \-v 's
|
||||
|
||||
62
jpegtran.c
62
jpegtran.c
@@ -4,7 +4,7 @@
|
||||
* This file was part of the Independent JPEG Group's software:
|
||||
* Copyright (C) 1995-2010, Thomas G. Lane, Guido Vollbeding.
|
||||
* libjpeg-turbo Modifications:
|
||||
* Copyright (C) 2010, 2014, 2017, D. R. Commander.
|
||||
* Copyright (C) 2010, 2014, 2017, 2019, D. R. Commander.
|
||||
* For conditions of distribution and use, see the accompanying README.ijg
|
||||
* file.
|
||||
*
|
||||
@@ -41,7 +41,10 @@
|
||||
|
||||
static const char *progname; /* program name for error messages */
|
||||
static char *icc_filename; /* for -icc switch */
|
||||
JDIMENSION max_scans; /* for -maxscans switch */
|
||||
static char *outfilename; /* for -outfile switch */
|
||||
boolean report; /* for -report switch */
|
||||
boolean strict; /* for -strict switch */
|
||||
static JCOPY_OPTION copyoption; /* -copy switch */
|
||||
static jpeg_transform_info transformoption; /* image transformation options */
|
||||
|
||||
@@ -87,7 +90,10 @@ usage(void)
|
||||
fprintf(stderr, " -icc FILE Embed ICC profile contained in FILE\n");
|
||||
fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n");
|
||||
fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n");
|
||||
fprintf(stderr, " -maxscans N Maximum number of scans to allow in input file\n");
|
||||
fprintf(stderr, " -outfile name Specify name for output file\n");
|
||||
fprintf(stderr, " -report Report transformation progress\n");
|
||||
fprintf(stderr, " -strict Treat all warnings as fatal\n");
|
||||
fprintf(stderr, " -verbose or -debug Emit debug output\n");
|
||||
fprintf(stderr, " -version Print version information and exit\n");
|
||||
fprintf(stderr, "Switches for wizards:\n");
|
||||
@@ -141,7 +147,10 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
/* Set up default JPEG parameters. */
|
||||
simple_progressive = FALSE;
|
||||
icc_filename = NULL;
|
||||
max_scans = 0;
|
||||
outfilename = NULL;
|
||||
report = FALSE;
|
||||
strict = FALSE;
|
||||
copyoption = JCOPYOPT_DEFAULT;
|
||||
transformoption.transform = JXFORM_NONE;
|
||||
transformoption.perfect = FALSE;
|
||||
@@ -261,6 +270,12 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
lval *= 1000L;
|
||||
cinfo->mem->max_memory_to_use = lval * 1000L;
|
||||
|
||||
} else if (keymatch(arg, "maxscans", 4)) {
|
||||
if (++argn >= argc) /* advance to next argument */
|
||||
usage();
|
||||
if (sscanf(argv[argn], "%u", &max_scans) != 1)
|
||||
usage();
|
||||
|
||||
} else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) {
|
||||
/* Enable entropy parm optimization. */
|
||||
#ifdef ENTROPY_OPT_SUPPORTED
|
||||
@@ -293,6 +308,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
|
||||
} else if (keymatch(arg, "report", 3)) {
|
||||
report = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "restart", 1)) {
|
||||
/* Restart interval in MCU rows (or in MCUs with 'b'). */
|
||||
long lval;
|
||||
@@ -338,6 +356,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
|
||||
} else if (keymatch(arg, "strict", 2)) {
|
||||
strict = TRUE;
|
||||
|
||||
} else if (keymatch(arg, "transpose", 1)) {
|
||||
/* Transpose (across UL-to-LR axis). */
|
||||
select_transform(JXFORM_TRANSPOSE);
|
||||
@@ -375,6 +396,19 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv,
|
||||
}
|
||||
|
||||
|
||||
METHODDEF(void)
|
||||
my_emit_message(j_common_ptr cinfo, int msg_level)
|
||||
{
|
||||
if (msg_level < 0) {
|
||||
/* Treat warning as fatal */
|
||||
cinfo->err->error_exit(cinfo);
|
||||
} else {
|
||||
if (cinfo->err->trace_level >= msg_level)
|
||||
cinfo->err->output_message(cinfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The main program.
|
||||
*/
|
||||
@@ -385,9 +419,7 @@ main(int argc, char **argv)
|
||||
struct jpeg_decompress_struct srcinfo;
|
||||
struct jpeg_compress_struct dstinfo;
|
||||
struct jpeg_error_mgr jsrcerr, jdsterr;
|
||||
#ifdef PROGRESS_REPORT
|
||||
struct cdjpeg_progress_mgr progress;
|
||||
#endif
|
||||
struct cdjpeg_progress_mgr src_progress, dst_progress;
|
||||
jvirt_barray_ptr *src_coef_arrays;
|
||||
jvirt_barray_ptr *dst_coef_arrays;
|
||||
int file_index;
|
||||
@@ -427,6 +459,9 @@ main(int argc, char **argv)
|
||||
jsrcerr.trace_level = jdsterr.trace_level;
|
||||
srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
|
||||
|
||||
if (strict)
|
||||
jsrcerr.emit_message = my_emit_message;
|
||||
|
||||
#ifdef TWO_FILE_COMMANDLINE
|
||||
/* Must have either -outfile switch or explicit output file name */
|
||||
if (outfilename == NULL) {
|
||||
@@ -492,9 +527,15 @@ main(int argc, char **argv)
|
||||
copyoption = JCOPYOPT_ALL_EXCEPT_ICC;
|
||||
}
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
start_progress_monitor((j_common_ptr)&dstinfo, &progress);
|
||||
#endif
|
||||
if (report) {
|
||||
start_progress_monitor((j_common_ptr)&dstinfo, &dst_progress);
|
||||
dst_progress.report = report;
|
||||
}
|
||||
if (report || max_scans != 0) {
|
||||
start_progress_monitor((j_common_ptr)&srcinfo, &src_progress);
|
||||
src_progress.report = report;
|
||||
src_progress.max_scans = max_scans;
|
||||
}
|
||||
|
||||
/* Specify data source for decompression */
|
||||
jpeg_stdio_src(&srcinfo, fp);
|
||||
@@ -587,9 +628,10 @@ main(int argc, char **argv)
|
||||
if (fp != stdout)
|
||||
fclose(fp);
|
||||
|
||||
#ifdef PROGRESS_REPORT
|
||||
end_progress_monitor((j_common_ptr)&dstinfo);
|
||||
#endif
|
||||
if (report)
|
||||
end_progress_monitor((j_common_ptr)&dstinfo);
|
||||
if (report || max_scans != 0)
|
||||
end_progress_monitor((j_common_ptr)&srcinfo);
|
||||
|
||||
if (icc_profile != NULL)
|
||||
free(icc_profile);
|
||||
|
||||
Reference in New Issue
Block a user