diff --git a/plugins/obs-filters/chroma-key-filter.c b/plugins/obs-filters/chroma-key-filter.c index e58fa84c3..0fac53041 100644 --- a/plugins/obs-filters/chroma-key-filter.c +++ b/plugins/obs-filters/chroma-key-filter.c @@ -33,7 +33,6 @@ struct chroma_key_filter_data { gs_effect_t *effect; gs_eparam_t *color_param; - gs_eparam_t *opacity_param; gs_eparam_t *contrast_param; gs_eparam_t *brightness_param; gs_eparam_t *gamma_param; @@ -45,6 +44,32 @@ struct chroma_key_filter_data { gs_eparam_t *spill_param; struct vec4 color; + float contrast; + float brightness; + float gamma; + + struct vec2 chroma; + float similarity; + float smoothness; + float spill; +}; + +struct chroma_key_filter_data_v2 { + obs_source_t *context; + + gs_effect_t *effect; + + gs_eparam_t *opacity_param; + gs_eparam_t *contrast_param; + gs_eparam_t *brightness_param; + gs_eparam_t *gamma_param; + + gs_eparam_t *pixel_size_param; + gs_eparam_t *chroma_param; + gs_eparam_t *similarity_param; + gs_eparam_t *smoothness_param; + gs_eparam_t *spill_param; + float opacity; float contrast; float brightness; @@ -65,20 +90,36 @@ static const char *chroma_key_name(void *unused) static const float cb_vec[] = {-0.100644f, -0.338572f, 0.439216f, 0.501961f}; static const float cr_vec[] = {0.439216f, -0.398942f, -0.040274f, 0.501961f}; -static inline void color_settings_update(struct chroma_key_filter_data *filter, - obs_data_t *settings) +static inline void +color_settings_update_v1(struct chroma_key_filter_data *filter, + obs_data_t *settings) { - uint32_t version = obs_source_get_version(filter->context); + uint32_t opacity = + (uint32_t)obs_data_get_int(settings, SETTING_OPACITY); + uint32_t color = 0xFFFFFF | (((opacity * 255) / 100) << 24); + double contrast = obs_data_get_double(settings, SETTING_CONTRAST); + double brightness = obs_data_get_double(settings, SETTING_BRIGHTNESS); + double gamma = obs_data_get_double(settings, SETTING_GAMMA); - if (version == 1) { - uint32_t opacity = - (uint32_t)obs_data_get_int(settings, SETTING_OPACITY); - uint32_t color = 0xFFFFFF | (((opacity * 255) / 100) << 24); - vec4_from_rgba(&filter->color, color); - } else if (version == 2) { - filter->opacity = - (float)obs_data_get_double(settings, SETTING_OPACITY); - } + contrast = (contrast < 0.0) ? (1.0 / (-contrast + 1.0)) + : (contrast + 1.0); + + brightness *= 0.5; + + gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0)); + + filter->contrast = (float)contrast; + filter->brightness = (float)brightness; + filter->gamma = (float)gamma; + + vec4_from_rgba(&filter->color, color); +} + +static inline void +color_settings_update_v2(struct chroma_key_filter_data_v2 *filter, + obs_data_t *settings) +{ + filter->opacity = (float)obs_data_get_double(settings, SETTING_OPACITY); double contrast = obs_data_get_double(settings, SETTING_CONTRAST); contrast = (contrast < 0.0) ? (1.0 / (-contrast + 1.0)) @@ -88,16 +129,14 @@ static inline void color_settings_update(struct chroma_key_filter_data *filter, filter->brightness = (float)obs_data_get_double(settings, SETTING_BRIGHTNESS); - if (version == 1) - filter->brightness *= 0.5f; - double gamma = obs_data_get_double(settings, SETTING_GAMMA); gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0)); filter->gamma = (float)gamma; } -static inline void chroma_settings_update(struct chroma_key_filter_data *filter, - obs_data_t *settings) +static inline void +chroma_settings_update_v1(struct chroma_key_filter_data *filter, + obs_data_t *settings) { int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY); int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS); @@ -129,15 +168,57 @@ static inline void chroma_settings_update(struct chroma_key_filter_data *filter, filter->spill = (float)spill / 1000.0f; } -static void chroma_key_update(void *data, obs_data_t *settings) +static inline void +chroma_settings_update_v2(struct chroma_key_filter_data_v2 *filter, + obs_data_t *settings) +{ + int64_t similarity = obs_data_get_int(settings, SETTING_SIMILARITY); + int64_t smoothness = obs_data_get_int(settings, SETTING_SMOOTHNESS); + int64_t spill = obs_data_get_int(settings, SETTING_SPILL); + uint32_t key_color = + (uint32_t)obs_data_get_int(settings, SETTING_KEY_COLOR); + const char *key_type = + obs_data_get_string(settings, SETTING_COLOR_TYPE); + struct vec4 key_rgb; + struct vec4 cb_v4; + struct vec4 cr_v4; + + if (strcmp(key_type, "green") == 0) + key_color = 0x00FF00; + else if (strcmp(key_type, "blue") == 0) + key_color = 0xFF9900; + else if (strcmp(key_type, "magenta") == 0) + key_color = 0xFF00FF; + + vec4_from_rgba(&key_rgb, key_color | 0xFF000000); + + memcpy(&cb_v4, cb_vec, sizeof(cb_v4)); + memcpy(&cr_v4, cr_vec, sizeof(cr_v4)); + filter->chroma.x = vec4_dot(&key_rgb, &cb_v4); + filter->chroma.y = vec4_dot(&key_rgb, &cr_v4); + + filter->similarity = (float)similarity / 1000.0f; + filter->smoothness = (float)smoothness / 1000.0f; + filter->spill = (float)spill / 1000.0f; +} + +static void chroma_key_update_v1(void *data, obs_data_t *settings) { struct chroma_key_filter_data *filter = data; - color_settings_update(filter, settings); - chroma_settings_update(filter, settings); + color_settings_update_v1(filter, settings); + chroma_settings_update_v1(filter, settings); } -static void chroma_key_destroy(void *data) +static void chroma_key_update_v2(void *data, obs_data_t *settings) +{ + struct chroma_key_filter_data_v2 *filter = data; + + color_settings_update_v2(filter, settings); + chroma_settings_update_v2(filter, settings); +} + +static void chroma_key_destroy_v1(void *data) { struct chroma_key_filter_data *filter = data; @@ -150,17 +231,24 @@ static void chroma_key_destroy(void *data) bfree(data); } -static void *chroma_key_create(obs_data_t *settings, obs_source_t *context) +static void chroma_key_destroy_v2(void *data) +{ + struct chroma_key_filter_data_v2 *filter = data; + + if (filter->effect) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect); + obs_leave_graphics(); + } + + bfree(data); +} + +static void *chroma_key_create_v1(obs_data_t *settings, obs_source_t *context) { struct chroma_key_filter_data *filter = bzalloc(sizeof(struct chroma_key_filter_data)); - char *effect_path = NULL; - uint32_t version = obs_source_get_version(context); - - if (version == 1) - effect_path = obs_module_file("chroma_key_filter.effect"); - else if (version == 2) - effect_path = obs_module_file("chroma_key_filter_v2.effect"); + char *effect_path = obs_module_file("chroma_key_filter.effect"); filter->context = context; @@ -168,13 +256,8 @@ static void *chroma_key_create(obs_data_t *settings, obs_source_t *context) filter->effect = gs_effect_create_from_file(effect_path, NULL); if (filter->effect) { - if (version == 1) - filter->color_param = gs_effect_get_param_by_name( - filter->effect, "color"); - else if (version == 2) - filter->opacity_param = gs_effect_get_param_by_name( - filter->effect, "opacity"); - + filter->color_param = + gs_effect_get_param_by_name(filter->effect, "color"); filter->contrast_param = gs_effect_get_param_by_name(filter->effect, "contrast"); filter->brightness_param = gs_effect_get_param_by_name( @@ -198,15 +281,60 @@ static void *chroma_key_create(obs_data_t *settings, obs_source_t *context) bfree(effect_path); if (!filter->effect) { - chroma_key_destroy(filter); + chroma_key_destroy_v1(filter); return NULL; } - chroma_key_update(filter, settings); + chroma_key_update_v1(filter, settings); return filter; } -static void chroma_key_render(void *data, gs_effect_t *effect) +static void *chroma_key_create_v2(obs_data_t *settings, obs_source_t *context) +{ + struct chroma_key_filter_data_v2 *filter = + bzalloc(sizeof(struct chroma_key_filter_data_v2)); + char *effect_path = obs_module_file("chroma_key_filter_v2.effect"); + + filter->context = context; + + obs_enter_graphics(); + + filter->effect = gs_effect_create_from_file(effect_path, NULL); + if (filter->effect) { + filter->opacity_param = + gs_effect_get_param_by_name(filter->effect, "opacity"); + filter->contrast_param = + gs_effect_get_param_by_name(filter->effect, "contrast"); + filter->brightness_param = gs_effect_get_param_by_name( + filter->effect, "brightness"); + filter->gamma_param = + gs_effect_get_param_by_name(filter->effect, "gamma"); + filter->chroma_param = gs_effect_get_param_by_name( + filter->effect, "chroma_key"); + filter->pixel_size_param = gs_effect_get_param_by_name( + filter->effect, "pixel_size"); + filter->similarity_param = gs_effect_get_param_by_name( + filter->effect, "similarity"); + filter->smoothness_param = gs_effect_get_param_by_name( + filter->effect, "smoothness"); + filter->spill_param = + gs_effect_get_param_by_name(filter->effect, "spill"); + } + + obs_leave_graphics(); + + bfree(effect_path); + + if (!filter->effect) { + chroma_key_destroy_v2(filter); + return NULL; + } + + chroma_key_update_v2(filter, settings); + return filter; +} + +static void chroma_key_render_v1(void *data, gs_effect_t *effect) { struct chroma_key_filter_data *filter = data; obs_source_t *target = obs_filter_get_target(filter->context); @@ -220,12 +348,36 @@ static void chroma_key_render(void *data, gs_effect_t *effect) vec2_set(&pixel_size, 1.0f / (float)width, 1.0f / (float)height); - if (filter->color_param) - gs_effect_set_vec4(filter->color_param, &filter->color); + gs_effect_set_vec4(filter->color_param, &filter->color); + gs_effect_set_float(filter->contrast_param, filter->contrast); + gs_effect_set_float(filter->brightness_param, filter->brightness); + gs_effect_set_float(filter->gamma_param, filter->gamma); + gs_effect_set_vec2(filter->chroma_param, &filter->chroma); + gs_effect_set_vec2(filter->pixel_size_param, &pixel_size); + gs_effect_set_float(filter->similarity_param, filter->similarity); + gs_effect_set_float(filter->smoothness_param, filter->smoothness); + gs_effect_set_float(filter->spill_param, filter->spill); - if (filter->opacity_param) - gs_effect_set_float(filter->opacity_param, filter->opacity); + obs_source_process_filter_end(filter->context, filter->effect, 0, 0); + UNUSED_PARAMETER(effect); +} + +static void chroma_key_render_v2(void *data, gs_effect_t *effect) +{ + struct chroma_key_filter_data_v2 *filter = data; + obs_source_t *target = obs_filter_get_target(filter->context); + uint32_t width = obs_source_get_base_width(target); + uint32_t height = obs_source_get_base_height(target); + struct vec2 pixel_size; + + if (!obs_source_process_filter_begin(filter->context, GS_RGBA, + OBS_ALLOW_DIRECT_RENDERING)) + return; + + vec2_set(&pixel_size, 1.0f / (float)width, 1.0f / (float)height); + + gs_effect_set_float(filter->opacity_param, filter->opacity); gs_effect_set_float(filter->contrast_param, filter->contrast); gs_effect_set_float(filter->brightness_param, filter->brightness); gs_effect_set_float(filter->gamma_param, filter->gamma); @@ -258,7 +410,7 @@ static bool key_type_changed(obs_properties_t *props, obs_property_t *p, return true; } -static obs_properties_t *chroma_key_properties(uint32_t version) +static obs_properties_t *chroma_key_properties_v1(void *data) { obs_properties_t *props = obs_properties_create(); @@ -281,37 +433,58 @@ static obs_properties_t *chroma_key_properties(uint32_t version) obs_properties_add_int_slider(props, SETTING_SPILL, TEXT_SPILL, 1, 1000, 1); - if (version == 1) { - obs_properties_add_int_slider(props, SETTING_OPACITY, - TEXT_OPACITY, 0, 100, 1); - } else if (version == 2) { - obs_properties_add_float_slider(props, SETTING_OPACITY, - TEXT_OPACITY, 0.0, 1.0, 0.0001); - } + obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0, + 100, 1); + obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, + -1.0, 1.0, 0.01); + obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, + TEXT_BRIGHTNESS, -1.0, 1.0, 0.01); + obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -1.0, + 1.0, 0.01); + UNUSED_PARAMETER(data); + return props; +} + +static obs_properties_t *chroma_key_properties_v2(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *p = obs_properties_add_list(props, SETTING_COLOR_TYPE, + TEXT_COLOR_TYPE, + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, obs_module_text("Green"), "green"); + obs_property_list_add_string(p, obs_module_text("Blue"), "blue"); + obs_property_list_add_string(p, obs_module_text("Magenta"), "magenta"); + obs_property_list_add_string(p, obs_module_text("Custom"), "custom"); + + obs_property_set_modified_callback(p, key_type_changed); + + obs_properties_add_color(props, SETTING_KEY_COLOR, TEXT_KEY_COLOR); + obs_properties_add_int_slider(props, SETTING_SIMILARITY, + TEXT_SIMILARITY, 1, 1000, 1); + obs_properties_add_int_slider(props, SETTING_SMOOTHNESS, + TEXT_SMOOTHNESS, 1, 1000, 1); + obs_properties_add_int_slider(props, SETTING_SPILL, TEXT_SPILL, 1, 1000, + 1); + + obs_properties_add_float_slider(props, SETTING_OPACITY, TEXT_OPACITY, + 0.0, 1.0, 0.0001); obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, -4.0, 4.0, 0.01); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, TEXT_BRIGHTNESS, -1.0, 1.0, 0.0001); obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -1.0, 1.0, 0.01); + + UNUSED_PARAMETER(data); return props; } -static obs_properties_t *chroma_key_properties_v1(void *data) -{ - UNUSED_PARAMETER(data); - return chroma_key_properties(1); -} - -static obs_properties_t *chroma_key_properties_v2(void *data) -{ - UNUSED_PARAMETER(data); - return chroma_key_properties(2); -} - -static void chroma_key_defaults(obs_data_t *settings) +static void chroma_key_defaults_v1(obs_data_t *settings) { + obs_data_set_default_int(settings, SETTING_OPACITY, 100); obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0); obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0); obs_data_set_default_double(settings, SETTING_GAMMA, 0.0); @@ -322,16 +495,17 @@ static void chroma_key_defaults(obs_data_t *settings) obs_data_set_default_int(settings, SETTING_SPILL, 100); } -static void chroma_key_defaults_v1(obs_data_t *settings) -{ - chroma_key_defaults(settings); - obs_data_set_default_int(settings, SETTING_OPACITY, 100); -} - static void chroma_key_defaults_v2(obs_data_t *settings) { - chroma_key_defaults(settings); obs_data_set_default_double(settings, SETTING_OPACITY, 1.0); + obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0); + obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0); + obs_data_set_default_double(settings, SETTING_GAMMA, 0.0); + obs_data_set_default_int(settings, SETTING_KEY_COLOR, 0x00FF00); + obs_data_set_default_string(settings, SETTING_COLOR_TYPE, "green"); + obs_data_set_default_int(settings, SETTING_SIMILARITY, 400); + obs_data_set_default_int(settings, SETTING_SMOOTHNESS, 80); + obs_data_set_default_int(settings, SETTING_SPILL, 100); } struct obs_source_info chroma_key_filter = { @@ -339,10 +513,10 @@ struct obs_source_info chroma_key_filter = { .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE, .get_name = chroma_key_name, - .create = chroma_key_create, - .destroy = chroma_key_destroy, - .video_render = chroma_key_render, - .update = chroma_key_update, + .create = chroma_key_create_v1, + .destroy = chroma_key_destroy_v1, + .video_render = chroma_key_render_v1, + .update = chroma_key_update_v1, .get_properties = chroma_key_properties_v1, .get_defaults = chroma_key_defaults_v1, }; @@ -353,10 +527,10 @@ struct obs_source_info chroma_key_filter_v2 = { .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, .get_name = chroma_key_name, - .create = chroma_key_create, - .destroy = chroma_key_destroy, - .video_render = chroma_key_render, - .update = chroma_key_update, + .create = chroma_key_create_v2, + .destroy = chroma_key_destroy_v2, + .video_render = chroma_key_render_v2, + .update = chroma_key_update_v2, .get_properties = chroma_key_properties_v2, .get_defaults = chroma_key_defaults_v2, };