libtiff: Correct tiffcrop output of RGB JPEG and tiffcp/tiffcrop documentation - issue #228

From dee5bda6c082d8e3f203a203447682dc7e5fbd92 Mon Sep 17 00:00:00 2001
From: Lee Howard <[EMAIL REDACTED]>
Date: Tue, 26 Mar 2024 18:04:04 -0700
Subject: [PATCH] Correct tiffcrop output of RGB JPEG and tiffcp/tiffcrop
 documentation - issue #228

---
 tools/tiffcp.c   |  6 +++---
 tools/tiffcrop.c | 31 +++++++++++++++++++++----------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/tools/tiffcp.c b/tools/tiffcp.c
index a8c0dfcb..91049bd7 100644
--- a/tools/tiffcp.c
+++ b/tools/tiffcp.c
@@ -641,9 +641,9 @@ static const char usage_info[] =
     " -c jpeg[:opts]  compress output with JPEG encoding\n"
     /* "    JPEG options:", */
     "    #            set compression quality level (0-100, default 75)\n"
-    "    r            do not change colorspace (raw)\n"
-    "    For example, -c jpeg:r:50 for JPEG-encoded with 50% comp. "
-    "quality and same colorspace\n"
+    "    r            outupt color image as RGB rather than YCbCr\n"
+    "    For example, -c jpeg:r:50 for JPEG-encoded RGB with 50% comp. "
+    "quality\n"
 #endif
 #ifdef JBIG_SUPPORT
     " -c jbig         compress output with ISO JBIG encoding\n"
diff --git a/tools/tiffcrop.c b/tools/tiffcrop.c
index 44677a7d..8bc84e63 100644
--- a/tools/tiffcrop.c
+++ b/tools/tiffcrop.c
@@ -487,8 +487,7 @@ static uint32_t g3opts = 0;
 static int ignore = FALSE; /* if true, ignore read errors */
 static uint32_t defg3opts = (uint32_t)-1;
 static int quality = 100; /* JPEG quality */
-/* static int    jpegcolormode = -1;        was JPEGCOLORMODE_RGB;  */
-static int jpegcolormode = JPEGCOLORMODE_RGB;
+static int jpegcolormode = -1;        /* means YCbCr or to not convert */
 static uint16_t defcompression = (uint16_t)-1;
 static uint16_t defpredictor = (uint16_t)-1;
 static int pageNum = 0;
@@ -755,7 +754,8 @@ static const char usage_info[] =
     " -c jpeg[:opts] Compress output with JPEG encoding\n"
     /* "    JPEG options:\n" */
     "    #        Set compression quality level (0-100, default 100)\n"
-    "    raw      Output same colorspace image as input (default)\n"
+    "    raw      Output same colorspace image as input\n"
+    "    rgb      Output color image as RGB (default is YCbCr)\n"
     "    For example, -c jpeg:raw:50 for JPEG-encoded with 50% comp. "
     "quality and the same colorspace\n"
 #endif
@@ -1652,10 +1652,8 @@ static int processCompressOptions(char *opt)
                 quality = atoi(cp + 1);
             else if (strneq(cp + 1, "raw", 3))
                 jpegcolormode = JPEGCOLORMODE_RAW;
-/* libjpeg does not support colorspace conversion to RGB
             else if (strneq(cp + 1, "rgb", 3))
                 jpegcolormode = JPEGCOLORMODE_RGB;
-*/
             else
                 usage(EXIT_FAILURE);
             cp = strchr(cp + 1, ':');
@@ -7068,7 +7066,6 @@ static int loadImage(TIFF *in, struct image_data *image, struct dump_opts *dump,
 
     if (input_compression == COMPRESSION_JPEG)
     { /* Force conversion to RGB */
-        jpegcolormode = JPEGCOLORMODE_RGB;
         TIFFSetField(in, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
     }
     /* The clause up to the read statement is taken from Tom Lane's tiffcp patch
@@ -8202,11 +8199,18 @@ static int writeSingleSection(TIFF *in, TIFF *out, struct image_data *image,
                                                                  : "mask");
             return (-1);
         }
-        if ((input_photometric == PHOTOMETRIC_RGB) &&
-            (jpegcolormode == JPEGCOLORMODE_RGB))
+        if (jpegcolormode == JPEGCOLORMODE_RGB && input_photometric == PHOTOMETRIC_YCBCR)
+        {
+            TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+        }
+        else if (jpegcolormode == -1 && input_photometric == PHOTOMETRIC_RGB)
+        {
             TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
+        }
         else
+        {
             TIFFSetField(out, TIFFTAG_PHOTOMETRIC, input_photometric);
+        }
     }
     else
     {
@@ -8969,11 +8973,18 @@ static int writeCroppedImage(TIFF *in, TIFF *out, struct image_data *image,
                                                                  : "mask");
             return (-1);
         }
-        if ((input_photometric == PHOTOMETRIC_RGB) &&
-            (jpegcolormode == JPEGCOLORMODE_RGB))
+        if (jpegcolormode == JPEGCOLORMODE_RGB && input_photometric == PHOTOMETRIC_YCBCR)
+        {
+            TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+        }
+        else if (jpegcolormode == -1 && input_photometric == PHOTOMETRIC_RGB)
+        {
             TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
+        }
         else
+        {
             TIFFSetField(out, TIFFTAG_PHOTOMETRIC, input_photometric);
+        }
     }
     else
     {