Hash :
e8b40f3c
Author :
Date :
2022-11-01T21:45:39
Vastly improve 12-bit JPEG integration
The Gordian knot that 7fec5074f962b20ed00b4f5da4533e1e8d4ed8ac attempted
to unravel was caused by the fact that there are several
data-precision-dependent (JSAMPLE-dependent) fields and methods in the
exposed libjpeg API structures, and if you change the exposed libjpeg
API structures, then you have to change the whole API. If you change
the whole API, then you have to provide a whole new library to support
the new API, and that makes it difficult to support multiple data
precisions in the same application. (It is not impossible, as example.c
demonstrated, but using data-precision-dependent libjpeg API structures
would have made the cjpeg, djpeg, and jpegtran source code hard to read,
so it made more sense to build, install, and package 12-bit-specific
versions of those applications.)
Unfortunately, the result of that initial integration effort was an
unreadable and unmaintainable mess, which is a problem for a library
that is an ISO/ITU-T reference implementation. Also, as I dug into the
problem of lossless JPEG support, I realized that 16-bit lossless JPEG
images are a thing, and supporting yet another version of the libjpeg
API just for those images is untenable.
In fact, however, the touch points for JSAMPLE in the exposed libjpeg
API structures are minimal:
- The colormap and sample_range_limit fields in jpeg_decompress_struct
- The alloc_sarray() and access_virt_sarray() methods in
jpeg_memory_mgr
- jpeg_write_scanlines() and jpeg_write_raw_data()
- jpeg_read_scanlines() and jpeg_read_raw_data()
- jpeg_skip_scanlines() and jpeg_crop_scanline()
(This is subtle, but both of those functions use JSAMPLE-dependent
opaque structures behind the scenes.)
It is much more readable and maintainable to provide 12-bit-specific
versions of those six top-level API functions and to document that the
aforementioned methods and fields must be type-cast when using 12-bit
samples. Since that eliminates the need to provide a 12-bit-specific
version of the exposed libjpeg API structures, we can:
- Compile only the precision-dependent libjpeg modules (the
coefficient buffer controllers, the colorspace converters, the
DCT/IDCT managers, the main buffer controllers, the preprocessing
and postprocessing controller, the downsampler and upsamplers, the
quantizers, the integer DCT methods, and the IDCT methods) for
multiple data precisions.
- Introduce 12-bit-specific methods into the various internal
structures defined in jpegint.h.
- Create precision-independent data type, macro, method, field, and
function names that are prefixed by an underscore, and use an
internal header to convert those into precision-dependent data
type, macro, method, field, and function names, based on the value
of BITS_IN_JSAMPLE, when compiling the precision-dependent libjpeg
modules.
- Expose precision-dependent jinit*() functions for each of the
precision-dependent libjpeg modules.
- Abstract the precision-dependent libjpeg modules by calling the
appropriate precision-dependent jinit*() function, based on the
value of cinfo->data_precision, from top-level libjpeg API
functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
/*
* cdjpeg.h
*
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1994-1997, Thomas G. Lane.
* Modified 2019 by Guido Vollbeding.
* libjpeg-turbo Modifications:
* Copyright (C) 2017, 2019, 2021-2022, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
* This file contains common declarations for the sample applications
* cjpeg and djpeg. It is NOT used by the core JPEG library.
*/
#define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */
#define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */
#include "jinclude.h"
#include "jpeglib.h"
#include "jerror.h" /* get library error codes too */
#include "cderror.h" /* get application-specific error codes */
/*
* Object interface for cjpeg's source file decoding modules
*/
typedef struct cjpeg_source_struct *cjpeg_source_ptr;
struct cjpeg_source_struct {
void (*start_input) (j_compress_ptr cinfo, cjpeg_source_ptr sinfo);
JDIMENSION (*get_pixel_rows) (j_compress_ptr cinfo, cjpeg_source_ptr sinfo);
void (*finish_input) (j_compress_ptr cinfo, cjpeg_source_ptr sinfo);
FILE *input_file;
JSAMPARRAY buffer;
#ifdef WITH_12BIT
J12SAMPARRAY buffer12;
#endif
JDIMENSION buffer_height;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
JDIMENSION max_pixels;
#endif
};
/*
* Object interface for djpeg's output file encoding modules
*/
typedef struct djpeg_dest_struct *djpeg_dest_ptr;
struct djpeg_dest_struct {
/* start_output is called after jpeg_start_decompress finishes.
* The color map will be ready at this time, if one is needed.
*/
void (*start_output) (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo);
/* Emit the specified number of pixel rows from the buffer. */
void (*put_pixel_rows) (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
JDIMENSION rows_supplied);
/* Finish up at the end of the image. */
void (*finish_output) (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo);
/* Re-calculate buffer dimensions based on output dimensions (for use with
partial image decompression.) If this is NULL, then the output format
does not support partial image decompression (BMP, in particular, cannot
support partial decompression because it uses an inversion buffer to write
the image in bottom-up order.) */
void (*calc_buffer_dimensions) (j_decompress_ptr cinfo,
djpeg_dest_ptr dinfo);
/* Target file spec; filled in by djpeg.c after object is created. */
FILE *output_file;
/* Output pixel-row buffer. Created by module init or start_output.
* Width is cinfo->output_width * cinfo->output_components;
* height is buffer_height.
*/
JSAMPARRAY buffer;
#ifdef WITH_12BIT
J12SAMPARRAY buffer12;
#endif
JDIMENSION buffer_height;
};
/*
* cjpeg/djpeg may need to perform extra passes to convert to or from
* the source/destination file format. The JPEG library does not know
* about these passes, but we'd like them to be counted by the progress
* monitor. We use an expanded progress monitor object to hold the
* additional pass count.
*/
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;
};
typedef struct cdjpeg_progress_mgr *cd_progress_ptr;
/* Module selection routines for I/O modules. */
EXTERN(cjpeg_source_ptr) jinit_read_bmp(j_compress_ptr cinfo,
boolean use_inversion_array);
EXTERN(djpeg_dest_ptr) jinit_write_bmp(j_decompress_ptr cinfo, boolean is_os2,
boolean use_inversion_array);
EXTERN(cjpeg_source_ptr) jinit_read_gif(j_compress_ptr cinfo);
EXTERN(djpeg_dest_ptr) jinit_write_gif(j_decompress_ptr cinfo, boolean is_lzw);
#ifdef WITH_12BIT
EXTERN(cjpeg_source_ptr) j12init_read_gif(j_compress_ptr cinfo);
EXTERN(djpeg_dest_ptr) j12init_write_gif(j_decompress_ptr cinfo,
boolean is_lzw);
#endif
EXTERN(cjpeg_source_ptr) jinit_read_ppm(j_compress_ptr cinfo);
EXTERN(djpeg_dest_ptr) jinit_write_ppm(j_decompress_ptr cinfo);
#ifdef WITH_12BIT
EXTERN(cjpeg_source_ptr) j12init_read_ppm(j_compress_ptr cinfo);
EXTERN(djpeg_dest_ptr) j12init_write_ppm(j_decompress_ptr cinfo);
#endif
EXTERN(cjpeg_source_ptr) jinit_read_targa(j_compress_ptr cinfo);
EXTERN(djpeg_dest_ptr) jinit_write_targa(j_decompress_ptr cinfo);
/* cjpeg support routines (in rdswitch.c) */
EXTERN(boolean) read_quant_tables(j_compress_ptr cinfo, char *filename,
boolean force_baseline);
EXTERN(boolean) read_scan_script(j_compress_ptr cinfo, char *filename);
EXTERN(boolean) set_quality_ratings(j_compress_ptr cinfo, char *arg,
boolean force_baseline);
EXTERN(boolean) set_quant_slots(j_compress_ptr cinfo, char *arg);
EXTERN(boolean) set_sample_factors(j_compress_ptr cinfo, char *arg);
/* djpeg support routines (in rdcolmap.c) */
EXTERN(void) read_color_map(j_decompress_ptr cinfo, FILE *infile);
#ifdef WITH_12BIT
EXTERN(void) read_color_map_12(j_decompress_ptr cinfo, FILE *infile);
#endif
/* common support routines (in cdjpeg.c) */
EXTERN(void) start_progress_monitor(j_common_ptr cinfo,
cd_progress_ptr progress);
EXTERN(void) end_progress_monitor(j_common_ptr cinfo);
EXTERN(boolean) keymatch(char *arg, const char *keyword, int minchars);
EXTERN(FILE *) read_stdin(void);
EXTERN(FILE *) write_stdout(void);
/* miscellaneous useful macros */
#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */
#define READ_BINARY "r"
#define WRITE_BINARY "w"
#else
#define READ_BINARY "rb"
#define WRITE_BINARY "wb"
#endif
#ifndef EXIT_FAILURE /* define exit() codes if not provided */
#define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_WARNING
#define EXIT_WARNING 2
#endif
#define IsExtRGB(cs) \
(cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB))