Vastly improve 12-bit JPEG integration

The Gordian knot that 7fec5074f9 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.
This commit is contained in:
DRC
2022-11-01 21:45:39 -05:00
parent 6c2bc901e2
commit e8b40f3c2b
114 changed files with 2903 additions and 5075 deletions

View File

@@ -450,9 +450,9 @@ shown are:
is not used for full-color output. Works on one pixel row at a time; may
require two passes to generate a color map. Note that the output will
always be a single component representing colormap indexes. In the current
design, the output values are JSAMPLEs, so an 8-bit compilation cannot
quantize to more than 256 colors. This is unlikely to be a problem in
practice.
design, the output values are JSAMPLEs or J12SAMPLEs, so the library cannot
quantize to more than 256 colors when using 8-bit data precision. This is
unlikely to be a problem in practice.
* Color reduction: this module handles color precision reduction, e.g.,
generating 15-bit color (5 bits/primary) from JPEG's 24-bit output.
@@ -538,28 +538,35 @@ there isn't any real need for it.
*** Data formats ***
Arrays of pixel sample values use the following data structure:
Arrays of 8-bit pixel sample values use the following data structure:
typedef something JSAMPLE; a pixel component value, 0..MAXJSAMPLE
typedef JSAMPLE *JSAMPROW; ptr to a row of samples
typedef JSAMPROW *JSAMPARRAY; ptr to a list of rows
typedef JSAMPARRAY *JSAMPIMAGE; ptr to a list of color-component arrays
The basic element type JSAMPLE will be one of unsigned char or short. Short
will be used if samples wider than 8 bits are to be supported (this is a
compile-time option). Otherwise, unsigned char is used.
Arrays of 12-bit pixel sample values use the following data structure:
With these conventions, JSAMPLE values can be assumed to be >= 0. This helps
typedef something J12SAMPLE; a pixel component value, 0..MAXJ12SAMPLE
typedef J12SAMPLE *J12SAMPROW; ptr to a row of samples
typedef J12SAMPROW *J12SAMPARRAY; ptr to a list of rows
typedef J12SAMPARRAY *J12SAMPIMAGE; ptr to a list of color-component arrays
The basic element type JSAMPLE (8-bit sample) will be unsigned char, and the
basic element type J12SAMPLE (12-bit sample) with be short.
With these conventions, J*SAMPLE values can be assumed to be >= 0. This helps
simplify correct rounding during downsampling, etc. The JPEG standard's
specification that sample values run from -128..127 is accommodated by
specification that 8-bit sample values run from -128..127 is accommodated by
subtracting 128 from the sample value in the DCT step. Similarly, during
decompression the output of the IDCT step will be immediately shifted back to
0..255. (NB: different values are required when 12-bit samples are in use.
The code is written in terms of MAXJSAMPLE and CENTERJSAMPLE, which will be
defined as 255 and 128 respectively in an 8-bit implementation, and as 4095
and 2048 in a 12-bit implementation.)
0..255. (NOTE: different values are required when 12-bit samples are in use.
When 8-bit samples are in use, the code uses MAXJSAMPLE and CENTERJSAMPLE,
which are defined as 255 and 128 respectively. When 12-bit samples are in use,
the code uses MAXJ12SAMPLE and CENTERJ12SAMPLE, which are defined as 4095 and
2048 respectively.)
We use a pointer per row, rather than a two-dimensional JSAMPLE array. This
We use a pointer per row, rather than a two-dimensional J*SAMPLE array. This
choice costs only a small amount of memory and has several benefits:
* Code using the data structure doesn't need to know the allocated width of
the rows. This simplifies edge expansion/compression, since we can work
@@ -589,9 +596,9 @@ be precomputed by copying the relevant pointer.
Since most image-processing applications prefer to work on images in which
the components of a pixel are stored together, the data passed to or from the
surrounding application uses the traditional convention: a single pixel is
represented by N consecutive JSAMPLE values, and an image row is an array of
(# of color components)*(image width) JSAMPLEs. One or more rows of data can
be represented by a pointer of type JSAMPARRAY in this scheme. This scheme is
represented by N consecutive J*SAMPLE values, and an image row is an array of
(# of color components)*(image width) J*SAMPLEs. One or more rows of data can
be represented by a pointer of type J*SAMPARRAY in this scheme. This scheme is
converted to component-wise storage inside the JPEG library. (Applications
that want to skip JPEG preprocessing or postprocessing will have to contend
with component-wise storage.)
@@ -730,17 +737,17 @@ The memory manager deals with three kinds of object:
pointers on MS-DOS machines.) Note that individual "large" objects cannot
exceed the size allowed by type size_t, which may be 64K or less on some
machines.
3. "Virtual" objects. These are large 2-D arrays of JSAMPLEs or JBLOCKs
3. "Virtual" objects. These are large 2-D arrays of J*SAMPLEs or JBLOCKs
(typically large enough for the entire image being processed). The
memory manager provides stripwise access to these arrays. On machines
without virtual memory, the rest of the array may be swapped out to a
temporary file.
(Note: JSAMPARRAY and JBLOCKARRAY data structures are a combination of large
(Note: J*SAMPARRAY and JBLOCKARRAY data structures are a combination of large
objects for the data proper and small objects for the row pointers. For
convenience and speed, the memory manager provides single routines to create
these structures. Similarly, virtual arrays include a small control block
and a JSAMPARRAY or JBLOCKARRAY working buffer, all created with one call.)
and a J*SAMPARRAY or JBLOCKARRAY working buffer, all created with one call.)
In the present implementation, virtual arrays are only permitted to have image
lifespan. (Permanent lifespan would not be reasonable, and pass lifespan is
@@ -767,7 +774,7 @@ To support all this, we establish the following protocol for doing business
with the memory manager:
1. Modules must request virtual arrays (which may have only image lifespan)
during the initial setup phase, i.e., in their jinit_xxx routines.
2. All "large" objects (including JSAMPARRAYs and JBLOCKARRAYs) must also be
2. All "large" objects (including J*SAMPARRAYs and JBLOCKARRAYs) must also be
allocated during initial setup.
3. realize_virt_arrays will be called at the completion of initial setup.
The above conventions ensure that sufficient information is available