tjLoadImage(): Don't convert RGB to grayscale

Loading RGB image files into a grayscale buffer isn't a particularly
useful feature, given that libjpeg-turbo can perform this conversion
much more optimally (with SIMD acceleration on some platforms) during
the compression process.  Also, the RGB2GRAY() macro was not producing
deterministic cross-platform results because of variations in the
round-off behavior of various floating point implementations, so
`tjunittest -bmp` was failing in i386 builds.
This commit is contained in:
DRC
2017-11-18 11:33:05 -06:00
parent 8c40ac8ae6
commit 479fa1d870
6 changed files with 61 additions and 138 deletions

View File

@@ -154,9 +154,5 @@ EXTERN(FILE *) write_stdout (void);
#define EXIT_WARNING 2
#endif
#define RGB2GRAY(r, g, b) \
(JSAMPLE)((double)(r) * 0.299 + (double)(g) * 0.587 + \
(double)(b) * 0.114 + 0.5)
#define IsExtRGB(cs) \
(cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB))

View File

@@ -2274,7 +2274,13 @@ If you choose option 1, <code>*jpegSize</code> should be set to the size of your
<tr><td class="paramname">width</td><td>pointer to an integer variable that will receive the width (in pixels) of the uncompressed image</td></tr>
<tr><td class="paramname">align</td><td>row alignment of the image buffer to be returned (must be a power of 2.) For instance, setting this parameter to 4 will cause all rows in the image buffer to be padded to the nearest 32-bit boundary, and setting this parameter to 1 will cause all rows in the image buffer to be unpadded.</td></tr>
<tr><td class="paramname">height</td><td>pointer to an integer variable that will receive the height (in pixels) of the uncompressed image</td></tr>
<tr><td class="paramname">pixelFormat</td><td>pointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. If <code>*pixelFormat</code> is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed">TJPF_UNKNOWN</a> prior to calling this function, then the uncompressed image buffer returned by the function will use the most optimal pixel format for the file type, and <code>*pixelFormat</code> will contain the ID of this pixel format upon successful return from the function. Otherwise, the uncompressed image buffer will use the <a class="el" href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">pixel format</a> specified in <code>*pixelFormat</code>, and pixel format conversion will be performed if necessary. If <code>*pixelFormat</code> is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a>, then the RGB or grayscale pixels stored in the file will be converted using a quick &amp; dirty algorithm that is suitable only for testing purposes (proper conversion between CMYK and other formats requires a color management system.)</td></tr>
<tr><td class="paramname">pixelFormat</td><td>pointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. The behavior of <a class="el" href="group___turbo_j_p_e_g.html#ga144b981d6b281ecca4cbb4709de75749" title="Load an uncompressed image from disk into memory.">tjLoadImage()</a> will vary depending on the value of <code>*pixelFormat</code> passed to the function:<ul>
<li><a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed">TJPF_UNKNOWN</a> : The uncompressed image buffer returned by the function will use the most optimal pixel format for the file type, and <code>*pixelFormat</code> will contain the ID of this pixel format upon successful return from the function.</li>
<li><a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a">TJPF_GRAY</a> : Only PGM files and 8-bit BMP files with a grayscale colormap can be loaded.</li>
<li><a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a> : The RGB or grayscale pixels stored in the file will be converted using a quick &amp; dirty algorithm that is suitable only for testing purposes (proper conversion between CMYK and other formats requires a color management system.)</li>
<li>Other <a class="el" href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">pixel formats</a> : The uncompressed image buffer will use the specified pixel format, and pixel format conversion will be performed if necessary.</li>
</ul>
</td></tr>
<tr><td class="paramname">flags</td><td>the bitwise OR of one or more of the <a class="el" href="group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec">flags</a>.</td></tr>
</table>
</dd>

54
rdbmp.c
View File

@@ -102,7 +102,7 @@ LOCAL(void)
read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
/* Read the colormap from a BMP file */
{
int i;
int i, gray = 1;
switch (mapentrysize) {
case 3:
@@ -111,6 +111,9 @@ read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
sinfo->colormap[1][i] != sinfo->colormap[0][i])
gray = 0;
}
break;
case 4:
@@ -120,12 +123,18 @@ read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
(void) read_byte(sinfo);
if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
sinfo->colormap[1][i] != sinfo->colormap[0][i])
gray = 0;
}
break;
default:
ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
break;
}
if (sinfo->cinfo->in_color_space == JCS_GRAYSCALE && !gray)
ERREXIT(sinfo->cinfo, JERR_BAD_IN_COLORSPACE);
}
@@ -165,7 +174,7 @@ get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
t = GETJSAMPLE(*inptr++);
*outptr++ = RGB2GRAY(colormap[0][t], colormap[1][t], colormap[2][t]);
*outptr++ = colormap[0][t];
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
@@ -233,12 +242,6 @@ get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
outptr = source->pub.buffer[0];
if (cinfo->in_color_space == JCS_EXT_BGR) {
MEMCOPY(outptr, inptr, source->row_width);
} else if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
*outptr++ = RGB2GRAY(r, g, b);
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
@@ -304,12 +307,6 @@ get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
if (cinfo->in_color_space == JCS_EXT_BGRX ||
cinfo->in_color_space == JCS_EXT_BGRA) {
MEMCOPY(outptr, inptr, source->row_width);
} else if (cinfo->in_color_space == JCS_GRAYSCALE) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
*outptr++ = RGB2GRAY(r, g, b);
}
} else if (cinfo->in_color_space == JCS_CMYK) {
for (col = cinfo->image_width; col > 0; col--) {
/* can omit GETJSAMPLE() safely */
@@ -554,16 +551,36 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
case 8:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_RGB;
if (IsExtRGB(cinfo->in_color_space))
cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
else if (cinfo->in_color_space == JCS_GRAYSCALE)
cinfo->input_components = 1;
else if (cinfo->in_color_space == JCS_CMYK)
cinfo->input_components = 4;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
row_width = (JDIMENSION) biWidth;
break;
case 24:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_BGR;
if (IsExtRGB(cinfo->in_color_space))
cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
else if (cinfo->in_color_space == JCS_CMYK)
cinfo->input_components = 4;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
row_width = (JDIMENSION) (biWidth * 3);
break;
case 32:
if (cinfo->in_color_space == JCS_UNKNOWN)
cinfo->in_color_space = JCS_EXT_BGRA;
if (IsExtRGB(cinfo->in_color_space))
cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
else if (cinfo->in_color_space == JCS_CMYK)
cinfo->input_components = 4;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
row_width = (JDIMENSION) (biWidth * 4);
break;
default:
@@ -601,15 +618,6 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
}
}
if (IsExtRGB(cinfo->in_color_space))
cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
else if (cinfo->in_color_space == JCS_GRAYSCALE)
cinfo->input_components = 1;
else if (cinfo->in_color_space == JCS_CMYK)
cinfo->input_components = 4;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
/* Allocate one-row buffer for returned data */
source->pub.buffer = (*cinfo->mem->alloc_sarray)
((j_common_ptr) cinfo, JPOOL_IMAGE,

71
rdppm.c
View File

@@ -277,38 +277,6 @@ get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
}
METHODDEF(JDIMENSION)
get_text_rgb_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading text-format PPM files with any maxval and
converting to grayscale */
{
ppm_source_ptr source = (ppm_source_ptr) sinfo;
FILE *infile = source->pub.input_file;
register JSAMPROW ptr;
register JSAMPLE *rescale = source->rescale;
JDIMENSION col;
unsigned int maxval = source->maxval;
ptr = source->pub.buffer[0];
if (maxval == MAXJSAMPLE) {
for (col = cinfo->image_width; col > 0; col--) {
JSAMPLE r = read_pbm_integer(cinfo, infile, maxval);
JSAMPLE g = read_pbm_integer(cinfo, infile, maxval);
JSAMPLE b = read_pbm_integer(cinfo, infile, maxval);
*ptr++ = RGB2GRAY(r, g, b);
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)];
JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)];
JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)];
*ptr++ = RGB2GRAY(r, g, b);
}
}
return 1;
}
METHODDEF(JDIMENSION)
get_text_rgb_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading text-format PPM files with any maxval and
@@ -468,41 +436,6 @@ get_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
}
METHODDEF(JDIMENSION)
get_rgb_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading raw-byte-format PPM files with any maxval and
converting to grayscale */
{
ppm_source_ptr source = (ppm_source_ptr) sinfo;
register JSAMPROW ptr;
register U_CHAR *bufferptr;
register JSAMPLE *rescale = source->rescale;
JDIMENSION col;
unsigned int maxval = source->maxval;
if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
ERREXIT(cinfo, JERR_INPUT_EOF);
ptr = source->pub.buffer[0];
bufferptr = source->iobuffer;
if (maxval == MAXJSAMPLE) {
for (col = cinfo->image_width; col > 0; col--) {
JSAMPLE r = *bufferptr++;
JSAMPLE g = *bufferptr++;
JSAMPLE b = *bufferptr++;
*ptr++ = RGB2GRAY(r, g, b);
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
JSAMPLE r = rescale[UCH(*bufferptr++)];
JSAMPLE g = rescale[UCH(*bufferptr++)];
JSAMPLE b = rescale[UCH(*bufferptr++)];
*ptr++ = RGB2GRAY(r, g, b);
}
}
return 1;
}
METHODDEF(JDIMENSION)
get_rgb_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading raw-byte-format PPM files with any maxval and
@@ -690,8 +623,6 @@ start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
source->pub.get_pixel_rows = get_text_rgb_row;
else if (cinfo->in_color_space == JCS_CMYK)
source->pub.get_pixel_rows = get_text_rgb_cmyk_row;
else if (cinfo->in_color_space == JCS_GRAYSCALE)
source->pub.get_pixel_rows = get_text_rgb_gray_row;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
need_iobuffer = FALSE;
@@ -739,8 +670,6 @@ start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
source->pub.get_pixel_rows = get_rgb_row;
else if (cinfo->in_color_space == JCS_CMYK)
source->pub.get_pixel_rows = get_rgb_cmyk_row;
else if (cinfo->in_color_space == JCS_GRAYSCALE)
source->pub.get_pixel_rows = get_rgb_gray_row;
else
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
}

View File

@@ -69,9 +69,6 @@ void usage(char *progName)
bailout(); \
}
#define RGB2GRAY(r, g, b) \
(unsigned char)((double)(r)*0.299+(double)(g)*0.587+(double)(b)*0.114+0.5)
const char *subNameLong[TJ_NUMSAMP]=
{
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
@@ -717,7 +714,7 @@ void initBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
unsigned char g=(j*256/height)%256;
unsigned char b=(j*256/height+i*256/width)%256;
memset(&buf[row*pitch+i*ps], 0, ps);
if(pf==TJPF_GRAY) buf[row*pitch+i*ps]=RGB2GRAY(r, g, b);
if(pf==TJPF_GRAY) buf[row*pitch+i*ps]=b;
else if(pf==TJPF_CMYK)
rgb_to_cmyk(r, g, b, &buf[row*pitch+i*ps+0], &buf[row*pitch+i*ps+1],
&buf[row*pitch+i*ps+2], &buf[row*pitch+i*ps+3]);
@@ -752,7 +749,7 @@ int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
unsigned char b=(j*256/height+i*256/width)%256;
if(pf==TJPF_GRAY)
{
if(buf[row*pitch+i*ps]!=RGB2GRAY(r, g, b))
if(buf[row*pitch+i*ps]!=b)
return 0;
}
else if(pf==TJPF_CMYK)
@@ -763,8 +760,7 @@ int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
&bf);
if(gray2rgb)
{
unsigned char gray=RGB2GRAY(r, g, b);
if(rf!=gray || gf!=gray || bf!=gray)
if(rf!=b || gf!=b || bf!=b)
return 0;
}
else if(rf!=r || gf!=g || bf!=b) return 0;
@@ -773,10 +769,9 @@ int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
{
if(gray2rgb)
{
unsigned char gray=RGB2GRAY(r, g, b);
if(buf[row*pitch+i*ps+roffset]!=gray ||
buf[row*pitch+i*ps+goffset]!=gray ||
buf[row*pitch+i*ps+boffset]!=gray)
if(buf[row*pitch+i*ps+roffset]!=b ||
buf[row*pitch+i*ps+goffset]!=b ||
buf[row*pitch+i*ps+boffset]!=b)
return 0;
}
else if(buf[row*pitch+i*ps+roffset]!=r ||
@@ -803,8 +798,8 @@ int doBmpTest(const char *ext, int width, int align, int height, int pf,
if(pf==TJPF_GRAY)
{
md5ref=!strcasecmp(ext, "ppm")? "bc77dea8eaf006aa187582b301f67e02":
"2670a3f8cf19d855183c02ccf18d2a35";
md5ref=!strcasecmp(ext, "ppm")? "112c682e82ce5de1cca089e20d60000b":
"51976530acf75f02beddf5d21149101d";
}
else
{
@@ -863,20 +858,6 @@ int doBmpTest(const char *ext, int width, int align, int height, int pf,
retval=-1; goto bailout;
}
}
else if(pf!=TJPF_CMYK)
{
tjFree(buf); buf=NULL;
pf=TJPF_GRAY;
if((buf=tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
flags))==NULL)
_throwtj();
pitch=PAD(width, align);
if(!cmpBitmap(buf, width, pitch, height, pf, flags, 0))
{
printf("\n Converting %s to grayscale failed\n", filename);
retval=-1; goto bailout;
}
}
unlink(filename);
bailout:

View File

@@ -1557,19 +1557,22 @@ DLLEXPORT unsigned char* DLLCALL tjAlloc(int bytes);
* (in pixels) of the uncompressed image
*
* @param pixelFormat pointer to an integer variable that specifies or will
* receive the pixel format of the uncompressed image buffer. If
* <tt>*pixelFormat</tt> is set to @ref TJPF_UNKNOWN prior to calling this
* function, then the uncompressed image buffer returned by the function will
* use the most optimal pixel format for the file type, and
* receive the pixel format of the uncompressed image buffer. The behavior of
* #tjLoadImage() will vary depending on the value of <tt>*pixelFormat</tt>
* passed to the function:
* - @ref TJPF_UNKNOWN : The uncompressed image buffer returned by the function
* will use the most optimal pixel format for the file type, and
* <tt>*pixelFormat</tt> will contain the ID of this pixel format upon
* successful return from the function. Otherwise, the uncompressed image
* buffer will use the @ref TJPF "pixel format" specified in
* <tt>*pixelFormat</tt>, and pixel format conversion will be performed if
* necessary. If <tt>*pixelFormat</tt> is set to @ref TJPF_CMYK, then the RGB
* or grayscale pixels stored in the file will be converted using a quick &
* dirty algorithm that is suitable only for testing purposes (proper
* conversion between CMYK and other formats requires a color management
* system.)
* successful return from the function.
* - @ref TJPF_GRAY : Only PGM files and 8-bit BMP files with a grayscale
* colormap can be loaded.
* - @ref TJPF_CMYK : The RGB or grayscale pixels stored in the file will be
* converted using a quick & dirty algorithm that is suitable only for testing
* purposes (proper conversion between CMYK and other formats requires a color
* management system.)
* - Other @ref TJPF "pixel formats" : The uncompressed image buffer will use
* the specified pixel format, and pixel format conversion will be performed if
* necessary.
*
* @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
* "flags".