From 14c712eb2a7c3b92f83c6edd0115d6eefccac476 Mon Sep 17 00:00:00 2001
From: Pavel Skakov <pavelsx@gmail.com>
Date: Fri, 4 Oct 2019 21:12:19 +0300
Subject: [PATCH] avcodec/tiff: add limited support for ReferenceBlackWhite and
 YCbCrCoefficients tags

Signed-off-by: Pavel Skakov <pavelsx@gmail.com>
---
 libavcodec/tiff.c              | 73 ++++++++++++++++++++++++++++++++++++++++--
 tests/ref/fate/exif-image-tiff |  2 +-
 2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index 8b39ca1ebc..069792b4c3 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -1216,6 +1216,13 @@ static void set_sar(TiffContext *s, unsigned tag, unsigned num, unsigned den)
     }
 }
 
+static int r_near(unsigned n1, unsigned d1, unsigned n2, unsigned d2)
+{
+    uint64_t v1 = (uint64_t)n1*d2;
+    uint64_t v2 = (uint64_t)n2*d1;
+    return v2 - d1 < v1 && v1 < v2 + d1;
+}
+
 static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
 {
     unsigned tag, type, count, off, value = 0, value2 = 1; // value2 is a denominator so init. to 1
@@ -1441,12 +1448,16 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
         break;
     case TIFF_PHOTOMETRIC:
         switch (value) {
+        case TIFF_PHOTOMETRIC_YCBCR:
+            s->avctx->colorspace = AVCOL_SPC_BT470BG;
+            // fallthrough
+        case TIFF_PHOTOMETRIC_RGB:
+            s->avctx->color_range = AVCOL_RANGE_JPEG;
+            // fallthrough
         case TIFF_PHOTOMETRIC_WHITE_IS_ZERO:
         case TIFF_PHOTOMETRIC_BLACK_IS_ZERO:
-        case TIFF_PHOTOMETRIC_RGB:
         case TIFF_PHOTOMETRIC_PALETTE:
         case TIFF_PHOTOMETRIC_SEPARATED:
-        case TIFF_PHOTOMETRIC_YCBCR:
         case TIFF_PHOTOMETRIC_CFA:
         case TIFF_PHOTOMETRIC_LINEAR_RAW: // Used by DNG images
             s->photometric = value;
@@ -1519,6 +1530,64 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
             }
         }
         break;
+    case TIFF_YCBCR_COEFFICIENTS:
+        if (s->photometric == TIFF_PHOTOMETRIC_YCBCR)
+            if (count != 3 || type != TIFF_RATIONAL) {
+                av_log(s->avctx, AV_LOG_ERROR, "YCbCrCoefficients are invalid\n");
+                return AVERROR_INVALIDDATA;
+            } else {
+                unsigned c[6];
+                for (i = 0; i < 6; i++)
+                    c[i] = ff_tget(&s->gb, TIFF_LONG, s->le);
+                if (r_near(c[0], c[1], 2125, 10000) && r_near(c[2], c[3], 7154, 10000) && r_near(c[4], c[5], 721, 10000))
+                    s->avctx->colorspace = AVCOL_SPC_BT709;
+                else if (!r_near(c[0], c[1], 299, 1000) || !r_near(c[2], c[3], 587, 1000) || !r_near(c[4], c[5], 114, 1000)) {
+                    av_log(s->avctx, AV_LOG_WARNING, "Unrecognized YCbCrCoefficients values: %.4f %.4f %.4f\n", (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5]);
+                    s->avctx->colorspace = AVCOL_SPC_UNSPECIFIED;
+                }
+            }
+        break;
+    case TIFF_REFERENCE_BW:
+        if (s->photometric == TIFF_PHOTOMETRIC_YCBCR || s->photometric == TIFF_PHOTOMETRIC_RGB)
+            if (count != 6 || type != TIFF_RATIONAL) {
+                av_log(s->avctx, AV_LOG_ERROR, "ReferenceBlackWhite is invalid\n");
+                return AVERROR_INVALIDDATA;
+            } else {
+                unsigned bpp = s->bpp/s->bppcount;
+                uint64_t mul = 1 << (bpp - 8);
+                uint64_t max_val = (1 << bpp) - 1;
+                uint64_t mid_val = 1 << (bpp - 1);
+                unsigned c[12];
+                for (i = 0; i < 12; i++)
+                    c[i] = ff_tget(&s->gb, TIFF_LONG, s->le);
+                if (s->photometric == TIFF_PHOTOMETRIC_YCBCR) {
+                    if (!c[0] && c[2] == max_val*c[3] && c[4] == mid_val*c[5] && c[6] == max_val*c[7] && c[8] == mid_val*c[9] && c[10] == max_val*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    // NOTE: TIFF 6.0 specification has an example where it mistakenly shows TV range c[0] as 15
+                    else if (c[0] ==  16*mul*c[1] && c[ 2] == 235*mul*c[3] &&
+                             c[4] == 128*mul*c[5] && c[ 6] == 240*mul*c[7] &&
+                             c[8] == 128*mul*c[9] && c[10] == 240*mul*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_MPEG;
+                    else {
+                        av_log(s->avctx, AV_LOG_WARNING, "Unrecognized ReferenceBlackWhite values: [%g;%g] [%g;%g] [%g;%g]\n",
+                            (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5], (float)c[6]/c[7], (float)c[8]/c[9], (float)c[10]/c[11]);
+                        s->avctx->color_range = AVCOL_RANGE_UNSPECIFIED;
+                    }
+                } else {
+                    if (!c[0] && c[2] == max_val*c[3] && !c[4] && c[6] == max_val*c[7] && !c[8] && c[10] == max_val*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    else if (c[0] == 16*mul*c[1] && c[ 2] == 235*mul*c[3] &&
+                             c[4] == 16*mul*c[5] && c[ 6] == 235*mul*c[7] &&
+                             c[8] == 16*mul*c[9] && c[10] == 235*mul*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_MPEG;
+                    else {
+                        av_log(s->avctx, AV_LOG_WARNING, "Unrecognized ReferenceBlackWhite values: [%g;%g] [%g;%g] [%g;%g]\n",
+                            (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5], (float)c[6]/c[7], (float)c[8]/c[9], (float)c[10]/c[11]);
+                        s->avctx->color_range = AVCOL_RANGE_UNSPECIFIED;
+                    }
+                }
+            }
+        break;
     case TIFF_T4OPTIONS:
         if (s->compr == TIFF_G3)
             s->fax_opts = value;
diff --git a/tests/ref/fate/exif-image-tiff b/tests/ref/fate/exif-image-tiff
index 51580601e1..ebf2f38d6e 100644
--- a/tests/ref/fate/exif-image-tiff
+++ b/tests/ref/fate/exif-image-tiff
@@ -22,7 +22,7 @@ display_picture_number=0
 interlaced_frame=0
 top_field_first=0
 repeat_pict=0
-color_range=unknown
+color_range=pc
 color_space=unknown
 color_primaries=unknown
 color_transfer=unknown
-- 
2.13.2.windows.1

