]> granicus.if.org Git - libjpeg-turbo/commitdiff
tjLoadImage(): Don't convert RGB to grayscale
authorDRC <information@libjpeg-turbo.org>
Sat, 18 Nov 2017 17:33:05 +0000 (11:33 -0600)
committerDRC <information@libjpeg-turbo.org>
Sat, 18 Nov 2017 17:40:53 +0000 (11:40 -0600)
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
doc/html/group___turbo_j_p_e_g.html
rdbmp.c
rdppm.c
tjunittest.c
turbojpeg.h

index 8c3b3b5d532022b08f597821aba3f07586c41660..a6532dc5c302880fbadac6e819a3afd36afe30a6 100644 (file)
--- 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))
index 4dc60309acf6f125f66f7960cd34c59b93db2894..40f8b286cd02669fbd749e27604c3de6fc55c8f0 100644 (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>
diff --git a/rdbmp.c b/rdbmp.c
index 49616d01f3e55743619d1f720bcb57218c86817d..44a62585d21aa729e622037d4b0360f7dfbe4bd4 100644 (file)
--- 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 24987cf1123ad6b0d7dad6fc743aff72381ccb2f..b7cd425ce5998b04212c593d661ee2db53b89693 100644 (file)
--- 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);
     }
index 4c1475aa750db1b4374372747189c73309e94337..e723a4f38b20b3e1c9f457d007bcf72f802ade86 100644 (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:
index 7ffaf048a2d8e5c7645acdc2e7c2fd4a1469513a..3a9c851ceb8cf1f3ae6037e21c1f1206e5d16d86 100644 (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".