Merge branch 'ijg.lossless' into dev

Lossless: Accommodate LJT colorspace/SIMD exts

In libjpeg-turbo, grayscale_convert() and null_convert() aren't the only
lossless color conversion algorithms.  We can also losslessly convert
RGB to and from any of the extended RGB colorspaces, and some platforms
have SIMD-accelerated null color conversion.

This commit also disallows RGB565 output in lossless mode, and it moves
the IsExtRGB() macro from cdjpeg.h to jpegint.h and repurposes it to
make jinit_color_converter() and jinit_color_deconverter() more
readable.
This commit is contained in:
DRC
2022-11-16 12:18:45 -06:00
7 changed files with 44 additions and 63 deletions

View File

@@ -164,6 +164,3 @@ EXTERN(FILE *) write_stdout(void);
#ifndef EXIT_WARNING #ifndef EXIT_WARNING
#define EXIT_WARNING 2 #define EXIT_WARNING 2
#endif #endif
#define IsExtRGB(cs) \
(cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB))

View File

@@ -1,4 +1,4 @@
.TH CJPEG 1 "14 November 2022" .TH CJPEG 1 "16 November 2022"
.SH NAME .SH NAME
cjpeg \- compress an image file to a JPEG file cjpeg \- compress an image file to a JPEG file
.SH SYNOPSIS .SH SYNOPSIS
@@ -176,8 +176,8 @@ unavailable when compressing or decompressing a lossless JPEG file:
.IP .IP
- Quality/quantization table selection - Quality/quantization table selection
.IP .IP
- Color conversion (the JPEG image will use the same color space as the input - Color space conversion (the JPEG image will use the same color space as the
image) input image)
.IP .IP
- DCT/IDCT algorithm selection - DCT/IDCT algorithm selection
.IP .IP

View File

@@ -577,24 +577,20 @@ _jinit_color_converter(j_compress_ptr cinfo)
break; break;
} }
/* Check num_components, set conversion method based on requested space */ /* Check num_components, set conversion method based on requested space.
* NOTE: We do not allow any lossy color conversion algorithms in lossless
* mode.
*/
switch (cinfo->jpeg_color_space) { switch (cinfo->jpeg_color_space) {
case JCS_GRAYSCALE: case JCS_GRAYSCALE:
if (cinfo->master->lossless &&
cinfo->in_color_space != cinfo->jpeg_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
if (cinfo->num_components != 1) if (cinfo->num_components != 1)
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
if (cinfo->in_color_space == JCS_GRAYSCALE) if (cinfo->in_color_space == JCS_GRAYSCALE)
cconvert->pub._color_convert = grayscale_convert; cconvert->pub._color_convert = grayscale_convert;
else if (cinfo->in_color_space == JCS_RGB || else if (IsExtRGB(cinfo->in_color_space)) {
cinfo->in_color_space == JCS_EXT_RGB ||
cinfo->in_color_space == JCS_EXT_RGBX ||
cinfo->in_color_space == JCS_EXT_BGR ||
cinfo->in_color_space == JCS_EXT_BGRX ||
cinfo->in_color_space == JCS_EXT_XBGR ||
cinfo->in_color_space == JCS_EXT_XRGB ||
cinfo->in_color_space == JCS_EXT_RGBA ||
cinfo->in_color_space == JCS_EXT_BGRA ||
cinfo->in_color_space == JCS_EXT_ABGR ||
cinfo->in_color_space == JCS_EXT_ARGB) {
#ifdef WITH_SIMD #ifdef WITH_SIMD
if (jsimd_can_rgb_gray()) if (jsimd_can_rgb_gray())
cconvert->pub._color_convert = jsimd_rgb_gray_convert; cconvert->pub._color_convert = jsimd_rgb_gray_convert;
@@ -611,6 +607,8 @@ _jinit_color_converter(j_compress_ptr cinfo)
break; break;
case JCS_RGB: case JCS_RGB:
if (cinfo->master->lossless && !IsExtRGB(cinfo->in_color_space))
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
if (cinfo->num_components != 3) if (cinfo->num_components != 3)
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
if (rgb_red[cinfo->in_color_space] == 0 && if (rgb_red[cinfo->in_color_space] == 0 &&
@@ -623,36 +621,19 @@ _jinit_color_converter(j_compress_ptr cinfo)
else else
#endif #endif
cconvert->pub._color_convert = null_convert; cconvert->pub._color_convert = null_convert;
} else if (cinfo->in_color_space == JCS_RGB || } else if (IsExtRGB(cinfo->in_color_space))
cinfo->in_color_space == JCS_EXT_RGB ||
cinfo->in_color_space == JCS_EXT_RGBX ||
cinfo->in_color_space == JCS_EXT_BGR ||
cinfo->in_color_space == JCS_EXT_BGRX ||
cinfo->in_color_space == JCS_EXT_XBGR ||
cinfo->in_color_space == JCS_EXT_XRGB ||
cinfo->in_color_space == JCS_EXT_RGBA ||
cinfo->in_color_space == JCS_EXT_BGRA ||
cinfo->in_color_space == JCS_EXT_ABGR ||
cinfo->in_color_space == JCS_EXT_ARGB)
cconvert->pub._color_convert = rgb_rgb_convert; cconvert->pub._color_convert = rgb_rgb_convert;
else else
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
break; break;
case JCS_YCbCr: case JCS_YCbCr:
if (cinfo->master->lossless &&
cinfo->in_color_space != cinfo->jpeg_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
if (cinfo->num_components != 3) if (cinfo->num_components != 3)
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
if (cinfo->in_color_space == JCS_RGB || if (IsExtRGB(cinfo->in_color_space)) {
cinfo->in_color_space == JCS_EXT_RGB ||
cinfo->in_color_space == JCS_EXT_RGBX ||
cinfo->in_color_space == JCS_EXT_BGR ||
cinfo->in_color_space == JCS_EXT_BGRX ||
cinfo->in_color_space == JCS_EXT_XBGR ||
cinfo->in_color_space == JCS_EXT_XRGB ||
cinfo->in_color_space == JCS_EXT_RGBA ||
cinfo->in_color_space == JCS_EXT_BGRA ||
cinfo->in_color_space == JCS_EXT_ABGR ||
cinfo->in_color_space == JCS_EXT_ARGB) {
#ifdef WITH_SIMD #ifdef WITH_SIMD
if (jsimd_can_rgb_ycc()) if (jsimd_can_rgb_ycc())
cconvert->pub._color_convert = jsimd_rgb_ycc_convert; cconvert->pub._color_convert = jsimd_rgb_ycc_convert;
@@ -674,6 +655,9 @@ _jinit_color_converter(j_compress_ptr cinfo)
break; break;
case JCS_CMYK: case JCS_CMYK:
if (cinfo->master->lossless &&
cinfo->in_color_space != cinfo->jpeg_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
if (cinfo->num_components != 4) if (cinfo->num_components != 4)
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
if (cinfo->in_color_space == JCS_CMYK) { if (cinfo->in_color_space == JCS_CMYK) {
@@ -688,6 +672,9 @@ _jinit_color_converter(j_compress_ptr cinfo)
break; break;
case JCS_YCCK: case JCS_YCCK:
if (cinfo->master->lossless &&
cinfo->in_color_space != cinfo->jpeg_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
if (cinfo->num_components != 4) if (cinfo->num_components != 4)
ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); ERREXIT(cinfo, JERR_BAD_J_COLORSPACE);
if (cinfo->in_color_space == JCS_CMYK) { if (cinfo->in_color_space == JCS_CMYK) {
@@ -716,13 +703,4 @@ _jinit_color_converter(j_compress_ptr cinfo)
cconvert->pub._color_convert = null_convert; cconvert->pub._color_convert = null_convert;
break; 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);
}
} }

View File

@@ -776,10 +776,15 @@ _jinit_color_deconverter(j_decompress_ptr cinfo)
/* Set out_color_components and conversion method based on requested space. /* Set out_color_components and conversion method based on requested space.
* Also clear the component_needed flags for any unused components, * Also clear the component_needed flags for any unused components,
* so that earlier pipeline stages can avoid useless computation. * so that earlier pipeline stages can avoid useless computation.
* NOTE: We do not allow any lossy color conversion algorithms in lossless
* mode.
*/ */
switch (cinfo->out_color_space) { switch (cinfo->out_color_space) {
case JCS_GRAYSCALE: case JCS_GRAYSCALE:
if (cinfo->master->lossless &&
cinfo->jpeg_color_space != cinfo->out_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
cinfo->out_color_components = 1; cinfo->out_color_components = 1;
if (cinfo->jpeg_color_space == JCS_GRAYSCALE || if (cinfo->jpeg_color_space == JCS_GRAYSCALE ||
cinfo->jpeg_color_space == JCS_YCbCr) { cinfo->jpeg_color_space == JCS_YCbCr) {
@@ -805,6 +810,8 @@ _jinit_color_deconverter(j_decompress_ptr cinfo)
case JCS_EXT_BGRA: case JCS_EXT_BGRA:
case JCS_EXT_ABGR: case JCS_EXT_ABGR:
case JCS_EXT_ARGB: case JCS_EXT_ARGB:
if (cinfo->master->lossless && cinfo->jpeg_color_space != JCS_RGB)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space];
if (cinfo->jpeg_color_space == JCS_YCbCr) { if (cinfo->jpeg_color_space == JCS_YCbCr) {
#ifdef WITH_SIMD #ifdef WITH_SIMD
@@ -831,6 +838,8 @@ _jinit_color_deconverter(j_decompress_ptr cinfo)
break; break;
case JCS_RGB565: case JCS_RGB565:
if (cinfo->master->lossless)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
cinfo->out_color_components = 3; cinfo->out_color_components = 3;
if (cinfo->dither_mode == JDITHER_NONE) { if (cinfo->dither_mode == JDITHER_NONE) {
if (cinfo->jpeg_color_space == JCS_YCbCr) { if (cinfo->jpeg_color_space == JCS_YCbCr) {
@@ -864,6 +873,9 @@ _jinit_color_deconverter(j_decompress_ptr cinfo)
break; break;
case JCS_CMYK: case JCS_CMYK:
if (cinfo->master->lossless &&
cinfo->jpeg_color_space != cinfo->out_color_space)
ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL);
cinfo->out_color_components = 4; cinfo->out_color_components = 4;
if (cinfo->jpeg_color_space == JCS_YCCK) { if (cinfo->jpeg_color_space == JCS_YCCK) {
cconvert->pub._color_convert = ycck_cmyk_convert; cconvert->pub._color_convert = ycck_cmyk_convert;
@@ -884,15 +896,6 @@ _jinit_color_deconverter(j_decompress_ptr cinfo)
break; 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) if (cinfo->quantize_colors)
cinfo->output_components = 1; /* single colormapped output component */ cinfo->output_components = 1; /* single colormapped output component */
else else

View File

@@ -7,7 +7,7 @@
* Lossless JPEG Modifications: * Lossless JPEG Modifications:
* Copyright (C) 1999, Ken Murchison. * Copyright (C) 1999, Ken Murchison.
* libjpeg-turbo Modifications: * libjpeg-turbo Modifications:
* Copyright (C) 2015-2016, 2019, 2021-2022, D. R. Commander. * Copyright (C) 2015-2017, 2019, 2021-2022, D. R. Commander.
* Copyright (C) 2015, Google, Inc. * Copyright (C) 2015, Google, Inc.
* Copyright (C) 2021, Alex Richardson. * Copyright (C) 2021, Alex Richardson.
* For conditions of distribution and use, see the accompanying README.ijg * For conditions of distribution and use, see the accompanying README.ijg
@@ -74,6 +74,9 @@ typedef __UINTPTR_TYPE__ JUINTPTR;
typedef size_t JUINTPTR; typedef size_t JUINTPTR;
#endif #endif
#define IsExtRGB(cs) \
(cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB))
/* /*
* Left shift macro that handles a negative operand without causing any * Left shift macro that handles a negative operand without causing any
* sanitizer warnings * sanitizer warnings

View File

@@ -1010,8 +1010,8 @@ jpeg_enable_lossless (j_compress_ptr cinfo, int predictor_selection_value,
* DCT/IDCT algorithm selection * DCT/IDCT algorithm selection
* Smoothing * Smoothing
* Downsampling/upsampling * Downsampling/upsampling
* Color conversion (the JPEG image will use the same color space as * Color space conversion (the JPEG image will use the same color
the input image) space as the input image)
* IDCT scaling * IDCT scaling
* Raw (downsampled) data input/output * Raw (downsampled) data input/output
* Transcoding of DCT coefficients * Transcoding of DCT coefficients

View File

@@ -177,8 +177,8 @@ Switches for advanced users:
following features will be unavailable when compressing following features will be unavailable when compressing
or decompressing a lossless JPEG file: or decompressing a lossless JPEG file:
* Quality/quantization table selection * Quality/quantization table selection
* Color conversion (the JPEG image will use the same * Color space conversion (the JPEG image will use the
color space as the input image) same color space as the input image)
* DCT/IDCT algorithm selection * DCT/IDCT algorithm selection
* Smoothing * Smoothing
* Downsampling/upsampling * Downsampling/upsampling