Skip to content

Commit cc25405

Browse files
committed
vo_gpu_next: linearize sRGB as a pure 2.2 power function
sRGB reference display is defined a 2.2 gamma device. To preserve the look of the sRGB as mastered on such device, linearize it as such. Note that sRGB encoding is piecewise with linear segment, which creates mismatch to pure power 2.2 function, but this is intended to be viewed on such display. See: IEC 61966-2-1-1999 https://community.acescentral.com/t/srgb-piece-wise-eotf-vs-pure-gamma/4024 KhronosGroup/DataFormat#19 https://gitlab.freedesktop.org/pq/color-and-hdr/-/issues/12 https://github.com/dylanraga/win11hdr-srgb-to-gamma2.2-icm
1 parent 0a53407 commit cc25405

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

DOCS/man/options.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7354,6 +7354,20 @@ them.
73547354
transfer function is SDR. This way you can control SDR output separately
73557355
from HDR output.
73567356

7357+
``--treat-srgb-as-power22=<no|input|output|both>``
7358+
When enabled, sRGB is (de)linearized using a pure power 2.2 curve instead of
7359+
the standard sRGB piecewise transfer function. The sRGB reference display is
7360+
defined as having a 2.2 power response. Enabling this option preserves the
7361+
intended visual appearance of sRGB content as it was mastered on such displays.
7362+
This option affects both input and output sRGB handling.
7363+
Default: ``both``. (Only for ``--vo=gpu-next``)
7364+
7365+
See for more details:
7366+
https://community.acescentral.com/t/srgb-piece-wise-eotf-vs-pure-gamma/4024
7367+
https://github.com/KhronosGroup/DataFormat/issues/19
7368+
https://gitlab.freedesktop.org/pq/color-and-hdr/-/issues/12
7369+
https://github.com/dylanraga/win11hdr-srgb-to-gamma2.2-icm
7370+
73577371
``--tone-mapping=<value>``
73587372
Specifies the algorithm used for tone-mapping images onto the target
73597373
display. This is relevant for both HDR->SDR conversion as well as gamut

video/out/gpu/video.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ static const struct gl_video_opts gl_video_opts_def = {
409409
.early_flush = -1,
410410
.shader_cache = true,
411411
.hwdec_interop = "auto",
412+
.treat_srgb_as_power22 = 3, // both
412413
};
413414

414415
static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
@@ -445,6 +446,8 @@ const struct m_sub_options gl_video_conf = {
445446
M_RANGE(10, 10000)},
446447
{"hdr-reference-white", OPT_CHOICE(hdr_reference_white, {"auto", 0}),
447448
M_RANGE(10, 10000)},
449+
{"treat-srgb-as-power22", OPT_CHOICE(treat_srgb_as_power22,
450+
{"no", 0}, {"input", 1}, {"output", 2}, {"both", 3})},
448451
{"target-contrast", OPT_CHOICE(target_contrast, {"auto", 0}, {"inf", -1}),
449452
M_RANGE(10, 1000000)},
450453
{"target-gamut", OPT_CHOICE_C(target_gamut, pl_csp_prim_names)},

video/out/gpu/video.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct gl_video_opts {
139139
int target_trc;
140140
int target_peak;
141141
int hdr_reference_white;
142+
int treat_srgb_as_power22;
142143
int target_contrast;
143144
int target_gamut;
144145
struct gl_tone_map_opts tone_map;

video/out/vo_gpu_next.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,13 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
675675
if (opts->hdr_reference_white && !pl_color_transfer_is_hdr(frame->color.transfer))
676676
frame->color.hdr.max_luma = opts->hdr_reference_white;
677677

678+
679+
if (opts->treat_srgb_as_power22 & 1 && frame->color.transfer == PL_COLOR_TRC_SRGB) {
680+
// The sRGB EOTF is a pure gamma 2.2 function. See reference display in
681+
// IEC 61966-2-1-1999. Linearize sRGB to display light.
682+
frame->color.transfer = PL_COLOR_TRC_GAMMA22;
683+
}
684+
678685
if (fp->hwdec) {
679686

680687
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(par.imgfmt);
@@ -1213,6 +1220,11 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
12131220
// Update again after possible max_luma change
12141221
if (p->icc_profile)
12151222
hint = p->icc_profile->csp;
1223+
// If preferred transfer is gamma 2.2, which is not defined as Vulkan
1224+
// swapchain colorspace, use sRGB instead. It has reference display with
1225+
// gamma 2.2 so it's the same target in practice.
1226+
if (hint.transfer == PL_COLOR_TRC_GAMMA22)
1227+
hint.transfer = PL_COLOR_TRC_SRGB;
12161228
if (!pass_colorspace)
12171229
pl_swapchain_colorspace_hint(p->sw, &hint);
12181230
} else if (!target_hint) {
@@ -1247,6 +1259,9 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
12471259
pl_frame_from_swapchain(&target, &swframe);
12481260
bool strict_sw_params = target_hint && !pass_colorspace && p->next_opts->target_hint_strict;
12491261
apply_target_options(p, &target, hint.hdr.min_luma, strict_sw_params);
1262+
// sRGB reference display is pure 2.2 power function, see IEC 61966-2-1-1999.
1263+
if (opts->treat_srgb_as_power22 & 3 && target.color.transfer == PL_COLOR_TRC_SRGB)
1264+
target.color.transfer = PL_COLOR_TRC_GAMMA22;
12501265
update_overlays(vo, p->osd_res,
12511266
(frame->current && opts->blend_subs) ? OSD_DRAW_OSD_ONLY : 0,
12521267
PL_OVERLAY_COORDS_DST_FRAME, &p->osd_state, &target, frame->current);
@@ -1618,10 +1633,22 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
16181633
if (args->scaled) {
16191634
// Apply target LUT, ICC profile and CSP override only in window mode
16201635
apply_target_options(p, &target, 0, false);
1636+
// sRGB reference display is pure 2.2 power function, see IEC 61966-2-1-1999.
1637+
// Round-trip back to sRGB if the source is also sRGB.
1638+
if (opts->treat_srgb_as_power22 & 1 &&
1639+
target.color.transfer == PL_COLOR_TRC_SRGB &&
1640+
mpi->params.color.transfer == PL_COLOR_TRC_SRGB)
1641+
{
1642+
target.color.transfer = PL_COLOR_TRC_GAMMA22;
1643+
}
16211644
} else if (args->native_csp) {
16221645
target.color = image.color;
16231646
} else {
1624-
target.color = pl_color_space_srgb;
1647+
target.color = (struct pl_color_space){
1648+
.primaries = PL_COLOR_PRIM_BT_709,
1649+
.transfer = opts->treat_srgb_as_power22 & 2 ? PL_COLOR_TRC_GAMMA22
1650+
: PL_COLOR_TRC_SRGB,
1651+
};
16251652
}
16261653

16271654
apply_crop(&image, src, mpi->params.w, mpi->params.h);

0 commit comments

Comments
 (0)