Commit 3044c6c2f for imagemagick.org
commit 3044c6c2f0d253b22f786dcba66844e8f4934339
Author: DRC <information@virtualgl.org>
Date: Sun Nov 16 11:53:20 2025 -0500
JPEG: Support 2-to-16-bit lossless JPEG images with libjpeg-turbo 3.1+; Support additional IDCT scaling factors with libjpeg-turbo and libjpeg v7+ (#8445)
* Support additional JPEG scaling factors w/ libjpeg-turbo or libjpeg v7+
* Support 12-bit lossy and 2-bit to 16-bit lossless JPEG images
- If compiled with libjpeg-turbo 3.1.x or later and lossless JPEG
compression is selected, directly encode pixel data with 2 to 16 bits
per channel into a lossless JPEG image with the same data precision,
rather than upconverting to an 8-bit-per-sample, 12-bit-per-sample, or
16-bit-per-sample lossless JPEG image. (If compiled with
libjpeg-turbo 3.0.x, upconverting is still performed.)
- If compiled with libjpeg-turbo 3.1.x or later, directly decode
lossless JPEG images with 2 to 16 bits per sample into pixel data with
the same data precision, rather than upconverting to
8-bit-per-channel, 12-bit-per-channel, or 16-bit-per-channel pixel
data.
- If compiled with libjpeg-turbo 3.0.x or later and lossy JPEG
compression is selected, upconvert or downconvert pixel data with 9 to
16 bits per channel into a 12-bit-per-sample lossy JPEG image.
- If compiled with libjpeg-turbo 2.1.x or earlier (or libjpeg),
upconvert or downconvert pixel data with 2 to 16 bits per channel into
an 8-bit-per-sample lossy JPEG image.
diff --git a/coders/jpeg.c b/coders/jpeg.c
index bd20cd2b9..f917f3e64 100644
--- a/coders/jpeg.c
+++ b/coders/jpeg.c
@@ -256,12 +256,23 @@ static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
%
*/
-static inline int JPEGGetSample(const struct jpeg_decompress_struct *jpeg_info,
- const JSAMPLE *p)
+static inline Quantum JPEGGetQuantum(
+ const struct jpeg_decompress_struct *jpeg_info,const JSAMPLE *p)
{
- if (jpeg_info->data_precision > 8)
- return((int) (*(unsigned short *) p));
- return((int) (*(JSAMPLE *) p));
+ if (jpeg_info->data_precision == 8)
+ return ScaleCharToQuantum(*p);
+ else if (jpeg_info->data_precision == 16)
+ return ScaleShortToQuantum(*(unsigned short *) p);
+ else if (jpeg_info->data_precision < 8)
+ {
+ QuantumAny range=GetQuantumRange(jpeg_info->data_precision);
+ return ScaleAnyToQuantum(*p,range);
+ }
+ else
+ {
+ QuantumAny range=GetQuantumRange(jpeg_info->data_precision);
+ return ScaleAnyToQuantum(*(unsigned short *) p,range);
+ }
}
static boolean FillInputBuffer(j_decompress_ptr compress_info)
@@ -1093,7 +1104,6 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
ssize_t
i,
- scale,
y;
struct jpeg_error_mgr
@@ -1224,8 +1234,17 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
if ((geometry_info.sigma != 0.0) &&
(scale_factor > (jpeg_info->output_height/geometry_info.sigma)))
scale_factor=jpeg_info->output_height/geometry_info.sigma;
+#if defined(LIBJPEG_TURBO_VERSION_NUMBER) || (JPEG_LIB_VERSION >= 70)
+ jpeg_info->scale_num=(unsigned int) (8.0/scale_factor+0.5);
+ if (jpeg_info->scale_num > 16U)
+ jpeg_info->scale_num=16U;
+ if (jpeg_info->scale_num < 1U)
+ jpeg_info->scale_num=1U;
+ jpeg_info->scale_denom=8U;
+#else
jpeg_info->scale_num=1U;
jpeg_info->scale_denom=(unsigned int) scale_factor;
+#endif
jpeg_calc_output_dimensions(jpeg_info);
if (image->debug != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
@@ -1447,8 +1466,6 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
}
}
- scale=65535U/(unsigned int) GetQuantumRange((size_t)
- jpeg_info->data_precision);
for (y=0; y < (ssize_t) image->rows; y++)
{
JDIMENSION
@@ -1463,31 +1480,25 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
/*
Uncompress one JPEG scanline.
*/
- switch (jpeg_info->data_precision)
- {
- case 16:
+ if (jpeg_info->data_precision > 12)
{
#if defined(MAGICKCORE_HAVE_JPEG16_READ_SCANLINES)
number_scanlines=jpeg16_read_scanlines(jpeg_info,(J16SAMPROW *)
&jpeg_pixels,1);
- break;
#endif
}
- case 12:
+ else if (jpeg_info->data_precision > 8)
{
#if defined(MAGICKCORE_HAVE_JPEG12_READ_SCANLINES)
number_scanlines=jpeg12_read_scanlines(jpeg_info,(J12SAMPROW *)
&jpeg_pixels,1);
- break;
#endif
}
- default:
+ else
{
number_scanlines=jpeg_read_scanlines(jpeg_info,(JSAMPROW *)
&jpeg_pixels,1);
- break;
}
- }
if (number_scanlines != 1)
(void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
"AnErrorHasOccurredReadingFromFile","`%s'",image->filename);
@@ -1510,8 +1521,12 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
Quantum
index;
- unsigned short
- pixel = (unsigned short) JPEGGetSample(jpeg_info,p);
+ unsigned short pixel;
+
+ if (jpeg_info->data_precision > 8)
+ pixel=(*(unsigned short *) p);
+ else
+ pixel=(*(JSAMPLE *) p);
index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
@@ -1528,17 +1543,13 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
*/
for (x=0; x < (ssize_t) image->columns; x++)
{
- SetPixelCyan(image,QuantumRange-ScaleShortToQuantum((unsigned short)
- (scale*JPEGGetSample(jpeg_info,p))),q);
+ SetPixelCyan(image,QuantumRange-JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
- SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
- (unsigned short) (scale*JPEGGetSample(jpeg_info,p))),q);
+ SetPixelMagenta(image,QuantumRange-JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
- SetPixelYellow(image,QuantumRange-ScaleShortToQuantum((unsigned short)
- (scale*JPEGGetSample(jpeg_info,p))),q);
+ SetPixelYellow(image,QuantumRange-JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
- SetPixelBlack(image,QuantumRange-ScaleShortToQuantum((unsigned short)
- (scale*JPEGGetSample(jpeg_info,p))),q);
+ SetPixelBlack(image,QuantumRange-JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
SetPixelAlpha(image,OpaqueAlpha,q);
q+=(ptrdiff_t) GetPixelChannels(image);
@@ -1552,14 +1563,11 @@ static Image *ReadOneJPEGImage(const ImageInfo *image_info,
*/
for (x=0; x < (ssize_t) image->columns; x++)
{
- SetPixelRed(image,ScaleShortToQuantum((unsigned short) (scale*
- JPEGGetSample(jpeg_info,p))),q);
+ SetPixelRed(image,JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
- SetPixelGreen(image,ScaleShortToQuantum((unsigned short) (scale*
- JPEGGetSample(jpeg_info,p))),q);
+ SetPixelGreen(image,JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
- SetPixelBlue(image,ScaleShortToQuantum((unsigned short) (scale*
- JPEGGetSample(jpeg_info,p))),q);
+ SetPixelBlue(image,JPEGGetQuantum(jpeg_info,p),q);
p+=(ptrdiff_t) bytes_per_pixel;
SetPixelAlpha(image,OpaqueAlpha,q);
q+=(ptrdiff_t) GetPixelChannels(image);
@@ -2351,11 +2359,22 @@ static char **SamplingFactorToList(const char *text)
}
static inline void JPEGSetSample(const struct jpeg_compress_struct *jpeg_info,
- const unsigned int scale,const Quantum pixel,JSAMPLE *q)
+ const Quantum pixel,JSAMPLE *q)
{
- if (jpeg_info->data_precision > 8)
- (*(unsigned short *) q)=(unsigned short) (ScaleQuantumToShort(pixel)/scale);
- *q=(JSAMPLE) ScaleQuantumToChar(pixel);
+ if (jpeg_info->data_precision == 8)
+ *q=(JSAMPLE) ScaleQuantumToChar(pixel);
+ else if (jpeg_info->data_precision == 16)
+ (*(unsigned short *) q)=(unsigned short) ScaleQuantumToShort(pixel);
+ else if (jpeg_info->data_precision < 8)
+ {
+ QuantumAny range=GetQuantumRange(jpeg_info->data_precision);
+ *q=(JSAMPLE) ScaleQuantumToAny(pixel,range);
+ }
+ else
+ {
+ QuantumAny range=GetQuantumRange(jpeg_info->data_precision);
+ (*(unsigned short *) q)=(unsigned short) ScaleQuantumToAny(pixel,range);
+ }
}
static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
@@ -2405,9 +2424,6 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
struct jpeg_error_mgr
jpeg_error;
- unsigned int
- scale;
-
/*
Open image file.
*/
@@ -2473,12 +2489,23 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
#if defined(C_LOSSLESS_SUPPORTED)
if (image_info->compression == LosslessJPEGCompression)
{
+#if defined(LIBJPEG_TURBO_VERSION_NUMBER) && LIBJPEG_TURBO_VERSION_NUMBER >= 3000090
+ jpeg_info->data_precision=(int) image->depth;
+#else
if (image->depth > 12)
jpeg_info->data_precision=16;
else if (image->depth > 8)
jpeg_info->data_precision=12;
+#endif
}
+ else
+#endif
+ {
+#if defined(MAGICKCORE_HAVE_JPEG12_WRITE_SCANLINES)
+ if (image->depth > 8)
+ jpeg_info->data_precision=12;
#endif
+ }
jpeg_info->in_color_space=JCS_RGB;
switch (image->colorspace)
{
@@ -2990,8 +3017,6 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
jps_image=DestroyImage(jps_image);
return(MagickFalse);
}
- scale=65535U/(unsigned short) GetQuantumRange((size_t)
- jpeg_info->data_precision);
for (y=0; y < (ssize_t) image->rows; y++)
{
const Quantum
@@ -3019,7 +3044,7 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
*/
for (x=0; x < (ssize_t) image->columns; x++)
{
- JPEGSetSample(jpeg_info,scale,ClampToQuantum(GetPixelLuma(image,p)),
+ JPEGSetSample(jpeg_info,ClampToQuantum(GetPixelLuma(image,p)),
q);
q+=(ptrdiff_t) bytes_per_pixel;
p+=(ptrdiff_t) GetPixelChannels(image);
@@ -3036,16 +3061,16 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
/*
Convert DirectClass packets to contiguous CMYK scanlines.
*/
- JPEGSetSample(jpeg_info,scale,(Quantum) ((QuantumRange-
+ JPEGSetSample(jpeg_info,(Quantum) ((QuantumRange-
GetPixelCyan(image,p))),q);
q+=(ptrdiff_t) bytes_per_pixel;
- JPEGSetSample(jpeg_info,scale,(Quantum) ((QuantumRange-
+ JPEGSetSample(jpeg_info,(Quantum) ((QuantumRange-
GetPixelMagenta(image,p))),q);
q+=(ptrdiff_t) bytes_per_pixel;
- JPEGSetSample(jpeg_info,scale,(Quantum) ((QuantumRange-
+ JPEGSetSample(jpeg_info,(Quantum) ((QuantumRange-
GetPixelYellow(image,p))),q);
q+=(ptrdiff_t) bytes_per_pixel;
- JPEGSetSample(jpeg_info,scale,(Quantum) ((QuantumRange-
+ JPEGSetSample(jpeg_info,(Quantum) ((QuantumRange-
GetPixelBlack(image,p))),q);
q+=(ptrdiff_t) bytes_per_pixel;
p+=(ptrdiff_t) GetPixelChannels(image);
@@ -3059,11 +3084,11 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
*/
for (x=0; x < (ssize_t) image->columns; x++)
{
- JPEGSetSample(jpeg_info,scale,GetPixelRed(image,p),q);
+ JPEGSetSample(jpeg_info,GetPixelRed(image,p),q);
q+=(ptrdiff_t) bytes_per_pixel;
- JPEGSetSample(jpeg_info,scale,GetPixelGreen(image,p),q);
+ JPEGSetSample(jpeg_info,GetPixelGreen(image,p),q);
q+=(ptrdiff_t) bytes_per_pixel;
- JPEGSetSample(jpeg_info,scale,GetPixelBlue(image,p),q);
+ JPEGSetSample(jpeg_info,GetPixelBlue(image,p),q);
q+=(ptrdiff_t) bytes_per_pixel;
p+=(ptrdiff_t) GetPixelChannels(image);
}
@@ -3073,31 +3098,25 @@ static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
/*
Compress one JPEG scanline.
*/
- switch (jpeg_info->data_precision)
- {
- case 16:
+ if (jpeg_info->data_precision > 12)
{
#if defined(MAGICKCORE_HAVE_JPEG16_WRITE_SCANLINES)
number_scanlines=jpeg16_write_scanlines(jpeg_info,(J16SAMPROW *)
&jpeg_pixels,1);
- break;
#endif
}
- case 12:
+ else if (jpeg_info->data_precision > 8)
{
#if defined(MAGICKCORE_HAVE_JPEG12_WRITE_SCANLINES)
number_scanlines=jpeg12_write_scanlines(jpeg_info,(J12SAMPROW *)
&jpeg_pixels,1);
- break;
#endif
}
- default:
+ else
{
number_scanlines=jpeg_write_scanlines(jpeg_info,(JSAMPROW *)
&jpeg_pixels,1);
- break;
}
- }
if (number_scanlines != 1)
(void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
"AnErrorHasOccurredWritingToFile","`%s'",image->filename);