[FFmpeg-devel] [PATCH v2 08/17] swscale: add ICC intent enum and option

Niklas Haas ffmpeg at haasn.xyz
Mon Dec 16 13:17:29 EET 2024


From: Niklas Haas <git at haasn.dev>

This setting can be used to infuence the type of tone and gamut mapping used
internally when color space conversions are required. As discussed at VDD'24,
the default was set to relative colorimetric clipping, which is approximately
associative, surjective and idempotent. As such, it roundtrips well, although
it is strictly speaking not associative on out-of-gamut colors.
---
 doc/APIchanges                |  3 +++
 doc/filters.texi              | 31 +++++++++++++++++++++++++++++++
 libswscale/graph.c            | 11 ++++++-----
 libswscale/options.c          |  6 ++++++
 libswscale/swscale.h          | 13 +++++++++++++
 libswscale/swscale_internal.h |  2 +-
 libswscale/version.h          |  2 +-
 libswscale/x86/output.asm     |  2 +-
 8 files changed, 62 insertions(+), 8 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 5c7e82d84f..7f5b3ef3d3 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-12-xx - xxxxxxxxxx - lsws 8.13.100 - swscale.h
+  Add enum SwsIntent and SwsContext.intent.
+
 2024-12-15 - xxxxxxxxxx - lavc 61.27.100 packet.h
   Add av_container_fifo_alloc_avpacket().
 
diff --git a/doc/filters.texi b/doc/filters.texi
index 900baf2f45..9068a8a4e7 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -21071,7 +21071,38 @@ Set libswscale input parameters for scaling algorithms that need them. See
 complete documentation. If not explicitly specified the filter applies
 empty parameters.
 
+ at item intent
+Set the ICC rendering intent to use when transforming between different color
+spaces. It accepts the following values:
 
+ at table @samp
+ at item perceptual
+Use a perceptually guided tone and gamut mapping curve. The exact details of
+the mapping used may change at any time and should not be relied on as stable.
+This intent is recommended for final viewing of image/video content in typical
+viewing settings.
+
+ at item relative_colorimetric
+Statically clip out-of-gamut colors using a colorimetric clipping curve which
+attempts to find the colorimetrically least dissimilar in-gamut color. This
+intent performs white point adaptation and black point adaptation. This is
+the default. This intent is recommended wherever faithful color reproduction
+is of the utmost importance, even at the cost of clipping.
+
+ at item absolute_colorimetric
+Hard clip out-of-gamut colors with no attempt at white or black point
+reproduction. This intent will reproduce in-gamut colors 1:1 on the output
+display as they would appear on the reference display, assuming the output
+display is appropriately calibrated.
+
+ at item saturation
+Performs saturation mapping - that is, stretches the input color volume
+directly onto the output color volume, in non-linear fashion that preserves the
+original signal appearance as much as possible. This intent is recommended for
+signal content evaluation, as it will not lead to any clipping. It is roughly
+analogous to not performing any color mapping, although it still takes into
+account the mastering display primaries and any differences in encoding TRC.
+ at end table
 
 @item size, s
 Set the video size. For the syntax of this option, check the
diff --git a/libswscale/graph.c b/libswscale/graph.c
index fbad1fe8c3..bf2f54b979 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -570,15 +570,16 @@ void sws_graph_free(SwsGraph **pgraph)
 /* Tests only options relevant to SwsGraph */
 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
 {
-    return c1->flags       == c2->flags       &&
-           c1->threads     == c2->threads     &&
-           c1->dither      == c2->dither      &&
-           c1->alpha_blend == c2->alpha_blend &&
-           c1->gamma_flag  == c2->gamma_flag  &&
+    return c1->flags         == c2->flags         &&
+           c1->threads       == c2->threads       &&
+           c1->dither        == c2->dither        &&
+           c1->alpha_blend   == c2->alpha_blend   &&
+           c1->gamma_flag    == c2->gamma_flag    &&
            c1->src_h_chr_pos == c2->src_h_chr_pos &&
            c1->src_v_chr_pos == c2->src_v_chr_pos &&
            c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
            c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
+           c1->intent        == c2->intent        &&
            !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
 
 }
diff --git a/libswscale/options.c b/libswscale/options.c
index 5eef26de06..08d369e620 100644
--- a/libswscale/options.c
+++ b/libswscale/options.c
@@ -84,6 +84,12 @@ static const AVOption swscale_options[] = {
     { "threads",         "number of threads",             OFFSET(threads),   AV_OPT_TYPE_INT,   {.i64 = 1 }, .flags = VE, .unit = "threads", .max = INT_MAX },
         { "auto",        "automatic selection",           0,                 AV_OPT_TYPE_CONST, {.i64 = 0 }, .flags = VE, .unit = "threads" },
 
+    { "intent",          "color mapping intent",        OFFSET(intent), AV_OPT_TYPE_INT,    { .i64 = SWS_INTENT_RELATIVE_COLORIMETRIC }, .flags = VE, .unit = "intent", .max = SWS_INTENT_NB - 1 },
+        { "perceptual",            "perceptual tone mapping",        0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_PERCEPTUAL            }, .flags = VE, .unit = "intent" },
+        { "relative_colorimetric", "relative colorimetric clipping", 0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_RELATIVE_COLORIMETRIC }, .flags = VE, .unit = "intent" },
+        { "saturation",            "saturation mapping",             0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_SATURATION            }, .flags = VE, .unit = "intent" },
+        { "absolute_colorimetric", "absolute colorimetric clipping", 0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_ABSOLUTE_COLORIMETRIC }, .flags = VE, .unit = "intent" },
+
     { NULL }
 };
 
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index fa3a0f01ab..10b52c34e2 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -162,6 +162,14 @@ typedef enum SwsFlags {
     SWS_ERROR_DIFFUSION = 1 << 23, ///< Set `SwsContext.dither` instead
 } SwsFlags;
 
+typedef enum SwsIntent {
+    SWS_INTENT_PERCEPTUAL = 0,            ///< Perceptual tone mapping
+    SWS_INTENT_RELATIVE_COLORIMETRIC = 1, ///< Relative colorimetric clipping
+    SWS_INTENT_SATURATION = 2,            ///< Saturation mapping
+    SWS_INTENT_ABSOLUTE_COLORIMETRIC = 3, ///< Absolute colorimetric clipping
+    SWS_INTENT_NB, ///< not part of the ABI
+} SwsIntent;
+
 /***********************************
  * Context creation and management *
  ***********************************/
@@ -226,6 +234,11 @@ typedef struct SwsContext {
     int dst_v_chr_pos; ///< Destination vertical chroma position
     int dst_h_chr_pos; ///< Destination horizontal chroma position
 
+    /**
+     * Desired ICC intent for color space conversions.
+     */
+    int intent;
+
     /* Remember to add new fields to graph.c:opts_equal() */
 } SwsContext;
 
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index 768e394560..98b8535c9b 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -699,7 +699,7 @@ static_assert(offsetof(SwsInternal, redDither) + DITHER32_INT == offsetof(SwsInt
 #if ARCH_X86_64
 /* x86 yuv2gbrp uses the SwsInternal for yuv coefficients
    if struct offsets change the asm needs to be updated too */
-static_assert(offsetof(SwsInternal, yuv2rgb_y_offset) == 40332,
+static_assert(offsetof(SwsInternal, yuv2rgb_y_offset) == 40348,
               "yuv2rgb_y_offset must be updated in x86 asm");
 #endif
 
diff --git a/libswscale/version.h b/libswscale/version.h
index 419721948a..f84fcc160d 100644
--- a/libswscale/version.h
+++ b/libswscale/version.h
@@ -28,7 +28,7 @@
 
 #include "version_major.h"
 
-#define LIBSWSCALE_VERSION_MINOR  12
+#define LIBSWSCALE_VERSION_MINOR  13
 #define LIBSWSCALE_VERSION_MICRO 100
 
 #define LIBSWSCALE_VERSION_INT  AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
diff --git a/libswscale/x86/output.asm b/libswscale/x86/output.asm
index 7a1e5d9bc1..f2e884780a 100644
--- a/libswscale/x86/output.asm
+++ b/libswscale/x86/output.asm
@@ -582,7 +582,7 @@ yuv2nv12cX_fn yuv2nv21
 
 %if ARCH_X86_64
 struc SwsInternal
-    .padding:           resb 40332 ; offsetof(SwsInternal, yuv2rgb_y_offset)
+    .padding:           resb 40348 ; offsetof(SwsInternal, yuv2rgb_y_offset)
     .yuv2rgb_y_offset:  resd 1
     .yuv2rgb_y_coeff:   resd 1
     .yuv2rgb_v2r_coeff: resd 1
-- 
2.47.0



More information about the ffmpeg-devel mailing list