From 479fa1d870d2f42d38eff69fe87a10ec2f8b4b26 Mon Sep 17 00:00:00 2001 From: DRC Date: Sat, 18 Nov 2017 11:33:05 -0600 Subject: [PATCH] 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. --- cdjpeg.h | 4 -- doc/html/group___turbo_j_p_e_g.html | 8 +++- rdbmp.c | 54 ++++++++++++---------- rdppm.c | 71 ----------------------------- tjunittest.c | 35 ++++---------- turbojpeg.h | 27 ++++++----- 6 files changed, 61 insertions(+), 138 deletions(-) diff --git a/cdjpeg.h b/cdjpeg.h index 8c3b3b5..a6532dc 100644 --- a/cdjpeg.h +++ b/cdjpeg.h @@ -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)) diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 4dc6030..40f8b28 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -2274,7 +2274,13 @@ If you choose option 1, *jpegSize should be set to the size of your widthpointer to an integer variable that will receive the width (in pixels) of the uncompressed image alignrow 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. heightpointer to an integer variable that will receive the height (in pixels) of the uncompressed image - pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. If *pixelFormat is set to 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 *pixelFormat will contain the ID of this pixel format upon successful return from the function. Otherwise, the uncompressed image buffer will use the pixel format specified in *pixelFormat, and pixel format conversion will be performed if necessary. If *pixelFormat is set to 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.) + pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. The behavior of tjLoadImage() will vary depending on the value of *pixelFormat passed to the function: + flagsthe bitwise OR of one or more of the flags. diff --git a/rdbmp.c b/rdbmp.c index 49616d0..44a6258 100644 --- a/rdbmp.c +++ b/rdbmp.c @@ -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, diff --git a/rdppm.c b/rdppm.c index 24987cf..b7cd425 100644 --- a/rdppm.c +++ b/rdppm.c @@ -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); } diff --git a/tjunittest.c b/tjunittest.c index 4c1475a..e723a4f 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -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: diff --git a/turbojpeg.h b/turbojpeg.h index 7ffaf04..3a9c851 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -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 - * *pixelFormat 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 *pixelFormat + * 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 * *pixelFormat 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 - * *pixelFormat, and pixel format conversion will be performed if - * necessary. If *pixelFormat 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". -- 2.40.0