diff --git a/CMakeLists.txt b/CMakeLists.txt index aac4091e..6de3e08a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,12 +273,12 @@ endif() # 1: + In-memory source/destination managers (libjpeg-turbo 1.3.x) # 2: + Partial image decompression functions (libjpeg-turbo 1.5.x) # 3: + ICC functions (libjpeg-turbo 2.0.x) -# 4: + 12-bit-per-component functions (libjpeg-turbo 2.2.x) +# 4: + 12-bit-per-component and lossless functions (libjpeg-turbo 2.2.x) # # libjpeg v8 API/ABI emulation: # 1: + Partial image decompression functions (libjpeg-turbo 1.5.x) # 2: + ICC functions (libjpeg-turbo 2.0.x) -# 3: + 12-bit-per-component functions (libjpeg-turbo 2.2.x) +# 3: + 12-bit-per-component and lossless functions (libjpeg-turbo 2.2.x) set(SO_AGE 3) if(NOT WITH_JPEG8) set(SO_AGE 4) @@ -535,10 +535,11 @@ if(CMAKE_EXECUTABLE_SUFFIX_TMP) endif() message(STATUS "CMAKE_EXECUTABLE_SUFFIX = ${CMAKE_EXECUTABLE_SUFFIX}") -set(JPEG_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c - jcicc.c jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c - jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c jdatadst.c - jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c jdicc.c jdinput.c +set(JPEG_SOURCES jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c + jcdiffct.c jchuff.c jcicc.c jcinit.c jclhuff.c jclossls.c jcmainct.c + jcmarker.c jcmaster.c jcomapi.c jcparam.c jcphuff.c jcprepct.c jcsample.c + jctrans.c jdapimin.c jdapistd.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c + jddctmgr.c jddiffct.c jdhuff.c jdicc.c jdinput.c jdlhuff.c jdlossls.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c jquant2.c jutils.c jmemmgr.c jmemnobs.c) @@ -557,10 +558,11 @@ endif() # Compile a separate version of these source files with 12-bit data # precision. -add_library(jpeg12 OBJECT jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jcmainct.c - jcprepct.c jcsample.c jdapistd.c jdcoefct.c jdcolor.c jddctmgr.c jdmainct.c - jdmerge.c jdpostct.c jdsample.c jfdctfst.c jfdctint.c jidctflt.c jidctfst.c - jidctint.c jidctred.c jquant1.c jquant2.c jutils.c) +add_library(jpeg12 OBJECT jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jcdiffct.c + jclossls.c jcmainct.c jcprepct.c jcsample.c jdapistd.c jdcoefct.c jdcolor.c + jddctmgr.c jddiffct.c jdlossls.c jdmainct.c jdmerge.c jdpostct.c jdsample.c + jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c + jquant2.c jutils.c) set_property(TARGET jpeg12 PROPERTY COMPILE_FLAGS "-DBITS_IN_JSAMPLE=12") if(WITH_SIMD) @@ -1046,6 +1048,8 @@ foreach(libtype ${TEST_LIBTYPES}) set(MD5_PPM_420M_ISLOW_3_8 343d19015531b7bbe746124127244fa8) set(MD5_PPM_420M_ISLOW_1_4 35fd59d866e44659edfa3c18db2a3edb) set(MD5_PPM_420M_ISLOW_1_8 ccaed48ac0aedefda5d4abe4013f4ad7) + set(MD5_JPEG_LOSSLESS a153297ef3ae6e51357be06fc5efca2a) + set(MD5_PPM_LOSSLESS 1da3fb2620e5a4e258e0fcb891bc67e8) set(MD5_PPM_420_ISLOW_SKIP15_31 86664cd9dc956536409e44e244d20a97) set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 452a21656115a163029cfba5c04fa76a) @@ -1121,6 +1125,8 @@ foreach(libtype ${TEST_LIBTYPES}) set(MD5_BMP_420_ISLOW_565D 6bde71526acc44bcff76f696df8638d2) set(MD5_BMP_420M_ISLOW_565 8dc0185245353cfa32ad97027342216f) set(MD5_BMP_420M_ISLOW_565D ce034037d212bc403330df6f915c161b) + set(MD5_JPEG_LOSSLESS dd21c541d22a87cccabdb48b796d8bcd) + set(MD5_PPM_LOSSLESS 64072f1dbdc5b3a187777788604971a5) set(MD5_PPM_420_ISLOW_SKIP15_31 c4c65c1e43d7275cd50328a61e6534f0) set(MD5_PPM_420_ISLOW_ARI_SKIP16_139 087c6b123db16ac00cb88c5b590bb74a) set(MD5_PPM_420_ISLOW_PROG_CROP62x62_71_71 @@ -1362,6 +1368,16 @@ foreach(libtype ${TEST_LIBTYPES}) ${MD5_BMP_420M_ISLOW_565D}) endif() + # Lossless (all arguments other than -lossless should have no effect) + add_bittest(${cjpeg} lossless + "-lossless;4;-quality;1;-grayscale;-optimize;-dct;float;-smooth;100;-baseline;-qslots;1,0,0;-sample;1x2,3x4,2x1" + ${testout}_lossless.jpg ${TESTIMAGES}/testorig.ppm + ${MD5_JPEG_LOSSLESS}) + add_bittest(${djpeg} lossless + "-fast;-scale;1/8;-dct;float;-dither;none;-nosmooth;-onepass" + ${testout}_lossless.ppm ${testout}_lossless.jpg + ${MD5_PPM_LOSSLESS} ${cjpeg}-${libtype}-lossless) + # Partial decode tests. These tests are designed to cover all of the # possible code paths in jpeg_skip_scanlines(). diff --git a/ChangeLog.md b/ChangeLog.md index 8431eb77..9f933826 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -30,6 +30,11 @@ tables. 3. All deprecated fields, constructors, and methods in the TurboJPEG Java API have been removed. +4. Added support for 8-bit and 12-bit lossless JPEG images. A new libjpeg API +function (`jpeg_enable_lossless()`) and cjpeg command-line argument +(`-lossless`) can be used to create a lossless JPEG image. (Decompression of +lossless JPEG images is handled automatically.) + 2.1.5 ===== diff --git a/README.ijg b/README.ijg index ab586812..8f376826 100644 --- a/README.ijg +++ b/README.ijg @@ -68,17 +68,17 @@ other abrupt features may not compress well with JPEG, and a higher JPEG quality may have to be used to avoid visible compression artifacts with such images. -JPEG is lossy, meaning that the output pixels are not necessarily identical to -the input pixels. However, on photographic content and other "smooth" images, -very good compression ratios can be obtained with no visible compression -artifacts, and extremely high compression ratios are possible if you are -willing to sacrifice image quality (by reducing the "quality" setting in the -compressor.) +JPEG is normally lossy, meaning that the output pixels are not necessarily +identical to the input pixels. However, on photographic content and other +"smooth" images, very good compression ratios can be obtained with no visible +compression artifacts, and extremely high compression ratios are possible if +you are willing to sacrifice image quality (by reducing the "quality" setting +in the compressor.) -This software implements JPEG baseline, extended-sequential, and progressive -compression processes. Provision is made for supporting all variants of these -processes, although some uncommon parameter settings aren't implemented yet. -We have made no provision for supporting the hierarchical or lossless +This software implements JPEG baseline, extended-sequential, progressive, and +lossless compression processes. Provision is made for supporting all variants +of these processes, although some uncommon parameter settings aren't +implemented yet. We have made no provision for supporting the hierarchical processes defined in the standard. We provide a set of library routines for reading and writing JPEG image files, @@ -241,7 +241,7 @@ This software implements ITU T.81 | ISO/IEC 10918 with some extensions from ITU T.871 | ISO/IEC 10918-5 (JPEG File Interchange Format-- see REFERENCES). Informally, the term "JPEG image" or "JPEG file" most often refers to JFIF or a subset thereof, but there are other formats containing the name "JPEG" that -are incompatible with the DCT-based JPEG standard or with JFIF (for instance, +are incompatible with the original JPEG standard or with JFIF (for instance, JPEG 2000 and JPEG XR). This software therefore does not support these formats. Indeed, one of the original reasons for developing this free software was to help force convergence on a common, interoperable format standard for diff --git a/cjpeg.1 b/cjpeg.1 index 80fe8e56..b7ad8ce0 100644 --- a/cjpeg.1 +++ b/cjpeg.1 @@ -1,4 +1,4 @@ -.TH CJPEG 1 "2 November 2022" +.TH CJPEG 1 "14 November 2022" .SH NAME cjpeg \- compress an image file to a JPEG file .SH SYNOPSIS @@ -155,6 +155,45 @@ Create JPEG file with N-bit data precision. N is 8 or 12; default is 8. 12-bit JPEG is not yet widely implemented, so many decoders will be unable to view a 12-bit JPEG file at all. .TP +.BI \-lossless " psv[,Pt]" +Create a lossless JPEG file using the specified predictor selection value +(1 through 7) and optional point transform (0 through +.nh +.I precision +.hy +- 1, where +.nh +.I precision +.hy +is the JPEG data precision in bits). A point transform value of 0 (the +default) is necessary in order to create a fully lossless JPEG file. (A +non-zero point transform value right-shifts the input samples by the specified +number of bits, which is effectively a form of lossy color quantization.) +.B Caution: +lossless JPEG is not yet widely implemented, so many decoders will be unable to +view a lossless JPEG file at all. Note that the following features will be +unavailable when compressing or decompressing a lossless JPEG file: +.IP +- Quality/quantization table selection +.IP +- Color conversion (the JPEG image will use the same color space as the input +image) +.IP +- DCT/IDCT algorithm selection +.IP +- Smoothing +.IP +- Downsampling/upsampling +.IP +- IDCT scaling +.IP +- Partial image decompression +.IP +- Transformations using +.B jpegtran +.IP +Any switches used to enable or configure those features will be ignored. +.TP .B \-arithmetic Use arithmetic coding. .B Caution: @@ -201,8 +240,8 @@ machines. Embed ICC color management profile contained in the specified file. .TP .BI \-restart " N" -Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is -attached to the number. +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks (samples in +lossless mode) if "B" is attached to the number. .B \-restart 0 (the default) means no restart markers. .TP diff --git a/cjpeg.c b/cjpeg.c index 16b4dc03..20274528 100644 --- a/cjpeg.c +++ b/cjpeg.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2011 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2013-2014, 2017, 2019-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -212,6 +214,9 @@ usage(void) fprintf(stderr, "Switches for advanced users:\n"); fprintf(stderr, " -precision N Create JPEG file with N-bit data precision\n"); fprintf(stderr, " (N is 8 or 12; default is 8)\n"); +#ifdef C_LOSSLESS_SUPPORTED + fprintf(stderr, " -lossless psv[,Pt] Create lossless JPEG file\n"); +#endif #ifdef C_ARITH_CODING_SUPPORTED fprintf(stderr, " -arithmetic Use arithmetic coding\n"); #endif @@ -265,6 +270,9 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, { int argn; char *arg; +#ifdef C_LOSSLESS_SUPPORTED + int psv, pt = 0; +#endif boolean force_baseline; boolean simple_progressive; char *qualityarg = NULL; /* saves -quality parm if any */ @@ -361,6 +369,27 @@ parse_switches(j_compress_ptr cinfo, int argc, char **argv, usage(); icc_filename = argv[argn]; + } else if (keymatch(arg, "lossless", 1)) { + /* Enable lossless mode. */ +#ifdef C_LOSSLESS_SUPPORTED + char ch = ',', *ptr; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d%c", &psv, &ch) < 1 || ch != ',') + usage(); + ptr = argv[argn]; + while (*ptr && *ptr++ != ','); /* advance to next segment of arg + string */ + if (*ptr) + sscanf(ptr, "%d", &pt); + jpeg_enable_lossless(cinfo, psv, pt); +#else + fprintf(stderr, "%s: sorry, lossless output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + } else if (keymatch(arg, "maxmemory", 3)) { /* Maximum memory in Kb (or Mb with 'm'). */ long lval; diff --git a/jcapimin.c b/jcapimin.c index fcb0b686..26d5b55d 100644 --- a/jcapimin.c +++ b/jcapimin.c @@ -23,6 +23,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jcmaster.h" /* @@ -94,6 +95,14 @@ jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) /* OK, I'm ready */ cinfo->global_state = CSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_comp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(my_comp_master)); + memset(cinfo->master, 0, sizeof(my_comp_master)); } diff --git a/jcapistd.c b/jcapistd.c index 4ce383bd..8192ff4f 100644 --- a/jcapistd.c +++ b/jcapistd.c @@ -136,6 +136,9 @@ _jpeg_write_raw_data(j_compress_ptr cinfo, _JSAMPIMAGE data, if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->next_scanline >= cinfo->image_height) { diff --git a/jccoefct.c b/jccoefct.c index e55896f4..2a5dde2d 100644 --- a/jccoefct.c +++ b/jccoefct.c @@ -9,7 +9,7 @@ * file. * * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. + * This controller is the top level of the lossy JPEG compressor proper. * The coefficient buffer lies between forward-DCT and entropy encoding steps. */ diff --git a/jccolor.c b/jccolor.c index 5b5954ff..c073b4a9 100644 --- a/jccolor.c +++ b/jccolor.c @@ -716,4 +716,13 @@ _jinit_color_converter(j_compress_ptr cinfo) cconvert->pub._color_convert = null_convert; break; } + + /* Prevent lossy color conversion in lossless mode */ + if (cinfo->master->lossless) { + if ((cinfo->jpeg_color_space == JCS_GRAYSCALE && + cinfo->in_color_space != JCS_GRAYSCALE) || + (cinfo->jpeg_color_space != JCS_GRAYSCALE && + cconvert->pub._color_convert != null_convert)) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + } } diff --git a/jcdiffct.c b/jcdiffct.c new file mode 100644 index 00000000..0bae0689 --- /dev/null +++ b/jcdiffct.c @@ -0,0 +1,411 @@ +/* + * jcdiffct.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the difference buffer controller for compression. + * This controller is the top level of the lossless JPEG compressor proper. + * The difference buffer lies between the prediction/differencing and entropy + * encoding steps. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* We use a full-image sample buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the + * full-image buffer is filled during the first pass, and the scaling, + * prediction and differencing steps are run during subsequent passes. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_SAMP_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_SAMP_BUFFER_SUPPORTED +#endif +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + _JSAMPROW cur_row[MAX_COMPONENTS]; /* row of point-transformed samples */ + _JSAMPROW prev_row[MAX_COMPONENTS]; /* previous row of Pt'd samples */ + JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ + + /* In multi-pass modes, we need a virtual sample array for each component. */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +} my_diff_controller; + +typedef my_diff_controller *my_diff_ptr; + + +/* Forward declarations */ +METHODDEF(boolean) compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf); +#ifdef FULL_SAMP_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass(j_compress_ptr cinfo, + _JSAMPIMAGE input_buf); +METHODDEF(boolean) compress_output(j_compress_ptr cinfo, + _JSAMPIMAGE input_buf); +#endif + + +LOCAL(void) +start_iMCU_row(j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + diff->MCU_rows_per_iMCU_row = 1; + } else { + if (diff->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + diff->mcu_ctr = 0; + diff->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_diff(j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* Because it is hitching a ride on the jpeg_forward_dct struct, + * start_pass_lossless() will be called at the start of the initial pass. + * This ensures that it will be called at the start of the Huffman + * optimization and output passes as well. + */ + if (pass_mode == JBUF_CRANK_DEST) + (*cinfo->fdct->start_pass) (cinfo); + + diff->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (diff->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_data; + break; +#ifdef FULL_SAMP_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (diff->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (diff->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + diff->pub._compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +#define SWAP_ROWS(rowa, rowb) { \ + _JSAMPROW temp = rowa; \ + rowa = rowb; rowb = temp; \ +} + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION MCU_count; /* number of MCUs encoded */ + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, compi, yoffset, samp_row, samp_rows, samps_across; + jpeg_component_info *compptr; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = diff->MCU_vert_offset; yoffset < diff->MCU_rows_per_iMCU_row; + yoffset++) { + + MCU_col_num = diff->mcu_ctr; + + /* Scale and predict each scanline of the MCU row separately. + * + * Note: We only do this if we are at the start of an MCU row, ie, + * we don't want to reprocess a row suspended by the output. + */ + if (MCU_col_num == 0) { + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + if (diff->iMCU_row_num < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + samp_rows = + (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + else { + /* Fill dummy difference rows at the bottom edge with zeros, which + * will encode to the smallest amount of data. + */ + for (samp_row = samp_rows; samp_row < compptr->v_samp_factor; + samp_row++) + memset(diff->diff_buf[compi][samp_row], 0, + jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor) * sizeof(JDIFF)); + } + } + samps_across = compptr->width_in_blocks; + + for (samp_row = 0; samp_row < samp_rows; samp_row++) { + (*losslessc->scaler_scale) (cinfo, + input_buf[compi][samp_row], + diff->cur_row[compi], + samps_across); + (*losslessc->predict_difference[compi]) + (cinfo, compi, diff->cur_row[compi], diff->prev_row[compi], + diff->diff_buf[compi][samp_row], samps_across); + SWAP_ROWS(diff->cur_row[compi], diff->prev_row[compi]); + } + } + } + /* Try to write the MCU row (or remaining portion of suspended MCU row). */ + MCU_count = + (*cinfo->entropy->encode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); + if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { + /* Suspension forced; update state counters and exit */ + diff->MCU_vert_offset = yoffset; + diff->mcu_ctr += MCU_col_num; + return FALSE; + } + /* Completed an MCU row, but perhaps not an iMCU row */ + diff->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + diff->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +#ifdef FULL_SAMP_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the image. + * This amount of data is read from the source buffer and saved into the + * virtual arrays. + * + * We must also emit the data to the compressor. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All components + * are loaded into the virtual arrays in this pass. However, it may be that + * only a subset of the components are emitted to the compressor during + * this first pass; be careful about looking at the scan-dependent variables + * (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION samps_across; + int ci, samp_row, samp_rows; + _JSAMPARRAY buffer; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[ci], + diff->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, TRUE); + + /* Count non-dummy sample rows in this iMCU row. */ + if (diff->iMCU_row_num < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + samp_rows = (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + } + samps_across = compptr->width_in_blocks; + + /* Perform point transform scaling and prediction/differencing for all + * non-dummy rows in this iMCU row. Each call on these functions + * processes a complete row of samples. + */ + for (samp_row = 0; samp_row < samp_rows; samp_row++) { + memcpy(buffer[samp_row], input_buf[ci][samp_row], + samps_across * sizeof(_JSAMPLE)); + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the compressor, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the compressor. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output(j_compress_ptr cinfo, _JSAMPIMAGE input_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + int ci, compi; + _JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + buffer[compi] = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[compi], + diff->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, FALSE); + } + + return compress_data(cinfo, buffer); +} + +#endif /* FULL_SAMP_BUFFER_SUPPORTED */ + + +/* + * Initialize difference buffer controller. + */ + +GLOBAL(void) +_jinit_c_diff_controller(j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_diff_ptr diff; + int ci, row; + jpeg_component_info *compptr; + + diff = (my_diff_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(my_diff_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *)diff; + diff->pub.start_pass = start_pass_diff; + + /* Create the prediction row buffers. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->cur_row[ci] = *(_JSAMPARRAY)(*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)1); + diff->prev_row[ci] = *(_JSAMPARRAY)(*cinfo->mem->alloc_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)1); + } + + /* Create the difference buffer. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + /* Prefill difference rows with zeros. We do this because only actual + * data is placed in the buffers during prediction/differencing, leaving + * any dummy differences at the right edge as zeros, which will encode + * to the smallest amount of data. + */ + for (row = 0; row < compptr->v_samp_factor; row++) + memset(diff->diff_buf[ci][row], 0, + jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor) * sizeof(JDIFF)); + } + + /* Create the sample buffer. */ + if (need_full_buffer) { +#ifdef FULL_SAMP_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor differences in each direction. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)jround_up((long)compptr->height_in_blocks, + (long)compptr->v_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else + diff->whole_image[0] = NULL; /* flag for no virtual arrays */ +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jchuff.c b/jchuff.c index cef5c880..dac832e9 100644 --- a/jchuff.c +++ b/jchuff.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 2014-2016, 2018-2022, D. R. Commander. * Copyright (C) 2015, Matthieu Darbois. @@ -30,7 +32,7 @@ #ifdef WITH_SIMD #include "jsimd.h" #else -#include "jchuff.h" +#include "jchuff.h" /* Declarations shared with jc*huff.c */ #endif #include @@ -251,7 +253,7 @@ start_pass_huff(j_compress_ptr cinfo, boolean gather_statistics) * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. * - * Note this is also used by jcphuff.c. + * Note this is also used by jcphuff.c and jclhuff.c. */ GLOBAL(void) @@ -327,12 +329,12 @@ jpeg_make_c_derived_tbl(j_compress_ptr cinfo, boolean isDC, int tblno, memset(dtbl->ehufco, 0, sizeof(dtbl->ehufco)); memset(dtbl->ehufsi, 0, sizeof(dtbl->ehufsi)); - /* This is also a convenient place to check for out-of-range - * and duplicated VAL entries. We allow 0..255 for AC symbols - * but only 0..15 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) + /* This is also a convenient place to check for out-of-range and duplicated + * VAL entries. We allow 0..255 for AC symbols but only 0..15 for DC in + * lossy mode and 0..16 for DC in lossless mode. (We could constrain them + * further based on data depth and mode, but this seems enough.) */ - maxsymbol = isDC ? 15 : 255; + maxsymbol = isDC ? (cinfo->master->lossless ? 16 : 15) : 255; for (p = 0; p < lastp; p++) { i = htbl->huffval[p]; @@ -918,7 +920,7 @@ encode_mcu_gather(j_compress_ptr cinfo, JBLOCKROW *MCU_data) /* * Generate the best Huffman code table for the given counts, fill htbl. - * Note this is also used by jcphuff.c. + * Note this is also used by jcphuff.c and jclhuff.c. * * The JPEG standard requires that no symbol be assigned a codeword of all * one bits (so that padding bits added at the end of a compressed segment diff --git a/jcinit.c b/jcinit.c index 5d61d429..aa3418df 100644 --- a/jcinit.c +++ b/jcinit.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -49,39 +51,68 @@ jinit_compress_master(j_compress_ptr cinfo) jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); } } - /* Forward DCT */ - if (cinfo->data_precision == 12) - j12init_forward_dct(cinfo); - else - jinit_forward_dct(cinfo); - /* Entropy encoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { -#ifdef C_ARITH_CODING_SUPPORTED - jinit_arith_encoder(cinfo); + + if (cinfo->master->lossless) { +#ifdef C_LOSSLESS_SUPPORTED + /* Prediction, sample differencing, and point transform */ + if (cinfo->data_precision == 12) + j12init_lossless_compressor(cinfo); + else + jinit_lossless_compressor(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_encoder(cinfo); + } + + /* Need a full-image difference buffer in any multi-pass mode. */ + if (cinfo->data_precision == 12) + j12init_c_diff_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); + else + jinit_c_diff_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); #else - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - if (cinfo->progressive_mode) { -#ifdef C_PROGRESSIVE_SUPPORTED - jinit_phuff_encoder(cinfo); + /* Forward DCT */ + if (cinfo->data_precision == 12) + j12init_forward_dct(cinfo); + else + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { +#ifdef C_ARITH_CODING_SUPPORTED + jinit_arith_encoder(cinfo); #else - ERREXIT(cinfo, JERR_NOT_COMPILED); + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); #endif - } else - jinit_huff_encoder(cinfo); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + if (cinfo->data_precision == 12) + j12init_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); + else + jinit_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || + cinfo->optimize_coding)); } - /* Need a full-image coefficient buffer in any multi-pass mode. */ - if (cinfo->data_precision == 12) { - j12init_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || - cinfo->optimize_coding)); + if (cinfo->data_precision == 12) j12init_c_main_controller(cinfo, FALSE /* never need full buffer here */); - } else { - jinit_c_coef_controller(cinfo, (boolean)(cinfo->num_scans > 1 || - cinfo->optimize_coding)); + else jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); - } jinit_marker_writer(cinfo); diff --git a/jclhuff.c b/jclhuff.c new file mode 100644 index 00000000..91cb37a4 --- /dev/null +++ b/jclhuff.c @@ -0,0 +1,587 @@ +/* + * jclhuff.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains Huffman entropy encoding routines for lossless JPEG. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ +#include "jchuff.h" /* Declarations shared with jc*huff.c */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* The legal range of a spatial difference is + * -32767 .. +32768. + * Hence the magnitude should always fit in 16 bits. + */ + +#define MAX_DIFF_BITS 16 + + +/* Expanded entropy encoder object for Huffman encoding in lossless mode. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + JLONG put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ +} savable_state; + + +typedef struct { + int ci, yoffset, MCU_width; +} lhe_input_ptr_info; + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; + + /* Pointers to derived tables to be used for each data unit within an MCU */ + c_derived_tbl *cur_tbls[C_MAX_BLOCKS_IN_MCU]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long *count_ptrs[NUM_HUFF_TBLS]; + + /* Pointers to stats tables to be used for each data unit within an MCU */ + long *cur_counts[C_MAX_BLOCKS_IN_MCU]; +#endif + + /* Pointers to the proper input difference row for each group of data units + * within an MCU. For each component, there are Vi groups of Hi data units. + */ + JDIFFROW input_ptr[C_MAX_BLOCKS_IN_MCU]; + + /* Number of input pointers in use for the current MCU. This is the sum + * of all Vi in the MCU. + */ + int num_input_ptrs; + + /* Information used for positioning the input pointers within the input + * difference rows. + */ + lhe_input_ptr_info input_ptr_info[C_MAX_BLOCKS_IN_MCU]; + + /* Index of the proper input pointer for each data unit within an MCU */ + int input_ptr_index[C_MAX_BLOCKS_IN_MCU]; + +} lhuff_entropy_encoder; + +typedef lhuff_entropy_encoder *lhuff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET *next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(JDIMENSION) encode_mcus_huff(j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, + JDIMENSION nMCU); +METHODDEF(void) finish_pass_huff(j_compress_ptr cinfo); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(JDIMENSION) encode_mcus_gather(j_compress_ptr cinfo, + JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, + JDIMENSION MCU_col_num, + JDIMENSION nMCU); +METHODDEF(void) finish_pass_gather(j_compress_ptr cinfo); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_lhuff(j_compress_ptr cinfo, boolean gather_statistics) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl, sampn, ptrn, yoffset, xoffset; + jpeg_component_info *compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcus = encode_mcus_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcus = encode_mcus_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->count_ptrs[dctbl] == NULL) + entropy->count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + 257 * sizeof(long)); + memset(entropy->count_ptrs[dctbl], 0, 257 * sizeof(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + &entropy->derived_tbls[dctbl]); + } + } + + /* Precalculate encoding info for each sample in an MCU of this scan */ + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { + compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; + ci = compptr->component_index; + for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { + /* Precalculate the setup info for each input pointer */ + entropy->input_ptr_info[ptrn].ci = ci; + entropy->input_ptr_info[ptrn].yoffset = yoffset; + entropy->input_ptr_info[ptrn].MCU_width = compptr->MCU_width; + for (xoffset = 0; xoffset < compptr->MCU_width; xoffset++, sampn++) { + /* Precalculate the input pointer index for each sample */ + entropy->input_ptr_index[sampn] = ptrn; + /* Precalculate which tables to use for each sample */ + entropy->cur_tbls[sampn] = entropy->derived_tbls[compptr->dc_tbl_no]; + entropy->cur_counts[sampn] = entropy->count_ptrs[compptr->dc_tbl_no]; + } + } + } + entropy->num_input_ptrs = ptrn; + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state, val, action) { \ + *(state)->next_output_byte++ = (JOCTET)(val); \ + if (--(state)->free_in_buffer == 0) \ + if (!dump_buffer(state)) \ + { action; } \ +} + + +LOCAL(boolean) +dump_buffer(working_state *state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr *dest = state->cinfo->dest; + + if (!(*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits(working_state *state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register JLONG put_buffer = (JLONG)code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((JLONG)1) << size) - 1; /* mask off any extra bits in code */ + + put_bits += size; /* new number of bits in buffer */ + + put_buffer <<= 24 - put_bits; /* align incoming bits */ + + put_buffer |= state->cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int)((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits(working_state *state) +{ + if (!emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart(working_state *state, int restart_num) +{ + if (!flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output nMCU MCUs' worth of Huffman-compressed differences. + */ + +METHODDEF(JDIMENSION) +encode_mcus_huff(j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + working_state state; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + state.cur = entropy->saved; + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (!emit_restart(&state, entropy->next_restart_num)) + return 0; + } + + /* Set input pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_input_ptrs; ptrn++) { + ci = entropy->input_ptr_info[ptrn].ci; + yoffset = entropy->input_ptr_info[ptrn].yoffset; + MCU_width = entropy->input_ptr_info[ptrn].MCU_width; + entropy->input_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp, temp2; + register int nbits; + c_derived_tbl *dctbl = entropy->cur_tbls[sampn]; + + /* Encode the difference per section H.1.2.2 */ + + /* Input the sample difference */ + temp = *entropy->input_ptr[entropy->input_ptr_index[sampn]]++; + + if (temp & 0x8000) { /* instead of temp < 0 */ + temp = (-temp) & 0x7FFF; /* absolute value, mod 2^16 */ + if (temp == 0) /* special case: magnitude = 32768 */ + temp2 = temp = 0x8000; + temp2 = ~temp; /* one's complement of magnitude */ + } else { + temp &= 0x7FFF; /* abs value mod 2^16 */ + temp2 = temp; /* magnitude */ + } + + /* Find the number of bits needed for the magnitude of the difference */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range difference values. + */ + if (nbits > MAX_DIFF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (!emit_bits(&state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return mcu_num; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits && /* emit_bits rejects calls with size 0 */ + nbits != 16) /* special case: no bits should be emitted */ + if (!emit_bits(&state, (unsigned int)temp2, nbits)) + return mcu_num; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + entropy->saved = state.cur; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + } + + return nMCU; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + state.cur = entropy->saved; + state.cinfo = cinfo; + + /* Flush out the last data */ + if (!flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + entropy->saved = state.cur; +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + +/* + * Trial-encode nMCU MCUs' worth of Huffman-compressed differences. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(JDIMENSION) +encode_mcus_gather(j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + /* Set input pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_input_ptrs; ptrn++) { + ci = entropy->input_ptr_info[ptrn].ci; + yoffset = entropy->input_ptr_info[ptrn].yoffset; + MCU_width = entropy->input_ptr_info[ptrn].MCU_width; + entropy->input_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + register int temp; + register int nbits; + long *counts = entropy->cur_counts[sampn]; + + /* Encode the difference per section H.1.2.2 */ + + /* Input the sample difference */ + temp = *entropy->input_ptr[entropy->input_ptr_index[sampn]]++; + + if (temp & 0x8000) { /* instead of temp < 0 */ + temp = (-temp) & 0x7FFF; /* absolute value, mod 2^16 */ + if (temp == 0) /* special case: magnitude = 32768 */ + temp = 0x8000; + } else + temp &= 0x7FFF; /* abs value mod 2^16 */ + + /* Find the number of bits needed for the magnitude of the difference */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range difference values. + */ + if (nbits > MAX_DIFF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + counts[nbits]++; + } + } + + return nMCU; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl; + jpeg_component_info *compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + memset(did_dc, 0, sizeof(did_dc)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + if (!did_dc[dctbl]) { + htblptr = &cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr)cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_lhuff_encoder(j_compress_ptr cinfo) +{ + lhuff_entropy_ptr entropy; + int i; + + entropy = (lhuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(lhuff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *)entropy; + entropy->pub.start_pass = start_pass_lhuff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->count_ptrs[i] = NULL; +#endif + } +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jclossls.c b/jclossls.c new file mode 100644 index 00000000..8459b6e7 --- /dev/null +++ b/jclossls.c @@ -0,0 +1,318 @@ +/* + * jclossls.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains prediction, sample differencing, and point transform + * routines for the lossless JPEG compressor. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" + +#ifdef C_LOSSLESS_SUPPORTED + + +/************************** Sample differencing **************************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate differencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and differencers as macros. Each of the differencing functions is simply a + * wrapper around a DIFFERENCE macro with the appropriate PREDICTOR macro + * passed as an argument. + */ + +/* Forward declarations */ +LOCAL(void) reset_predictor(j_compress_ptr cinfo, int ci); + + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 prev_row[0] + + +/* + * 1-Dimensional differencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + */ + +#define DIFFERENCE_1D(INITIAL_PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; \ + boolean restart = FALSE; \ + int samp, Ra; \ + \ + samp = *input_buf++; \ + *diff_buf++ = samp - INITIAL_PREDICTOR; \ + \ + while (--width) { \ + Ra = samp; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR1; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--(losslessc->restart_rows_to_go[ci]) == 0) { \ + reset_predictor(cinfo, ci); \ + restart = TRUE; \ + } \ + } + + +/* + * 2-Dimensional differencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + */ + +#define DIFFERENCE_2D(PREDICTOR) \ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; \ + int samp, Ra, Rb, Rc; \ + \ + Rb = *prev_row++; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR2; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = *prev_row++; \ + Ra = samp; \ + samp = *input_buf++; \ + *diff_buf++ = samp - PREDICTOR; \ + } \ + \ + /* Account for restart interval (no-op if not using restarts) */ \ + if (cinfo->restart_interval) { \ + if (--losslessc->restart_rows_to_go[ci] == 0) \ + reset_predictor(cinfo, ci); \ + } + + +/* + * Differencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is differenced using the vertical + * predictor (2). The rest of the samples are differenced using the predictor + * specified in the scan header. + */ + +METHODDEF(void) +jpeg_difference1(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTOR2); + (void)(restart); +} + +METHODDEF(void) +jpeg_difference2(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR2); + (void)(Ra); + (void)(Rc); +} + +METHODDEF(void) +jpeg_difference3(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR3); + (void)(Ra); +} + +METHODDEF(void) +jpeg_difference4(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_difference5(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_difference6(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_difference7(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_2D(PREDICTOR7); + (void)(Rc); +} + + +/* + * Differencer for the first row in a scan or restart interval. The first + * sample in the row is differenced using the special predictor constant + * x = 2 ^ (P-Pt-1). The rest of the samples are differenced using the + * 1-D horizontal predictor (1). + */ + +METHODDEF(void) +jpeg_difference_first_row(j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, _JSAMPROW prev_row, + JDIFFROW diff_buf, JDIMENSION width) +{ + DIFFERENCE_1D(INITIAL_PREDICTORx); + + /* + * Now that we have differenced the first row, we want to use the + * differencer that corresponds to the predictor specified in the + * scan header. + * + * Note that we don't do this if we have just reset the predictor + * for a new restart interval. + */ + if (!restart) { + switch (cinfo->Ss) { + case 1: + losslessc->predict_difference[ci] = jpeg_difference1; + break; + case 2: + losslessc->predict_difference[ci] = jpeg_difference2; + break; + case 3: + losslessc->predict_difference[ci] = jpeg_difference3; + break; + case 4: + losslessc->predict_difference[ci] = jpeg_difference4; + break; + case 5: + losslessc->predict_difference[ci] = jpeg_difference5; + break; + case 6: + losslessc->predict_difference[ci] = jpeg_difference6; + break; + case 7: + losslessc->predict_difference[ci] = jpeg_difference7; + break; + } + } +} + +/* + * Reset predictor at the start of a pass or restart interval. + */ + +LOCAL(void) +reset_predictor(j_compress_ptr cinfo, int ci) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + + /* Initialize restart counter */ + losslessc->restart_rows_to_go[ci] = + cinfo->restart_interval / cinfo->MCUs_per_row; + + /* Set difference function to first row function */ + losslessc->predict_difference[ci] = jpeg_difference_first_row; +} + + +/********************** Sample downscaling by 2^Pt ***********************/ + +METHODDEF(void) +simple_downscale(j_compress_ptr cinfo, + _JSAMPROW input_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + while (width--) + *output_buf++ = (_JSAMPLE)RIGHT_SHIFT(*input_buf++, cinfo->Al); +} + + +METHODDEF(void) +noscale(j_compress_ptr cinfo, + _JSAMPROW input_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + memcpy(output_buf, input_buf, width * sizeof(_JSAMPLE)); +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_lossless(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc = (lossless_comp_ptr)cinfo->fdct; + int ci; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessc->scaler_scale = simple_downscale; + else + losslessc->scaler_scale = noscale; + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. + */ + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Set predictors for start of pass */ + for (ci = 0; ci < cinfo->num_components; ci++) + reset_predictor(cinfo, ci); +} + + +/* + * Initialize the lossless compressor. + */ + +GLOBAL(void) +_jinit_lossless_compressor(j_compress_ptr cinfo) +{ + lossless_comp_ptr losslessc; + + /* Create subobject in permanent pool */ + losslessc = (lossless_comp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(jpeg_lossless_compressor)); + cinfo->fdct = (struct jpeg_forward_dct *)losslessc; + losslessc->pub.start_pass = start_pass_lossless; +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcmainct.c b/jcmainct.c index 6be59913..a5ddf181 100644 --- a/jcmainct.c +++ b/jcmainct.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -81,20 +83,20 @@ process_data_simple_main(j_compress_ptr cinfo, _JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail) { my_main_ptr main_ptr = (my_main_ptr)cinfo->main; + JDIMENSION data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (main_ptr->cur_iMCU_row < cinfo->total_iMCU_rows) { /* Read input data if we haven't filled the main buffer yet */ - if (main_ptr->rowgroup_ctr < DCTSIZE) + if (main_ptr->rowgroup_ctr < data_unit) (*cinfo->prep->_pre_process_data) (cinfo, input_buf, in_row_ctr, in_rows_avail, main_ptr->buffer, - &main_ptr->rowgroup_ctr, - (JDIMENSION)DCTSIZE); + &main_ptr->rowgroup_ctr, data_unit); /* If we don't have a full iMCU row buffered, return to application for * more data. Note that preprocessor will always pad to fill the iMCU row * at the bottom of the image. */ - if (main_ptr->rowgroup_ctr != DCTSIZE) + if (main_ptr->rowgroup_ctr != data_unit) return; /* Send the completed row to the compressor */ @@ -134,6 +136,7 @@ _jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) my_main_ptr main_ptr; int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); @@ -159,8 +162,8 @@ _jinit_c_main_controller(j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { main_ptr->buffer[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - compptr->width_in_blocks * DCTSIZE, - (JDIMENSION)(compptr->v_samp_factor * DCTSIZE)); + compptr->width_in_blocks * data_unit, + (JDIMENSION)(compptr->v_samp_factor * data_unit)); } } } diff --git a/jcmarker.c b/jcmarker.c index 20f861fe..a064d4dd 100644 --- a/jcmarker.c +++ b/jcmarker.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -497,25 +499,26 @@ write_file_header(j_compress_ptr cinfo) METHODDEF(void) write_frame_header(j_compress_ptr cinfo) { - int ci, prec; + int ci, prec = 0; boolean is_baseline; jpeg_component_info *compptr; - /* Emit DQT for each quantization table. - * Note that emit_dqt() suppresses any duplicate tables. - */ - prec = 0; - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - prec += emit_dqt(cinfo, compptr->quant_tbl_no); + if (!cinfo->master->lossless) { + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ } - /* now prec is nonzero iff there are any 16-bit quant tables. */ /* Check for a non-baseline specification. * Note we assume that Huffman table numbers won't be changed later. */ if (cinfo->arith_code || cinfo->progressive_mode || - cinfo->data_precision != 8) { + cinfo->master->lossless || cinfo->data_precision != 8) { is_baseline = FALSE; } else { is_baseline = TRUE; @@ -540,6 +543,8 @@ write_frame_header(j_compress_ptr cinfo) } else { if (cinfo->progressive_mode) emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (cinfo->master->lossless) + emit_sof(cinfo, M_SOF3); /* SOF code for lossless Huffman */ else if (is_baseline) emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ else @@ -574,10 +579,11 @@ write_scan_header(j_compress_ptr cinfo) for (i = 0; i < cinfo->comps_in_scan; i++) { compptr = cinfo->cur_comp_info[i]; /* DC needs no table for refinement scan */ - if (cinfo->Ss == 0 && cinfo->Ah == 0) + if ((cinfo->Ss == 0 && cinfo->Ah == 0) || cinfo->master->lossless) emit_dht(cinfo, compptr->dc_tbl_no, FALSE); - /* AC needs no table when not present */ - if (cinfo->Se) + /* AC needs no table when not present, and lossless mode uses only DC + tables. */ + if (cinfo->Se && !cinfo->master->lossless) emit_dht(cinfo, compptr->ac_tbl_no, TRUE); } } diff --git a/jcmaster.c b/jcmaster.c index a5d318e1..81d855fa 100644 --- a/jcmaster.c +++ b/jcmaster.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2016, 2018, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -19,38 +21,7 @@ #include "jinclude.h" #include "jpeglib.h" #include "jpegapicomp.h" - - -/* Private state */ - -typedef enum { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ -} c_pass_type; - -typedef struct { - struct jpeg_comp_master pub; /* public fields */ - - c_pass_type pass_type; /* the type of the current pass */ - - int pass_number; /* # of passes completed */ - int total_passes; /* total # of passes needed */ - - int scan_number; /* current index in scan_info[] */ - - /* - * This is here so we can add libjpeg-turbo version/build information to the - * global string table without introducing a new global symbol. Adding this - * information to the global string table allows one to examine a binary - * object and determine which version of libjpeg-turbo it was built from or - * linked against. - */ - const char *jpeg_version; - -} my_comp_master; - -typedef my_comp_master *my_master_ptr; +#include "jcmaster.h" /* @@ -68,11 +39,13 @@ GLOBAL(void) jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo) /* Do computations that are needed before master selection phase */ { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* Hardwire it to "no scaling" */ cinfo->jpeg_width = cinfo->image_width; cinfo->jpeg_height = cinfo->image_height; - cinfo->min_DCT_h_scaled_size = DCTSIZE; - cinfo->min_DCT_v_scaled_size = DCTSIZE; + cinfo->min_DCT_h_scaled_size = data_unit; + cinfo->min_DCT_v_scaled_size = data_unit; } #endif @@ -85,6 +58,7 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) jpeg_component_info *compptr; long samplesperrow; JDIMENSION jd_samplesperrow; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; #if JPEG_LIB_VERSION >= 70 #if JPEG_LIB_VERSION >= 80 @@ -140,17 +114,17 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) compptr->component_index = ci; /* For compression, we never do DCT scaling. */ #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = DCTSIZE; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = data_unit; #else - compptr->DCT_scaled_size = DCTSIZE; + compptr->DCT_scaled_size = data_unit; #endif - /* Size in DCT blocks */ + /* Size in data units */ compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width * (long)compptr->h_samp_factor, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height * (long)compptr->v_samp_factor, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Size in samples */ compptr->downsampled_width = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width * (long)compptr->h_samp_factor, @@ -163,15 +137,19 @@ initial_setup(j_compress_ptr cinfo, boolean transcode_only) } /* Compute number of fully interleaved MCU rows (number of times that - * main controller will call coefficient controller). + * main controller will call coefficient or difference controller). */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); } -#ifdef C_MULTISCAN_FILES_SUPPORTED +#if defined(C_MULTISCAN_FILES_SUPPORTED) || defined(C_LOSSLESS_SUPPORTED) +#define NEED_SCAN_SCRIPT +#endif + +#ifdef NEED_SCAN_SCRIPT LOCAL(void) validate_script(j_compress_ptr cinfo) @@ -192,13 +170,29 @@ validate_script(j_compress_ptr cinfo) if (cinfo->num_scans <= 0) ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); +#ifndef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->num_scans > 1) + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 && scanptr->Se == 0) { +#ifdef C_LOSSLESS_SUPPORTED + cinfo->master->lossless = TRUE; + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; * for progressive JPEG, no scan can have this. */ - scanptr = cinfo->scan_info; - if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2 - 1) { + else if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2 - 1) { #ifdef C_PROGRESSIVE_SUPPORTED cinfo->progressive_mode = TRUE; + cinfo->master->lossless = FALSE; last_bitpos_ptr = &last_bitpos[0][0]; for (ci = 0; ci < cinfo->num_components; ci++) for (coefi = 0; coefi < DCTSIZE2; coefi++) @@ -207,7 +201,7 @@ validate_script(j_compress_ptr cinfo) ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - cinfo->progressive_mode = FALSE; + cinfo->progressive_mode = cinfo->master->lossless = FALSE; for (ci = 0; ci < cinfo->num_components; ci++) component_sent[ci] = FALSE; } @@ -270,9 +264,25 @@ validate_script(j_compress_ptr cinfo) } #endif } else { - /* For sequential JPEG, all progression parameters must be these: */ - if (Ss != 0 || Se != DCTSIZE2 - 1 || Ah != 0 || Al != 0) - ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); +#ifdef C_LOSSLESS_SUPPORTED + if (cinfo->master->lossless) { + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N-1 for N-bit precision, which is what + * we allow here. Values greater than or equal to the data precision + * will result in a blank image. + */ + if (Ss < 1 || Ss > 7 || /* predictor selection value */ + Se != 0 || Ah != 0 || + Al < 0 || Al >= cinfo->data_precision) /* point transform */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else +#endif + { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2 - 1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } /* Make sure components are not sent twice */ for (ci = 0; ci < ncomps; ci++) { thisi = scanptr->component_index[ci]; @@ -304,7 +314,7 @@ validate_script(j_compress_ptr cinfo) } } -#endif /* C_MULTISCAN_FILES_SUPPORTED */ +#endif /* NEED_SCAN_SCRIPT */ LOCAL(void) @@ -313,7 +323,7 @@ select_scan_parameters(j_compress_ptr cinfo) { int ci; -#ifdef C_MULTISCAN_FILES_SUPPORTED +#ifdef NEED_SCAN_SCRIPT if (cinfo->scan_info != NULL) { /* Prepare for current scan --- the script is already validated */ my_master_ptr master = (my_master_ptr)cinfo->master; @@ -339,10 +349,12 @@ select_scan_parameters(j_compress_ptr cinfo) for (ci = 0; ci < cinfo->num_components; ci++) { cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; } - cinfo->Ss = 0; - cinfo->Se = DCTSIZE2 - 1; - cinfo->Ah = 0; - cinfo->Al = 0; + if (!cinfo->master->lossless) { + cinfo->Ss = 0; + cinfo->Se = DCTSIZE2 - 1; + cinfo->Ah = 0; + cinfo->Al = 0; + } } } @@ -354,6 +366,7 @@ per_scan_setup(j_compress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -368,7 +381,7 @@ per_scan_setup(j_compress_ptr cinfo) compptr->MCU_width = 1; compptr->MCU_height = 1; compptr->MCU_blocks = 1; - compptr->MCU_sample_width = DCTSIZE; + compptr->MCU_sample_width = data_unit; compptr->last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height * as the number of block rows present in the last iMCU row. @@ -391,10 +404,10 @@ per_scan_setup(j_compress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_width, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long)cinfo->_jpeg_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); cinfo->blocks_in_MCU = 0; @@ -404,7 +417,7 @@ per_scan_setup(j_compress_ptr cinfo) compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; - compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + compptr->MCU_sample_width = compptr->MCU_width * data_unit; /* Figure number of non-dummy blocks in last MCU column & row */ tmp = (int)(compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; @@ -476,7 +489,8 @@ prepare_for_pass(j_compress_ptr cinfo) /* Do Huffman optimization for a scan after the first one. */ select_scan_parameters(cinfo); per_scan_setup(cinfo); - if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code || + cinfo->master->lossless) { (*cinfo->entropy->start_pass) (cinfo, TRUE); (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); master->pub.call_pass_startup = FALSE; @@ -585,22 +599,15 @@ finish_pass_master(j_compress_ptr cinfo) GLOBAL(void) jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) { - my_master_ptr master; + my_master_ptr master = (my_master_ptr)cinfo->master; - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, - sizeof(my_comp_master)); - cinfo->master = (struct jpeg_comp_master *)master; master->pub.prepare_for_pass = prepare_for_pass; master->pub.pass_startup = pass_startup; master->pub.finish_pass = finish_pass_master; master->pub.is_last_pass = FALSE; - /* Validate parameters, determine derived values */ - initial_setup(cinfo, transcode_only); - if (cinfo->scan_info != NULL) { -#ifdef C_MULTISCAN_FILES_SUPPORTED +#ifdef NEED_SCAN_SCRIPT validate_script(cinfo); #else ERREXIT(cinfo, JERR_NOT_COMPILED); @@ -610,9 +617,30 @@ jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only) cinfo->num_scans = 1; } - if (cinfo->progressive_mode && !cinfo->arith_code) /* TEMPORARY HACK ??? */ + /* Disable smoothing and subsampling in lossless mode, since those are lossy + * algorithms. Set the JPEG colorspace to the input colorspace. Disable raw + * (downsampled) data input, because it isn't particularly useful without + * subsampling and has not been tested in lossless mode. + */ + if (cinfo->master->lossless) { + int ci; + jpeg_component_info *compptr; + + cinfo->raw_data_in = FALSE; + cinfo->smoothing_factor = 0; + jpeg_default_colorspace(cinfo); + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) + compptr->h_samp_factor = compptr->v_samp_factor = 1; + } + + /* Validate parameters, determine derived values */ + initial_setup(cinfo, transcode_only); + + if (cinfo->master->lossless || /* TEMPORARY HACK ??? */ + (cinfo->progressive_mode && !cinfo->arith_code)) cinfo->optimize_coding = TRUE; /* assume default tables no good for - progressive mode */ + progressive mode or lossless mode */ if (cinfo->data_precision == 12) cinfo->optimize_coding = TRUE; /* assume default tables no good for 12-bit data precision */ diff --git a/jcmaster.h b/jcmaster.h new file mode 100644 index 00000000..3b13289b --- /dev/null +++ b/jcmaster.h @@ -0,0 +1,43 @@ +/* + * jcmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains master control structure for the JPEG compressor. + */ + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ + + /* + * This is here so we can add libjpeg-turbo version/build information to the + * global string table without introducing a new global symbol. Adding this + * information to the global string table allows one to examine a binary + * object and determine which version of libjpeg-turbo it was built from or + * linked against. + */ + const char *jpeg_version; + +} my_comp_master; + +typedef my_comp_master *my_master_ptr; diff --git a/jcparam.c b/jcparam.c index 0c59a79f..30bb23b7 100644 --- a/jcparam.c +++ b/jcparam.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2003-2008 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 2018, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -295,7 +297,10 @@ jpeg_default_colorspace(j_compress_ptr cinfo) case JCS_EXT_BGRA: case JCS_EXT_ABGR: case JCS_EXT_ARGB: - jpeg_set_colorspace(cinfo, JCS_YCbCr); + if (cinfo->master->lossless) + jpeg_set_colorspace(cinfo, JCS_RGB); + else + jpeg_set_colorspace(cinfo, JCS_YCbCr); break; case JCS_YCbCr: jpeg_set_colorspace(cinfo, JCS_YCbCr); @@ -474,6 +479,11 @@ jpeg_simple_progression(j_compress_ptr cinfo) if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->master->lossless) { + cinfo->master->lossless = FALSE; + jpeg_default_colorspace(cinfo); + } + /* Figure space needed for script. Calculation must match code below! */ if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { /* Custom script for YCbCr color images. */ @@ -538,3 +548,38 @@ jpeg_simple_progression(j_compress_ptr cinfo) } #endif /* C_PROGRESSIVE_SUPPORTED */ + + +#ifdef C_LOSSLESS_SUPPORTED + +/* + * Enable lossless mode. + */ + +GLOBAL(void) +jpeg_enable_lossless(j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) +{ + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + cinfo->master->lossless = TRUE; + cinfo->Ss = predictor_selection_value; + cinfo->Se = 0; + cinfo->Ah = 0; + cinfo->Al = point_transform; + + /* The JPEG spec simply gives the range 0..15 for Al (Pt), but that seems + * wrong: the upper bound ought to depend on data precision. Perhaps they + * really meant 0..N-1 for N-bit precision, which is what we allow here. + * Values greater than or equal to the data precision will result in a blank + * image. + */ + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); +} + +#endif /* C_LOSSLESS_SUPPORTED */ diff --git a/jcphuff.c b/jcphuff.c index 25107f60..b00b8a04 100644 --- a/jcphuff.c +++ b/jcphuff.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2011, 2015, 2018, 2021-2022, D. R. Commander. * Copyright (C) 2016, 2018, Matthieu Darbois. @@ -24,7 +26,7 @@ #ifdef WITH_SIMD #include "jsimd.h" #else -#include "jchuff.h" +#include "jchuff.h" /* Declarations shared with jc*huff.c */ #endif #include diff --git a/jcprepct.c b/jcprepct.c index 26d31533..3873440b 100644 --- a/jcprepct.c +++ b/jcprepct.c @@ -1,8 +1,10 @@ /* * jcprepct.c * - * This file is part of the Independent JPEG Group's software: + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -138,6 +140,7 @@ pre_process_data(j_compress_ptr cinfo, _JSAMPARRAY input_buf, int numrows, ci; JDIMENSION inrows; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; while (*in_row_ctr < in_rows_avail && *out_row_group_ctr < out_row_groups_avail) { @@ -175,7 +178,8 @@ pre_process_data(j_compress_ptr cinfo, _JSAMPARRAY input_buf, if (prep->rows_to_go == 0 && *out_row_group_ctr < out_row_groups_avail) { for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { - expand_bottom_edge(output_buf[ci], compptr->width_in_blocks * DCTSIZE, + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * data_unit, (int)(*out_row_group_ctr * compptr->v_samp_factor), (int)(out_row_groups_avail * compptr->v_samp_factor)); } @@ -269,6 +273,7 @@ create_context_buffer(j_compress_ptr cinfo) int ci, i; jpeg_component_info *compptr; _JSAMPARRAY true_buffer, fake_buffer; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Grab enough space for fake row pointers for all the components; * we need five row groups' worth of pointers for each component. @@ -286,7 +291,7 @@ create_context_buffer(j_compress_ptr cinfo) */ true_buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(((long)compptr->width_in_blocks * DCTSIZE * + (JDIMENSION)(((long)compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION)(3 * rgroup_height)); /* Copy true buffer row pointers into the middle of the fake row array */ @@ -315,6 +320,7 @@ _jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer) my_prep_ptr prep; int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); @@ -347,7 +353,7 @@ _jinit_c_prep_controller(j_compress_ptr cinfo, boolean need_full_buffer) ci++, compptr++) { prep->color_buf[ci] = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray) ((j_common_ptr)cinfo, JPOOL_IMAGE, - (JDIMENSION)(((long)compptr->width_in_blocks * DCTSIZE * + (JDIMENSION)(((long)compptr->width_in_blocks * data_unit * cinfo->max_h_samp_factor) / compptr->h_samp_factor), (JDIMENSION)cinfo->max_v_samp_factor); } diff --git a/jcsample.c b/jcsample.c index 79bc8885..2e153e4a 100644 --- a/jcsample.c +++ b/jcsample.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright 2009 Pierre Ossman for Cendio AB * Copyright (C) 2014, MIPS Technologies, Inc., California. @@ -150,7 +152,8 @@ int_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, { int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; _JSAMPROW inptr, outptr; JLONG outvalue; @@ -195,12 +198,14 @@ METHODDEF(void) fullsize_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, _JSAMPARRAY input_data, _JSAMPARRAY output_data) { + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + /* Copy the data */ _jcopy_sample_rows(input_data, 0, output_data, 0, cinfo->max_v_samp_factor, cinfo->image_width); /* Edge-expand */ expand_right_edge(output_data, cinfo->max_v_samp_factor, cinfo->image_width, - compptr->width_in_blocks * DCTSIZE); + compptr->width_in_blocks * data_unit); } @@ -222,7 +227,8 @@ h2v1_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, { int outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register _JSAMPROW inptr, outptr; register int bias; @@ -258,7 +264,8 @@ h2v2_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, { int inrow, outrow; JDIMENSION outcol; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register _JSAMPROW inptr0, inptr1, outptr; register int bias; @@ -300,7 +307,8 @@ h2v2_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, { int inrow, outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register _JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; JLONG membersum, neighsum, memberscale, neighscale; @@ -388,7 +396,8 @@ fullsize_smooth_downsample(j_compress_ptr cinfo, jpeg_component_info *compptr, { int outrow; JDIMENSION colctr; - JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; + JDIMENSION output_cols = compptr->width_in_blocks * data_unit; register _JSAMPROW inptr, above_ptr, below_ptr, outptr; JLONG membersum, neighsum, memberscale, neighscale; int colsum, lastcolsum, nextcolsum; diff --git a/jctrans.c b/jctrans.c index cd742448..ae52e398 100644 --- a/jctrans.c +++ b/jctrans.c @@ -42,6 +42,9 @@ LOCAL(void) transencode_coef_controller(j_compress_ptr cinfo, GLOBAL(void) jpeg_write_coefficients(j_compress_ptr cinfo, jvirt_barray_ptr *coef_arrays) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Mark all tables to be written */ @@ -72,6 +75,9 @@ jpeg_copy_critical_parameters(j_decompress_ptr srcinfo, j_compress_ptr dstinfo) JQUANT_TBL *c_quant, *slot_quant; int tblno, ci, coefi; + if (srcinfo->master->lossless) + ERREXIT(dstinfo, JERR_NOTIMPL); + /* Safety check to ensure start_compress not called yet. */ if (dstinfo->global_state != CSTATE_START) ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); diff --git a/jdapimin.c b/jdapimin.c index 7446b43c..51ca552d 100644 --- a/jdapimin.c +++ b/jdapimin.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2016, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -164,7 +166,10 @@ default_decompress_parms(j_decompress_ptr cinfo) cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ else { TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); - cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + if (cinfo->master->lossless) + cinfo->jpeg_color_space = JCS_RGB; /* assume it's RGB */ + else + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ } } /* Always guess RGB is proper output colorspace. */ diff --git a/jdapistd.c b/jdapistd.c index 0fe14b29..82b20a8f 100644 --- a/jdapistd.c +++ b/jdapistd.c @@ -175,6 +175,9 @@ _jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if ((cinfo->global_state != DSTATE_SCANNING && cinfo->global_state != DSTATE_BUFIMAGE) || cinfo->output_scanline != 0) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); @@ -443,6 +446,9 @@ _jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + /* Two-pass color quantization is not supported. */ if (cinfo->quantize_colors && cinfo->two_pass_quantize) ERREXIT(cinfo, JERR_NOTIMPL); @@ -623,6 +629,9 @@ _jpeg_read_raw_data(j_decompress_ptr cinfo, _JSAMPIMAGE data, if (cinfo->data_precision != BITS_IN_JSAMPLE) ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state != DSTATE_RAW_OK) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { diff --git a/jdcoefct.c b/jdcoefct.c index 5dc6a0cf..85cbcb79 100644 --- a/jdcoefct.c +++ b/jdcoefct.c @@ -11,7 +11,7 @@ * file. * * This file contains the coefficient buffer controller for decompression. - * This controller is the top level of the JPEG decompressor proper. + * This controller is the top level of the lossy JPEG decompressor proper. * The coefficient buffer lies between entropy decoding and inverse-DCT steps. * * In buffered-image mode, this controller is the interface between diff --git a/jdcolor.c b/jdcolor.c index aa26aa84..01f2318e 100644 --- a/jdcolor.c +++ b/jdcolor.c @@ -884,6 +884,15 @@ _jinit_color_deconverter(j_decompress_ptr cinfo) break; } + /* Prevent lossy color conversion in lossless mode */ + if (cinfo->master->lossless) { + if ((cinfo->out_color_space == JCS_GRAYSCALE && + cinfo->jpeg_color_space != JCS_GRAYSCALE) || + (cinfo->out_color_space != JCS_GRAYSCALE && + cconvert->pub._color_convert != null_convert)) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + } + if (cinfo->quantize_colors) cinfo->output_components = 1; /* single colormapped output component */ else diff --git a/jddiffct.c b/jddiffct.c new file mode 100644 index 00000000..d818f145 --- /dev/null +++ b/jddiffct.c @@ -0,0 +1,403 @@ +/* + * jddiffct.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the [un]difference buffer controller for decompression. + * This controller is the top level of the lossless JPEG decompressor proper. + * The difference buffer lies between the entropy decoding and + * prediction/undifferencing steps. The undifference buffer lies between the + * prediction/undifferencing and scaling steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ + + +#ifdef D_LOSSLESS_SUPPORTED + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + unsigned int restart_rows_to_go; /* MCU rows left in this restart + interval */ + unsigned int MCU_vert_offset; /* counts MCU rows within iMCU row */ + unsigned int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + JDIFFARRAY diff_buf[MAX_COMPONENTS]; /* iMCU row of differences */ + JDIFFARRAY undiff_buf[MAX_COMPONENTS]; /* iMCU row of undiff'd samples */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual sample array for each component. */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_diff_controller; + +typedef my_diff_controller *my_diff_ptr; + +/* Forward declarations */ +METHODDEF(int) decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) output_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf); +#endif + + +LOCAL(void) +start_iMCU_row(j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + diff->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + diff->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + diff->MCU_ctr = 0; + diff->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + + /* Because it is hitching a ride on the jpeg_inverse_dct struct, + * start_pass_lossless() will be called at the start of the output pass. + * This ensures that it will be called at the start of the input pass as + * well. + */ + (*cinfo->idct->start_pass) (cinfo); + + /* Check that the restart interval is an integer multiple of the number + * of MCUs in an MCU row. + */ + if (cinfo->restart_interval % cinfo->MCUs_per_row != 0) + ERREXIT2(cinfo, JERR_BAD_RESTART, + cinfo->restart_interval, cinfo->MCUs_per_row); + + /* Initialize restart counter */ + diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; + + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Check for a restart marker & resynchronize decoder, undifferencer. + * Returns FALSE if must suspend. + */ + +METHODDEF(boolean) +process_restart(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + + if (!(*cinfo->entropy->process_restart) (cinfo)) + return FALSE; + + (*losslessd->predict_process_restart) (cinfo); + + /* Reset restart counter */ + diff->restart_rows_to_go = cinfo->restart_interval / cinfo->MCUs_per_row; + + return TRUE; +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass(j_decompress_ptr cinfo) +{ + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the supplied buffer. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION MCU_count; /* number of MCUs decoded */ + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, compi, row, prev_row; + unsigned int yoffset; + jpeg_component_info *compptr; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = diff->MCU_vert_offset; yoffset < diff->MCU_rows_per_iMCU_row; + yoffset++) { + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (diff->restart_rows_to_go == 0) + if (!process_restart(cinfo)) + return JPEG_SUSPENDED; + } + + MCU_col_num = diff->MCU_ctr; + /* Try to fetch an MCU row (or remaining portion of suspended MCU row). */ + MCU_count = + (*cinfo->entropy->decode_mcus) (cinfo, + diff->diff_buf, yoffset, MCU_col_num, + cinfo->MCUs_per_row - MCU_col_num); + if (MCU_count != cinfo->MCUs_per_row - MCU_col_num) { + /* Suspension forced; update state counters and exit */ + diff->MCU_vert_offset = yoffset; + diff->MCU_ctr += MCU_count; + return JPEG_SUSPENDED; + } + + /* Account for restart interval (no-op if not using restarts) */ + diff->restart_rows_to_go--; + + /* Completed an MCU row, but perhaps not an iMCU row */ + diff->MCU_ctr = 0; + } + + /* + * Undifference and scale each scanline of the disassembled MCU row + * separately. We do not process dummy samples at the end of a scanline + * or dummy rows at the end of the image. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + for (row = 0, prev_row = compptr->v_samp_factor - 1; + row < (cinfo->input_iMCU_row == last_iMCU_row ? + compptr->last_row_height : compptr->v_samp_factor); + prev_row = row, row++) { + (*losslessd->predict_undifference[compi]) + (cinfo, compi, diff->diff_buf[compi][row], + diff->undiff_buf[compi][prev_row], diff->undiff_buf[compi][row], + compptr->width_in_blocks); + (*losslessd->scaler_scale) (cinfo, diff->undiff_buf[compi][row], + output_buf[compi][row], + compptr->width_in_blocks); + } + } + + /* Completed the iMCU row, advance counters for next one. + * + * NB: output_data will increment output_iMCU_row. + * This counter is not needed for the single-pass case + * or the input side of the multi-pass case. + */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data(j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image sample buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data(j_decompress_ptr cinfo) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + int ci, compi; + _JSAMPARRAY buffer[MAX_COMPS_IN_SCAN]; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + compi = compptr->component_index; + buffer[compi] = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[compi], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, TRUE); + } + + return decompress_data(cinfo, buffer); +} + + +/* + * Output some data from the full-image sample buffer in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +output_data(j_decompress_ptr cinfo, _JSAMPIMAGE output_buf) +{ + my_diff_ptr diff = (my_diff_ptr)cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int ci, samp_rows, row; + _JSAMPARRAY buffer; + jpeg_component_info *compptr; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (_JSAMPARRAY)(*cinfo->mem->access_virt_sarray) + ((j_common_ptr)cinfo, diff->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION)compptr->v_samp_factor, FALSE); + + if (cinfo->output_iMCU_row < last_iMCU_row) + samp_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + samp_rows = (int)(compptr->height_in_blocks % compptr->v_samp_factor); + if (samp_rows == 0) samp_rows = compptr->v_samp_factor; + } + + for (row = 0; row < samp_rows; row++) { + memcpy(output_buf[ci][row], buffer[row], + compptr->width_in_blocks * sizeof(_JSAMPLE)); + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize difference buffer controller. + */ + +GLOBAL(void) +_jinit_d_diff_controller(j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_diff_ptr diff; + int ci; + jpeg_component_info *compptr; + + diff = (my_diff_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(my_diff_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *)diff; + diff->pub.start_input_pass = start_input_pass; + diff->pub.start_output_pass = start_output_pass; + + /* Create the [un]difference buffers. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + diff->diff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + diff->undiff_buf[ci] = + ALLOC_DARRAY(JPOOL_IMAGE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)compptr->v_samp_factor); + } + + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component. */ + int access_rows; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; + diff->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION)jround_up((long)compptr->width_in_blocks, + (long)compptr->h_samp_factor), + (JDIMENSION)jround_up((long)compptr->height_in_blocks, + (long)compptr->v_samp_factor), + (JDIMENSION)access_rows); + } + diff->pub.consume_data = consume_data; + diff->pub._decompress_data = output_data; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + diff->pub.consume_data = dummy_consume_data; + diff->pub._decompress_data = decompress_data; + diff->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdhuff.c b/jdhuff.c index f685a670..cd8c0847 100644 --- a/jdhuff.c +++ b/jdhuff.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 2016, 2018-2019, 2022, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. @@ -24,7 +26,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jdhuff.h" /* Declarations shared with jdphuff.c */ +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ #include "jpegapicomp.h" #include "jstdhuff.c" @@ -134,7 +136,7 @@ start_pass_huff_decoder(j_decompress_ptr cinfo) * Compute the derived values for a Huffman table. * This routine also performs some validation checks on the table. * - * Note this is also used by jdphuff.c. + * Note this is also used by jdphuff.c and jdlhuff.c. */ GLOBAL(void) @@ -245,14 +247,14 @@ jpeg_make_d_derived_tbl(j_decompress_ptr cinfo, boolean isDC, int tblno, /* Validate symbols as being reasonable. * For AC tables, we make no check, but accept all byte values 0..255. - * For DC tables, we require the symbols to be in range 0..15. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) + * For DC tables, we require the symbols to be in range 0..15 in lossy mode + * and 0..16 in lossless mode. (Tighter bounds could be applied depending on + * the data depth and mode, but this is sufficient to ensure safe decoding.) */ if (isDC) { for (i = 0; i < numsymbols; i++) { int sym = htbl->huffval[i]; - if (sym < 0 || sym > 15) + if (sym < 0 || sym > (cinfo->master->lossless ? 16 : 15)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); } } @@ -260,7 +262,7 @@ jpeg_make_d_derived_tbl(j_decompress_ptr cinfo, boolean isDC, int tblno, /* - * Out-of-line code for bit fetching (shared with jdphuff.c). + * Out-of-line code for bit fetching (shared with jdphuff.c and jdlhuff.c). * See jdhuff.h for info about usage. * Note: current values of get_buffer and bits_left are passed as parameters, * but are returned in the corresponding fields of the state struct. diff --git a/jdhuff.h b/jdhuff.h index cfa0b7f5..3eee002c 100644 --- a/jdhuff.h +++ b/jdhuff.h @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010-2011, 2015-2016, 2021, D. R. Commander. * Copyright (C) 2018, Matthias Räncker. @@ -10,8 +12,9 @@ * file. * * This file contains declarations for Huffman entropy decoding routines - * that are shared between the sequential decoder (jdhuff.c) and the - * progressive decoder (jdphuff.c). No other modules need to see these. + * that are shared between the sequential decoder (jdhuff.c), the progressive + * decoder (jdphuff.c), and the lossless decoder (jdlhuff.c). No other modules + * need to see these. */ #include "jconfigint.h" diff --git a/jdinput.c b/jdinput.c index 7268c609..61af85c0 100644 --- a/jdinput.c +++ b/jdinput.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2010, 2016, 2018, 2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. @@ -11,8 +13,9 @@ * * This file contains input control logic for the JPEG decompressor. * These routines are concerned with controlling the decompressor's input - * processing (marker reading and coefficient decoding). The actual input - * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + * processing (marker reading and coefficient/difference decoding). + * The actual input reading is done in jdmarker.c, jdhuff.c, jdphuff.c, + * and jdlhuff.c. */ #define JPEG_INTERNALS @@ -46,6 +49,7 @@ initial_setup(j_decompress_ptr cinfo) { int ci; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; /* Make sure image isn't bigger than I can handle */ if ((long)cinfo->image_height > (long)JPEG_MAX_DIMENSION || @@ -78,36 +82,36 @@ initial_setup(j_decompress_ptr cinfo) } #if JPEG_LIB_VERSION >= 80 - cinfo->block_size = DCTSIZE; + cinfo->block_size = data_unit; cinfo->natural_order = jpeg_natural_order; cinfo->lim_Se = DCTSIZE2 - 1; #endif - /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. - * In the full decompressor, this will be overridden by jdmaster.c; + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE in lossy + * mode. In the full decompressor, this will be overridden by jdmaster.c; * but in the transcoder, jdmaster.c is not used, so we must do it here. */ #if JPEG_LIB_VERSION >= 70 - cinfo->min_DCT_h_scaled_size = cinfo->min_DCT_v_scaled_size = DCTSIZE; + cinfo->min_DCT_h_scaled_size = cinfo->min_DCT_v_scaled_size = data_unit; #else - cinfo->min_DCT_scaled_size = DCTSIZE; + cinfo->min_DCT_scaled_size = data_unit; #endif /* Compute dimensions of components */ for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; ci++, compptr++) { #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = DCTSIZE; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = data_unit; #else - compptr->DCT_scaled_size = DCTSIZE; + compptr->DCT_scaled_size = data_unit; #endif - /* Size in DCT blocks */ + /* Size in data units */ compptr->width_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->image_width * (long)compptr->h_samp_factor, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long)cinfo->image_height * (long)compptr->v_samp_factor, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Set the first and last MCU columns to decompress from multi-scan images. * By default, decompress all of the MCU columns. */ @@ -133,7 +137,7 @@ initial_setup(j_decompress_ptr cinfo) /* Compute number of fully interleaved MCU rows. */ cinfo->total_iMCU_rows = (JDIMENSION) jdiv_round_up((long)cinfo->image_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); /* Decide whether file contains multiple scans */ if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) @@ -150,6 +154,7 @@ per_scan_setup(j_decompress_ptr cinfo) { int ci, mcublks, tmp; jpeg_component_info *compptr; + int data_unit = cinfo->master->lossless ? 1 : DCTSIZE; if (cinfo->comps_in_scan == 1) { @@ -160,14 +165,14 @@ per_scan_setup(j_decompress_ptr cinfo) cinfo->MCUs_per_row = compptr->width_in_blocks; cinfo->MCU_rows_in_scan = compptr->height_in_blocks; - /* For noninterleaved scan, always one block per MCU */ + /* For noninterleaved scan, always one data unit per MCU */ compptr->MCU_width = 1; compptr->MCU_height = 1; compptr->MCU_blocks = 1; compptr->MCU_sample_width = compptr->_DCT_scaled_size; compptr->last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. + * as the number of data unit rows present in the last iMCU row. */ tmp = (int)(compptr->height_in_blocks % compptr->v_samp_factor); if (tmp == 0) tmp = compptr->v_samp_factor; @@ -187,22 +192,22 @@ per_scan_setup(j_decompress_ptr cinfo) /* Overall image size in MCUs */ cinfo->MCUs_per_row = (JDIMENSION) jdiv_round_up((long)cinfo->image_width, - (long)(cinfo->max_h_samp_factor * DCTSIZE)); + (long)(cinfo->max_h_samp_factor * data_unit)); cinfo->MCU_rows_in_scan = (JDIMENSION) jdiv_round_up((long)cinfo->image_height, - (long)(cinfo->max_v_samp_factor * DCTSIZE)); + (long)(cinfo->max_v_samp_factor * data_unit)); cinfo->blocks_in_MCU = 0; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; - /* Sampling factors give # of blocks of component in each MCU */ + /* Sampling factors give # of data units of component in each MCU */ compptr->MCU_width = compptr->h_samp_factor; compptr->MCU_height = compptr->v_samp_factor; compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; compptr->MCU_sample_width = compptr->MCU_width * compptr->_DCT_scaled_size; - /* Figure number of non-dummy blocks in last MCU column & row */ + /* Figure number of non-dummy data units in last MCU column & row */ tmp = (int)(compptr->width_in_blocks % compptr->MCU_width); if (tmp == 0) tmp = compptr->MCU_width; compptr->last_col_width = tmp; @@ -281,7 +286,8 @@ METHODDEF(void) start_input_pass(j_decompress_ptr cinfo) { per_scan_setup(cinfo); - latch_quant_tables(cinfo); + if (!cinfo->master->lossless) + latch_quant_tables(cinfo); (*cinfo->entropy->start_pass) (cinfo); (*cinfo->coef->start_input_pass) (cinfo); cinfo->inputctl->consume_input = cinfo->coef->consume_data; @@ -290,8 +296,8 @@ start_input_pass(j_decompress_ptr cinfo) /* * Finish up after inputting a compressed-data scan. - * This is called by the coefficient controller after it's read all - * the expected data of the scan. + * This is called by the coefficient or difference controller after it's read + * all the expected data of the scan. */ METHODDEF(void) @@ -307,8 +313,8 @@ finish_input_pass(j_decompress_ptr cinfo) * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. * * The consume_input method pointer points either here or to the - * coefficient controller's consume_data routine, depending on whether - * we are reading a compressed data segment or inter-segment markers. + * coefficient or difference controller's consume_data routine, depending on + * whether we are reading a compressed data segment or inter-segment markers. */ METHODDEF(int) diff --git a/jdlhuff.c b/jdlhuff.c new file mode 100644 index 00000000..fb142d8f --- /dev/null +++ b/jdlhuff.c @@ -0,0 +1,303 @@ +/* + * jdlhuff.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains Huffman entropy decoding routines for lossless JPEG. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" /* Private declarations for lossless codec */ +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ + + +#ifdef D_LOSSLESS_SUPPORTED + +typedef struct { + int ci, yoffset, MCU_width; +} lhd_output_ptr_info; + +/* + * Expanded entropy decoder object for Huffman decoding in lossless mode. + */ + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcus: */ + + /* Pointers to derived tables to be used for each data unit within an MCU */ + d_derived_tbl *cur_tbls[D_MAX_BLOCKS_IN_MCU]; + + /* Pointers to the proper output difference row for each group of data units + * within an MCU. For each component, there are Vi groups of Hi data units. + */ + JDIFFROW output_ptr[D_MAX_BLOCKS_IN_MCU]; + + /* Number of output pointers in use for the current MCU. This is the sum + * of all Vi in the MCU. + */ + int num_output_ptrs; + + /* Information used for positioning the output pointers within the output + * difference rows. + */ + lhd_output_ptr_info output_ptr_info[D_MAX_BLOCKS_IN_MCU]; + + /* Index of the proper output pointer for each data unit within an MCU */ + int output_ptr_index[D_MAX_BLOCKS_IN_MCU]; + +} lhuff_entropy_decoder; + +typedef lhuff_entropy_decoder *lhuff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_lhuff_decoder(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int ci, dctbl, sampn, ptrn, yoffset, xoffset; + jpeg_component_info *compptr; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + /* Make sure requested tables are present */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + &entropy->derived_tbls[dctbl]); + } + + /* Precalculate decoding info for each sample in an MCU of this scan */ + for (sampn = 0, ptrn = 0; sampn < cinfo->blocks_in_MCU;) { + compptr = cinfo->cur_comp_info[cinfo->MCU_membership[sampn]]; + ci = compptr->component_index; + for (yoffset = 0; yoffset < compptr->MCU_height; yoffset++, ptrn++) { + /* Precalculate the setup info for each output pointer */ + entropy->output_ptr_info[ptrn].ci = ci; + entropy->output_ptr_info[ptrn].yoffset = yoffset; + entropy->output_ptr_info[ptrn].MCU_width = compptr->MCU_width; + for (xoffset = 0; xoffset < compptr->MCU_width; xoffset++, sampn++) { + /* Precalculate the output pointer index for each sample */ + entropy->output_ptr_index[sampn] = ptrn; + /* Precalculate which table to use for each sample */ + entropy->cur_tbls[sampn] = entropy->derived_tbls[compptr->dc_tbl_no]; + } + } + } + entropy->num_output_ptrs = ptrn; + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#define AVOID_TABLES +#ifdef AVOID_TABLES + +#define NEG_1 ((unsigned int)-1) +#define HUFF_EXTEND(x, s) \ + ((x) + ((((x) - (1 << ((s) - 1))) >> 31) & (((NEG_1) << (s)) + 1))) + +#else + +#define HUFF_EXTEND(x, s) \ + ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = { /* entry n is 2**(n-1) */ + 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 +}; + +static const int extend_offset[16] = { /* entry n is (-1 << n) + 1 */ + 0, ((-1) << 1) + 1, ((-1) << 2) + 1, ((-1) << 3) + 1, ((-1) << 4) + 1, + ((-1) << 5) + 1, ((-1) << 6) + 1, ((-1) << 7) + 1, ((-1) << 8) + 1, + ((-1) << 9) + 1, ((-1) << 10) + 1, ((-1) << 11) + 1, ((-1) << 12) + 1, + ((-1) << 13) + 1, ((-1) << 14) + 1, ((-1) << 15) + 1 +}; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (!(*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return nMCU MCUs' worth of Huffman-compressed differences. + * Each MCU is also disassembled and placed accordingly in diff_buf. + * + * MCU_col_num specifies the column of the first MCU being requested within + * the MCU row. This tells us where to position the output row pointers in + * diff_buf. + * + * Returns the number of MCUs decoded. This may be less than nMCU MCUs if + * data source requested suspension. In that case no changes have been made + * to permanent state. (Exception: some output differences may already have + * been assigned. This is harmless for this module, since we'll just + * re-assign them on the next call.) + */ + +METHODDEF(JDIMENSION) +decode_mcus(j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, JDIMENSION nMCU) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + lhuff_entropy_ptr entropy = (lhuff_entropy_ptr)cinfo->entropy; + int sampn, ci, yoffset, MCU_width, ptrn; + JDIMENSION mcu_num; + BITREAD_STATE_VARS; + + /* Set output pointer locations based on MCU_col_num */ + for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) { + ci = entropy->output_ptr_info[ptrn].ci; + yoffset = entropy->output_ptr_info[ptrn].yoffset; + MCU_width = entropy->output_ptr_info[ptrn].MCU_width; + entropy->output_ptr[ptrn] = + diff_buf[ci][MCU_row_num + yoffset] + (MCU_col_num * MCU_width); + } + + /* + * If we've run out of data, zero out the buffers and return. + * By resetting the undifferencer, the output samples will be CENTERJSAMPLE. + * + * NB: We should find a way to do this without interacting with the + * undifferencer module directly. + */ + if (entropy->pub.insufficient_data) { + for (ptrn = 0; ptrn < entropy->num_output_ptrs; ptrn++) + jzero_far((void FAR *)entropy->output_ptr[ptrn], + nMCU * entropy->output_ptr_info[ptrn].MCU_width * + sizeof(JDIFF)); + + (*losslessd->predict_process_restart) (cinfo); + + } else { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo, entropy->bitstate); + + /* Outer loop handles the number of MCUs requested */ + + for (mcu_num = 0; mcu_num < nMCU; mcu_num++) { + + /* Inner loop handles the samples in the MCU */ + for (sampn = 0; sampn < cinfo->blocks_in_MCU; sampn++) { + d_derived_tbl *dctbl = entropy->cur_tbls[sampn]; + register int s, r; + + /* Section H.2.2: decode the sample difference */ + HUFF_DECODE(s, br_state, dctbl, return mcu_num, label1); + if (s) { + if (s == 16) /* special case: always output 32768 */ + s = 32768; + else { /* normal case: fetch subsequent bits */ + CHECK_BIT_BUFFER(br_state, s, return mcu_num); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + } + + /* Output the sample difference */ + *entropy->output_ptr[entropy->output_ptr_index[sampn]]++ = (JDIFF)s; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo, entropy->bitstate); + } + } + + return nMCU; +} + + +/* + * Module initialization routine for lossless mode Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_lhuff_decoder(j_decompress_ptr cinfo) +{ + lhuff_entropy_ptr entropy; + int i; + + entropy = (lhuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, + sizeof(lhuff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *)entropy; + entropy->pub.start_pass = start_pass_lhuff_decoder; + entropy->pub.decode_mcus = decode_mcus; + entropy->pub.process_restart = process_restart; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdlossls.c b/jdlossls.c new file mode 100644 index 00000000..96790bff --- /dev/null +++ b/jdlossls.c @@ -0,0 +1,287 @@ +/* + * jdlossls.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains prediction, sample undifferencing, point transform, and + * sample scaling routines for the lossless JPEG decompressor. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jlossls.h" + +#ifdef D_LOSSLESS_SUPPORTED + + +/**************** Sample undifferencing (reconstruction) *****************/ + +/* + * In order to avoid a performance penalty for checking which predictor is + * being used and which row is being processed for each call of the + * undifferencer, and to promote optimization, we have separate undifferencing + * functions for each predictor selection value. + * + * We are able to avoid duplicating source code by implementing the predictors + * and undifferencers as macros. Each of the undifferencing functions is + * simply a wrapper around an UNDIFFERENCE macro with the appropriate PREDICTOR + * macro passed as an argument. + */ + +/* Predictor for the first column of the first row: 2^(P-Pt-1) */ +#define INITIAL_PREDICTORx (1 << (cinfo->data_precision - cinfo->Al - 1)) + +/* Predictor for the first column of the remaining rows: Rb */ +#define INITIAL_PREDICTOR2 prev_row[0] + + +/* + * 1-Dimensional undifferencer routine. + * + * This macro implements the 1-D horizontal predictor (1). INITIAL_PREDICTOR + * is used as the special case predictor for the first column, which must be + * either INITIAL_PREDICTOR2 or INITIAL_PREDICTORx. The remaining samples + * use PREDICTOR1. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. +*/ + +#define UNDIFFERENCE_1D(INITIAL_PREDICTOR) \ + int Ra; \ + \ + Ra = (*diff_buf++ + INITIAL_PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Ra = (*diff_buf++ + PREDICTOR1) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * 2-Dimensional undifferencer routine. + * + * This macro implements the 2-D horizontal predictors (#2-7). PREDICTOR2 is + * used as the special case predictor for the first column. The remaining + * samples use PREDICTOR, which is a function of Ra, Rb, and Rc. + * + * Because prev_row and output_buf may point to the same storage area (in an + * interleaved image with Vi=1, for example), we must take care to buffer Rb/Rc + * before writing the current reconstructed sample value into output_buf. + * + * The reconstructed sample is supposed to be calculated modulo 2^16, so we + * logically AND the result with 0xFFFF. + */ + +#define UNDIFFERENCE_2D(PREDICTOR) \ + int Ra, Rb, Rc; \ + \ + Rb = *prev_row++; \ + Ra = (*diff_buf++ + PREDICTOR2) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + \ + while (--width) { \ + Rc = Rb; \ + Rb = *prev_row++; \ + Ra = (*diff_buf++ + PREDICTOR) & 0xFFFF; \ + *undiff_buf++ = Ra; \ + } + + +/* + * Undifferencers for the second and subsequent rows in a scan or restart + * interval. The first sample in the row is undifferenced using the vertical + * predictor (2). The rest of the samples are undifferenced using the + * predictor specified in the scan header. + */ + +METHODDEF(void) +jpeg_undifference1(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_1D(INITIAL_PREDICTOR2); +} + +METHODDEF(void) +jpeg_undifference2(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR2); + (void)(Rc); +} + +METHODDEF(void) +jpeg_undifference3(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR3); +} + +METHODDEF(void) +jpeg_undifference4(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR4); +} + +METHODDEF(void) +jpeg_undifference5(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR5); +} + +METHODDEF(void) +jpeg_undifference6(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR6); +} + +METHODDEF(void) +jpeg_undifference7(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + UNDIFFERENCE_2D(PREDICTOR7); + (void)(Rc); +} + + +/* + * Undifferencer for the first row in a scan or restart interval. The first + * sample in the row is undifferenced using the special predictor constant + * x=2^(P-Pt-1). The rest of the samples are undifferenced using the + * 1-D horizontal predictor (1). + */ + +METHODDEF(void) +jpeg_undifference_first_row(j_decompress_ptr cinfo, int comp_index, + JDIFFROW diff_buf, JDIFFROW prev_row, + JDIFFROW undiff_buf, JDIMENSION width) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + + UNDIFFERENCE_1D(INITIAL_PREDICTORx); + + /* + * Now that we have undifferenced the first row, we want to use the + * undifferencer that corresponds to the predictor specified in the + * scan header. + */ + switch (cinfo->Ss) { + case 1: + losslessd->predict_undifference[comp_index] = jpeg_undifference1; + break; + case 2: + losslessd->predict_undifference[comp_index] = jpeg_undifference2; + break; + case 3: + losslessd->predict_undifference[comp_index] = jpeg_undifference3; + break; + case 4: + losslessd->predict_undifference[comp_index] = jpeg_undifference4; + break; + case 5: + losslessd->predict_undifference[comp_index] = jpeg_undifference5; + break; + case 6: + losslessd->predict_undifference[comp_index] = jpeg_undifference6; + break; + case 7: + losslessd->predict_undifference[comp_index] = jpeg_undifference7; + break; + } +} + + +/*********************** Sample upscaling by 2^Pt ************************/ + +METHODDEF(void) +simple_upscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + while (width--) + *output_buf++ = (_JSAMPLE)(*diff_buf++ << cinfo->Al); +} + +METHODDEF(void) +noscale(j_decompress_ptr cinfo, + JDIFFROW diff_buf, _JSAMPROW output_buf, JDIMENSION width) +{ + while (width--) + *output_buf++ = (_JSAMPLE)(*diff_buf++); +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_pass_lossless(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd = (lossless_decomp_ptr)cinfo->idct; + int ci; + + /* Check that the scan parameters Ss, Se, Ah, Al are OK for lossless JPEG. + * + * Ss is the predictor selection value (psv). Legal values for sequential + * lossless JPEG are: 1 <= psv <= 7. + * + * Se and Ah are not used and should be zero. + * + * Al specifies the point transform (Pt). + * Legal values are: 0 <= Pt <= (data precision - 1). + */ + if (cinfo->Ss < 1 || cinfo->Ss > 7 || + cinfo->Se != 0 || cinfo->Ah != 0 || + cinfo->Al < 0 || cinfo->Al >= cinfo->data_precision) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + + /* Set undifference functions to first row function */ + for (ci = 0; ci < cinfo->num_components; ci++) + losslessd->predict_undifference[ci] = jpeg_undifference_first_row; + + /* Set scaler function based on Pt */ + if (cinfo->Al) + losslessd->scaler_scale = simple_upscale; + else + losslessd->scaler_scale = noscale; +} + + +/* + * Initialize the lossless decompressor. + */ + +GLOBAL(void) +_jinit_lossless_decompressor(j_decompress_ptr cinfo) +{ + lossless_decomp_ptr losslessd; + + /* Create subobject in permanent pool */ + losslessd = (lossless_decomp_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, + sizeof(jpeg_lossless_decompressor)); + cinfo->idct = (struct jpeg_inverse_dct *)losslessd; + losslessd->pub.start_pass = start_pass_lossless; +} + +#endif /* D_LOSSLESS_SUPPORTED */ diff --git a/jdmainct.c b/jdmainct.c index 2fe07ae4..0883e970 100644 --- a/jdmainct.c +++ b/jdmainct.c @@ -22,10 +22,11 @@ /* * In the current system design, the main buffer need never be a full-image - * buffer; any full-height buffers will be found inside the coefficient or - * postprocessing controllers. Nonetheless, the main controller is not - * trivial. Its responsibility is to provide context rows for upsampling/ - * rescaling, and doing this in an efficient fashion is a bit tricky. + * buffer; any full-height buffers will be found inside the coefficient, + * difference, or postprocessing controllers. Nonetheless, the main controller + * is not trivial. Its responsibility is to provide context rows for + * upsampling/rescaling, and doing this in an efficient fashion is a bit + * tricky. * * Postprocessor input data is counted in "row groups". A row group * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) @@ -37,20 +38,20 @@ * row group (times any additional scale factor that the upsampler is * applying). * - * The coefficient controller will deliver data to us one iMCU row at a time; - * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or - * exactly min_DCT_scaled_size row groups. (This amount of data corresponds - * to one row of MCUs when the image is fully interleaved.) Note that the - * number of sample rows varies across components, but the number of row - * groups does not. Some garbage sample rows may be included in the last iMCU - * row at the bottom of the image. + * The coefficient or difference controller will deliver data to us one iMCU + * row at a time; each iMCU row contains v_samp_factor * DCT_scaled_size sample + * rows, or exactly min_DCT_scaled_size row groups. (This amount of data + * corresponds to one row of MCUs when the image is fully interleaved.) Note + * that the number of sample rows varies across components, but the number of + * row groups does not. Some garbage sample rows may be included in the last + * iMCU row at the bottom of the image. * * Depending on the vertical scaling algorithm used, the upsampler may need * access to the sample row(s) above and below its current input row group. * The upsampler is required to set need_context_rows TRUE at global selection * time if so. When need_context_rows is FALSE, this controller can simply - * obtain one iMCU row at a time from the coefficient controller and dole it - * out as row groups to the postprocessor. + * obtain one iMCU row at a time from the coefficient or difference controller + * and dole it out as row groups to the postprocessor. * * When need_context_rows is TRUE, this controller guarantees that the buffer * passed to postprocessing contains at least one row group's worth of samples diff --git a/jdmarker.c b/jdmarker.c index f7eba615..acd28ce6 100644 --- a/jdmarker.c +++ b/jdmarker.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2012, 2015, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -237,7 +239,8 @@ get_soi(j_decompress_ptr cinfo) LOCAL(boolean) -get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_lossless, + boolean is_arith) /* Process a SOFn marker */ { JLONG length; @@ -246,6 +249,7 @@ get_sof(j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) INPUT_VARS(cinfo); cinfo->progressive_mode = is_prog; + cinfo->master->lossless = is_lossless; cinfo->arith_code = is_arith; INPUT_2BYTES(cinfo, length, return FALSE); @@ -990,32 +994,40 @@ read_markers(j_decompress_ptr cinfo) case M_SOF0: /* Baseline */ case M_SOF1: /* Extended sequential, Huffman */ - if (!get_sof(cinfo, FALSE, FALSE)) + if (!get_sof(cinfo, FALSE, FALSE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF2: /* Progressive, Huffman */ - if (!get_sof(cinfo, TRUE, FALSE)) + if (!get_sof(cinfo, TRUE, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF3: /* Lossless, Huffman */ + if (!get_sof(cinfo, FALSE, TRUE, FALSE)) return JPEG_SUSPENDED; break; case M_SOF9: /* Extended sequential, arithmetic */ - if (!get_sof(cinfo, FALSE, TRUE)) + if (!get_sof(cinfo, FALSE, FALSE, TRUE)) return JPEG_SUSPENDED; break; case M_SOF10: /* Progressive, arithmetic */ - if (!get_sof(cinfo, TRUE, TRUE)) + if (!get_sof(cinfo, TRUE, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF11: /* Lossless, arithmetic */ + if (!get_sof(cinfo, FALSE, TRUE, TRUE)) return JPEG_SUSPENDED; break; /* Currently unsupported SOFn types */ - case M_SOF3: /* Lossless, Huffman */ case M_SOF5: /* Differential sequential, Huffman */ case M_SOF6: /* Differential progressive, Huffman */ case M_SOF7: /* Differential lossless, Huffman */ case M_JPG: /* Reserved for JPEG extensions */ - case M_SOF11: /* Lossless, arithmetic */ case M_SOF13: /* Differential sequential, arithmetic */ case M_SOF14: /* Differential progressive, arithmetic */ case M_SOF15: /* Differential lossless, arithmetic */ diff --git a/jdmaster.c b/jdmaster.c index 745a29ed..aa4d8a3b 100644 --- a/jdmaster.c +++ b/jdmaster.c @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 2016, 2019, 2022, D. R. Commander. * Copyright (C) 2013, Linaro Limited. @@ -97,154 +99,154 @@ jpeg_core_output_dimensions(j_decompress_ptr cinfo) int ci; jpeg_component_info *compptr; - /* Compute actual output image dimensions and DCT scaling choices. */ - if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom) { - /* Provide 1/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 1; - cinfo->_min_DCT_v_scaled_size = 1; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 2) { - /* Provide 2/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 2L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 2L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 2; - cinfo->_min_DCT_v_scaled_size = 2; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 3) { - /* Provide 3/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 3L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 3L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 3; - cinfo->_min_DCT_v_scaled_size = 3; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 4) { - /* Provide 4/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 4L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 4L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 4; - cinfo->_min_DCT_v_scaled_size = 4; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 5) { - /* Provide 5/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 5L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 5L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 5; - cinfo->_min_DCT_v_scaled_size = 5; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 6) { - /* Provide 6/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 6L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 6L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 6; - cinfo->_min_DCT_v_scaled_size = 6; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 7) { - /* Provide 7/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 7L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 7L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 7; - cinfo->_min_DCT_v_scaled_size = 7; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 8) { - /* Provide 8/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 8L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 8L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 8; - cinfo->_min_DCT_v_scaled_size = 8; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 9) { - /* Provide 9/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 9L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 9L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 9; - cinfo->_min_DCT_v_scaled_size = 9; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 10) { - /* Provide 10/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 10L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 10L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 10; - cinfo->_min_DCT_v_scaled_size = 10; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 11) { - /* Provide 11/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 11L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 11L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 11; - cinfo->_min_DCT_v_scaled_size = 11; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 12) { - /* Provide 12/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 12L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 12L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 12; - cinfo->_min_DCT_v_scaled_size = 12; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 13) { - /* Provide 13/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 13L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 13L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 13; - cinfo->_min_DCT_v_scaled_size = 13; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 14) { - /* Provide 14/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 14L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 14L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 14; - cinfo->_min_DCT_v_scaled_size = 14; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 15) { - /* Provide 15/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 15L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 15L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 15; - cinfo->_min_DCT_v_scaled_size = 15; - } else { - /* Provide 16/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * 16L, (long)DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * 16L, (long)DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 16; - cinfo->_min_DCT_v_scaled_size = 16; + if (!cinfo->master->lossless) { + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom) { + /* Provide 1/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 1; + cinfo->_min_DCT_v_scaled_size = 1; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 2) { + /* Provide 2/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 2L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 2L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 2; + cinfo->_min_DCT_v_scaled_size = 2; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 3) { + /* Provide 3/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 3L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 3L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 3; + cinfo->_min_DCT_v_scaled_size = 3; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 4) { + /* Provide 4/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 4L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 4L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 4; + cinfo->_min_DCT_v_scaled_size = 4; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 5) { + /* Provide 5/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 5L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 5L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 5; + cinfo->_min_DCT_v_scaled_size = 5; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 6) { + /* Provide 6/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 6L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 6L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 6; + cinfo->_min_DCT_v_scaled_size = 6; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 7) { + /* Provide 7/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 7L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 7L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 7; + cinfo->_min_DCT_v_scaled_size = 7; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 8) { + /* Provide 8/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 8L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 8L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 8; + cinfo->_min_DCT_v_scaled_size = 8; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 9) { + /* Provide 9/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 9L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 9L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 9; + cinfo->_min_DCT_v_scaled_size = 9; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 10) { + /* Provide 10/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 10L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 10L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 10; + cinfo->_min_DCT_v_scaled_size = 10; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 11) { + /* Provide 11/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 11L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 11L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 11; + cinfo->_min_DCT_v_scaled_size = 11; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 12) { + /* Provide 12/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 12L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 12L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 12; + cinfo->_min_DCT_v_scaled_size = 12; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 13) { + /* Provide 13/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 13L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 13L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 13; + cinfo->_min_DCT_v_scaled_size = 13; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 14) { + /* Provide 14/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 14L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 14L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 14; + cinfo->_min_DCT_v_scaled_size = 14; + } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 15) { + /* Provide 15/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 15L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 15L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 15; + cinfo->_min_DCT_v_scaled_size = 15; + } else { + /* Provide 16/block_size scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * 16L, (long)DCTSIZE); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * 16L, (long)DCTSIZE); + cinfo->_min_DCT_h_scaled_size = 16; + cinfo->_min_DCT_v_scaled_size = 16; + } + + /* Recompute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->_DCT_h_scaled_size = cinfo->_min_DCT_h_scaled_size; + compptr->_DCT_v_scaled_size = cinfo->_min_DCT_v_scaled_size; + } + } else +#endif /* !IDCT_SCALING_SUPPORTED */ + { + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size, + * and has computed unscaled downsampled_width and downsampled_height. + */ } - - /* Recompute dimensions of components */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - compptr->_DCT_h_scaled_size = cinfo->_min_DCT_h_scaled_size; - compptr->_DCT_v_scaled_size = cinfo->_min_DCT_v_scaled_size; - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ } @@ -273,54 +275,56 @@ jpeg_calc_output_dimensions(j_decompress_ptr cinfo) #ifdef IDCT_SCALING_SUPPORTED - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code adapts subsampling ratios which are powers of 2. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - int ssize = cinfo->_min_DCT_scaled_size; - while (ssize < DCTSIZE && - ((cinfo->max_h_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->h_samp_factor * ssize * 2) == 0) && - ((cinfo->max_v_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->v_samp_factor * ssize * 2) == 0)) { - ssize = ssize * 2; - } + if (!cinfo->master->lossless) { + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code adapts subsampling ratios which are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->_min_DCT_scaled_size; + while (ssize < DCTSIZE && + ((cinfo->max_h_samp_factor * cinfo->_min_DCT_scaled_size) % + (compptr->h_samp_factor * ssize * 2) == 0) && + ((cinfo->max_v_samp_factor * cinfo->_min_DCT_scaled_size) % + (compptr->v_samp_factor * ssize * 2) == 0)) { + ssize = ssize * 2; + } #if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = ssize; + compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = ssize; #else - compptr->DCT_scaled_size = ssize; + compptr->DCT_scaled_size = ssize; #endif - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Size in samples, after IDCT scaling */ - compptr->downsampled_width = (JDIMENSION) - jdiv_round_up((long)cinfo->image_width * - (long)(compptr->h_samp_factor * compptr->_DCT_scaled_size), - (long)(cinfo->max_h_samp_factor * DCTSIZE)); - compptr->downsampled_height = (JDIMENSION) - jdiv_round_up((long)cinfo->image_height * - (long)(compptr->v_samp_factor * compptr->_DCT_scaled_size), - (long)(cinfo->max_v_samp_factor * DCTSIZE)); - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, - * and has computed unscaled downsampled_width and downsampled_height. - */ + } + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long)cinfo->image_width * + (long)(compptr->h_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long)cinfo->image_height * + (long)(compptr->v_samp_factor * + compptr->_DCT_scaled_size), + (long)(cinfo->max_v_samp_factor * DCTSIZE)); + } + } else #endif /* IDCT_SCALING_SUPPORTED */ + { + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + } /* Report number of components in selected colorspace. */ /* Probably this should be in the color conversion module... */ @@ -478,6 +482,17 @@ master_selection(j_decompress_ptr cinfo) long samplesperrow; JDIMENSION jd_samplesperrow; + /* Disable IDCT scaling and raw (downsampled) data output in lossless mode. + * IDCT scaling is not useful in lossless mode, and it must be disabled in + * order to properly calculate the output dimensions. Raw data output isn't + * particularly useful without subsampling and has not been tested in + * lossless mode. + */ + if (cinfo->master->lossless) { + cinfo->raw_data_out = FALSE; + cinfo->scale_num = cinfo->scale_denom = 1; + } + /* Initialize dimensions and other stuff */ jpeg_calc_output_dimensions(cinfo); prepare_range_limit_table(cinfo); @@ -548,71 +563,96 @@ master_selection(j_decompress_ptr cinfo) */ } - if (cinfo->data_precision == 12) { - /* Post-processing: in particular, color conversion first */ - if (!cinfo->raw_data_out) { - if (master->using_merged_upsample) { + /* Post-processing: in particular, color conversion first */ + if (!cinfo->raw_data_out) { + if (master->using_merged_upsample) { #ifdef UPSAMPLE_MERGING_SUPPORTED + if (cinfo->data_precision == 12) j12init_merged_upsampler(cinfo); /* does color conversion too */ -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else { - j12init_color_deconverter(cinfo); - j12init_upsampler(cinfo); - } - j12init_d_post_controller(cinfo, cinfo->enable_2pass_quant); - } - /* Inverse DCT */ - j12init_inverse_dct(cinfo); - } else { - /* Post-processing: in particular, color conversion first */ - if (!cinfo->raw_data_out) { - if (master->using_merged_upsample) { -#ifdef UPSAMPLE_MERGING_SUPPORTED + else jinit_merged_upsampler(cinfo); /* does color conversion too */ #else - ERREXIT(cinfo, JERR_NOT_COMPILED); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif + } else { + if (cinfo->data_precision == 12) { + j12init_color_deconverter(cinfo); + j12init_upsampler(cinfo); } else { jinit_color_deconverter(cinfo); jinit_upsampler(cinfo); } - jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); } - /* Inverse DCT */ - jinit_inverse_dct(cinfo); + if (cinfo->data_precision == 12) + j12init_d_post_controller(cinfo, cinfo->enable_2pass_quant); + else + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); } - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { -#ifdef D_ARITH_CODING_SUPPORTED - jinit_arith_decoder(cinfo); + + if (cinfo->master->lossless) { +#ifdef D_LOSSLESS_SUPPORTED + /* Prediction, sample undifferencing, point transform, and sample size + * scaling + */ + if (cinfo->data_precision == 12) + j12init_lossless_decompressor(cinfo); + else + jinit_lossless_decompressor(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + jinit_lhuff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + if (cinfo->data_precision == 12) + j12init_d_diff_controller(cinfo, use_c_buffer); + else + jinit_d_diff_controller(cinfo, use_c_buffer); #else - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + ERREXIT(cinfo, JERR_NOT_COMPILED); #endif } else { - if (cinfo->progressive_mode) { -#ifdef D_PROGRESSIVE_SUPPORTED - jinit_phuff_decoder(cinfo); + /* Inverse DCT */ + if (cinfo->data_precision == 12) + j12init_inverse_dct(cinfo); + else + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { +#ifdef D_ARITH_CODING_SUPPORTED + jinit_arith_decoder(cinfo); #else - ERREXIT(cinfo, JERR_NOT_COMPILED); + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); #endif - } else - jinit_huff_decoder(cinfo); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || + cinfo->buffered_image; + if (cinfo->data_precision == 12) + j12init_d_coef_controller(cinfo, use_c_buffer); + else + jinit_d_coef_controller(cinfo, use_c_buffer); } - /* Initialize principal buffer controllers. */ - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - if (cinfo->data_precision == 12) { - j12init_d_coef_controller(cinfo, use_c_buffer); - - if (!cinfo->raw_data_out) + if (!cinfo->raw_data_out) { + if (cinfo->data_precision == 12) j12init_d_main_controller(cinfo, FALSE /* never need full buffer here */); - } else { - jinit_d_coef_controller(cinfo, use_c_buffer); - - if (!cinfo->raw_data_out) + else jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); } diff --git a/jdphuff.c b/jdphuff.c index 9680ebcb..bf97333a 100644 --- a/jdphuff.c +++ b/jdphuff.c @@ -3,6 +3,8 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2015-2016, 2018-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -23,7 +25,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jdhuff.h" /* Declarations shared with jdhuff.c */ +#include "jdhuff.h" /* Declarations shared with jd*huff.c */ #include diff --git a/jdtrans.c b/jdtrans.c index a24dab14..719813f6 100644 --- a/jdtrans.c +++ b/jdtrans.c @@ -48,6 +48,9 @@ LOCAL(void) transdecode_master_selection(j_decompress_ptr cinfo); GLOBAL(jvirt_barray_ptr *) jpeg_read_coefficients(j_decompress_ptr cinfo) { + if (cinfo->master->lossless) + ERREXIT(cinfo, JERR_NOTIMPL); + if (cinfo->global_state == DSTATE_READY) { /* First call: initialize active modules */ transdecode_master_selection(cinfo); diff --git a/jerror.h b/jerror.h index eb44a114..39362fdd 100644 --- a/jerror.h +++ b/jerror.h @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2014, 2017, 2021-2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -53,7 +55,8 @@ JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") #if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") #endif -JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCT_COEF, + "DCT coefficient (lossy) or spatial difference (lossless) out of range") JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") #if JPEG_LIB_VERSION >= 70 JMESSAGE(JERR_BAD_DROP_SAMPLING, @@ -69,9 +72,9 @@ JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") JMESSAGE(JERR_BAD_PROGRESSION, - "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") + "Invalid progressive/lossless parameters Ss=%d Se=%d Ah=%d Al=%d") JMESSAGE(JERR_BAD_PROG_SCRIPT, - "Invalid progressive parameters at scan script entry %d") + "Invalid progressive/lossless parameters at scan script entry %d") JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") @@ -180,7 +183,7 @@ JMESSAGE(JTRC_THUMB_PALETTE, JMESSAGE(JTRC_THUMB_RGB, "JFIF extension marker: RGB thumbnail image, length %u") JMESSAGE(JTRC_UNKNOWN_IDS, - "Unrecognized component IDs %d %d %d, assuming YCbCr") + "Unrecognized component IDs %d %d %d, assuming YCbCr (lossy) or RGB (lossless)") JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") @@ -211,6 +214,8 @@ JMESSAGE(JWRN_BOGUS_ICC, "Corrupt JPEG data: bad ICC marker") JMESSAGE(JERR_BAD_DROP_SAMPLING, "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") #endif +JMESSAGE(JERR_BAD_RESTART, + "Invalid restart interval %d; must be an integer multiple of the number of MCUs in an MCU row (%d)") #ifdef JMAKE_ENUM_LIST diff --git a/jlossls.h b/jlossls.h new file mode 100644 index 00000000..38c45818 --- /dev/null +++ b/jlossls.h @@ -0,0 +1,104 @@ +/* + * jlossls.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1998, Thomas G. Lane. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. + * libjpeg-turbo Modifications: + * Copyright (C) 2022, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This include file contains common declarations for the lossless JPEG + * codec modules. + */ + +#ifndef JLOSSLS_H +#define JLOSSLS_H + +#if defined(C_LOSSLESS_SUPPORTED) || defined(D_LOSSLESS_SUPPORTED) + +#define JPEG_INTERNALS +#include "jpeglib.h" +#include "jsamplecomp.h" + + +#define ALLOC_DARRAY(pool_id, diffsperrow, numrows) \ + (JDIFFARRAY)(*cinfo->mem->alloc_sarray) \ + ((j_common_ptr)cinfo, pool_id, \ + (diffsperrow) * sizeof(JDIFF) / sizeof(_JSAMPLE), numrows) + + +/* + * Table H.1: Predictors for lossless coding. + */ + +#define PREDICTOR1 Ra +#define PREDICTOR2 Rb +#define PREDICTOR3 Rc +#define PREDICTOR4 (int)((JLONG)Ra + (JLONG)Rb - (JLONG)Rc) +#define PREDICTOR5 (int)((JLONG)Ra + RIGHT_SHIFT((JLONG)Rb - (JLONG)Rc, 1)) +#define PREDICTOR6 (int)((JLONG)Rb + RIGHT_SHIFT((JLONG)Ra - (JLONG)Rc, 1)) +#define PREDICTOR7 (int)RIGHT_SHIFT((JLONG)Ra + (JLONG)Rb, 1) + +#endif + + +#ifdef C_LOSSLESS_SUPPORTED + +typedef void (*predict_difference_method_ptr) (j_compress_ptr cinfo, int ci, + _JSAMPROW input_buf, + _JSAMPROW prev_row, + JDIFFROW diff_buf, + JDIMENSION width); + +/* Lossless compressor */ +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* It is useful to allow each component to have a separate diff method. */ + predict_difference_method_ptr predict_difference[MAX_COMPONENTS]; + + /* MCU rows left in the restart interval for each component */ + unsigned int restart_rows_to_go[MAX_COMPONENTS]; + + /* Sample scaling */ + void (*scaler_scale) (j_compress_ptr cinfo, _JSAMPROW input_buf, + _JSAMPROW output_buf, JDIMENSION width); +} jpeg_lossless_compressor; + +typedef jpeg_lossless_compressor *lossless_comp_ptr; + +#endif /* C_LOSSLESS_SUPPORTED */ + + +#ifdef D_LOSSLESS_SUPPORTED + +typedef void (*predict_undifference_method_ptr) (j_decompress_ptr cinfo, + int comp_index, + JDIFFROW diff_buf, + JDIFFROW prev_row, + JDIFFROW undiff_buf, + JDIMENSION width); + +/* Lossless decompressor */ +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* Prediction, undifferencing */ + void (*predict_process_restart) (j_decompress_ptr cinfo); + + /* It is useful to allow each component to have a separate undiff method. */ + predict_undifference_method_ptr predict_undifference[MAX_COMPONENTS]; + + /* Sample scaling */ + void (*scaler_scale) (j_decompress_ptr cinfo, JDIFFROW diff_buf, + _JSAMPROW output_buf, JDIMENSION width); +} jpeg_lossless_decompressor; + +typedef jpeg_lossless_decompressor *lossless_decomp_ptr; + +#endif /* D_LOSSLESS_SUPPORTED */ + +#endif /* JLOSSLS_H */ diff --git a/jmorecfg.h b/jmorecfg.h index 58031495..cda9b83e 100644 --- a/jmorecfg.h +++ b/jmorecfg.h @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, 2022, D. R. Commander. * For conditions of distribution and use, see the accompanying README.ijg @@ -232,14 +234,16 @@ typedef int boolean; #define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define C_LOSSLESS_SUPPORTED /* Lossless JPEG? */ #define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ /* Note: if you selected 12-bit data precision, it is dangerous to turn off * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit * precision, so jchuff.c normally uses entropy optimization to compute * usable tables for higher precision. If you don't want to do optimization, * you'll have to supply different default Huffman tables. - * The exact same statements apply for progressive JPEG: the default tables - * don't work for progressive mode. (This may get fixed, however.) + * The exact same statements apply for progressive and lossless JPEG: + * the default tables don't work for progressive mode or lossless mode. + * (This may get fixed, however.) */ #define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ @@ -247,6 +251,7 @@ typedef int boolean; #define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ #define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define D_LOSSLESS_SUPPORTED /* Lossless JPEG? */ #define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ #define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ #define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ diff --git a/jpegint.h b/jpegint.h index 3387bfd2..decd5557 100644 --- a/jpegint.h +++ b/jpegint.h @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2015-2016, 2019, 2021-2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. @@ -17,6 +19,17 @@ */ +/* Representation of a spatial difference value. + * This should be a signed value of at least 16 bits; int is usually OK. + */ + +typedef int JDIFF; + +typedef JDIFF FAR *JDIFFROW; /* pointer to one row of difference values */ +typedef JDIFFROW *JDIFFARRAY; /* ptr to some rows (a 2-D diff array) */ +typedef JDIFFARRAY *JDIFFIMAGE; /* a 3-D diff array: top index is color */ + + /* Declarations for both compression & decompression */ typedef enum { /* Operating modes for buffer controllers */ @@ -80,6 +93,7 @@ struct jpeg_comp_master { /* State variables made visible to other modules */ boolean call_pass_startup; /* True if pass_startup must be called */ boolean is_last_pass; /* True during last pass */ + boolean lossless; /* True if lossless mode is enabled */ }; /* Main buffer control (downsampled-data buffer) */ @@ -107,7 +121,9 @@ struct jpeg_c_prep_controller { JDIMENSION out_row_groups_avail); }; -/* Coefficient buffer control */ +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ struct jpeg_c_coef_controller { void (*start_pass) (j_compress_ptr cinfo, J_BUF_MODE pass_mode); boolean (*compress_data) (j_compress_ptr cinfo, JSAMPIMAGE input_buf); @@ -138,9 +154,13 @@ struct jpeg_downsampler { boolean need_context_rows; /* TRUE if need rows above & below */ }; -/* Forward DCT (also controls coefficient quantization) */ +/* Lossy mode: Forward DCT (also controls coefficient quantization) + * Lossless mode: Prediction, sample differencing, and point transform + */ struct jpeg_forward_dct { void (*start_pass) (j_compress_ptr cinfo); + + /* Lossy mode */ /* perhaps this should be an array??? */ void (*forward_DCT) (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY sample_data, JBLOCKROW coef_blocks, @@ -155,7 +175,14 @@ struct jpeg_forward_dct { /* Entropy encoding */ struct jpeg_entropy_encoder { void (*start_pass) (j_compress_ptr cinfo, boolean gather_statistics); + + /* Lossy mode */ boolean (*encode_mcu) (j_compress_ptr cinfo, JBLOCKROW *MCU_data); + /* Lossless mode */ + JDIMENSION (*encode_mcus) (j_compress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU); + void (*finish_pass) (j_compress_ptr cinfo); }; @@ -183,6 +210,7 @@ struct jpeg_decomp_master { /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + boolean lossless; /* True if decompressing a lossless image */ /* Partial decompression variables */ JDIMENSION first_iMCU_col; @@ -216,13 +244,25 @@ struct jpeg_d_main_controller { JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); }; -/* Coefficient buffer control */ +/* Lossy mode: Coefficient buffer control + * Lossless mode: Difference buffer control + */ struct jpeg_d_coef_controller { void (*start_input_pass) (j_decompress_ptr cinfo); int (*consume_data) (j_decompress_ptr cinfo); void (*start_output_pass) (j_decompress_ptr cinfo); int (*decompress_data) (j_decompress_ptr cinfo, JSAMPIMAGE output_buf); int (*decompress_data_12) (j_decompress_ptr cinfo, J12SAMPIMAGE output_buf); + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* Lossy mode */ /* Pointer to array of coefficient virtual arrays, or NULL if none */ jvirt_barray_ptr *coef_arrays; }; @@ -266,14 +306,24 @@ struct jpeg_marker_reader { /* Entropy decoding */ struct jpeg_entropy_decoder { void (*start_pass) (j_decompress_ptr cinfo); + + /* Lossy mode */ boolean (*decode_mcu) (j_decompress_ptr cinfo, JBLOCKROW *MCU_data); + /* Lossless mode */ + JDIMENSION (*decode_mcus) (j_decompress_ptr cinfo, JDIFFIMAGE diff_buf, + JDIMENSION MCU_row_num, JDIMENSION MCU_col_num, + JDIMENSION nMCU); + boolean (*process_restart) (j_decompress_ptr cinfo); /* This is here to share code between baseline and progressive decoders; */ /* other modules probably should not use it */ boolean insufficient_data; /* set TRUE after emitting warning */ }; -/* Inverse DCT (also performs dequantization) */ +/* Lossy mode: Inverse DCT (also performs dequantization) + * Lossless mode: Prediction, sample undifferencing, point transform, and + * sample size scaling + */ typedef void (*inverse_DCT_method_ptr) (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, @@ -287,6 +337,8 @@ typedef void (*inverse_DCT_12_method_ptr) (j_decompress_ptr cinfo, struct jpeg_inverse_dct { void (*start_pass) (j_decompress_ptr cinfo); + + /* Lossy mode */ /* It is useful to allow each component to have a separate IDCT method. */ inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; inverse_DCT_12_method_ptr inverse_DCT_12[MAX_COMPONENTS]; @@ -386,6 +438,15 @@ EXTERN(void) jinit_huff_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_phuff_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_arith_encoder(j_compress_ptr cinfo); EXTERN(void) jinit_marker_writer(j_compress_ptr cinfo); +#ifdef C_LOSSLESS_SUPPORTED +EXTERN(void) jinit_c_diff_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_c_diff_controller(j_compress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) jinit_lhuff_encoder(j_compress_ptr cinfo); +EXTERN(void) jinit_lossless_compressor(j_compress_ptr cinfo); +EXTERN(void) j12init_lossless_compressor(j_compress_ptr cinfo); +#endif /* Decompression module initialization routines */ EXTERN(void) jinit_master_decompress(j_decompress_ptr cinfo); @@ -418,6 +479,15 @@ EXTERN(void) jinit_2pass_quantizer(j_decompress_ptr cinfo); EXTERN(void) j12init_2pass_quantizer(j_decompress_ptr cinfo); EXTERN(void) jinit_merged_upsampler(j_decompress_ptr cinfo); EXTERN(void) j12init_merged_upsampler(j_decompress_ptr cinfo); +#ifdef D_LOSSLESS_SUPPORTED +EXTERN(void) jinit_d_diff_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) j12init_d_diff_controller(j_decompress_ptr cinfo, + boolean need_full_buffer); +EXTERN(void) jinit_lhuff_decoder(j_decompress_ptr cinfo); +EXTERN(void) jinit_lossless_decompressor(j_decompress_ptr cinfo); +EXTERN(void) j12init_lossless_decompressor(j_decompress_ptr cinfo); +#endif /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr(j_common_ptr cinfo); diff --git a/jpeglib.h b/jpeglib.h index c3f7f1e6..336efbac 100644 --- a/jpeglib.h +++ b/jpeglib.h @@ -4,6 +4,8 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. + * Lossless JPEG Modifications: + * Copyright (C) 1999, Ken Murchison. * libjpeg-turbo Modifications: * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, 2022, D. R. Commander. * Copyright (C) 2015, Google, Inc. @@ -43,6 +45,13 @@ extern "C" { * if you want to be compatible. */ +/* NOTE: In lossless mode, an MCU contains one or more samples rather than one + * or more 8x8 DCT blocks, so the term "data unit" is used to generically + * describe a sample in lossless mode or an 8x8 DCT block in lossy mode. To + * preserve backward API/ABI compatibility, the field and macro names retain + * the "block" terminology. + */ + #define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ #define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ #define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ @@ -57,9 +66,9 @@ extern "C" { * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe * sometimes emits noncompliant files doesn't mean you should too. */ -#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on data units/MCU */ #ifndef D_MAX_BLOCKS_IN_MCU -#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on data units/MCU */ #endif @@ -142,17 +151,20 @@ typedef struct { /* Remaining fields should be treated as private by applications. */ /* These values are computed during compression or decompression startup: */ - /* Component's size in DCT blocks. - * Any dummy blocks added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. + /* Component's size in data units. + * In lossy mode, any dummy blocks added to complete an MCU are not counted; + * therefore these values do not depend on whether a scan is interleaved or + * not. In lossless mode, these are always equal to the image width and + * height. */ JDIMENSION width_in_blocks; JDIMENSION height_in_blocks; - /* Size of a DCT block in samples. Always DCTSIZE for compression. - * For decompression this is the size of the output from one DCT block, + /* Size of a data unit in samples. Always DCTSIZE for lossy compression. + * For lossy decompression this is the size of the output from one DCT block, * reflecting any scaling we choose to apply during the IDCT step. - * Values from 1 to 16 are supported. - * Note that different components may receive different IDCT scalings. + * Values from 1 to 16 are supported. Note that different components may + * receive different IDCT scalings. In lossless mode, this is always equal + * to 1. */ #if JPEG_LIB_VERSION >= 70 int DCT_h_scaled_size; @@ -163,8 +175,10 @@ typedef struct { /* The downsampled dimensions are the component's actual, unpadded number * of samples at the main buffer (preprocessing/compression interface), thus * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, IDCT scaling is included, so + * and similarly for height. For lossy decompression, IDCT scaling is + * included, so * downsampled_width = ceil(image_width * Hi/Hmax * DCT_[h_]scaled_size/DCTSIZE) + * In lossless mode, these are always equal to the image width and height. */ JDIMENSION downsampled_width; /* actual width in samples */ JDIMENSION downsampled_height; /* actual height in samples */ @@ -176,12 +190,12 @@ typedef struct { /* These values are computed before starting a scan of the component. */ /* The decompressor output side may not use these variables. */ - int MCU_width; /* number of blocks per MCU, horizontally */ - int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_width; /* number of data units per MCU, horizontally */ + int MCU_height; /* number of data units per MCU, vertically */ int MCU_blocks; /* MCU_width * MCU_height */ int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_[h_]scaled_size */ - int last_col_width; /* # of non-dummy blocks across in last MCU */ - int last_row_height; /* # of non-dummy blocks down in last MCU */ + int last_col_width; /* # of non-dummy data units across in last MCU */ + int last_row_height; /* # of non-dummy data units down in last MCU */ /* Saved quantization table for component; NULL if none yet saved. * See jdinput.c comments about the need for this information. @@ -199,8 +213,12 @@ typedef struct { typedef struct { int comps_in_scan; /* number of components encoded in this scan */ int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ - int Ss, Se; /* progressive JPEG spectral selection parms */ - int Ah, Al; /* progressive JPEG successive approx. parms */ + int Ss, Se; /* progressive JPEG spectral selection parms + (Ss is the predictor selection value in + lossless mode) */ + int Ah, Al; /* progressive JPEG successive approx. parms + (Al is the point transform value in lossless + mode) */ } jpeg_scan_info; /* The decompressor can save APPn and COM markers in a list of these: */ @@ -426,11 +444,13 @@ struct jpeg_compress_struct { int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ #endif - JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ - /* The coefficient controller receives data in units of MCU rows as defined - * for fully interleaved scans (whether the JPEG file is interleaved or not). - * There are v_samp_factor * DCTSIZE sample rows of each component in an - * "iMCU" (interleaved MCU) row. + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coefficient or + difference controller */ + /* The coefficient or difference controller receives data in units of MCU + * rows as defined for fully interleaved scans (whether the JPEG file is + * interleaved or not). In lossy mode, there are v_samp_factor * DCTSIZE + * sample rows of each component in an "iMCU" (interleaved MCU) row. In + * lossless mode, total_iMCU_rows is always equal to the image height. */ /* @@ -444,12 +464,13 @@ struct jpeg_compress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int blocks_in_MCU; /* # of DCT blocks per MCU */ + int blocks_in_MCU; /* # of data units per MCU */ int MCU_membership[C_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ #if JPEG_LIB_VERSION >= 80 int block_size; /* the basic DCT block size: 1..16 */ @@ -658,12 +679,13 @@ struct jpeg_decompress_struct { #endif JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ - /* The coefficient controller's input and output progress is measured in - * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows - * in fully interleaved JPEG scans, but are used whether the scan is - * interleaved or not. We define an iMCU row as v_samp_factor DCT block - * rows of each component. Therefore, the IDCT output contains + /* The coefficient or difference controller's input and output progress is + * measured in units of "iMCU" (interleaved MCU) rows. These are the same as + * MCU rows in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. In lossy mode, we define an iMCU row as v_samp_factor + * DCT block rows of each component. Therefore, the IDCT output contains * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. + * In lossless mode, total_iMCU_rows is always equal to the image height. */ JSAMPLE *sample_range_limit; /* table for fast range-limiting @@ -684,12 +706,13 @@ struct jpeg_decompress_struct { JDIMENSION MCUs_per_row; /* # of MCUs across the image */ JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ - int blocks_in_MCU; /* # of DCT blocks per MCU */ + int blocks_in_MCU; /* # of data units per MCU */ int MCU_membership[D_MAX_BLOCKS_IN_MCU]; /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ + /* i'th data unit in an MCU */ - int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + int Ss, Se, Ah, Al; /* progressive/lossless JPEG parameters for + scan */ #if JPEG_LIB_VERSION >= 80 /* These fields are derived from Se of first SOS marker. @@ -960,6 +983,9 @@ EXTERN(void) jpeg_add_quant_table(j_compress_ptr cinfo, int which_tbl, const unsigned int *basic_table, int scale_factor, boolean force_baseline); EXTERN(int) jpeg_quality_scaling(int quality); +EXTERN(void) jpeg_enable_lossless(j_compress_ptr cinfo, + int predictor_selection_value, + int point_transform); EXTERN(void) jpeg_simple_progression(j_compress_ptr cinfo); EXTERN(void) jpeg_suppress_tables(j_compress_ptr cinfo, boolean suppress); EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table(j_common_ptr cinfo); diff --git a/jsamplecomp.h b/jsamplecomp.h index fc8aff2e..2222766a 100644 --- a/jsamplecomp.h +++ b/jsamplecomp.h @@ -73,6 +73,10 @@ #define _jinit_color_converter j12init_color_converter #define _jinit_downsampler j12init_downsampler #define _jinit_forward_dct j12init_forward_dct +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_c_diff_controller j12init_c_diff_controller +#define _jinit_lossless_compressor j12init_lossless_compressor +#endif #define _jinit_d_main_controller j12init_d_main_controller #define _jinit_d_coef_controller j12init_d_coef_controller @@ -83,6 +87,10 @@ #define _jinit_1pass_quantizer j12init_1pass_quantizer #define _jinit_2pass_quantizer j12init_2pass_quantizer #define _jinit_merged_upsampler j12init_merged_upsampler +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_d_diff_controller j12init_d_diff_controller +#define _jinit_lossless_decompressor j12init_lossless_decompressor +#endif #define _jcopy_sample_rows j12copy_sample_rows @@ -180,6 +188,10 @@ #define _jinit_color_converter jinit_color_converter #define _jinit_downsampler jinit_downsampler #define _jinit_forward_dct jinit_forward_dct +#ifdef C_LOSSLESS_SUPPORTED +#define _jinit_c_diff_controller jinit_c_diff_controller +#define _jinit_lossless_compressor jinit_lossless_compressor +#endif #define _jinit_d_main_controller jinit_d_main_controller #define _jinit_d_coef_controller jinit_d_coef_controller @@ -190,6 +202,10 @@ #define _jinit_1pass_quantizer jinit_1pass_quantizer #define _jinit_2pass_quantizer jinit_2pass_quantizer #define _jinit_merged_upsampler jinit_merged_upsampler +#ifdef D_LOSSLESS_SUPPORTED +#define _jinit_d_diff_controller jinit_d_diff_controller +#define _jinit_lossless_decompressor jinit_lossless_decompressor +#endif #define _jcopy_sample_rows jcopy_sample_rows diff --git a/jversion.h.in b/jversion.h.in index dca4f08f..d1a8d989 100644 --- a/jversion.h.in +++ b/jversion.h.in @@ -48,6 +48,7 @@ "Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \ "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ + "Copyright (C) 1999 Ken Murchison\n" \ "Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding" #define JCOPYRIGHT_SHORT \ diff --git a/libjpeg.txt b/libjpeg.txt index 519113c0..f81037ee 100644 --- a/libjpeg.txt +++ b/libjpeg.txt @@ -2,6 +2,8 @@ USING THE IJG JPEG LIBRARY This file was part of the Independent JPEG Group's software: Copyright (C) 1994-2013, Thomas G. Lane, Guido Vollbeding. +Lossless JPEG Modifications: +Copyright (C) 1999, Ken Murchison. libjpeg-turbo Modifications: Copyright (C) 2010, 2014-2018, 2020, 2022, D. R. Commander. Copyright (C) 2015, Google, Inc. @@ -97,7 +99,6 @@ the ISO JPEG standard; most baseline, extended-sequential, and progressive JPEG processes are supported. (Our subset includes all features now in common use.) Unsupported ISO options include: * Hierarchical storage - * Lossless JPEG * DNL marker * Nonintegral subsampling ratios We support both 8- and 12-bit data precision. @@ -994,6 +995,29 @@ jpeg_simple_progression (j_compress_ptr cinfo) unless you want to make a custom scan sequence. You must ensure that the JPEG color space is set correctly before calling this routine. +jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value, + int point_transform) + Enables lossless mode with the specified predictor selection value + (1 - 7) and optional point transform (0 - {precision}-1, where + {precision} is the JPEG data precision). A point transform value of 0 + is necessary in order to create a fully lossless JPEG image. (A + non-zero point transform value right-shifts the input samples by the + specified number of bits, which is effectively a form of lossy color + quantization.) Note that the following features will be unavailable + when compressing or decompressing lossless JPEG images: + * Partial image decompression + * Quality/quantization table selection + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * Color conversion (the JPEG image will use the same color space as + the input image) + * IDCT scaling + * Raw (downsampled) data input/output + * Transcoding of DCT coefficients + Any parameters used to enable or configure those features will be + ignored. + Compression parameters (cinfo fields) include: @@ -1061,15 +1085,15 @@ boolean optimize_coding unsigned int restart_interval int restart_in_rows To emit restart markers in the JPEG file, set one of these nonzero. - Set restart_interval to specify the exact interval in MCU blocks. - Set restart_in_rows to specify the interval in MCU rows. (If - restart_in_rows is not 0, then restart_interval is set after the - image width in MCUs is computed.) Defaults are zero (no restarts). - One restart marker per MCU row is often a good choice. - NOTE: the overhead of restart markers is higher in grayscale JPEG - files than in color files, and MUCH higher in progressive JPEGs. - If you use restarts, you may want to use larger intervals in those - cases. + Set restart_interval to specify the exact interval in MCU blocks + (samples in lossless mode). Set restart_in_rows to specify the + interval in MCU rows. (If restart_in_rows is not 0, then + restart_interval is set after the image width in MCUs is computed.) + Defaults are zero (no restarts). One restart marker per MCU row is + often a good choice. NOTE: the overhead of restart markers is higher + in grayscale JPEG files than in color files, and MUCH higher in + progressive JPEGs. If you use restarts, you may want to use larger + intervals in those cases. const jpeg_scan_info *scan_info int num_scans diff --git a/structure.txt b/structure.txt index 22bed44e..e3c2bd40 100644 --- a/structure.txt +++ b/structure.txt @@ -2,8 +2,10 @@ IJG JPEG LIBRARY: SYSTEM ARCHITECTURE This file was part of the Independent JPEG Group's software: Copyright (C) 1991-2012, Thomas G. Lane, Guido Vollbeding. -It was modified by The libjpeg-turbo Project to include only information -relevant to libjpeg-turbo. +Lossless JPEG Modifications: +Copyright (C) 1999, Ken Murchison. +libjpeg-turbo Modifications: +Copyright (C) 2022, D. R. Commander. For conditions of distribution and use, see the accompanying README.ijg file. @@ -23,8 +25,10 @@ In this document, JPEG-specific terminology follows the JPEG standard: A "sample" is a single component value (i.e., one number in the image data). A "coefficient" is a frequency coefficient (a DCT transform output number). A "block" is an 8x8 group of samples or coefficients. - An "MCU" (minimum coded unit) is an interleaved set of blocks of size - determined by the sampling factors, or a single block in a + A "data unit" is an abstract data type that is either a block for lossy + (DCT-based) codecs or a sample for lossless (predictive) codecs. + An "MCU" (minimum coded unit) is an interleaved set of data units of size + determined by the sampling factors, or a single data unit in a noninterleaved scan. We do not use the terms "pixel" and "sample" interchangeably. When we say pixel, we mean an element of the full-size image, while a sample is an element @@ -45,13 +49,8 @@ command-line user interface and I/O routines for several uncompressed image formats. This document concentrates on the library itself. We desire the library to be capable of supporting all JPEG baseline, extended -sequential, and progressive DCT processes. Hierarchical processes are not -supported. - -The library does not support the lossless (spatial) JPEG process. Lossless -JPEG shares little or no code with lossy JPEG, and would normally be used -without the extensive pre- and post-processing provided by this library. -We feel that lossless JPEG is better handled by a separate library. +sequential, progressive DCT, and lossless (spatial) processes. Hierarchical +processes are not supported. Within these limits, any set of compression parameters allowed by the JPEG spec should be readable for decompression. (We can be more restrictive about @@ -100,9 +99,13 @@ elements: * Color space conversion (e.g., RGB to YCbCr). * Edge expansion and downsampling. Optionally, this step can do simple smoothing --- this is often helpful for low-quality source data. - JPEG proper: + Lossy JPEG proper: * MCU assembly, DCT, quantization. * Entropy coding (sequential or progressive, Huffman or arithmetic). + Lossless JPEG proper: + * Point transform. + * Prediction, differencing. + * Entropy coding (Huffman or arithmetic) In addition to these modules we need overall control, marker generation, and support code (memory management & error handling). There is also a @@ -112,9 +115,13 @@ do something else with the data. The decompressor library contains the following main elements: - JPEG proper: + Lossy JPEG proper: * Entropy decoding (sequential or progressive, Huffman or arithmetic). * Dequantization, inverse DCT, MCU disassembly. + Lossless JPEG proper: + * Entropy decoding (Huffman or arithmetic). + * Prediction, undifferencing. + * Point transform, sample size scaling. Postprocessing: * Upsampling. Optionally, this step may be able to do more general rescaling of the image. @@ -280,7 +287,8 @@ task performed by any one controller. *** Compression object structure *** -Here is a sketch of the logical structure of the JPEG compression library: +Here is a sketch of the logical structure of the JPEG compression library in +lossy mode: |-- Colorspace conversion |-- Preprocessing controller --| @@ -290,6 +298,19 @@ Main controller --| |-- Coefficient controller --| |-- Entropy encoding +... and in lossless mode: + + |-- Colorspace conversion + |-- Preprocessing controller --| + | |-- Downsampling +Main controller --| + | |-- Point transform + | | + |-- Difference controller --|-- Prediction, differencing + | + |-- Lossless mode entropy + encoding + This sketch also describes the flow of control (subroutine calls) during typical image data processing. Each of the components shown in the diagram is an "object" which may have several different implementations available. One @@ -343,6 +364,22 @@ The objects shown above are: during each pass, and the coder must emit the appropriate subset of coefficients. +* Difference controller: buffer controller for the spatial difference data. + When emitting a multiscan JPEG file, this controller is responsible for + buffering the full image. The equivalent of one fully interleaved MCU row + of subsampled data is processed per call, even when the JPEG file is + noninterleaved. + +* Point transform: Downscale the data by the point transform value. + +* Prediction and differencing: Calculate the predictor and subtract it + from the input. Works on one scanline per call. The difference + controller supplies the prior scanline, which is used for prediction. + +* Lossless mode entropy encoding: Perform Huffman or arithmetic entropy coding + and emit the coded data to the data destination module. This module handles + MCU assembly. Works on one MCU row per call. + In addition to the above objects, the compression library includes these objects: @@ -383,7 +420,8 @@ decompression; the progress monitor, if used, may be shared as well. *** Decompression object structure *** -Here is a sketch of the logical structure of the JPEG decompression library: +Here is a sketch of the logical structure of the JPEG decompression library in +lossy mode: |-- Entropy decoding |-- Coefficient controller --| @@ -394,6 +432,21 @@ Main controller --| |-- Color quantization |-- Color precision reduction +... and in lossless mode: + + |-- Lossless mode entropy + | decoding + | + |-- Difference controller --|-- Prediction, undifferencing + | | + | |-- Point transform, sample size + | scaling +Main controller --| + | |-- Upsampling + |-- Postprocessing controller --| |-- Colorspace conversion + |-- Color quantization + |-- Color precision reduction + As before, this diagram also represents typical control flow. The objects shown are: @@ -427,6 +480,22 @@ shown are: that emit fewer samples per DCT block, not the full 8x8. Works on one DCT block at a time. +* Difference controller: buffer controller for the spatial difference data. + When reading a multiscan JPEG file, this controller is responsible for + buffering the full image. The equivalent of one fully interleaved MCU row + is processed per call, even when the source JPEG file is noninterleaved. + +* Lossless mode entropy decoding: Read coded data from the data source module + and perform Huffman or arithmetic entropy decoding. Works on one MCU row per + call. + +* Prediction and undifferencing: Calculate the predictor and add it to the + decoded difference. Works on one scanline per call. The difference + controller supplies the prior scanline, which is used for prediction. + +* Point transform and sample size scaling: Upscale the data by the point + transform value and downscale it to fit into the compiled-in sample size. + * Postprocessing controller: buffer controller for the color quantization input buffer, when quantization is in use. (Without quantization, this controller just calls the upsampler.) For two-pass quantization, this diff --git a/usage.txt b/usage.txt index 9f510cd2..aaddb83a 100644 --- a/usage.txt +++ b/usage.txt @@ -163,6 +163,31 @@ file size is about the same --- often a little smaller. Switches for advanced users: + -lossless psv[,Pt] Create a lossless JPEG file using the specified + predictor selection value (1 - 7) and optional point + transform (0 - {precision}-1, where {precision} is the + JPEG data precision in bits). A point transform value + of 0 (the default) is necessary in order to create a + fully lossless JPEG file. (A non-zero point transform + value right-shifts the input samples by the specified + number of bits, which is effectively a form of lossy + color quantization.) CAUTION: lossless JPEG is not yet + widely implemented, so many decoders will be unable to + view a lossless JPEG file at all. Note that the + following features will be unavailable when compressing + or decompressing a lossless JPEG file: + * Quality/quantization table selection + * Color conversion (the JPEG image will use the same + color space as the input image) + * DCT/IDCT algorithm selection + * Smoothing + * Downsampling/upsampling + * IDCT scaling + * Partial image decompression + * Transformations using jpegtran + Any switches used to enable or configure those features + will be ignored. + -arithmetic Use arithmetic coding. CAUTION: arithmetic coded JPEG is not yet widely implemented, so many decoders will be unable to view an arithmetic coded JPEG file at @@ -207,8 +232,9 @@ Switches for advanced users: same results on all machines. -restart N Emit a JPEG restart marker every N MCU rows, or every - N MCU blocks if "B" is attached to the number. - -restart 0 (the default) means no restart markers. + N MCU blocks (samples in lossless mode) if "B" is + attached to the number. -restart 0 (the default) means + no restart markers. -smooth N Smooth the input image to eliminate dithering noise. N, ranging from 1 to 100, indicates the strength of diff --git a/win/jpeg62.def b/win/jpeg62.def index cdcce592..63e5ec85 100644 --- a/win/jpeg62.def +++ b/win/jpeg62.def @@ -128,3 +128,4 @@ EXPORTS jpeg12_write_scanlines @ 127 ; jpeg12_skip_scanlines @ 128 ; jpeg12_crop_scanline @ 129 ; + jpeg_enable_lossless @ 130 ; diff --git a/win/jpeg7.def b/win/jpeg7.def index 3b197757..0c269a64 100644 --- a/win/jpeg7.def +++ b/win/jpeg7.def @@ -130,3 +130,4 @@ EXPORTS jpeg12_write_scanlines @ 129 ; jpeg12_skip_scanlines @ 130 ; jpeg12_crop_scanline @ 131 ; + jpeg_enable_lossless @ 132 ; diff --git a/win/jpeg8.def b/win/jpeg8.def index 220e8c5f..2bb84300 100644 --- a/win/jpeg8.def +++ b/win/jpeg8.def @@ -131,3 +131,4 @@ EXPORTS jpeg12_write_scanlines @ 130 ; jpeg12_skip_scanlines @ 131 ; jpeg12_crop_scanline @ 132 ; + jpeg_enable_lossless @ 133 ; diff --git a/wizard.txt b/wizard.txt index 0e155f9e..a62fc1a5 100644 --- a/wizard.txt +++ b/wizard.txt @@ -116,23 +116,23 @@ Multiple Scan / Progression Control By default, cjpeg emits a single-scan sequential JPEG file. The -progressive switch generates a progressive JPEG file using a default series -of progression parameters. You can create multiple-scan sequential JPEG -files or progressive JPEG files with custom progression parameters by using -the -scans switch: +of progression parameters. You can create multiple-scan sequential or lossless +JPEG files or progressive JPEG files with custom progression parameters by +using the -scans switch: -scans file Use the scan sequence given in the named file. The specified file should be a text file containing a "scan script". The script specifies the contents and ordering of the scans to be emitted. Each entry in the script defines one scan. A scan definition specifies -the components to be included in the scan, and for progressive JPEG it also -specifies the progression parameters Ss,Se,Ah,Al for the scan. Scan -definitions are separated by semicolons (';'). A semicolon after the last -scan definition is optional. +the components to be included in the scan, and for progressive and lossless +JPEG it also specifies the progression/lossless parameters Ss,Se,Ah,Al for the +scan. Scan definitions are separated by semicolons (';'). A semicolon after +the last scan definition is optional. Each scan definition contains one to four component indexes, optionally -followed by a colon (':') and the four progressive-JPEG parameters. The -component indexes denote which color component(s) are to be transmitted in +followed by a colon (':') and the four progressive/lossless-JPEG parameters. +The component indexes denote which color component(s) are to be transmitted in the scan. Components are numbered in the order in which they appear in the JPEG SOF marker, with the first component being numbered 0. (Note that these indexes are not the "component ID" codes assigned to the components, just @@ -143,13 +143,24 @@ The progression parameters for each scan are: Se Zigzag index of last coefficient included in scan Ah Zero for first scan of a coefficient, else Al of prior scan Al Successive approximation low bit position for scan -If the progression parameters are omitted, the values 0,63,0,0 are used, -producing a sequential JPEG file. cjpeg automatically determines whether + +The lossless parameters for each scan are: + Ss Predictor selection value + Se Must be zero + Ah Must be zero + Al Point transform value + +If the progression/lossless parameters are omitted, the values 0,63,0,0 are +used, producing a sequential JPEG file. cjpeg automatically determines whether the script represents a progressive or sequential file, by observing whether -Ss and Se values other than 0 and 63 appear. (The -progressive switch is -not needed to specify this; in fact, it is ignored when -scans appears.) -The scan script must meet the JPEG restrictions on progression sequences. -(cjpeg checks that the spec's requirements are obeyed.) More specifically: +Ss and Se values other than 0 and 63 appear. cjpeg also automatically +determines whether the script represents a lossless file, by observing whether +Ss (the predictor selection value) is non-zero and Se is zero, which are +illegal values for progressive and sequential files. (The -progressive and +-lossless switches are not needed to specify this; in fact, they are ignored +when -scans appears.) The scan script must meet the JPEG restrictions on +progression sequences. (cjpeg checks that the spec's requirements are obeyed.) +More specifically: * An AC scan cannot include coefficients from more than one component.