From 5e24edbf73f3a897fd203e36963e9cf5db5e688d Mon Sep 17 00:00:00 2001
From: Pavel Skakov <pavelsx@gmail.com>
Date: Thu, 3 Oct 2019 21:28:10 +0300
Subject: [PATCH] avcodec/tiff: add limited support for ReferenceBlackWhite and
 YCbCrCoefficients tags

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

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index 8b39ca1ebc..90215fce09 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -1441,12 +1441,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 +1523,72 @@ 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 {
+                // LibTIFF handles rational values by converting them to/from floats, so we do the same in tests for equality
+                float coef[3];
+                for (i = 0; i < 3; i++) {
+                    unsigned num = ff_tget(&s->gb, TIFF_LONG, s->le);
+                    unsigned den = ff_tget(&s->gb, TIFF_LONG, s->le);
+                    if (!den) {
+                        av_log(s->avctx, AV_LOG_ERROR, "YCbCrCoefficients divisor is zero\n");
+                        return AVERROR_INVALIDDATA;
+                    }
+                    coef[i] = (float)num/den;
+                }
+                if (coef[0] == 0.2125f && coef[1] == 0.7154f && coef[2] == 0.0721f)
+                    s->avctx->colorspace = AVCOL_SPC_BT709;
+                else if (coef[0] != 0.299f || coef[1] != 0.587f || coef[2] != 0.114f) {
+                    av_log(s->avctx, AV_LOG_WARNING, "Unrecognized YCbCrCoefficients values: %.4f %.4f %.4f\n", coef[0], coef[1], coef[2]);
+                    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;
+                unsigned mul = 1 << (bpp - 8);
+                float max_val = (1 << bpp) - 1;
+                float coef[6];
+                for (i = 0; i < 6; i++) {
+                    unsigned num = ff_tget(&s->gb, TIFF_LONG, s->le);
+                    unsigned den = ff_tget(&s->gb, TIFF_LONG, s->le);
+                    if (!den) {
+                        av_log(s->avctx, AV_LOG_ERROR, "ReferenceBlackWhite divisor is zero\n");
+                        return AVERROR_INVALIDDATA;
+                    }
+                    coef[i] = (float)num/den;
+                }
+                if (s->photometric == TIFF_PHOTOMETRIC_YCBCR) {
+                    if (!coef[0] && coef[1] == max_val && coef[2] == (max_val + 1)/2 && coef[3] == max_val && coef[4] == coef[2] && coef[5] == coef[3])
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    // NOTE: TIFF 6.0 specification has an example where it mistakenly shows TV range coef[0] as 15
+                    else if (coef[0] == 16*mul && coef[1] == 235*mul && coef[2] == 128*mul && coef[3] == 240*mul && coef[4] == coef[2] && coef[5] == coef[3])
+                        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", coef[0], coef[1], coef[2], coef[3], coef[4], coef[5]);
+                        s->avctx->color_range = AVCOL_RANGE_UNSPECIFIED;
+                    }
+                } else {
+                    if (!coef[0] && coef[1] == max_val && !coef[2] && coef[3] == max_val && !coef[4] && coef[5] == max_val)
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    else if (coef[0] == 16*mul && coef[1] == 235*mul && coef[2] == coef[0] && coef[3] == coef[1] && coef[2] == coef[0] && coef[3] == coef[1])
+                        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", coef[0], coef[1], coef[2], coef[3], coef[4], coef[5]);
+                        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

