@@ -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 );
@@ -1246,6 +1253,37 @@ static bool draw_frame(struct vo *vo, struct vo_frame *frame)
12461253 pl_frame_from_swapchain (& target , & swframe );
12471254 bool strict_sw_params = target_hint && !pass_colorspace && p -> next_opts -> target_hint_strict ;
12481255 apply_target_options (p , & target , hint .hdr .min_luma , strict_sw_params );
1256+ if (target .color .transfer == PL_COLOR_TRC_SRGB ) {
1257+ // sRGB reference display is pure 2.2 power function, see IEC 61966-2-1-1999.
1258+ if (opts -> treat_srgb_as_power22 & 2 )
1259+ target .color .transfer = PL_COLOR_TRC_GAMMA22 ;
1260+
1261+ // TODO: Vulkan on Wayland currently interprets VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
1262+ // in ambiguous way, depending if compositor advertises sRGB support.
1263+ // For now, our default `treat_srgb_as_power22` value, assumes that sRGB
1264+ // is handled as pure gamma 2.2 function, but at the same time, we
1265+ // select sRGB only if it's explicitly requested.
1266+ // There is currently no clear path forward to resolve this ambiguity.
1267+ // Depending how it's resolved in Wayland Protocol, Mesa, things will
1268+ // change.
1269+ // See: <https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/456>
1270+ #ifdef _WIN32
1271+ // Windows uses the sRGB piecewise function. Send piecewise sRGB to
1272+ // Windows in HDR mode so that it can be converted to PQ, the same way
1273+ // as mpv does internally. Note that in SDR mode, even with ACM enabled,
1274+ // Windows assumes the display is sRGB. It doesn't perform gamma
1275+ // conversion, or any conversions would roundtrip back to sRGB.
1276+ // In which case the EOTF depends on the display.
1277+ // Ideally, compositors would agree on how to handle sRGB, but I’ll
1278+ // leave that part of the story for the reader to explore.
1279+ // Note: Older Windows versions, without ACM, were not able to convert
1280+ // sRGB to PQ output. We are not concerned about this case, as it would
1281+ // look wrong anyway.
1282+ bool target_pq = !target_unknown && target_csp .transfer == PL_COLOR_TRC_PQ ;
1283+ if (opts -> treat_srgb_as_power22 & 4 && target_pq )
1284+ target .color .transfer = PL_COLOR_TRC_SRGB ;
1285+ #endif
1286+ }
12491287 update_overlays (vo , p -> osd_res ,
12501288 (frame -> current && opts -> blend_subs ) ? OSD_DRAW_OSD_ONLY : 0 ,
12511289 PL_OVERLAY_COORDS_DST_FRAME , & p -> osd_state , & target , frame -> current );
@@ -1623,6 +1661,17 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
16231661 target .color = pl_color_space_srgb ;
16241662 }
16251663
1664+ // sRGB reference display is pure 2.2 power function, see IEC 61966-2-1-1999.
1665+ // Round-trip back to sRGB if the source is also sRGB. In other cases, we
1666+ // use piecewise sRGB transfer function, as this is likely the be expected
1667+ // for file encoding.
1668+ if (opts -> treat_srgb_as_power22 & 1 &&
1669+ target .color .transfer == PL_COLOR_TRC_SRGB &&
1670+ mpi -> params .color .transfer == PL_COLOR_TRC_SRGB )
1671+ {
1672+ target .color .transfer = PL_COLOR_TRC_GAMMA22 ;
1673+ }
1674+
16261675 apply_crop (& image , src , mpi -> params .w , mpi -> params .h );
16271676 apply_crop (& target , dst , fbo -> params .w , fbo -> params .h );
16281677 update_tm_viz (& pars -> color_map_params , & target );
0 commit comments