From 1bfe065564604659b7703e75b1bb750c031fdc81 Mon Sep 17 00:00:00 2001
From: Carl Eugen Hoyos <ce.hoyos@grassfish.com>
Date: Sun, 23 Oct 2022 16:31:53 +0200
Subject: [PATCH] lavc/tiff: Support dng cropping, needs -flags +unaligned

Fixes samples mentioned in ticket #4364.
---
 libavcodec/tiff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/tiff.h |  3 ++
 2 files changed, 86 insertions(+)

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index fd9db18c0b..33edff8213 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -1492,6 +1492,89 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
     case DNG_WHITE_LEVEL:
         s->white_level = value;
         break;
+    case DNG_CROP_ORIGIN:
+        if (count != 2 || type != TIFF_SHORT && type != TIFF_LONG && type != TIFF_RATIONAL) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid crop origin (count: %d, type: %d)\n", count, type);
+            break;
+        }
+        if (type == TIFF_RATIONAL) {
+            unsigned denum1, denum2;
+            value  = ff_tget(&s->gb, TIFF_LONG, s->le);
+            denum1 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            value2 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            denum2 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            if (denum1 != 1 || denum2 != 1) {
+                av_log(s->avctx, AV_LOG_WARNING, "Unsuported crop origin\n");
+                break;
+            }
+        } else {
+            value  = ff_tget(&s->gb, type, s->le);
+            value2 = ff_tget(&s->gb, type, s->le);
+        }
+        av_log(s->avctx, AV_LOG_DEBUG, "dng crop orogin: %d/%d\n", value, value2);
+        if (value >= s->width || value2 >= s->height) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid crop origin (%d/%d)\n", value, value2);
+            break;
+        }
+        if ((value || value2) && !s->avctx->flags & AV_CODEC_FLAG_UNALIGNED ) {
+            av_log(s->avctx, AV_LOG_WARNING,"Correct DNG cropping needs -flags +unaligned\n");
+        } else {
+            frame->crop_left = value;
+            frame->crop_top = value2;
+        }
+        break;
+    case DNG_CROP_SIZE:
+        if (count != 2 || type != TIFF_SHORT && type != TIFF_LONG && type != TIFF_RATIONAL) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid crop size (count: %d, type: %d)\n", count, type);
+            break;
+        }
+        if (type == TIFF_RATIONAL) {
+            unsigned denum1, denum2;
+            value  = ff_tget(&s->gb, TIFF_LONG, s->le);
+            denum1 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            value2 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            denum2 = ff_tget(&s->gb, TIFF_LONG, s->le);
+            if (denum1 != 1 || denum2 != 1) {
+                av_log(s->avctx, AV_LOG_WARNING, "Unsuported crop size\n");
+                break;
+            }
+        } else {
+            value  = ff_tget(&s->gb, type, s->le);
+            value2 = ff_tget(&s->gb, type, s->le);
+        }
+        av_log(s->avctx, AV_LOG_DEBUG, "dng crop size %d x %d\n", value, value2);
+        if (value + frame->crop_left >= s->width || value2 + frame->crop_top >= s->height) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid crop size (%d/%d)\n", value, value2);
+            break;
+        }
+        frame->crop_right = s->width - frame->crop_left - value;
+        frame->crop_bottom = s->height - frame->crop_top - value2;
+        break;
+    case DNG_ACTIVE_AREA: {
+        unsigned top, left, bottom, right;
+        if (count != 4 || type != TIFF_SHORT && type != TIFF_LONG) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid Active Area (count: %d, type: %d)\n", count, type);
+            break;
+        }
+        top    = ff_tget(&s->gb, type, s->le);
+        left   = ff_tget(&s->gb, type, s->le);
+        bottom = ff_tget(&s->gb, type, s->le);
+        right  = ff_tget(&s->gb, type, s->le);
+        if (top + frame->crop_top >= s->height || top > frame->crop_bottom || left + frame->crop_left >= s->width || left > frame->crop_right) {
+            av_log(s->avctx, AV_LOG_WARNING, "Invalid Active Area (%d/%d/%d/%d)\n", top, left, bottom, right);
+            break;
+        }
+        av_log(s->avctx, AV_LOG_DEBUG, "dng active area %d / %d / %d / %d\n", top, left, bottom, right);
+        if ((top || left) && !s->avctx->flags & AV_CODEC_FLAG_UNALIGNED ) {
+            av_log(s->avctx, AV_LOG_WARNING,"Correct DNG cropping needs -flags +unaligned\n");
+        } else {
+            frame->crop_top += top;
+            frame->crop_bottom -= top;
+            frame->crop_left += left;
+            frame->crop_right -= left;
+        }
+        break;
+    }
     case TIFF_CFA_PATTERN_DIM:
         if (count != 2 || (ff_tget(&s->gb, type, s->le) != 2 &&
                            ff_tget(&s->gb, type, s->le) != 2)) {
diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h
index e67c59abad..94a91412e5 100644
--- a/libavcodec/tiff.h
+++ b/libavcodec/tiff.h
@@ -106,6 +106,8 @@ enum DngTags {
     DNG_LINEARIZATION_TABLE = 0xC618,
     DNG_BLACK_LEVEL         = 0xC61A,
     DNG_WHITE_LEVEL         = 0xC61D,
+    DNG_CROP_ORIGIN         = 0xC61F,
+    DNG_CROP_SIZE           = 0xC620,
     DNG_COLOR_MATRIX1       = 0xC621,
     DNG_COLOR_MATRIX2       = 0xC622,
     DNG_CAMERA_CALIBRATION1 = 0xC623,
@@ -113,6 +115,7 @@ enum DngTags {
     DNG_ANALOG_BALANCE      = 0xC627,
     DNG_AS_SHOT_NEUTRAL     = 0xC628,
     DNG_AS_SHOT_WHITE_XY    = 0xC629,
+    DNG_ACTIVE_AREA         = 0xC68D,
 };
 
 /** list of CinemaDNG tags */
-- 
2.17.1

