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. +@item intent +Set the ICC rendering intent to use when transforming between different color +spaces. It accepts the following values: +@table @samp +@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. + +@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. + +@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. + +@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. +@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 2a559e12f5..b04aa182d2 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