diff --git a/plugins/win-capture/dc-capture.c b/plugins/win-capture/dc-capture.c index a8839ba89..783798def 100644 --- a/plugins/win-capture/dc-capture.c +++ b/plugins/win-capture/dc-capture.c @@ -69,11 +69,20 @@ void dc_capture_init(struct dc_capture *capture, int x, int y, uint32_t width, bih->biHeight = height; bih->biPlanes = 1; - capture->hdc = CreateCompatibleDC(NULL); - capture->bmp = - CreateDIBSection(capture->hdc, &bi, DIB_RGB_COLORS, - (void **)&capture->bits, NULL, 0); - capture->old_bmp = SelectObject(capture->hdc, capture->bmp); + const HDC hdc = CreateCompatibleDC(NULL); + if (hdc) { + const HBITMAP bmp = CreateDIBSection( + capture->hdc, &bi, DIB_RGB_COLORS, + (void **)&capture->bits, NULL, 0); + if (bmp) { + capture->hdc = hdc; + capture->bmp = bmp; + capture->old_bmp = SelectObject(capture->hdc, + capture->bmp); + } else { + DeleteDC(capture->hdc); + } + } } } @@ -179,48 +188,67 @@ void dc_capture_capture(struct dc_capture *capture, HWND window) capture->texture_written = true; } -static void draw_texture(struct dc_capture *capture, bool texcoords_centered) -{ - gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); - gs_technique_t *tech = gs_effect_get_technique(effect, "Draw"); - gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); - - gs_texture_t *texture = capture->texture; - const bool compatibility = capture->compatibility; - bool linear_sample = compatibility; - if (!linear_sample && !texcoords_centered) { - gs_texture_t *const extra_texture = capture->extra_texture; - gs_copy_texture(extra_texture, texture); - texture = extra_texture; - linear_sample = true; - } - - const bool previous = gs_framebuffer_srgb_enabled(); - gs_enable_framebuffer_srgb(linear_sample); - gs_enable_blending(false); - - if (linear_sample) - gs_effect_set_texture_srgb(image, texture); - else - gs_effect_set_texture(image, texture); - - const uint32_t flip = compatibility ? GS_FLIP_V : 0; - const size_t passes = gs_technique_begin(tech); - for (size_t i = 0; i < passes; i++) { - if (gs_technique_begin_pass(tech, i)) { - gs_draw_sprite(texture, flip, 0, 0); - - gs_technique_end_pass(tech); - } - } - gs_technique_end(tech); - - gs_enable_blending(true); - gs_enable_framebuffer_srgb(previous); -} - void dc_capture_render(struct dc_capture *capture, bool texcoords_centered) { - if (capture->valid && capture->texture_written) - draw_texture(capture, texcoords_centered); + if (capture->valid && capture->texture_written) { + gs_texture_t *texture = capture->texture; + const bool compatibility = capture->compatibility; + bool linear_sample = compatibility; + if (!linear_sample && !texcoords_centered) { + gs_texture_t *const extra_texture = + capture->extra_texture; + gs_copy_texture(extra_texture, texture); + texture = extra_texture; + linear_sample = true; + } + + const char *tech_name = "Draw"; + float multiplier = 1.f; + switch (gs_get_color_space()) { + case GS_CS_SRGB_16F: + case GS_CS_709_EXTENDED: + if (!linear_sample) + tech_name = "DrawSrgbDecompress"; + break; + case GS_CS_709_SCRGB: + if (linear_sample) + tech_name = "DrawMultiply"; + else + tech_name = "DrawSrgbDecompressMultiply"; + multiplier = obs_get_video_sdr_white_level() / 80.f; + } + + gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); + gs_technique_t *tech = + gs_effect_get_technique(effect, tech_name); + gs_eparam_t *image = + gs_effect_get_param_by_name(effect, "image"); + + const bool previous = gs_framebuffer_srgb_enabled(); + gs_enable_framebuffer_srgb(linear_sample); + gs_enable_blending(false); + + if (linear_sample) + gs_effect_set_texture_srgb(image, texture); + else + gs_effect_set_texture(image, texture); + + gs_eparam_t *multiplier_param = + gs_effect_get_param_by_name(effect, "multiplier"); + gs_effect_set_float(multiplier_param, multiplier); + + const uint32_t flip = compatibility ? GS_FLIP_V : 0; + const size_t passes = gs_technique_begin(tech); + for (size_t i = 0; i < passes; i++) { + if (gs_technique_begin_pass(tech, i)) { + gs_draw_sprite(texture, flip, 0, 0); + + gs_technique_end_pass(tech); + } + } + gs_technique_end(tech); + + gs_enable_blending(true); + gs_enable_framebuffer_srgb(previous); + } } diff --git a/plugins/win-capture/duplicator-monitor-capture.c b/plugins/win-capture/duplicator-monitor-capture.c index fc9ee6587..ec0fc7ca0 100644 --- a/plugins/win-capture/duplicator-monitor-capture.c +++ b/plugins/win-capture/duplicator-monitor-capture.c @@ -39,6 +39,8 @@ typedef struct winrt_capture *(*PFN_winrt_capture_init_monitor)( typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture); typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture); +typedef enum gs_color_space (*PFN_winrt_capture_get_color_space)( + const struct winrt_capture *capture); typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture); typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture); typedef uint32_t (*PFN_winrt_capture_height)( @@ -51,6 +53,7 @@ struct winrt_exports { PFN_winrt_capture_init_monitor winrt_capture_init_monitor; PFN_winrt_capture_free winrt_capture_free; PFN_winrt_capture_active winrt_capture_active; + PFN_winrt_capture_get_color_space winrt_capture_get_color_space; PFN_winrt_capture_render winrt_capture_render; PFN_winrt_capture_width winrt_capture_width; PFN_winrt_capture_height winrt_capture_height; @@ -299,6 +302,7 @@ static bool load_winrt_imports(struct winrt_exports *exports, void *module, WINRT_IMPORT(winrt_capture_init_monitor); WINRT_IMPORT(winrt_capture_free); WINRT_IMPORT(winrt_capture_active); + WINRT_IMPORT(winrt_capture_get_color_space); WINRT_IMPORT(winrt_capture_render); WINRT_IMPORT(winrt_capture_width); WINRT_IMPORT(winrt_capture_height); @@ -541,13 +545,37 @@ static void duplicator_capture_render(void *data, gs_effect_t *unused) gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot)); } + const char *tech_name = "Draw"; + float multiplier = 1.f; + const enum gs_color_space current_space = gs_get_color_space(); + if (gs_texture_get_color_format(texture) == GS_RGBA16F) { + switch (current_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + tech_name = "DrawMultiplyTonemap"; + multiplier = + 80.f / obs_get_video_sdr_white_level(); + break; + case GS_CS_709_EXTENDED: + tech_name = "DrawMultiply"; + multiplier = + 80.f / obs_get_video_sdr_white_level(); + } + } else if (current_space == GS_CS_709_SCRGB) { + tech_name = "DrawMultiply"; + multiplier = obs_get_video_sdr_white_level() / 80.f; + } + gs_effect_t *const opaque_effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); - while (gs_effect_loop(opaque_effect, "Draw")) { - gs_eparam_t *image = gs_effect_get_param_by_name( - opaque_effect, "image"); - gs_effect_set_texture_srgb(image, texture); + gs_eparam_t *multiplier_param = gs_effect_get_param_by_name( + opaque_effect, "multiplier"); + gs_effect_set_float(multiplier_param, multiplier); + gs_eparam_t *image_param = + gs_effect_get_param_by_name(opaque_effect, "image"); + gs_effect_set_texture_srgb(image_param, texture); + while (gs_effect_loop(opaque_effect, tech_name)) { gs_draw_sprite(texture, 0, 0, 0); } @@ -673,6 +701,43 @@ static obs_properties_t *duplicator_capture_properties(void *data) return props; } +enum gs_color_space +duplicator_capture_get_color_space(void *data, size_t count, + const enum gs_color_space *preferred_spaces) +{ + enum gs_color_space capture_space = GS_CS_SRGB; + + struct duplicator_capture *capture = data; + if (capture->method == METHOD_WGC) { + if (capture->capture_winrt) { + capture_space = + capture->exports.winrt_capture_get_color_space( + capture->capture_winrt); + } + } else { + if (capture->duplicator) { + gs_texture_t *const texture = + gs_duplicator_get_texture(capture->duplicator); + if (texture) { + capture_space = (gs_texture_get_color_format( + texture) == GS_RGBA16F) + ? GS_CS_709_EXTENDED + : GS_CS_SRGB; + } + } + } + + enum gs_color_space space = capture_space; + for (size_t i = 0; i < count; ++i) { + const enum gs_color_space preferred_space = preferred_spaces[i]; + space = preferred_space; + if (preferred_space == capture_space) + break; + } + + return space; +} + struct obs_source_info duplicator_capture_info = { .id = "monitor_capture", .type = OBS_SOURCE_TYPE_INPUT, @@ -689,4 +754,5 @@ struct obs_source_info duplicator_capture_info = { .get_defaults = duplicator_capture_defaults, .get_properties = duplicator_capture_properties, .icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE, + .video_get_color_space = duplicator_capture_get_color_space, }; diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index bfe0e75e5..1ca02648a 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -37,6 +37,8 @@ typedef void (*PFN_winrt_capture_free)(struct winrt_capture *capture); typedef BOOL (*PFN_winrt_capture_active)(const struct winrt_capture *capture); typedef BOOL (*PFN_winrt_capture_show_cursor)(struct winrt_capture *capture, BOOL visible); +typedef enum gs_color_space (*PFN_winrt_capture_get_color_space)( + const struct winrt_capture *capture); typedef void (*PFN_winrt_capture_render)(struct winrt_capture *capture); typedef uint32_t (*PFN_winrt_capture_width)(const struct winrt_capture *capture); typedef uint32_t (*PFN_winrt_capture_height)( @@ -50,6 +52,7 @@ struct winrt_exports { PFN_winrt_capture_free winrt_capture_free; PFN_winrt_capture_active winrt_capture_active; PFN_winrt_capture_show_cursor winrt_capture_show_cursor; + PFN_winrt_capture_get_color_space winrt_capture_get_color_space; PFN_winrt_capture_render winrt_capture_render; PFN_winrt_capture_width winrt_capture_width; PFN_winrt_capture_height winrt_capture_height; @@ -238,6 +241,7 @@ static bool load_winrt_imports(struct winrt_exports *exports, void *module, WINRT_IMPORT(winrt_capture_free); WINRT_IMPORT(winrt_capture_active); WINRT_IMPORT(winrt_capture_show_cursor); + WINRT_IMPORT(winrt_capture_get_color_space); WINRT_IMPORT(winrt_capture_render); WINRT_IMPORT(winrt_capture_width); WINRT_IMPORT(winrt_capture_height); @@ -644,6 +648,30 @@ static void wc_render(void *data, gs_effect_t *effect) UNUSED_PARAMETER(effect); } +enum gs_color_space +wc_get_color_space(void *data, size_t count, + const enum gs_color_space *preferred_spaces) +{ + struct window_capture *wc = data; + + enum gs_color_space capture_space = GS_CS_SRGB; + + if ((wc->method == METHOD_WGC) && wc->capture_winrt) { + capture_space = wc->exports.winrt_capture_get_color_space( + wc->capture_winrt); + } + + enum gs_color_space space = capture_space; + for (size_t i = 0; i < count; ++i) { + const enum gs_color_space preferred_space = preferred_spaces[i]; + space = preferred_space; + if (preferred_space == capture_space) + break; + } + + return space; +} + struct obs_source_info window_capture_info = { .id = "window_capture", .type = OBS_SOURCE_TYPE_INPUT, @@ -661,4 +689,5 @@ struct obs_source_info window_capture_info = { .get_defaults = wc_defaults, .get_properties = wc_properties, .icon_type = OBS_ICON_TYPE_WINDOW_CAPTURE, + .video_get_color_space = wc_get_color_space, };