libtiff: WebP codec: turn exact mode when creating lossless files to avoid altering R,G,B values in areas where alpha=0

From 6aeda415e085fc623af25753bf71ab15289152c1 Mon Sep 17 00:00:00 2001
From: Even Rouault <[EMAIL REDACTED]>
Date: Tue, 11 Jul 2023 19:25:54 +0200
Subject: [PATCH] WebP codec: turn exact mode when creating lossless files to
 avoid altering R,G,B values in areas where alpha=0

Fixes https://github.com/OSGeo/gdal/issues/8038
---
 libtiff/tif_webp.c | 29 ++++++++++++++++++++++++++---
 libtiff/tiff.h     |  3 ++-
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/libtiff/tif_webp.c b/libtiff/tif_webp.c
index 5ed4194d..bf9d77eb 100644
--- a/libtiff/tif_webp.c
+++ b/libtiff/tif_webp.c
@@ -47,9 +47,11 @@ typedef struct
 {
     uint16_t nSamples; /* number of samples per pixel */
 
-    int lossless;               /* lossy/lossless compression */
-    int quality_level;          /* compression level */
-    WebPPicture sPicture;       /* WebP Picture */
+    int lossless;         /* lossy/lossless compression */
+    int lossless_exact;   /* lossless exact mode. If TRUE, R,G,B values in areas
+                             with alpha = 0 will be preserved */
+    int quality_level;    /* compression level */
+    WebPPicture sPicture; /* WebP Picture */
     WebPConfig sEncoderConfig;  /* WebP encoder config */
     uint8_t *pBuffer;           /* buffer to hold raw data on encoding */
     unsigned int buffer_offset; /* current offset into the buffer */
@@ -519,6 +521,9 @@ static int TWebPSetupEncode(TIFF *tif)
     if (sp->lossless)
     {
         sp->sPicture.use_argb = 1;
+#if WEBP_ENCODER_ABI_VERSION >= 0x0209
+        sp->sEncoderConfig.exact = sp->lossless_exact;
+#endif
     }
 #endif
 
@@ -753,6 +758,17 @@ static int TWebPVSetField(TIFF *tif, uint32_t tag, va_list ap)
                 "Need to upgrade WEBP driver, this version doesn't support "
                 "lossless compression.");
             return 0;
+#endif
+        case TIFFTAG_WEBP_LOSSLESS_EXACT:
+#if WEBP_ENCODER_ABI_VERSION >= 0x0209
+            sp->lossless_exact = va_arg(ap, int);
+            return 1;
+#else
+            TIFFErrorExtR(
+                tif, module,
+                "Need to upgrade WEBP driver, this version doesn't support "
+                "lossless compression.");
+            return 0;
 #endif
         default:
             return (*sp->vsetparent)(tif, tag, ap);
@@ -772,6 +788,9 @@ static int TWebPVGetField(TIFF *tif, uint32_t tag, va_list ap)
         case TIFFTAG_WEBP_LOSSLESS:
             *va_arg(ap, int *) = sp->lossless;
             break;
+        case TIFFTAG_WEBP_LOSSLESS_EXACT:
+            *va_arg(ap, int *) = sp->lossless_exact;
+            break;
         default:
             return (*sp->vgetparent)(tif, tag, ap);
     }
@@ -784,6 +803,9 @@ static const TIFFField TWebPFields[] = {
     {TIFFTAG_WEBP_LOSSLESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
      TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "WEBP lossless/lossy",
      NULL},
+    {TIFFTAG_WEBP_LOSSLESS_EXACT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
+     TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "WEBP exact lossless",
+     NULL},
 };
 
 int TIFFInitWebP(TIFF *tif, int scheme)
@@ -822,6 +844,7 @@ int TIFFInitWebP(TIFF *tif, int scheme)
     /* Default values for codec-specific fields */
     sp->quality_level = 75; /* default comp. level */
     sp->lossless = 0;       /* default to false */
+    sp->lossless_exact = 1; /* exact lossless mode (if lossless enabled) */
     sp->state = 0;
     sp->nSamples = 0;
     sp->psDecoder = NULL;
diff --git a/libtiff/tiff.h b/libtiff/tiff.h
index b2d11866..d8da33dc 100644
--- a/libtiff/tiff.h
+++ b/libtiff/tiff.h
@@ -646,7 +646,7 @@ typedef enum
 #define TIFFTAG_EP_EXPOSUREINDEX 37397            /* Exposure index */
 #define TIFFTAG_EP_STANDARDID 37398               /* TIFF/EP standard version, n.n.n.n */
 #define TIFFTAG_EP_SENSINGMETHOD 37399            /* Type of image sensor */
-/* 
+/*
  * TIFF/EP tags equivalent to EXIF tags
  *     Note that TIFF-EP and EXIF use nearly the same metadata tag set, but TIFF-EP stores the tags in IFD 0,
  *     while EXIF store the tags in a separate IFD. Either location is allowed by DNG, but the EXIF location is preferred.
@@ -761,6 +761,7 @@ typedef enum
 #define TIFFTAG_LERC_MAXZERROR 65567   /* LERC maximum error */
 #define TIFFTAG_WEBP_LEVEL 65568       /* WebP compression level */
 #define TIFFTAG_WEBP_LOSSLESS 65569    /* WebP lossless/lossy */
+#define TIFFTAG_WEBP_LOSSLESS_EXACT 65571  /* WebP lossless exact mode. Set-only mode. Default is 1. Can be set to 0 to increase compression rate, but R,G,B in areas where alpha = 0 will not be preserved */
 #define TIFFTAG_DEFLATE_SUBCODEC 65570 /* ZIP codec: to get/set the sub-codec to use. Will default to libdeflate when available */
 #define DEFLATE_SUBCODEC_ZLIB 0
 #define DEFLATE_SUBCODEC_LIBDEFLATE 1