From bac06faa9ea6866593175700baac47b7b4b1b56e Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Mon, 28 Aug 2023 15:37:17 -0500 Subject: [PATCH 01/15] Initial code for masking. --- data/locale/en-US.ini | 15 +- data/shaders/effect_mask_crop.effect | 45 ++++++ src/blur/dual_kawase.c | 1 + src/obs-composite-blur-filter.c | 212 ++++++++++++++++++++++++++- src/obs-composite-blur-filter.h | 36 ++++- 5 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 data/shaders/effect_mask_crop.effect diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 2c626d0..ecc4f53 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -28,4 +28,17 @@ CompositeBlurFilter.Pixelate.Hexagonal="Hexagonal" CompositeBlurFilter.Pixelate.Triakis="Triakis" CompositeBlurFilter.Pixelate.Circle="Circle" CompositeBlurFilter.Pixelate.Triangle="Triangle" -CompositeBlurFilter.DualKawase.Passes="Blur Radius" \ No newline at end of file +CompositeBlurFilter.DualKawase.Passes="Blur Radius" +CompositeBlurFilter.EffectMask="Effect Mask" +CompositeBlurFilter.EffectMask.CropParameters="Crop Parameters" +CompositeBlurFilter.EffectMask.None="None" +CompositeBlurFilter.EffectMask.Crop="Crop" +CompositeBlurFilter.EffectMask.Rectangle="Rectangle" +CompositeBlurFilter.EffectMask.Circle="Circle" +CompositeBlurFilter.EffectMask.Source="Source" +CompositeBlurFilter.EffectMask.Image="Image" +CompositeBlurFilter.EffectMask.Crop.Top="Top" +CompositeBlurFilter.EffectMask.Crop.Bottom="Bottom" +CompositeBlurFilter.EffectMask.Crop.Left="Left" +CompositeBlurFilter.EffectMask.Crop.Right="Right" +CompositeBlurFilter.EffectMask.CornerRadius \ No newline at end of file diff --git a/data/shaders/effect_mask_crop.effect b/data/shaders/effect_mask_crop.effect new file mode 100644 index 0000000..0600af1 --- /dev/null +++ b/data/shaders/effect_mask_crop.effect @@ -0,0 +1,45 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +uniform texture2d filtered_image; + +uniform float2 scale; +uniform float2 offset; +uniform bool inv = false; + +sampler_state textureSampler{ + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + return v_in; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 transform_coord = v_in.uv * scale - offset; + return float4(1.0, 1.0, 0.0, 1.0); + if(transform_coord.x < 0.0f || transform_coord.y < 0.0f || transform_coord.x > 1.0f || transform_coord.y > 1.0f) { + return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); + } + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} \ No newline at end of file diff --git a/src/blur/dual_kawase.c b/src/blur/dual_kawase.c index e1359fa..01d6ef5 100644 --- a/src/blur/dual_kawase.c +++ b/src/blur/dual_kawase.c @@ -133,6 +133,7 @@ static void dual_kawase_blur(composite_blur_filter_data_t *data) { gs_texture_t *texture = gs_texrender_get_texture(data->input_texrender); if (data->kawase_passes <= 1) { + // TODO- COPY INPUT TO OUTPUT TO PRESERVE INPUT. gs_texrender_t *tmp = data->input_texrender; data->input_texrender = data->output_texrender; data->output_texrender = tmp; diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 207c498..4de08f7 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -53,6 +53,14 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->kernel_texture = NULL; filter->pixelate_type = 1; filter->pixelate_type_last = -1; + filter->mask_crop_left = 0.0f; + filter->mask_crop_right = 0.0f; + filter->mask_crop_top = 0.0f; + filter->mask_crop_bot = 0.0f; + filter->mask_type = 0; + filter->mask_type_last = -1; + filter->mask_crop_corner_radius = 0.0f; + filter->mask_crop_invert = false; da_init(filter->kernel); @@ -78,6 +86,9 @@ static void composite_blur_destroy(void *data) if (filter->mix_effect) { gs_effect_destroy(filter->mix_effect); } + if (filter->effect_mask_effect) { + gs_effect_destroy(filter->effect_mask_effect); + } if (filter->render) { gs_texrender_destroy(filter->render); } @@ -139,6 +150,23 @@ static void composite_blur_update(void *data, obs_data_t *settings) filter->reload = true; } + filter->mask_type = (int)obs_data_get_int(settings, "effect_mask"); + filter->mask_crop_top = + (float)obs_data_get_double(settings, "effect_mask_crop_top"); + filter->mask_crop_bot = + (float)obs_data_get_double(settings, "effect_mask_crop_bottom"); + filter->mask_crop_left = + (float)obs_data_get_double(settings, "effect_mask_crop_left"); + filter->mask_crop_right = + (float)obs_data_get_double(settings, "effect_mask_crop_right"); + filter->mask_crop_corner_radius = (float)obs_data_get_double( + settings, "effect_mask_crop_corner_radius"); + + if (filter->mask_type != filter->mask_type_last) { + filter->mask_type_last = filter->mask_type; + effect_mask_load_effect(filter); + } + filter->radius = (float)obs_data_get_double(settings, "radius"); filter->passes = (int)obs_data_get_int(settings, "passes"); filter->kawase_passes = @@ -238,6 +266,11 @@ static void composite_blur_video_render(void *data, gs_effect_t *effect) // 2. Apply effect to texture, and render texture to video filter->video_render(filter); + if (filter->mask_type != EFFECT_MASK_TYPE_NONE) { + // Swap output and render + apply_effect_mask(filter); + } + // 3. Draw result (filter->output_texrender) to source draw_output_to_source(filter); filter->rendered = true; @@ -246,6 +279,69 @@ static void composite_blur_video_render(void *data, gs_effect_t *effect) filter->rendering = false; } +static void apply_effect_mask(composite_blur_filter_data_t *filter) +{ + switch (filter->mask_type) { + case EFFECT_MASK_TYPE_CROP: + apply_effect_mask_crop(filter); + break; + } +} + +static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) +{ + // Swap output with render + gs_texrender_t *tmp = filter->output_texrender; + filter->output_texrender = filter->render; + filter->render = tmp; + + gs_effect_t *effect = filter->effect_mask_effect; + gs_texture_t *texture = + gs_texrender_get_texture(filter->input_texrender); + gs_texture_t *filtered_texture = + gs_texrender_get_texture(filter->render); + + if (!effect || !texture) { + return; + } + + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); + gs_effect_set_texture(image, texture); + + gs_eparam_t *filtered_image = + gs_effect_get_param_by_name(effect, "filtered_image"); + gs_effect_set_texture(filtered_image, filtered_texture); + + gs_eparam_t *scale = gs_effect_get_param_by_name(effect, "scale"); + struct vec2 scale_v; + scale_v.x = 1.0f / + (1.0f - filter->mask_crop_right - filter->mask_crop_left); + scale_v.y = + 1.0f / (1.0f - filter->mask_crop_bot - filter->mask_crop_top); + gs_effect_set_vec2(scale, &scale_v); + + gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); + struct vec2 offset_v; + offset_v.x = filter->mask_crop_left; + offset_v.y = filter->mask_crop_top; + gs_effect_set_vec2(offset, &offset_v); + + set_blending_parameters(); + + filter->output_texrender = + create_or_reset_texrender(filter->output_texrender); + + if (gs_texrender_begin(filter->output_texrender, filter->width, + filter->height)) { + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(texture, 0, filter->width, + filter->height); + gs_texrender_end(filter->output_texrender); + } + texture = gs_texrender_get_texture(filter->output_texrender); + gs_blend_state_pop(); +} + static obs_properties_t *composite_blur_properties(void *data) { struct composite_blur_filter_data *filter = data; @@ -370,9 +466,83 @@ static obs_properties_t *composite_blur_properties(void *data) obs_enum_sources(add_source_to_list, p); obs_enum_scenes(add_source_to_list, p); + obs_property_t *effect_mask_list = obs_properties_add_list( + props, "effect_mask", + obs_module_text("CompositeBlurFilter.EffectMask"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_NONE_LABEL), + EFFECT_MASK_TYPE_NONE); + obs_property_list_add_int(effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_CROP_LABEL), + EFFECT_MASK_TYPE_CROP); + + obs_property_set_modified_callback(effect_mask_list, + setting_effect_mask_modified); + + obs_properties_t *effect_mask_crop = obs_properties_create(); + + obs_properties_add_float_slider( + effect_mask_crop, "effect_mask_crop_top", + obs_module_text("CompositeBlurFilter.EffectMask.Crop.Top"), 0.0, + 100.01, 0.1); + + obs_properties_add_float_slider( + effect_mask_crop, "effect_mask_crop_bottom", + obs_module_text("CompositeBlurFilter.EffectMask.Crop.Bottom"), + 0.0, 100.01, 0.1); + + obs_properties_add_float_slider( + effect_mask_crop, "effect_mask_crop_left", + obs_module_text("CompositeBlurFilter.EffectMask.Crop.Left"), + 0.0, 100.01, 0.1); + + obs_properties_add_float_slider( + effect_mask_crop, "effect_mask_crop_right", + obs_module_text("CompositeBlurFilter.EffectMask.Crop.Right"), + 0.0, 100.01, 0.1); + + obs_properties_add_float_slider( + effect_mask_crop, "effect_mask_crop_corner_radius", + obs_module_text("CompositeBlurFilter.EffectMask.CornerRadius"), + 0.0, 50.01, 0.1); + + obs_properties_add_group( + props, "effect_mask_crop", + obs_module_text( + "CompositeBlurFilter.EffectMask.CropParameters"), + OBS_GROUP_NORMAL, effect_mask_crop); + return props; } +static bool setting_effect_mask_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + int mask_type = (int)obs_data_get_int(settings, "effect_mask"); + switch (mask_type) { + case EFFECT_MASK_TYPE_NONE: + setting_visibility("effect_mask_crop", false, props); + break; + case EFFECT_MASK_TYPE_CROP: + setting_visibility("effect_mask_crop", true, props); + break; + } + return true; +} + +static void effect_mask_load_effect(composite_blur_filter_data_t *filter) +{ + switch (filter->mask_type) { + case EFFECT_MASK_TYPE_CROP: + load_crop_mask_effect(filter); + break; + } +} + static bool setting_blur_algorithm_modified(void *data, obs_properties_t *props, obs_property_t *p, obs_data_t *settings) @@ -594,6 +764,46 @@ static void load_composite_effect(composite_blur_filter_data_t *filter) } } +static void load_crop_mask_effect(composite_blur_filter_data_t *filter) +{ + if (filter->composite_effect != NULL) { + obs_enter_graphics(); + gs_effect_destroy(filter->composite_effect); + filter->composite_effect = NULL; + obs_leave_graphics(); + } + + char *shader_text = NULL; + struct dstr filename = {0}; + dstr_cat(&filename, obs_get_module_data_path(obs_current_module())); + dstr_cat(&filename, "/shaders/effect_mask_crop.effect"); + shader_text = load_shader_from_file(filename.array); + char *errors = NULL; + + obs_enter_graphics(); + filter->composite_effect = gs_effect_create(shader_text, NULL, &errors); + obs_leave_graphics(); + + bfree(shader_text); + if (filter->composite_effect == NULL) { + blog(LOG_WARNING, + "[obs-composite-blur] Unable to load effect_mask_crop.effect file. Errors:\n%s", + (errors == NULL || strlen(errors) == 0 ? "(None)" + : errors)); + bfree(errors); + } else { + size_t effect_count = + gs_effect_get_num_params(filter->composite_effect); + for (size_t effect_index = 0; effect_index < effect_count; + effect_index++) { + gs_eparam_t *param = gs_effect_get_param_by_idx( + filter->composite_effect, effect_index); + struct gs_effect_param_info info; + gs_effect_get_param_info(param, &info); + } + } +} + static void load_mix_effect(composite_blur_filter_data_t *filter) { if (filter->mix_effect != NULL) { @@ -617,7 +827,7 @@ static void load_mix_effect(composite_blur_filter_data_t *filter) bfree(shader_text); if (filter->mix_effect == NULL) { blog(LOG_WARNING, - "[obs-composite-blur] Unable to load composite.effect file. Errors:\n%s", + "[obs-composite-blur] Unable to load mix.effect file. Errors:\n%s", (errors == NULL || strlen(errors) == 0 ? "(None)" : errors)); bfree(errors); diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 3e8a4d8..d1c8d32 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -45,6 +45,19 @@ #define PIXELATE_TYPE_TRIANGLE 4 #define PIXELATE_TYPE_TRIANGLE_LABEL "CompositeBlurFilter.Pixelate.Triangle" +#define EFFECT_MASK_TYPE_NONE 0 +#define EFFECT_MASK_TYPE_NONE_LABEL "CompositeBlurFilter.EffectMask.None" +#define EFFECT_MASK_TYPE_CROP 1 +#define EFFECT_MASK_TYPE_CROP_LABEL "CompositeBlurFilter.EffectMask.Crop" +#define EFFECT_MASK_TYPE_RECT 2 +#define EFFECT_MASK_TYPE_RECT_LABEL "CompositeBlurFilter.EffectMask.Rectangle" +#define EFFECT_MASK_TYPE_CIRCLE 3 +#define EFFECT_MASK_TYPE_CIRCLE_LABEL "CompositeBlurFilter.EffectMask.Circle" +#define EFFECT_MASK_TYPE_SOURCE 4 +#define EFFECT_MASK_TYPE_SOURCE_LABEL "CompositeBlurFilter.EffectMask.Source" +#define EFFECT_MASK_TYPE_IMAGE 5 +#define EFFECT_MASK_TYPE_IMAGE_LABEL "CompositeBlurFilter.EffectMask.Image" + typedef DARRAY(float) fDarray; struct composite_blur_filter_data; @@ -58,6 +71,7 @@ struct composite_blur_filter_data { gs_effect_t *effect_2; gs_effect_t *composite_effect; gs_effect_t *mix_effect; + gs_effect_t *effect_mask_effect; // Render pipeline bool input_rendered; @@ -97,6 +111,17 @@ struct composite_blur_filter_data { int kawase_passes; int pixelate_type; int pixelate_type_last; + + // Mask + int mask_type; + int mask_type_last; + float mask_crop_left; + float mask_crop_right; + float mask_crop_top; + float mask_crop_bot; + float mask_crop_corner_radius; + bool mask_crop_invert; + obs_weak_source_t *background; uint32_t width; uint32_t height; @@ -131,7 +156,9 @@ extern gs_texture_t *blend_composite(gs_texture_t *texture, static bool setting_blur_algorithm_modified(void *data, obs_properties_t *props, obs_property_t *p, obs_data_t *settings); - +static bool setting_effect_mask_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings); static bool setting_blur_types_modified(void *data, obs_properties_t *props, obs_property_t *p, obs_data_t *settings); @@ -143,4 +170,9 @@ static void set_blur_radius_settings(const char *name, float min_val, static bool settings_blur_area(obs_properties_t *props, obs_data_t *settings); static bool settings_blur_directional(obs_properties_t *props); static bool settings_blur_zoom(obs_properties_t *props); -static bool settings_blur_tilt_shift(obs_properties_t *props); \ No newline at end of file +static bool settings_blur_tilt_shift(obs_properties_t *props); + +static void apply_effect_mask(composite_blur_filter_data_t *filter); +static void apply_effect_mask_crop(composite_blur_filter_data_t *filter); +static void load_crop_mask_effect(composite_blur_filter_data_t *filter); +static void effect_mask_load_effect(composite_blur_filter_data_t *filter); \ No newline at end of file From 9539187bf31a82e96c453ada2ff17100845b65b1 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Tue, 29 Aug 2023 11:53:12 -0500 Subject: [PATCH 02/15] Adds crop mask, with optional rounded corners. --- data/locale/en-US.ini | 11 +++-- data/shaders/effect_mask_crop.effect | 48 ++++++++++++++++-- src/obs-composite-blur-filter.c | 74 +++++++++++++++++++--------- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index ecc4f53..9279beb 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -37,8 +37,9 @@ CompositeBlurFilter.EffectMask.Rectangle="Rectangle" CompositeBlurFilter.EffectMask.Circle="Circle" CompositeBlurFilter.EffectMask.Source="Source" CompositeBlurFilter.EffectMask.Image="Image" -CompositeBlurFilter.EffectMask.Crop.Top="Top" -CompositeBlurFilter.EffectMask.Crop.Bottom="Bottom" -CompositeBlurFilter.EffectMask.Crop.Left="Left" -CompositeBlurFilter.EffectMask.Crop.Right="Right" -CompositeBlurFilter.EffectMask.CornerRadius \ No newline at end of file +CompositeBlurFilter.EffectMask.Crop.Top="Top (%)" +CompositeBlurFilter.EffectMask.Crop.Bottom="Bottom (%)" +CompositeBlurFilter.EffectMask.Crop.Left="Left (%)" +CompositeBlurFilter.EffectMask.Crop.Right="Right (%)" +CompositeBlurFilter.EffectMask.CornerRadius="Corner Radius (%)" +CompositeBlurFilter.EffectMask.Invert="Invert Mask?" \ No newline at end of file diff --git a/data/shaders/effect_mask_crop.effect b/data/shaders/effect_mask_crop.effect index 0600af1..a28bd08 100644 --- a/data/shaders/effect_mask_crop.effect +++ b/data/shaders/effect_mask_crop.effect @@ -4,6 +4,8 @@ uniform texture2d filtered_image; uniform float2 scale; uniform float2 offset; +uniform float2 box_aspect_ratio; +uniform float corner_radius; uniform bool inv = false; sampler_state textureSampler{ @@ -27,12 +29,52 @@ VertData mainTransform(VertData v_in) float4 mainImage(VertData v_in) : TARGET { - float2 transform_coord = v_in.uv * scale - offset; - return float4(1.0, 1.0, 0.0, 1.0); + float2 transform_coord = (v_in.uv - offset) * scale; if(transform_coord.x < 0.0f || transform_coord.y < 0.0f || transform_coord.x > 1.0f || transform_coord.y > 1.0f) { + // Outside + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + } + // Inside + // 1. Scale to 1:1 ratio, shorten long side + float2 inner_coord = transform_coord * box_aspect_ratio; + if( + inner_coord.x > corner_radius && inner_coord.y > corner_radius && + inner_coord.x < (box_aspect_ratio.x - corner_radius) && inner_coord.y < (box_aspect_ratio.y - corner_radius) + ) { return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); } - return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + if(inner_coord.x < corner_radius && inner_coord.y < corner_radius) { + // top left + if (distance(inner_coord, float2(corner_radius, corner_radius)) > corner_radius) { + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + } + } else if(inner_coord.x < corner_radius && inner_coord.y > (box_aspect_ratio.y - corner_radius)) { + // bot left + if (distance(inner_coord, float2(corner_radius, box_aspect_ratio.y - corner_radius)) > corner_radius) { + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + } + } else if(inner_coord.x > (box_aspect_ratio.x - corner_radius) && inner_coord.y < corner_radius) { + // top right + if (distance(inner_coord, float2(box_aspect_ratio.x - corner_radius, corner_radius)) > corner_radius) { + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + } + } else if(inner_coord.x > (box_aspect_ratio.x - corner_radius) && inner_coord.y > (box_aspect_ratio.y - corner_radius)) { + // bot right + if (distance(inner_coord, float2(box_aspect_ratio.x - corner_radius, box_aspect_ratio.y - corner_radius)) > corner_radius) { + return inv ? filtered_image.Sample(textureSampler, v_in.uv) : image.Sample(textureSampler, v_in.uv); + } + } + + return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); + + // else if( + // transform_coord.x > corner_radius && transform_coord.x < 1.0f-corner_radius && + // transform_coord.y > corner_radius && transform_coord.y < 1.0f-corner_radius + // ) { + // return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); + // } + // return float4(1.0, 0.0, 1.0, 1.0); + } technique Draw diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 4de08f7..929c842 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -161,7 +161,8 @@ static void composite_blur_update(void *data, obs_data_t *settings) (float)obs_data_get_double(settings, "effect_mask_crop_right"); filter->mask_crop_corner_radius = (float)obs_data_get_double( settings, "effect_mask_crop_corner_radius"); - + filter->mask_crop_invert = + obs_data_get_bool(settings, "effect_mask_crop_invert"); if (filter->mask_type != filter->mask_type_last) { filter->mask_type_last = filter->mask_type; effect_mask_load_effect(filter); @@ -218,7 +219,6 @@ static void get_input_source(composite_blur_filter_data_t *filter) filter->height)) { set_blending_parameters(); - //set_render_parameters(); gs_ortho(0.0f, (float)filter->width, 0.0f, (float)filter->height, -100.0f, 100.0f); @@ -284,12 +284,20 @@ static void apply_effect_mask(composite_blur_filter_data_t *filter) switch (filter->mask_type) { case EFFECT_MASK_TYPE_CROP: apply_effect_mask_crop(filter); + break; + case EFFECT_MASK_TYPE_SOURCE: + break; } } static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) { + float right = filter->mask_crop_right / 100.0f; + float left = filter->mask_crop_left / 100.0f; + float top = filter->mask_crop_top / 100.0f; + float bot = filter->mask_crop_bot / 100.0f; + // Swap output with render gs_texrender_t *tmp = filter->output_texrender; filter->output_texrender = filter->render; @@ -301,10 +309,9 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) gs_texture_t *filtered_texture = gs_texrender_get_texture(filter->render); - if (!effect || !texture) { + if (!effect || !texture || !filtered_texture) { return; } - gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); @@ -314,18 +321,36 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) gs_eparam_t *scale = gs_effect_get_param_by_name(effect, "scale"); struct vec2 scale_v; - scale_v.x = 1.0f / - (1.0f - filter->mask_crop_right - filter->mask_crop_left); - scale_v.y = - 1.0f / (1.0f - filter->mask_crop_bot - filter->mask_crop_top); + scale_v.x = 1.0f / max(1.0f - right - left, 1.e-6f); + scale_v.y = 1.0f / max(1.0f - bot - top, 1.e-6f); gs_effect_set_vec2(scale, &scale_v); + gs_eparam_t *box_aspect_ratio = + gs_effect_get_param_by_name(effect, "box_aspect_ratio"); + struct vec2 box_ar; + box_ar.x = (1.0f - right - left) * filter->width / + min(filter->width, filter->height); + box_ar.y = (1.0f - bot - top) * filter->height / + min(filter->width, filter->height); + gs_effect_set_vec2(box_aspect_ratio, &box_ar); + gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); struct vec2 offset_v; - offset_v.x = filter->mask_crop_left; - offset_v.y = filter->mask_crop_top; + offset_v.x = 1.0f - right - left > 0.0f ? left : -1000.0f; + offset_v.y = 1.0f - bot - top > 0.0f ? top : -1000.0f; gs_effect_set_vec2(offset, &offset_v); + gs_eparam_t *invert = gs_effect_get_param_by_name(effect, "inv"); + bool invert_v = filter->mask_crop_invert; + gs_effect_set_bool(invert, invert_v); + + float radius = filter->mask_crop_corner_radius / 100.0f * + min(box_ar.x, box_ar.y); + + gs_eparam_t *corner_radius = + gs_effect_get_param_by_name(effect, "corner_radius"); + gs_effect_set_float(corner_radius, radius); + set_blending_parameters(); filter->output_texrender = @@ -486,27 +511,31 @@ static obs_properties_t *composite_blur_properties(void *data) obs_properties_add_float_slider( effect_mask_crop, "effect_mask_crop_top", obs_module_text("CompositeBlurFilter.EffectMask.Crop.Top"), 0.0, - 100.01, 0.1); + 100.01, 0.01); obs_properties_add_float_slider( effect_mask_crop, "effect_mask_crop_bottom", obs_module_text("CompositeBlurFilter.EffectMask.Crop.Bottom"), - 0.0, 100.01, 0.1); + 0.0, 100.01, 0.01); obs_properties_add_float_slider( effect_mask_crop, "effect_mask_crop_left", obs_module_text("CompositeBlurFilter.EffectMask.Crop.Left"), - 0.0, 100.01, 0.1); + 0.0, 100.01, 0.01); obs_properties_add_float_slider( effect_mask_crop, "effect_mask_crop_right", obs_module_text("CompositeBlurFilter.EffectMask.Crop.Right"), - 0.0, 100.01, 0.1); + 0.0, 100.01, 0.01); obs_properties_add_float_slider( effect_mask_crop, "effect_mask_crop_corner_radius", obs_module_text("CompositeBlurFilter.EffectMask.CornerRadius"), - 0.0, 50.01, 0.1); + 0.0, 50.01, 0.01); + + obs_properties_add_bool( + effect_mask_crop, "effect_mask_crop_invert", + obs_module_text("CompositeBlurFilter.EffectMask.Invert")); obs_properties_add_group( props, "effect_mask_crop", @@ -766,10 +795,10 @@ static void load_composite_effect(composite_blur_filter_data_t *filter) static void load_crop_mask_effect(composite_blur_filter_data_t *filter) { - if (filter->composite_effect != NULL) { + if (filter->effect_mask_effect != NULL) { obs_enter_graphics(); - gs_effect_destroy(filter->composite_effect); - filter->composite_effect = NULL; + gs_effect_destroy(filter->effect_mask_effect); + filter->effect_mask_effect = NULL; obs_leave_graphics(); } @@ -781,11 +810,12 @@ static void load_crop_mask_effect(composite_blur_filter_data_t *filter) char *errors = NULL; obs_enter_graphics(); - filter->composite_effect = gs_effect_create(shader_text, NULL, &errors); + filter->effect_mask_effect = + gs_effect_create(shader_text, NULL, &errors); obs_leave_graphics(); bfree(shader_text); - if (filter->composite_effect == NULL) { + if (filter->effect_mask_effect == NULL) { blog(LOG_WARNING, "[obs-composite-blur] Unable to load effect_mask_crop.effect file. Errors:\n%s", (errors == NULL || strlen(errors) == 0 ? "(None)" @@ -793,11 +823,11 @@ static void load_crop_mask_effect(composite_blur_filter_data_t *filter) bfree(errors); } else { size_t effect_count = - gs_effect_get_num_params(filter->composite_effect); + gs_effect_get_num_params(filter->effect_mask_effect); for (size_t effect_index = 0; effect_index < effect_count; effect_index++) { gs_eparam_t *param = gs_effect_get_param_by_idx( - filter->composite_effect, effect_index); + filter->effect_mask_effect, effect_index); struct gs_effect_param_info info; gs_effect_get_param_info(param, &info); } From 6507058c45a36f7f9dac9dad99ecb1cf79037b10 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Tue, 29 Aug 2023 15:46:20 -0500 Subject: [PATCH 03/15] Adds source masking, with options to mask by alpha, grayscale, luminosity, or manual --- data/locale/en-US.ini | 16 +- data/shaders/effect_mask_source.effect | 48 ++++ src/obs-composite-blur-filter.c | 331 ++++++++++++++++++++++++- src/obs-composite-blur-filter.h | 29 ++- 4 files changed, 420 insertions(+), 4 deletions(-) create mode 100644 data/shaders/effect_mask_source.effect diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 9279beb..1275e38 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1,4 +1,8 @@ CompositeBlurFilter="Composite Blur" +CompositeBlurFilter.Channel.Alpha="Alpha" +CompositeBlurFilter.Channel.Red="Red" +CompositeBlurFilter.Channel.Green="Green" +CompositeBlurFilter.Channel.Blue="Blue" CompositeBlurFilter.BlurAlgorithm="Blur Algorithm" CompositeBlurFilter.BlurType="Blur Type" CompositeBlurFilter.Radius="Blur radius" @@ -30,7 +34,7 @@ CompositeBlurFilter.Pixelate.Circle="Circle" CompositeBlurFilter.Pixelate.Triangle="Triangle" CompositeBlurFilter.DualKawase.Passes="Blur Radius" CompositeBlurFilter.EffectMask="Effect Mask" -CompositeBlurFilter.EffectMask.CropParameters="Crop Parameters" +CompositeBlurFilter.EffectMask.CropParameters="Crop Mask Parameters" CompositeBlurFilter.EffectMask.None="None" CompositeBlurFilter.EffectMask.Crop="Crop" CompositeBlurFilter.EffectMask.Rectangle="Rectangle" @@ -42,4 +46,12 @@ CompositeBlurFilter.EffectMask.Crop.Bottom="Bottom (%)" CompositeBlurFilter.EffectMask.Crop.Left="Left (%)" CompositeBlurFilter.EffectMask.Crop.Right="Right (%)" CompositeBlurFilter.EffectMask.CornerRadius="Corner Radius (%)" -CompositeBlurFilter.EffectMask.Invert="Invert Mask?" \ No newline at end of file +CompositeBlurFilter.EffectMask.Invert="Invert Mask?" +CompositeBlurFilter.EffectMask.SourceParameters="Source Mask Parameters" +CompositeBlurFilter.EffectMask.Source.Source="Source" +CompositeBlurFilter.EffectMask.Source.Filter="Mask Using" +CompositeBlurFilter.EffectMask.Source.Alpha="Alpha Channel" +CompositeBlurFilter.EffectMask.Source.Grayscale="Grayscale Value" +CompositeBlurFilter.EffectMask.Source.Luminosity="Luminosity" +CompositeBlurFilter.EffectMask.Source.Sliders="Manual RGBA Sliders" +CompositeBlurFilter.EffectMask.Source.Multiplier="Multiplier" diff --git a/data/shaders/effect_mask_source.effect b/data/shaders/effect_mask_source.effect new file mode 100644 index 0000000..b4a91b9 --- /dev/null +++ b/data/shaders/effect_mask_source.effect @@ -0,0 +1,48 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +uniform texture2d filtered_image; +uniform texture2d alpha_source; + +uniform float4 rgba_weights; +uniform float multiplier = 1.0; + +uniform bool inv = false; + +sampler_state textureSampler{ + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + return v_in; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float4 alpha_sample = alpha_source.Sample(textureSampler, v_in.uv) * rgba_weights; + float alpha = (alpha_sample.r + alpha_sample.g + alpha_sample.b + alpha_sample.a); + alpha = inv ? 1.0-alpha : alpha; + alpha *= multiplier; + alpha = clamp(alpha, 0.0, 1.0); + return filtered_image.Sample(textureSampler, v_in.uv) * alpha + image.Sample(textureSampler, v_in.uv) * (1.0-alpha); + //return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} \ No newline at end of file diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 929c842..95b324b 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -62,6 +62,15 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->mask_crop_corner_radius = 0.0f; filter->mask_crop_invert = false; + filter->mask_source_filter_type = EFFECT_MASK_SOURCE_FILTER_ALPHA; + filter->mask_source_filter_red = 0.0; + filter->mask_source_filter_green = 0.0; + filter->mask_source_filter_blue = 0.0; + filter->mask_source_filter_alpha = 0.0; + filter->mask_source_multiplier = 1.0; + filter->mask_source_source = NULL; + filter->mask_source_invert = false; + da_init(filter->kernel); obs_source_update(source, settings); @@ -168,6 +177,60 @@ static void composite_blur_update(void *data, obs_data_t *settings) effect_mask_load_effect(filter); } + filter->mask_source_filter_type = (int)obs_data_get_int( + settings, "effect_mask_source_filter_list"); + switch (filter->mask_source_filter_type) { + case EFFECT_MASK_SOURCE_FILTER_ALPHA: + filter->mask_source_filter_red = 0.0f; + filter->mask_source_filter_green = 0.0f; + filter->mask_source_filter_blue = 0.0f; + filter->mask_source_filter_alpha = 1.0f; + break; + case EFFECT_MASK_SOURCE_FILTER_GRAYSCALE: + filter->mask_source_filter_red = 0.33334f; + filter->mask_source_filter_green = 0.33333f; + filter->mask_source_filter_blue = 0.33333f; + filter->mask_source_filter_alpha = 0.0f; + break; + case EFFECT_MASK_SOURCE_FILTER_LUMINOSITY: + filter->mask_source_filter_red = 0.299f; + filter->mask_source_filter_green = 0.587f; + filter->mask_source_filter_blue = 0.114f; + filter->mask_source_filter_alpha = 0.0f; + break; + case EFFECT_MASK_SOURCE_FILTER_SLIDERS: + filter->mask_source_filter_red = (float)obs_data_get_double( + settings, "effect_mask_source_filter_red"); + filter->mask_source_filter_green = (float)obs_data_get_double( + settings, "effect_mask_source_filter_green"); + filter->mask_source_filter_blue = (float)obs_data_get_double( + settings, "effect_mask_source_filter_blue"); + filter->mask_source_filter_alpha = (float)obs_data_get_double( + settings, "effect_mask_source_filter_alpha"); + break; + } + + const char *mask_source_name = + obs_data_get_string(settings, "effect_mask_source_source"); + obs_source_t *mask_source = + (mask_source_name && strlen(mask_source_name)) + ? obs_get_source_by_name(mask_source_name) + : NULL; + if (mask_source) { + obs_weak_source_release(filter->mask_source_source); + filter->mask_source_source = + obs_source_get_weak_source(mask_source); + obs_source_release(mask_source); + } else { + filter->mask_source_source = NULL; + } + + filter->mask_source_multiplier = (float)obs_data_get_double( + settings, "effect_mask_source_filter_multiplier"); + + filter->mask_source_invert = + obs_data_get_bool(settings, "effect_mask_source_invert"); + filter->radius = (float)obs_data_get_double(settings, "radius"); filter->passes = (int)obs_data_get_int(settings, "passes"); filter->kawase_passes = @@ -286,11 +349,119 @@ static void apply_effect_mask(composite_blur_filter_data_t *filter) apply_effect_mask_crop(filter); break; case EFFECT_MASK_TYPE_SOURCE: - + apply_effect_mask_source(filter); break; } } +static void apply_effect_mask_source(composite_blur_filter_data_t *filter) +{ + // Get source + obs_source_t *source = + filter->mask_source_source + ? obs_weak_source_get_source(filter->mask_source_source) + : NULL; + if (!source) { + return; + } + + const enum gs_color_space preferred_spaces[] = { + GS_CS_SRGB, + GS_CS_SRGB_16F, + GS_CS_709_EXTENDED, + }; + const enum gs_color_space space = obs_source_get_color_space( + source, OBS_COUNTOF(preferred_spaces), preferred_spaces); + const enum gs_color_format format = gs_get_format_from_space(space); + + // Set up a tex renderer for source + gs_texrender_t *source_render = gs_texrender_create(format, GS_ZS_NONE); + uint32_t base_width = obs_source_get_base_width(source); + uint32_t base_height = obs_source_get_base_height(source); + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + if (gs_texrender_begin_with_color_space(source_render, base_width, + base_height, space)) { + const float w = (float)base_width; + const float h = (float)base_height; + uint32_t flags = obs_source_get_output_flags(source); + const bool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0; + const bool async = (flags & OBS_SOURCE_ASYNC) != 0; + struct vec4 clear_color; + + vec4_zero(&clear_color); + gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); + gs_ortho(0.0f, w, 0.0f, h, -100.0f, 100.0f); + + if (!custom_draw && !async) + obs_source_default_render(source); + else + obs_source_video_render(source); + gs_texrender_end(source_render); + } + gs_blend_state_pop(); + obs_source_release(source); + gs_texture_t *alpha_texture = gs_texrender_get_texture(source_render); + + // Swap output with render + gs_texrender_t *tmp = filter->output_texrender; + filter->output_texrender = filter->render; + filter->render = tmp; + + gs_effect_t *effect = filter->effect_mask_effect; + gs_texture_t *texture = + gs_texrender_get_texture(filter->input_texrender); + gs_texture_t *filtered_texture = + gs_texrender_get_texture(filter->render); + + if (!effect || !texture || !filtered_texture) { + return; + } + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); + gs_effect_set_texture(image, texture); + + gs_eparam_t *filtered_image = + gs_effect_get_param_by_name(effect, "filtered_image"); + gs_effect_set_texture(filtered_image, filtered_texture); + + gs_eparam_t *alpha_source = + gs_effect_get_param_by_name(effect, "alpha_source"); + gs_effect_set_texture(alpha_source, alpha_texture); + + gs_eparam_t *invert = gs_effect_get_param_by_name(effect, "inv"); + bool invert_v = filter->mask_source_invert; + gs_effect_set_bool(invert, invert_v); + + gs_eparam_t *rgba_weights = + gs_effect_get_param_by_name(effect, "rgba_weights"); + struct vec4 weights; + weights.x = filter->mask_source_filter_red; + weights.y = filter->mask_source_filter_green; + weights.z = filter->mask_source_filter_blue; + weights.w = filter->mask_source_filter_alpha; + gs_effect_set_vec4(rgba_weights, &weights); + + gs_eparam_t *multiplier = + gs_effect_get_param_by_name(effect, "multiplier"); + gs_effect_set_float(multiplier, filter->mask_source_multiplier); + + set_blending_parameters(); + + filter->output_texrender = + create_or_reset_texrender(filter->output_texrender); + + if (gs_texrender_begin(filter->output_texrender, filter->width, + filter->height)) { + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(texture, 0, filter->width, + filter->height); + gs_texrender_end(filter->output_texrender); + } + texture = gs_texrender_get_texture(filter->output_texrender); + gs_texrender_destroy(source_render); + gs_blend_state_pop(); +} + static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) { float right = filter->mask_crop_right / 100.0f; @@ -502,10 +673,86 @@ static obs_properties_t *composite_blur_properties(void *data) obs_property_list_add_int(effect_mask_list, obs_module_text(EFFECT_MASK_TYPE_CROP_LABEL), EFFECT_MASK_TYPE_CROP); + obs_property_list_add_int( + effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_SOURCE_LABEL), + EFFECT_MASK_TYPE_SOURCE); obs_property_set_modified_callback(effect_mask_list, setting_effect_mask_modified); + obs_properties_t *effect_mask_source = obs_properties_create(); + + obs_property_t *effect_mask_source_source = obs_properties_add_list( + effect_mask_source, "effect_mask_source_source", + obs_module_text("CompositeBlurFilter.EffectMask.Source.Source"), + OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(effect_mask_source_source, "None", ""); + obs_enum_sources(add_source_to_list, effect_mask_source_source); + obs_enum_scenes(add_source_to_list, effect_mask_source_source); + + obs_property_t *effect_mask_source_filter_list = obs_properties_add_list( + effect_mask_source, "effect_mask_source_filter_list", + obs_module_text("CompositeBlurFilter.EffectMask.Source.Filter"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int( + effect_mask_source_filter_list, + obs_module_text(EFFECT_MASK_SOURCE_FILTER_ALPHA_LABEL), + EFFECT_MASK_SOURCE_FILTER_ALPHA); + obs_property_list_add_int( + effect_mask_source_filter_list, + obs_module_text(EFFECT_MASK_SOURCE_FILTER_GRAYSCALE_LABEL), + EFFECT_MASK_SOURCE_FILTER_GRAYSCALE); + obs_property_list_add_int( + effect_mask_source_filter_list, + obs_module_text(EFFECT_MASK_SOURCE_FILTER_LUMINOSITY_LABEL), + EFFECT_MASK_SOURCE_FILTER_LUMINOSITY); + obs_property_list_add_int( + effect_mask_source_filter_list, + obs_module_text(EFFECT_MASK_SOURCE_FILTER_SLIDERS_LABEL), + EFFECT_MASK_SOURCE_FILTER_SLIDERS); + + obs_property_set_modified_callback( + effect_mask_source_filter_list, + setting_effect_mask_source_filter_modified); + + obs_properties_add_float_slider( + effect_mask_source, "effect_mask_source_filter_red", + obs_module_text("CompositeBlurFilter.Channel.Red"), -100.01, + 100.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_source, "effect_mask_source_filter_green", + obs_module_text("CompositeBlurFilter.Channel.Green"), -100.01, + 100.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_source, "effect_mask_source_filter_blue", + obs_module_text("CompositeBlurFilter.Channel.Blue"), -100.01, + 100.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_source, "effect_mask_source_filter_alpha", + obs_module_text("CompositeBlurFilter.Channel.Alpha"), -100.01, + 100.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_source, "effect_mask_source_filter_multiplier", + obs_module_text( + "CompositeBlurFilter.EffectMask.Source.Multiplier"), + -100.01, 100.01, 0.01); + + obs_properties_add_bool( + effect_mask_source, "effect_mask_source_invert", + obs_module_text("CompositeBlurFilter.EffectMask.Invert")); + + obs_properties_add_group( + props, "effect_mask_source", + obs_module_text( + "CompositeBlurFilter.EffectMask.SourceParameters"), + OBS_GROUP_NORMAL, effect_mask_source); + obs_properties_t *effect_mask_crop = obs_properties_create(); obs_properties_add_float_slider( @@ -546,6 +793,38 @@ static obs_properties_t *composite_blur_properties(void *data) return props; } +static bool setting_effect_mask_source_filter_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + int source_filter_type = (int)obs_data_get_int( + settings, "effect_mask_source_filter_list"); + switch (source_filter_type) { + case EFFECT_MASK_SOURCE_FILTER_SLIDERS: + setting_visibility("effect_mask_source_filter_red", true, + props); + setting_visibility("effect_mask_source_filter_green", true, + props); + setting_visibility("effect_mask_source_filter_blue", true, + props); + setting_visibility("effect_mask_source_filter_alpha", true, + props); + break; + default: + setting_visibility("effect_mask_source_filter_red", false, + props); + setting_visibility("effect_mask_source_filter_green", false, + props); + setting_visibility("effect_mask_source_filter_blue", false, + props); + setting_visibility("effect_mask_source_filter_alpha", false, + props); + break; + } + return true; +} + static bool setting_effect_mask_modified(obs_properties_t *props, obs_property_t *p, obs_data_t *settings) @@ -555,9 +834,15 @@ static bool setting_effect_mask_modified(obs_properties_t *props, switch (mask_type) { case EFFECT_MASK_TYPE_NONE: setting_visibility("effect_mask_crop", false, props); + setting_visibility("effect_mask_source", false, props); break; case EFFECT_MASK_TYPE_CROP: setting_visibility("effect_mask_crop", true, props); + setting_visibility("effect_mask_source", false, props); + break; + case EFFECT_MASK_TYPE_SOURCE: + setting_visibility("effect_mask_crop", false, props); + setting_visibility("effect_mask_source", true, props); break; } return true; @@ -569,6 +854,9 @@ static void effect_mask_load_effect(composite_blur_filter_data_t *filter) case EFFECT_MASK_TYPE_CROP: load_crop_mask_effect(filter); break; + case EFFECT_MASK_TYPE_SOURCE: + load_source_mask_effect(filter); + break; } } @@ -834,6 +1122,47 @@ static void load_crop_mask_effect(composite_blur_filter_data_t *filter) } } +static void load_source_mask_effect(composite_blur_filter_data_t *filter) +{ + if (filter->effect_mask_effect != NULL) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect_mask_effect); + filter->effect_mask_effect = NULL; + obs_leave_graphics(); + } + + char *shader_text = NULL; + struct dstr filename = {0}; + dstr_cat(&filename, obs_get_module_data_path(obs_current_module())); + dstr_cat(&filename, "/shaders/effect_mask_source.effect"); + shader_text = load_shader_from_file(filename.array); + char *errors = NULL; + + obs_enter_graphics(); + filter->effect_mask_effect = + gs_effect_create(shader_text, NULL, &errors); + obs_leave_graphics(); + + bfree(shader_text); + if (filter->effect_mask_effect == NULL) { + blog(LOG_WARNING, + "[obs-composite-blur] Unable to load effect_mask_crop.effect file. Errors:\n%s", + (errors == NULL || strlen(errors) == 0 ? "(None)" + : errors)); + bfree(errors); + } else { + size_t effect_count = + gs_effect_get_num_params(filter->effect_mask_effect); + for (size_t effect_index = 0; effect_index < effect_count; + effect_index++) { + gs_eparam_t *param = gs_effect_get_param_by_idx( + filter->effect_mask_effect, effect_index); + struct gs_effect_param_info info; + gs_effect_get_param_info(param, &info); + } + } +} + static void load_mix_effect(composite_blur_filter_data_t *filter) { if (filter->mix_effect != NULL) { diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index d1c8d32..92fef7a 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -58,6 +58,19 @@ #define EFFECT_MASK_TYPE_IMAGE 5 #define EFFECT_MASK_TYPE_IMAGE_LABEL "CompositeBlurFilter.EffectMask.Image" +#define EFFECT_MASK_SOURCE_FILTER_ALPHA 0 +#define EFFECT_MASK_SOURCE_FILTER_ALPHA_LABEL \ + "CompositeBlurFilter.EffectMask.Source.Alpha" +#define EFFECT_MASK_SOURCE_FILTER_GRAYSCALE 1 +#define EFFECT_MASK_SOURCE_FILTER_GRAYSCALE_LABEL \ + "CompositeBlurFilter.EffectMask.Source.Grayscale" +#define EFFECT_MASK_SOURCE_FILTER_LUMINOSITY 2 +#define EFFECT_MASK_SOURCE_FILTER_LUMINOSITY_LABEL \ + "CompositeBlurFilter.EffectMask.Source.Luminosity" +#define EFFECT_MASK_SOURCE_FILTER_SLIDERS 3 +#define EFFECT_MASK_SOURCE_FILTER_SLIDERS_LABEL \ + "CompositeBlurFilter.EffectMask.Source.Sliders" + typedef DARRAY(float) fDarray; struct composite_blur_filter_data; @@ -121,6 +134,14 @@ struct composite_blur_filter_data { float mask_crop_bot; float mask_crop_corner_radius; bool mask_crop_invert; + int mask_source_filter_type; + float mask_source_filter_red; + float mask_source_filter_green; + float mask_source_filter_blue; + float mask_source_filter_alpha; + float mask_source_multiplier; + bool mask_source_invert; + obs_weak_source_t *mask_source_source; obs_weak_source_t *background; uint32_t width; @@ -174,5 +195,11 @@ static bool settings_blur_tilt_shift(obs_properties_t *props); static void apply_effect_mask(composite_blur_filter_data_t *filter); static void apply_effect_mask_crop(composite_blur_filter_data_t *filter); +static void apply_effect_mask_source(composite_blur_filter_data_t *filter); static void load_crop_mask_effect(composite_blur_filter_data_t *filter); -static void effect_mask_load_effect(composite_blur_filter_data_t *filter); \ No newline at end of file +static void load_source_mask_effect(composite_blur_filter_data_t *filter); +static void effect_mask_load_effect(composite_blur_filter_data_t *filter); + +static bool setting_effect_mask_source_filter_modified(obs_properties_t *props, + obs_property_t *p, + obs_data_t *settings); \ No newline at end of file From c7645eb03441d441f659c80139129c542c2793cd Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Tue, 29 Aug 2023 17:28:45 -0500 Subject: [PATCH 04/15] Fixes build on MacOS --- src/obs-composite-blur-filter.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 95b324b..7d30eed 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -492,17 +492,17 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) gs_eparam_t *scale = gs_effect_get_param_by_name(effect, "scale"); struct vec2 scale_v; - scale_v.x = 1.0f / max(1.0f - right - left, 1.e-6f); - scale_v.y = 1.0f / max(1.0f - bot - top, 1.e-6f); + scale_v.x = 1.0f / fmax(1.0f - right - left, 1.e-6f); + scale_v.y = 1.0f / fmax(1.0f - bot - top, 1.e-6f); gs_effect_set_vec2(scale, &scale_v); gs_eparam_t *box_aspect_ratio = gs_effect_get_param_by_name(effect, "box_aspect_ratio"); struct vec2 box_ar; box_ar.x = (1.0f - right - left) * filter->width / - min(filter->width, filter->height); + fmin(filter->width, filter->height); box_ar.y = (1.0f - bot - top) * filter->height / - min(filter->width, filter->height); + fmin(filter->width, filter->height); gs_effect_set_vec2(box_aspect_ratio, &box_ar); gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); @@ -516,7 +516,7 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) gs_effect_set_bool(invert, invert_v); float radius = filter->mask_crop_corner_radius / 100.0f * - min(box_ar.x, box_ar.y); + fmin(box_ar.x, box_ar.y); gs_eparam_t *corner_radius = gs_effect_get_param_by_name(effect, "corner_radius"); From 918f725b901b6d10eb3202a231bb8ae86854c861 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Tue, 29 Aug 2023 19:47:55 -0500 Subject: [PATCH 05/15] Refactor param data into data struct. Change mask functions to use new struct. --- src/blur/box.c | 6 -- src/blur/dual_kawase.c | 4 - src/blur/gaussian.c | 6 -- src/blur/pixelate.c | 8 -- src/obs-composite-blur-filter.c | 136 +++++++++++++++++++++----------- src/obs-composite-blur-filter.h | 68 ++++++++++++++-- 6 files changed, 151 insertions(+), 77 deletions(-) diff --git a/src/blur/box.c b/src/blur/box.c index e366f7d..c729171 100644 --- a/src/blur/box.c +++ b/src/blur/box.c @@ -381,8 +381,6 @@ static void load_1d_box_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -402,8 +400,6 @@ static void load_tiltshift_box_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -423,8 +419,6 @@ static void load_radial_box_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } diff --git a/src/blur/dual_kawase.c b/src/blur/dual_kawase.c index 01d6ef5..26d0eac 100644 --- a/src/blur/dual_kawase.c +++ b/src/blur/dual_kawase.c @@ -208,8 +208,6 @@ load_dual_kawase_down_sample_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -230,8 +228,6 @@ load_dual_kawase_up_sample_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } diff --git a/src/blur/gaussian.c b/src/blur/gaussian.c index c1b558a..5f5ac6a 100644 --- a/src/blur/gaussian.c +++ b/src/blur/gaussian.c @@ -380,8 +380,6 @@ static void load_1d_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -406,8 +404,6 @@ static void load_motion_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -432,8 +428,6 @@ static void load_radial_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } diff --git a/src/blur/pixelate.c b/src/blur/pixelate.c index 39e22ec..09f175d 100644 --- a/src/blur/pixelate.c +++ b/src/blur/pixelate.c @@ -94,8 +94,6 @@ static void load_pixelate_square_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -115,8 +113,6 @@ static void load_pixelate_hexagonal_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -136,8 +132,6 @@ static void load_pixelate_circle_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } @@ -157,8 +151,6 @@ static void load_pixelate_triangle_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; } } } diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 7d30eed..4bb8e43 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -43,16 +43,36 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->kawase_passes = 1; filter->rendering = false; filter->reload = true; - filter->param_uv_size = NULL; - filter->param_dir = NULL; - filter->param_radius = NULL; - filter->param_background = NULL; filter->video_render = NULL; filter->load_effect = NULL; filter->update = NULL; filter->kernel_texture = NULL; filter->pixelate_type = 1; filter->pixelate_type_last = -1; + +// Params + filter->param_uv_size = NULL; + filter->param_radius = NULL; + filter->param_texel_step = NULL; + filter->param_kernel_size = NULL; + filter->param_offset = NULL; + filter->param_weight = NULL; + filter->param_kernel_texture = NULL; + filter->param_radial_center = NULL; + filter->param_focus_width = NULL; + filter->param_focus_center = NULL; + filter->param_focus_angle = NULL; + filter->param_background = NULL; + filter->param_mask_crop_scale = NULL; + filter->param_mask_crop_offset = NULL; + filter->param_mask_crop_box_aspect_ratio = NULL; + filter->param_mask_crop_corner_radius = NULL; + filter->param_mask_crop_invert = NULL; + filter->param_mask_source_alpha_source = NULL; + filter->param_mask_source_rgba_weights = NULL; + filter->param_mask_source_multiplier = NULL; + filter->param_mask_source_invert = NULL; + filter->mask_crop_left = 0.0f; filter->mask_crop_right = 0.0f; filter->mask_crop_top = 0.0f; @@ -420,31 +440,32 @@ static void apply_effect_mask_source(composite_blur_filter_data_t *filter) gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - gs_eparam_t *filtered_image = - gs_effect_get_param_by_name(effect, "filtered_image"); - gs_effect_set_texture(filtered_image, filtered_texture); + if(filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, filtered_texture); + } - gs_eparam_t *alpha_source = - gs_effect_get_param_by_name(effect, "alpha_source"); - gs_effect_set_texture(alpha_source, alpha_texture); + if(filter->param_mask_source_alpha_source) { + gs_effect_set_texture(filter->param_mask_source_alpha_source, alpha_texture); + } - gs_eparam_t *invert = gs_effect_get_param_by_name(effect, "inv"); - bool invert_v = filter->mask_source_invert; - gs_effect_set_bool(invert, invert_v); + if(filter->param_mask_source_invert) { + gs_effect_set_bool(filter->param_mask_source_invert, filter->mask_source_invert); + } - gs_eparam_t *rgba_weights = - gs_effect_get_param_by_name(effect, "rgba_weights"); + // TODO- Move weights calculation to update. + // Move vec4 weights into data structure. struct vec4 weights; weights.x = filter->mask_source_filter_red; weights.y = filter->mask_source_filter_green; weights.z = filter->mask_source_filter_blue; weights.w = filter->mask_source_filter_alpha; - gs_effect_set_vec4(rgba_weights, &weights); - - gs_eparam_t *multiplier = - gs_effect_get_param_by_name(effect, "multiplier"); - gs_effect_set_float(multiplier, filter->mask_source_multiplier); + if(filter->param_mask_source_rgba_weights) { + gs_effect_set_vec4(filter->param_mask_source_rgba_weights, &weights); + } + if(filter->param_mask_source_multiplier) { + gs_effect_set_float(filter->param_mask_source_multiplier, filter->mask_source_multiplier); + } set_blending_parameters(); filter->output_texrender = @@ -485,43 +506,44 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) } gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); + + if(filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, filtered_texture); + } - gs_eparam_t *filtered_image = - gs_effect_get_param_by_name(effect, "filtered_image"); - gs_effect_set_texture(filtered_image, filtered_texture); - - gs_eparam_t *scale = gs_effect_get_param_by_name(effect, "scale"); - struct vec2 scale_v; - scale_v.x = 1.0f / fmax(1.0f - right - left, 1.e-6f); - scale_v.y = 1.0f / fmax(1.0f - bot - top, 1.e-6f); - gs_effect_set_vec2(scale, &scale_v); + struct vec2 scale; + scale.x = 1.0f / fmax(1.0f - right - left, 1.e-6f); + scale.y = 1.0f / fmax(1.0f - bot - top, 1.e-6f); + if(filter->param_mask_crop_scale) { + gs_effect_set_vec2(filter->param_mask_crop_scale, &scale); + } - gs_eparam_t *box_aspect_ratio = - gs_effect_get_param_by_name(effect, "box_aspect_ratio"); struct vec2 box_ar; box_ar.x = (1.0f - right - left) * filter->width / fmin(filter->width, filter->height); box_ar.y = (1.0f - bot - top) * filter->height / fmin(filter->width, filter->height); - gs_effect_set_vec2(box_aspect_ratio, &box_ar); + if(filter->param_mask_crop_box_aspect_ratio) { + gs_effect_set_vec2(filter->param_mask_crop_box_aspect_ratio, &box_ar); + } - gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); - struct vec2 offset_v; - offset_v.x = 1.0f - right - left > 0.0f ? left : -1000.0f; - offset_v.y = 1.0f - bot - top > 0.0f ? top : -1000.0f; - gs_effect_set_vec2(offset, &offset_v); + struct vec2 offset; + offset.x = 1.0f - right - left > 0.0f ? left : -1000.0f; + offset.y = 1.0f - bot - top > 0.0f ? top : -1000.0f; + if(filter->param_mask_crop_offset) { + gs_effect_set_vec2(filter->param_mask_crop_offset, &offset); + } - gs_eparam_t *invert = gs_effect_get_param_by_name(effect, "inv"); bool invert_v = filter->mask_crop_invert; - gs_effect_set_bool(invert, invert_v); + if(filter->param_mask_crop_invert) { + gs_effect_set_bool(filter->param_mask_crop_invert, invert_v); + } float radius = filter->mask_crop_corner_radius / 100.0f * - fmin(box_ar.x, box_ar.y); - - gs_eparam_t *corner_radius = - gs_effect_get_param_by_name(effect, "corner_radius"); - gs_effect_set_float(corner_radius, radius); - + fmin(box_ar.x, box_ar.y); + if(filter->param_mask_crop_corner_radius) { + gs_effect_set_float(filter->param_mask_crop_corner_radius, radius); + } set_blending_parameters(); filter->output_texrender = @@ -1118,6 +1140,19 @@ static void load_crop_mask_effect(composite_blur_filter_data_t *filter) filter->effect_mask_effect, effect_index); struct gs_effect_param_info info; gs_effect_get_param_info(param, &info); + if (strcmp(info.name, "filtered_image") == 0) { + filter->param_filtered_image = param; + } else if(strcmp(info.name, "scale") == 0) { + filter->param_mask_crop_scale = param; + } else if(strcmp(info.name, "offset") == 0) { + filter->param_mask_crop_offset = param; + } else if(strcmp(info.name, "box_aspect_ratio") == 0) { + filter->param_mask_crop_box_aspect_ratio = param; + } else if(strcmp(info.name, "corner_radius") == 0) { + filter->param_mask_crop_corner_radius = param; + } else if(strcmp(info.name, "inv") == 0) { + filter->param_mask_crop_invert = param; + } } } } @@ -1159,6 +1194,17 @@ static void load_source_mask_effect(composite_blur_filter_data_t *filter) filter->effect_mask_effect, effect_index); struct gs_effect_param_info info; gs_effect_get_param_info(param, &info); + if (strcmp(info.name, "filtered_image") == 0) { + filter->param_filtered_image = param; + } else if(strcmp(info.name, "alpha_source") == 0) { + filter->param_mask_source_alpha_source = param; + } else if(strcmp(info.name, "rgba_weights") == 0) { + filter->param_mask_source_rgba_weights = param; + } else if(strcmp(info.name, "multiplier") == 0) { + filter->param_mask_source_multiplier= param; + } else if(strcmp(info.name, "inv") == 0) { + filter->param_mask_source_invert = param; + } } } } diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 92fef7a..2914855 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -91,9 +91,10 @@ struct composite_blur_filter_data { gs_texrender_t *input_texrender; bool output_rendered; gs_texrender_t *output_texrender; - + // Frame Buffers gs_texrender_t *render; gs_texrender_t *render2; + // Renderer for composite render step gs_texrender_t *composite_render; gs_eparam_t *param_uv_size; @@ -116,43 +117,94 @@ struct composite_blur_filter_data { float tilt_shift_center; float tilt_shift_width; float tilt_shift_angle; + // Blur Filter Common int blur_algorithm; int blur_algorithm_last; int blur_type; int blur_type_last; + + gs_eparam_t *param_uv_size; + struct vec2 uv_size; + gs_eparam_t *param_radius; + float radius; + float radius_last; + gs_eparam_t *param_texel_step; + struct vec2 texel_step; + + // Gaussuan Blur + gs_eparam_t *param_kernel_size; + size_t kernel_size; + gs_eparam_t *param_offset; + fDarray offset; + gs_eparam_t *param_weight; + fDarray kernel; + gs_eparam_t *param_kernel_texture; + gs_texture_t *kernel_texture; + + // Box Blur int passes; + + // Kawase Blur int kawase_passes; + + // Pixelate Blur int pixelate_type; int pixelate_type_last; + // Radial Blur + gs_eparam_t *param_radial_center; + float center_x; + float center_y; + + // Motion/Directional Blur + float angle; + + // Tilt-Shift + gs_eparam_t *param_focus_width; + float tilt_shift_width; + gs_eparam_t *param_focus_center; + float tilt_shift_center; + gs_eparam_t *param_focus_angle; + float tilt_shift_angle; + + // Compositing + gs_eparam_t *param_background; + obs_weak_source_t *background; + // Mask int mask_type; int mask_type_last; + gs_eparam_t *param_filtered_image; float mask_crop_left; float mask_crop_right; float mask_crop_top; float mask_crop_bot; + gs_eparam_t *param_mask_crop_scale; + gs_eparam_t *param_mask_crop_offset; + gs_eparam_t *param_mask_crop_box_aspect_ratio; + gs_eparam_t *param_mask_crop_corner_radius; float mask_crop_corner_radius; + gs_eparam_t *param_mask_crop_invert; bool mask_crop_invert; int mask_source_filter_type; float mask_source_filter_red; float mask_source_filter_green; float mask_source_filter_blue; float mask_source_filter_alpha; + gs_eparam_t *param_mask_source_alpha_source; + gs_eparam_t *param_mask_source_rgba_weights; + gs_eparam_t *param_mask_source_multiplier; float mask_source_multiplier; + gs_eparam_t *param_mask_source_invert; bool mask_source_invert; obs_weak_source_t *mask_source_source; - obs_weak_source_t *background; + bool rendering; + bool reload; + uint32_t width; uint32_t height; - // Gaussian Kernel - fDarray kernel; - fDarray offset; - gs_texture_t *kernel_texture; - size_t kernel_size; - // Callback Functions void (*video_render)(composite_blur_filter_data_t *filter); void (*load_effect)(composite_blur_filter_data_t *filter); From 05d4364e6c35b87ef9d4e7bd2e1f584cb97801f2 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Tue, 29 Aug 2023 20:48:47 -0500 Subject: [PATCH 06/15] Adds defaults callback for filter settings, and sets up some sane defaults. --- CMakeLists.txt | 2 +- buildspec.json | 160 ++++++++++++++++---------------- src/obs-composite-blur-filter.c | 22 +++-- src/obs-composite-blur-filter.h | 1 + src/version.h | 4 +- 5 files changed, 100 insertions(+), 89 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 24aa366..8150dfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ else() cmake_minimum_required(VERSION 3.18) endif() -project(obs-composite-blur VERSION 0.0.3) +project(obs-composite-blur VERSION 0.0.7) set(PROJECT_FULL_NAME "OBS Composite Blur") # Set new UUIDs when you start to create a new plugin. diff --git a/buildspec.json b/buildspec.json index 7ba004a..c1a1a78 100644 --- a/buildspec.json +++ b/buildspec.json @@ -1,83 +1,83 @@ { - "dependencies": { - "obs-studio": { - "version": "28.0.0-beta1", - "repository": "https://github.com/obsproject/obs-studio.git", - "branch": "master", - "hash": "43a49dca47344a5170159ef99b86b97f90d4e4ad" - }, - "prebuilt": { - "version": "2022-08-02", - "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", - "label": "Pre-built obs-deps", - "hashes": { - "macos-x86_64": "7637e52305e6fc53014b5aabd583f1a4490b1d97450420e977cae9a336a29525", - "macos-arm64": "755e0fa69b17a3ae444e1befa9d91d77e3cafe628fbd1c6333686091826595cd", - "macos-universal": "de057e73e6fe0825664c258ca2dd6798c41ae580bf4d896e1647676a4941934a", - "windows-x64": "2192d8ce780c4281b807cd457994963669e5202659ecd92f19b54c3e7d0c1915", - "windows-x86": "9f8582ab5891b000869d6484ea591add9fbac9f1c91b56c7b85fdfd56a261c1b" - } - }, - "qt5": { - "version": "2022-08-02", - "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", - "label": "Pre-built Qt5", - "hashes": { - "macos-x86_64": "3d0381a52b0e4d49967936c4357f79ac711f43564329304a6db5c90edadd2697", - "macos-arm64": "f4b32548c0530f121956bf0a9a70c36ecbbfca81073d39c396a1759baf2a05c3", - "macos-universal": "9a6cf3b9a6c9efee6ba10df649202e8075e99f3c54ae88dc9a36dbc9d7471c1e", - "windows-x64": "6488a33a474f750d5a4a268a5e20c78bb40799d99136a1b7ce3365a843cb2fd7", - "windows-x86": "a916e09b0a874036801deab2c8a7ec14fdf5d268aa5511eac5bf40727e0c4e33" - }, - "pdb-hashes": { - "windows-x64": "e0e5070143fcad9311a68ce5685d8ba8f34f581ed6942b7a92d360f94ca1ba11", - "windows-x86": "36642d1052aa461964f46c17610477b0d9b9defbe2d745ccaacb85f805c1bec2" - } - }, - "qt6": { - "version": "2022-08-02", - "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", - "label": "Pre-built Qt6", - "hashes": { - "macos-x86_64": "a83f72a11023b03b6cb2dc365f0a66ad9df31163bbb4fe2df32d601856a9fad3", - "macos-arm64": "2f30af90c049670a5660656adbb440668aa1b0567f75a5f29e1def9108928403", - "macos-universal": "252e6684f43ab9c6f262c73af739e2296ce391b998da2c4ee04c254aaa07db18", - "windows-x64": "e5509b54196a3f935250cc4b9c54160c8e588fd0f92bc078a2a64f9d9e2e4e93", - "windows-x86": "24fc03bef153a0e027c1479e42eb08097a4ea1d70a4710825be0783d0626cb0d" - }, - "pdb-hashes": { - "windows-x64": "60e5b1d2bc4d7c431bc05f14e3b1e85e088788c372fa85f58717cd6c49555a46", - "windows-x86": "f34d1a89fc85d92913bd6c7f75ec5c28471d74db708c98161100bc8b75f8fc63" - } - } + "dependencies": { + "obs-studio": { + "version": "28.0.0-beta1", + "repository": "https://github.com/obsproject/obs-studio.git", + "branch": "master", + "hash": "43a49dca47344a5170159ef99b86b97f90d4e4ad" }, - "platformConfig": { - "macos-x86_64": { - "qtVersion": 6, - "deploymentTarget": "10.15" - }, - "macos-arm64": { - "qtVersion": 6, - "deploymentTarget": "11.0" - }, - "macos-universal": { - "qtVersion": 6, - "deploymentTarget": "10.15" - }, - "windows-x64": { - "qtVersion": 6, - "visualStudio": "Visual Studio 17 2022", - "platformSDK": "10.0.20348.0" - }, - "windows-x86": { - "qtVersion": 6, - "visualStudio": "Visual Studio 17 2022", - "platformSDK": "10.0.20348.0" - }, - "linux-x86_64": { - "qtVersion": 6 - } + "prebuilt": { + "version": "2022-08-02", + "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", + "label": "Pre-built obs-deps", + "hashes": { + "macos-x86_64": "7637e52305e6fc53014b5aabd583f1a4490b1d97450420e977cae9a336a29525", + "macos-arm64": "755e0fa69b17a3ae444e1befa9d91d77e3cafe628fbd1c6333686091826595cd", + "macos-universal": "de057e73e6fe0825664c258ca2dd6798c41ae580bf4d896e1647676a4941934a", + "windows-x64": "2192d8ce780c4281b807cd457994963669e5202659ecd92f19b54c3e7d0c1915", + "windows-x86": "9f8582ab5891b000869d6484ea591add9fbac9f1c91b56c7b85fdfd56a261c1b" + } }, - "name": "obs-composite-blur", - "version": "0.0.3" -} \ No newline at end of file + "qt5": { + "version": "2022-08-02", + "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", + "label": "Pre-built Qt5", + "hashes": { + "macos-x86_64": "3d0381a52b0e4d49967936c4357f79ac711f43564329304a6db5c90edadd2697", + "macos-arm64": "f4b32548c0530f121956bf0a9a70c36ecbbfca81073d39c396a1759baf2a05c3", + "macos-universal": "9a6cf3b9a6c9efee6ba10df649202e8075e99f3c54ae88dc9a36dbc9d7471c1e", + "windows-x64": "6488a33a474f750d5a4a268a5e20c78bb40799d99136a1b7ce3365a843cb2fd7", + "windows-x86": "a916e09b0a874036801deab2c8a7ec14fdf5d268aa5511eac5bf40727e0c4e33" + }, + "pdb-hashes": { + "windows-x64": "e0e5070143fcad9311a68ce5685d8ba8f34f581ed6942b7a92d360f94ca1ba11", + "windows-x86": "36642d1052aa461964f46c17610477b0d9b9defbe2d745ccaacb85f805c1bec2" + } + }, + "qt6": { + "version": "2022-08-02", + "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", + "label": "Pre-built Qt6", + "hashes": { + "macos-x86_64": "a83f72a11023b03b6cb2dc365f0a66ad9df31163bbb4fe2df32d601856a9fad3", + "macos-arm64": "2f30af90c049670a5660656adbb440668aa1b0567f75a5f29e1def9108928403", + "macos-universal": "252e6684f43ab9c6f262c73af739e2296ce391b998da2c4ee04c254aaa07db18", + "windows-x64": "e5509b54196a3f935250cc4b9c54160c8e588fd0f92bc078a2a64f9d9e2e4e93", + "windows-x86": "24fc03bef153a0e027c1479e42eb08097a4ea1d70a4710825be0783d0626cb0d" + }, + "pdb-hashes": { + "windows-x64": "60e5b1d2bc4d7c431bc05f14e3b1e85e088788c372fa85f58717cd6c49555a46", + "windows-x86": "f34d1a89fc85d92913bd6c7f75ec5c28471d74db708c98161100bc8b75f8fc63" + } + } + }, + "platformConfig": { + "macos-x86_64": { + "qtVersion": 6, + "deploymentTarget": "10.15" + }, + "macos-arm64": { + "qtVersion": 6, + "deploymentTarget": "11.0" + }, + "macos-universal": { + "qtVersion": 6, + "deploymentTarget": "10.15" + }, + "windows-x64": { + "qtVersion": 6, + "visualStudio": "Visual Studio 17 2022", + "platformSDK": "10.0.20348.0" + }, + "windows-x86": { + "qtVersion": 6, + "visualStudio": "Visual Studio 17 2022", + "platformSDK": "10.0.20348.0" + }, + "linux-x86_64": { + "qtVersion": 6 + } + }, + "name": "obs-composite-blur", + "version": "0.0.7" +} diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 4bb8e43..9bbcc37 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -17,7 +17,9 @@ struct obs_source_info obs_composite_blur = { .video_tick = composite_blur_video_tick, .get_width = composite_blur_width, .get_height = composite_blur_height, - .get_properties = composite_blur_properties}; + .get_properties = composite_blur_properties, + .get_defaults = composite_blur_defaults + }; static const char *composite_blur_name(void *unused) { @@ -25,6 +27,15 @@ static const char *composite_blur_name(void *unused) return obs_module_text("CompositeBlurFilter"); } +static void composite_blur_defaults(obs_data_t *settings) { + obs_data_set_default_double(settings, "radius", 0.0); + obs_data_set_default_string(settings, "background", "None"); + obs_data_set_default_int(settings, "passes", 1); + obs_data_set_default_int(settings, "kawase_passes", 1); + obs_data_set_default_string(settings, "effect_mask_source_source", "None"); + obs_data_set_default_double(settings, "effect_mask_source_filter_multiplier", 1.0); +} + static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) { composite_blur_filter_data_t *filter = @@ -92,9 +103,8 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->mask_source_invert = false; da_init(filter->kernel); - + //composite_blur_defaults(settings); obs_source_update(source, settings); - return filter; } @@ -1304,9 +1314,9 @@ gs_texture_t *blend_composite(gs_texture_t *texture, obs_source_release(source); gs_texture_t *tex = gs_texrender_get_texture(source_render); - gs_eparam_t *background = gs_effect_get_param_by_name( - composite_effect, "background"); - gs_effect_set_texture(background, tex); + if(data->param_background) { + gs_effect_set_texture(data->param_background, tex); + } gs_eparam_t *image = gs_effect_get_param_by_name(composite_effect, "image"); gs_effect_set_texture(image, texture); diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 2914855..525052c 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -213,6 +213,7 @@ struct composite_blur_filter_data { static const char *composite_blur_name(void *type_data); static void *composite_blur_create(obs_data_t *settings, obs_source_t *source); +static void composite_blur_defaults(obs_data_t *settings); static void composite_blur_destroy(void *data); static uint32_t composite_blur_width(void *data); static uint32_t composite_blur_height(void *data); diff --git a/src/version.h b/src/version.h index 3d15f1b..85c5e35 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #pragma once -#define PROJECT_VERSION "0.0.3" +#define PROJECT_VERSION "0.0.6" #define PROJECT_VERSION_MAJOR 0 #define PROJECT_VERSION_MINOR 0 -#define PROJECT_VERSION_PATCH 3 +#define PROJECT_VERSION_PATCH 6 From 8d4d29cc8caa027b5a49cef2b8c34036da7e5a4d Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 05:36:09 -0500 Subject: [PATCH 07/15] Switches box blur to using data param pointers. --- src/blur/box.c | 177 ++++++++++++++++---------------- src/obs-composite-blur-filter.c | 3 +- src/version.h | 4 +- 3 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/blur/box.c b/src/blur/box.c index c729171..7223e02 100644 --- a/src/blur/box.c +++ b/src/blur/box.c @@ -75,26 +75,23 @@ static void box_area_blur(composite_blur_filter_data_t *data) texture = blend_composite(texture, data); const int passes = data->passes < 1 ? 1 : data->passes; for (int i = 0; i < passes; i++) { + // 1. First pass- apply 1D blur kernel to horizontal dir. data->render2 = create_or_reset_texrender(data->render2); gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - const float radius = (float)data->radius; - gs_eparam_t *radius_param = - gs_effect_get_param_by_name(effect, "radius"); - gs_effect_set_float(radius_param, radius); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; - - // 1. First pass- apply 1D blur kernel to horizontal dir. + if(data->param_radius) { + gs_effect_set_float(data->param_radius, data->radius); + } - direction.x = 1.0f / data->width; - direction.y = 0.0f; - gs_effect_set_vec2(texel_step, &direction); + struct vec2 texel_step; + texel_step.x = 1.0f / data->width; + texel_step.y = 0.0f; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } set_blending_parameters(); @@ -115,9 +112,11 @@ static void box_area_blur(composite_blur_filter_data_t *data) image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - direction.x = 0.0f; - direction.y = 1.0f / data->height; - gs_effect_set_vec2(texel_step, &direction); + texel_step.x = 0.0f; + texel_step.y = 1.0f / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -156,24 +155,22 @@ static void box_directional_blur(composite_blur_filter_data_t *data) data->render2 = data->output_texrender; data->output_texrender = tmp; + // 1. Single pass- blur only in one direction gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - const float radius = (float)data->radius; - gs_eparam_t *radius_param = - gs_effect_get_param_by_name(effect, "radius"); - gs_effect_set_float(radius_param, radius); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; + if(data->param_radius) { + gs_effect_set_float(data->param_radius, data->radius); + } - // 1. Single pass- blur only in one direction - float rads = -data->angle * (M_PI / 180.0f); - direction.x = (float)cos(rads) / data->width; - direction.y = (float)sin(rads) / data->height; - gs_effect_set_vec2(texel_step, &direction); + struct vec2 texel_step; + float rads = -data->angle * ((float)M_PI / 180.0f); + texel_step.x = (float)cos(rads) / data->width; + texel_step.y = (float)sin(rads) / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } set_blending_parameters(); @@ -215,37 +212,30 @@ static void box_zoom_blur(composite_blur_filter_data_t *data) data->render2 = data->output_texrender; data->output_texrender = tmp; + // 1. Single pass- blur only in one direction gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - const float radius = (float)data->radius; - gs_eparam_t *radius_param = - gs_effect_get_param_by_name(effect, "radius"); - gs_effect_set_float(radius_param, radius); - - gs_eparam_t *radial_center = - gs_effect_get_param_by_name(effect, "radial_center"); - - struct vec2 coord; - - coord.x = data->center_x; - coord.y = data->center_y; - - // 1. Single pass- blur only in one direction - gs_effect_set_vec2(radial_center, &coord); - - gs_eparam_t *uv_size = - gs_effect_get_param_by_name(effect, "uv_size"); + if(data->param_radius) { + gs_effect_set_float(data->param_radius, data->radius); + } - struct vec2 size; - size.x = (float)data->width; - size.y = (float)data->height; + struct vec2 radial_center; + radial_center.x = data->center_x; + radial_center.y = data->center_y; + if(data->param_radial_center) { + gs_effect_set_vec2(data->param_radial_center, &radial_center); + } - gs_effect_set_vec2(uv_size, &size); + struct vec2 uv_size; + uv_size.x = (float)data->width; + uv_size.y = (float)data->height; + if(data->param_uv_size) { + gs_effect_set_vec2(data->param_uv_size, &uv_size); + } set_blending_parameters(); - //set_render_parameters(); data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -265,7 +255,7 @@ static void box_zoom_blur(composite_blur_filter_data_t *data) } /* - * Performs an area blur using the box kernel. Blur is + * Performs an area blur using the box kernel. Blur is * equal in both x and y directions. */ static void box_tilt_shift_blur(composite_blur_filter_data_t *data) @@ -280,52 +270,47 @@ static void box_tilt_shift_blur(composite_blur_filter_data_t *data) texture = blend_composite(texture, data); for (int i = 0; i < data->passes; i++) { + // 1. First pass- apply 1D blur kernel to horizontal dir. data->render2 = create_or_reset_texrender(data->render2); gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - const float radius = (float)data->radius; - gs_eparam_t *radius_param = - gs_effect_get_param_by_name(effect, "radius"); - gs_effect_set_float(radius_param, radius); + + if(data->param_radius) { + gs_effect_set_float(data->param_radius, (float)data->radius); + } const float focus_center = 1.0f - (float)data->tilt_shift_center; - gs_eparam_t *focus_center_param = - gs_effect_get_param_by_name(effect, "focus_center"); - gs_effect_set_float(focus_center_param, focus_center); + if(data->param_focus_center) { + gs_effect_set_float(data->param_focus_center, focus_center); + } const float focus_width = (float)data->tilt_shift_width / 2.0f; - gs_eparam_t *focus_width_param = - gs_effect_get_param_by_name(effect, "focus_width"); - gs_effect_set_float(focus_width_param, focus_width); + if(data->param_focus_width) { + gs_effect_set_float(data->param_focus_width, focus_width); + } const float focus_angle = - (float)data->tilt_shift_angle * (3.14159f / 180.0f); - gs_eparam_t *focus_angle_param = - gs_effect_get_param_by_name(effect, "focus_angle"); - gs_effect_set_float(focus_angle_param, focus_angle); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; - - // 1. First pass- apply 1D blur kernel to horizontal dir. - - direction.x = 1.0f / data->width; - direction.y = 0.0f; - - gs_effect_set_vec2(texel_step, &direction); + (float)data->tilt_shift_angle * (M_PI / 180.0f); + if(data->param_focus_angle) { + gs_effect_set_float(data->param_focus_angle, focus_angle); + } - gs_eparam_t *uv_size = - gs_effect_get_param_by_name(effect, "uv_size"); + struct vec2 texel_step; + texel_step.x = 1.0f / data->width; + texel_step.y = 0.0f; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } struct vec2 size; size.x = (float)data->width; size.y = (float)data->height; - - gs_effect_set_vec2(uv_size, &size); + if(data->param_uv_size) { + gs_effect_set_vec2(data->param_uv_size, &size); + } set_blending_parameters(); @@ -346,9 +331,11 @@ static void box_tilt_shift_blur(composite_blur_filter_data_t *data) image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - direction.x = 0.0f; - direction.y = 1.0f / data->height; - gs_effect_set_vec2(texel_step, &direction); + texel_step.x = 0.0f; + texel_step.y = 1.0f / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -379,8 +366,10 @@ static void load_1d_box_effect(composite_blur_filter_data_t *filter) filter->effect, effect_index); struct gs_effect_param_info info; gs_effect_get_param_info(param, &info); - if (strcmp(info.name, "uv_size") == 0) { - filter->param_uv_size = param; + if (strcmp(info.name, "texel_step") == 0) { + filter->param_texel_step = param; + } else if(strcmp(info.name, "radius") == 0) { + filter->param_radius = param; } } } @@ -400,6 +389,16 @@ static void load_tiltshift_box_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "texel_step") == 0) { + filter->param_texel_step = param; + } else if(strcmp(info.name, "radius") == 0) { + filter->param_radius = param; + } else if (strcmp(info.name, "focus_center") == 0) { + filter->param_focus_center = param; + } else if(strcmp(info.name, "focus_width") == 0) { + filter->param_focus_width = param; + } else if(strcmp(info.name, "focus_angle") == 0) { + filter->param_focus_angle = param; } } } @@ -419,6 +418,10 @@ static void load_radial_box_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if(strcmp(info.name, "radius") == 0) { + filter->param_radius = param; + } else if(strcmp(info.name, "radial_center") == 0) { + filter->param_radial_center = param; } } } diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 9bbcc37..7737166 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -353,7 +353,8 @@ static void composite_blur_video_render(void *data, gs_effect_t *effect) filter->rendering = true; if (filter->video_render) { - // 1. Get the input source as a texture renderer: + // 1. Get the input source as a texture renderer + // accessed as filter->input_texrender after call get_input_source(filter); // 2. Apply effect to texture, and render texture to video diff --git a/src/version.h b/src/version.h index 85c5e35..99d962e 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #pragma once -#define PROJECT_VERSION "0.0.6" +#define PROJECT_VERSION "0.0.7" #define PROJECT_VERSION_MAJOR 0 #define PROJECT_VERSION_MINOR 0 -#define PROJECT_VERSION_PATCH 6 +#define PROJECT_VERSION_PATCH 7 From 422475f9a9de3e5295e798660d3116582ea33719 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 06:01:35 -0500 Subject: [PATCH 08/15] Switches gaussian blur to using data param pointers. --- data/shaders/gaussian_1d_texture.effect | 2 +- data/shaders/gaussian_radial_texture.effect | 2 +- src/blur/gaussian.c | 238 +++++++++++--------- 3 files changed, 132 insertions(+), 110 deletions(-) diff --git a/data/shaders/gaussian_1d_texture.effect b/data/shaders/gaussian_1d_texture.effect index d8a13c4..680a912 100644 --- a/data/shaders/gaussian_1d_texture.effect +++ b/data/shaders/gaussian_1d_texture.effect @@ -6,11 +6,11 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform texture2d kernel_texture; uniform float2 uv_size; uniform float2 texel_step; uniform int kernel_size; +uniform texture2d kernel_texture; sampler_state textureSampler{ Filter = Linear; diff --git a/data/shaders/gaussian_radial_texture.effect b/data/shaders/gaussian_radial_texture.effect index 52e3f39..9f41f30 100644 --- a/data/shaders/gaussian_radial_texture.effect +++ b/data/shaders/gaussian_radial_texture.effect @@ -6,10 +6,10 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform texture2d kernel_texture; uniform float2 uv_size; uniform int kernel_size; +uniform texture2d kernel_texture; uniform float2 radial_center; sampler_state textureSampler{ diff --git a/src/blur/gaussian.c b/src/blur/gaussian.c index 5f5ac6a..8f2188f 100644 --- a/src/blur/gaussian.c +++ b/src/blur/gaussian.c @@ -12,9 +12,6 @@ void set_gaussian_blur_types(obs_properties_t *props) TYPE_ZOOM); obs_property_list_add_int(p, obs_module_text(TYPE_MOTION_LABEL), TYPE_MOTION); - // obs_property_list_add_int(p, - // obs_module_text(TYPE_TILTSHIFT_LABEL), - // TYPE_TILTSHIFT); } void gaussian_setup_callbacks(composite_blur_filter_data_t *data) @@ -69,7 +66,7 @@ void load_effect_gaussian(composite_blur_filter_data_t *filter) } /* - * Performs an area blur using the gaussian kernel. Blur is + * Performs an area blur using the gaussian kernel. Blur is * equal in both x and y directions. */ static void gaussian_area_blur(composite_blur_filter_data_t *data) @@ -85,37 +82,37 @@ static void gaussian_area_blur(composite_blur_filter_data_t *data) data->render2 = create_or_reset_texrender(data->render2); + // 1. First pass- apply 1D blur kernel to horizontal dir. gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); #ifdef _WIN32 - gs_eparam_t *weight = gs_effect_get_param_by_name(effect, "weight"); - - gs_effect_set_val(weight, data->kernel.array, - data->kernel.num * sizeof(float)); - - gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); - gs_effect_set_val(offset, data->offset.array, - data->offset.num * sizeof(float)); + if(data->param_weight) { + gs_effect_set_val(data->param_weight, data->kernel.array, + data->kernel.num * sizeof(float)); + } + if(data->param_offset) { + gs_effect_set_val(data->param_offset, data->offset.array, + data->offset.num * sizeof(float)); + } #else - gs_eparam_t *kernel_texture = - gs_effect_get_param_by_name(effect, "kernel_texture"); - gs_effect_set_texture(kernel_texture, data->kernel_texture); + if(data->param_kernel_texture) { + gs_effect_set_texture(data->param_kernel_texture, data->kernel_texture); + } #endif const int k_size = (int)data->kernel_size; - gs_eparam_t *kernel_size = - gs_effect_get_param_by_name(effect, "kernel_size"); - gs_effect_set_int(kernel_size, k_size); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; + if(data->param_kernel_size) { + gs_effect_set_int(data->param_kernel_size, k_size); + } - // 1. First pass- apply 1D blur kernel to horizontal dir. - direction.x = 1.0f / data->width; - direction.y = 0.0f; - gs_effect_set_vec2(texel_step, &direction); + + struct vec2 texel_step; + texel_step.x = 1.0f / data->width; + texel_step.y = 0.0f; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } set_blending_parameters(); @@ -135,13 +132,16 @@ static void gaussian_area_blur(composite_blur_filter_data_t *data) gs_effect_set_texture(image, texture); #ifndef _WIN32 - kernel_texture = gs_effect_get_param_by_name(effect, "kernel_texture"); - gs_effect_set_texture(kernel_texture, data->kernel_texture); + if(data->param_kernel_texture) { + gs_effect_set_texture(data->param_kernel_texture, data->kernel_texture); + } #endif - direction.x = 0.0f; - direction.y = 1.0f / data->height; - gs_effect_set_vec2(texel_step, &direction); + texel_step.x = 0.0f; + texel_step.y = 1.0f / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -172,41 +172,39 @@ static void gaussian_directional_blur(composite_blur_filter_data_t *data) texture = blend_composite(texture, data); + // 1. Single pass- blur only in one direction gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); #ifdef _WIN32 - gs_eparam_t *weight = gs_effect_get_param_by_name(effect, "weight"); - - gs_effect_set_val(weight, data->kernel.array, - data->kernel.num * sizeof(float)); - - gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); - gs_effect_set_val(offset, data->offset.array, - data->offset.num * sizeof(float)); + if(data->param_weight) { + gs_effect_set_val(data->param_weight, data->kernel.array, + data->kernel.num * sizeof(float)); + } + if(data->param_offset) { + gs_effect_set_val(data->param_offset, data->offset.array, + data->offset.num * sizeof(float)); + } #else - gs_eparam_t *kernel_texture = - gs_effect_get_param_by_name(effect, "kernel_texture"); - gs_effect_set_texture(kernel_texture, data->kernel_texture); + if(data->param_kernel_texture) { + gs_effect_set_texture(data->param_kernel_texture, data->kernel_texture); + } #endif const int k_size = (int)data->kernel_size; - gs_eparam_t *kernel_size = - gs_effect_get_param_by_name(effect, "kernel_size"); - gs_effect_set_int(kernel_size, k_size); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; + if(data->param_kernel_size) { + gs_effect_set_int(data->param_kernel_size, k_size); + } - // 1. Single pass- blur only in one direction + struct vec2 texel_step; float rads = -data->angle * (M_PI / 180.0f); - direction.x = (float)cos(rads) / data->width; - direction.y = (float)sin(rads) / data->height; - gs_effect_set_vec2(texel_step, &direction); + texel_step.x = (float)cos(rads) / data->width; + texel_step.y = (float)sin(rads) / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } set_blending_parameters(); - //set_render_parameters(); data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -237,41 +235,39 @@ static void gaussian_motion_blur(composite_blur_filter_data_t *data) texture = blend_composite(texture, data); + // 1. Single pass- blur only in one direction gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); #ifdef _WIN32 - gs_eparam_t *weight = gs_effect_get_param_by_name(effect, "weight"); - - gs_effect_set_val(weight, data->kernel.array, - data->kernel.num * sizeof(float)); - - gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); - gs_effect_set_val(offset, data->offset.array, - data->offset.num * sizeof(float)); + if(data->param_weight) { + gs_effect_set_val(data->param_weight, data->kernel.array, + data->kernel.num * sizeof(float)); + } + if(data->param_offset) { + gs_effect_set_val(data->param_offset, data->offset.array, + data->offset.num * sizeof(float)); + } #else - gs_eparam_t *kernel_texture = - gs_effect_get_param_by_name(effect, "kernel_texture"); - gs_effect_set_texture(kernel_texture, data->kernel_texture); + if(data->param_kernel_texture) { + gs_effect_set_texture(data->param_kernel_texture, data->kernel_texture); + } #endif const int k_size = (int)data->kernel_size; - gs_eparam_t *kernel_size = - gs_effect_get_param_by_name(effect, "kernel_size"); - gs_effect_set_int(kernel_size, k_size); - - gs_eparam_t *texel_step = - gs_effect_get_param_by_name(effect, "texel_step"); - struct vec2 direction; + if(data->param_kernel_size) { + gs_effect_set_int(data->param_kernel_size, k_size); + } - // 1. Single pass- blur only in one direction + struct vec2 texel_step; float rads = -data->angle * (M_PI / 180.0f); - direction.x = (float)cos(rads) / data->width; - direction.y = (float)sin(rads) / data->height; - gs_effect_set_vec2(texel_step, &direction); + texel_step.x = (float)cos(rads) / data->width; + texel_step.y = (float)sin(rads) / data->height; + if(data->param_texel_step) { + gs_effect_set_vec2(data->param_texel_step, &texel_step); + } set_blending_parameters(); - //set_render_parameters(); data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -303,47 +299,43 @@ static void gaussian_zoom_blur(composite_blur_filter_data_t *data) texture = blend_composite(texture, data); + // 1. Single pass- blur only in one direction gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); #ifdef _WIN32 - gs_eparam_t *weight = gs_effect_get_param_by_name(effect, "weight"); - - gs_effect_set_val(weight, data->kernel.array, - data->kernel.num * sizeof(float)); - - gs_eparam_t *offset = gs_effect_get_param_by_name(effect, "offset"); - gs_effect_set_val(offset, data->offset.array, - data->offset.num * sizeof(float)); + if(data->param_weight) { + gs_effect_set_val(data->param_weight, data->kernel.array, + data->kernel.num * sizeof(float)); + } + if(data->param_offset) { + gs_effect_set_val(data->param_offset, data->offset.array, + data->offset.num * sizeof(float)); + } #else - gs_eparam_t *kernel_texture = - gs_effect_get_param_by_name(effect, "kernel_texture"); - gs_effect_set_texture(kernel_texture, data->kernel_texture); + if(data->param_kernel_texture) { + gs_effect_set_texture(data->param_kernel_texture, data->kernel_texture); + } #endif const int k_size = (int)data->kernel_size; - gs_eparam_t *kernel_size = - gs_effect_get_param_by_name(effect, "kernel_size"); - gs_effect_set_int(kernel_size, k_size); - - gs_eparam_t *radial_center = - gs_effect_get_param_by_name(effect, "radial_center"); - - struct vec2 coord; - - coord.x = data->center_x; - coord.y = data->center_y; - - // 1. Single pass- blur only in one direction - gs_effect_set_vec2(radial_center, &coord); - - gs_eparam_t *uv_size = gs_effect_get_param_by_name(effect, "uv_size"); + if(data->param_kernel_size) { + gs_effect_set_int(data->param_kernel_size, k_size); + } - struct vec2 size; - size.x = (float)data->width; - size.y = (float)data->height; + struct vec2 radial_center; + radial_center.x = data->center_x; + radial_center.y = data->center_y; + if(data->param_radial_center) { + gs_effect_set_vec2(data->param_radial_center, &radial_center); + } - gs_effect_set_vec2(uv_size, &size); + struct vec2 uv_size; + uv_size.x = (float)data->width; + uv_size.y = (float)data->height; + if(data->param_uv_size) { + gs_effect_set_vec2(data->param_uv_size, &uv_size); + } set_blending_parameters(); @@ -380,6 +372,16 @@ static void load_1d_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "texel_step") == 0) { + filter->param_texel_step = param; + } else if (strcmp(info.name, "offset") == 0) { + filter->param_offset = param; + } else if (strcmp(info.name, "weight") == 0) { + filter->param_weight = param; + } else if (strcmp(info.name, "kernel_size") == 0) { + filter->param_kernel_size = param; + } else if (strcmp(info.name, "kernel_texture") == 0) { + filter->param_kernel_texture = param; } } } @@ -404,6 +406,16 @@ static void load_motion_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "texel_step") == 0) { + filter->param_texel_step = param; + } else if (strcmp(info.name, "offset") == 0) { + filter->param_offset = param; + } else if (strcmp(info.name, "weight") == 0) { + filter->param_weight = param; + } else if (strcmp(info.name, "kernel_size") == 0) { + filter->param_kernel_size = param; + } else if (strcmp(info.name, "kernel_texture") == 0) { + filter->param_kernel_texture = param; } } } @@ -428,6 +440,16 @@ static void load_radial_gaussian_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "offset") == 0) { + filter->param_offset = param; + } else if (strcmp(info.name, "weight") == 0) { + filter->param_weight = param; + } else if (strcmp(info.name, "kernel_size") == 0) { + filter->param_kernel_size = param; + } else if (strcmp(info.name, "kernel_texture") == 0) { + filter->param_kernel_texture = param; + } else if (strcmp(info.name, "radial_center") == 0) { + filter->param_radial_center = param; } } } From 19f0291e9ac5bb9f505eda029fe7751dbfd44d05 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 06:15:36 -0500 Subject: [PATCH 09/15] Switches pixelate blur to using data param pointers. --- data/shaders/pixelate_circle.effect | 2 +- data/shaders/pixelate_hexagonal.effect | 2 +- data/shaders/pixelate_square.effect | 2 +- data/shaders/pixelate_triangle.effect | 2 +- src/blur/pixelate.c | 25 +++++++++++++++++-------- src/obs-composite-blur-filter.c | 4 +--- src/obs-composite-blur-filter.h | 1 + 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/data/shaders/pixelate_circle.effect b/data/shaders/pixelate_circle.effect index 5cfb439..d3f0b4e 100644 --- a/data/shaders/pixelate_circle.effect +++ b/data/shaders/pixelate_circle.effect @@ -1,7 +1,7 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform float2 uv_size; +uniform float2 uv_size; uniform float pixel_size; sampler_state textureSampler{ diff --git a/data/shaders/pixelate_hexagonal.effect b/data/shaders/pixelate_hexagonal.effect index b331b11..8fe9794 100644 --- a/data/shaders/pixelate_hexagonal.effect +++ b/data/shaders/pixelate_hexagonal.effect @@ -6,8 +6,8 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform float2 uv_size; +uniform float2 uv_size; uniform float pixel_size; sampler_state textureSampler{ diff --git a/data/shaders/pixelate_square.effect b/data/shaders/pixelate_square.effect index 9122738..956a6b5 100644 --- a/data/shaders/pixelate_square.effect +++ b/data/shaders/pixelate_square.effect @@ -1,7 +1,7 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform float2 uv_size; +uniform float2 uv_size; uniform float pixel_size; sampler_state textureSampler{ diff --git a/data/shaders/pixelate_triangle.effect b/data/shaders/pixelate_triangle.effect index 8788f98..41e8dbc 100644 --- a/data/shaders/pixelate_triangle.effect +++ b/data/shaders/pixelate_triangle.effect @@ -2,8 +2,8 @@ uniform float4x4 ViewProj; uniform texture2d image; -uniform float2 uv_size; +uniform float2 uv_size; uniform float pixel_size; sampler_state textureSampler{ diff --git a/src/blur/pixelate.c b/src/blur/pixelate.c index 09f175d..042dd85 100644 --- a/src/blur/pixelate.c +++ b/src/blur/pixelate.c @@ -53,15 +53,16 @@ static void pixelate_square_blur(composite_blur_filter_data_t *data) gs_effect_set_texture(image, texture); const float radius = (float)fmax((float)data->radius, 1.0f); - gs_eparam_t *radius_param = - gs_effect_get_param_by_name(effect, "pixel_size"); - gs_effect_set_float(radius_param, radius); + if(data->param_pixel_size) { + gs_effect_set_float(data->param_pixel_size, radius); + } - gs_eparam_t *uv_size = gs_effect_get_param_by_name(effect, "uv_size"); - struct vec2 size; - size.x = (float)data->width; - size.y = (float)data->height; - gs_effect_set_vec2(uv_size, &size); + struct vec2 uv_size; + uv_size.x = (float)data->width; + uv_size.y = (float)data->height; + if(data->param_uv_size) { + gs_effect_set_vec2(data->param_uv_size, &uv_size); + } data->output_texrender = create_or_reset_texrender(data->output_texrender); @@ -94,6 +95,8 @@ static void load_pixelate_square_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -113,6 +116,8 @@ static void load_pixelate_hexagonal_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -132,6 +137,8 @@ static void load_pixelate_circle_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -151,6 +158,8 @@ static void load_pixelate_triangle_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "uv_size") == 0) { filter->param_uv_size = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 7737166..af11306 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -74,6 +74,7 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->param_focus_center = NULL; filter->param_focus_angle = NULL; filter->param_background = NULL; + filter->param_pixel_size = NULL; filter->param_mask_crop_scale = NULL; filter->param_mask_crop_offset = NULL; filter->param_mask_crop_box_aspect_ratio = NULL; @@ -1256,9 +1257,6 @@ static void load_mix_effect(composite_blur_filter_data_t *filter) filter->mix_effect, effect_index); struct gs_effect_param_info info; gs_effect_get_param_info(param, &info); - if (strcmp(info.name, "background") == 0) { - filter->param_background = param; - } } } } diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 525052c..01f7fc9 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -148,6 +148,7 @@ struct composite_blur_filter_data { int kawase_passes; // Pixelate Blur + gs_eparam_t *param_pixel_size; int pixelate_type; int pixelate_type_last; From b2232d24691b974033848891230b64ee080e65d4 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 06:27:10 -0500 Subject: [PATCH 10/15] Adds plugin version info to filter parameters panel. --- src/obs-composite-blur-filter.c | 3 +++ src/obs-composite-blur-filter.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index af11306..129c76a 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -824,6 +824,9 @@ static obs_properties_t *composite_blur_properties(void *data) "CompositeBlurFilter.EffectMask.CropParameters"), OBS_GROUP_NORMAL, effect_mask_crop); + obs_properties_add_text(props, "plugin_info", PLUGIN_INFO, + OBS_TEXT_INFO); + return props; } diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 01f7fc9..7020c29 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -8,8 +8,13 @@ #include +#include "version.h" #include "obs-utils.h" +#define PLUGIN_INFO \ + "Composite Blur (" PROJECT_VERSION \ + ") by FiniteSingularity" + #define ALGO_NONE 0 #define ALGO_NONE_LABEL "None" #define ALGO_GAUSSIAN 1 From 7442c8800482ce4822bd0ebc681d732e7956ec3d Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 14:06:26 -0500 Subject: [PATCH 11/15] Adds circle masking. --- data/locale/en-US.ini | 4 + data/shaders/effect_mask_circle.effect | 47 ++++ src/obs-composite-blur-filter.c | 284 +++++++++++++++++++++---- src/obs-composite-blur-filter.h | 12 +- 4 files changed, 301 insertions(+), 46 deletions(-) create mode 100644 data/shaders/effect_mask_circle.effect diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 1275e38..dca82b6 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -55,3 +55,7 @@ CompositeBlurFilter.EffectMask.Source.Grayscale="Grayscale Value" CompositeBlurFilter.EffectMask.Source.Luminosity="Luminosity" CompositeBlurFilter.EffectMask.Source.Sliders="Manual RGBA Sliders" CompositeBlurFilter.EffectMask.Source.Multiplier="Multiplier" +CompositeBlurFilter.EffectMask.Circle.CenterX="Center X" +CompositeBlurFilter.EffectMask.Circle.CenterY="Center Y" +CompositeBlurFilter.EffectMask.Circle.Radius="Radius" +CompositeBlurFilter.EffectMask.CircleParameters="Circle Mask Parameters" \ No newline at end of file diff --git a/data/shaders/effect_mask_circle.effect b/data/shaders/effect_mask_circle.effect new file mode 100644 index 0000000..73505f0 --- /dev/null +++ b/data/shaders/effect_mask_circle.effect @@ -0,0 +1,47 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +uniform texture2d filtered_image; + +uniform float circle_radius; +uniform float2 center; +uniform float2 uv_scale; +uniform bool inv = false; + +sampler_state textureSampler{ + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertData mainTransform(VertData v_in) +{ + v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); + return v_in; +} + +float4 mainImage(VertData v_in) : TARGET +{ + float2 uv_prime = v_in.uv * uv_scale; + float2 center_prime = center * uv_scale; + float dist = distance(uv_prime, center_prime); + if (dist > circle_radius) { + return !inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); + } + return inv ? image.Sample(textureSampler, v_in.uv) : filtered_image.Sample(textureSampler, v_in.uv); +} + +technique Draw +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} \ No newline at end of file diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 129c76a..8e95ed5 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -18,8 +18,7 @@ struct obs_source_info obs_composite_blur = { .get_width = composite_blur_width, .get_height = composite_blur_height, .get_properties = composite_blur_properties, - .get_defaults = composite_blur_defaults - }; + .get_defaults = composite_blur_defaults}; static const char *composite_blur_name(void *unused) { @@ -27,13 +26,22 @@ static const char *composite_blur_name(void *unused) return obs_module_text("CompositeBlurFilter"); } -static void composite_blur_defaults(obs_data_t *settings) { - obs_data_set_default_double(settings, "radius", 0.0); +static void composite_blur_defaults(obs_data_t *settings) +{ + obs_data_set_default_double(settings, "radius", 10.0); obs_data_set_default_string(settings, "background", "None"); obs_data_set_default_int(settings, "passes", 1); - obs_data_set_default_int(settings, "kawase_passes", 1); - obs_data_set_default_string(settings, "effect_mask_source_source", "None"); - obs_data_set_default_double(settings, "effect_mask_source_filter_multiplier", 1.0); + obs_data_set_default_int(settings, "kawase_passes", 10); + obs_data_set_default_string(settings, "effect_mask_source_source", + "None"); + obs_data_set_default_double( + settings, "effect_mask_source_filter_multiplier", 1.0); + obs_data_set_default_double(settings, "effect_mask_circle_center_x", + 50.0); + obs_data_set_default_double(settings, "effect_mask_circle_center_y", + 50.0); + obs_data_set_default_double(settings, "effect_mask_circle_radius", + 10.0); } static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) @@ -61,7 +69,7 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->pixelate_type = 1; filter->pixelate_type_last = -1; -// Params + // Params filter->param_uv_size = NULL; filter->param_radius = NULL; filter->param_texel_step = NULL; @@ -84,6 +92,10 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->param_mask_source_rgba_weights = NULL; filter->param_mask_source_multiplier = NULL; filter->param_mask_source_invert = NULL; + filter->param_mask_circle_center = NULL; + filter->param_mask_circle_radius = NULL; + filter->param_mask_circle_inv = NULL; + filter->param_mask_circle_uv_scale = NULL; filter->mask_crop_left = 0.0f; filter->mask_crop_right = 0.0f; @@ -262,6 +274,15 @@ static void composite_blur_update(void *data, obs_data_t *settings) filter->mask_source_invert = obs_data_get_bool(settings, "effect_mask_source_invert"); + filter->mask_circle_center_x = (float)obs_data_get_double( + settings, "effect_mask_circle_center_x"); + filter->mask_circle_center_y = (float)obs_data_get_double( + settings, "effect_mask_circle_center_y"); + filter->mask_circle_radius = (float)obs_data_get_double( + settings, "effect_mask_circle_radius"); + filter->mask_circle_inv = + obs_data_get_bool(settings, "effect_mask_circle_invert"); + filter->radius = (float)obs_data_get_double(settings, "radius"); filter->passes = (int)obs_data_get_int(settings, "passes"); filter->kawase_passes = @@ -383,6 +404,8 @@ static void apply_effect_mask(composite_blur_filter_data_t *filter) case EFFECT_MASK_TYPE_SOURCE: apply_effect_mask_source(filter); break; + case EFFECT_MASK_TYPE_CIRCLE: + apply_effect_mask_circle(filter); } } @@ -452,16 +475,19 @@ static void apply_effect_mask_source(composite_blur_filter_data_t *filter) gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - if(filter->param_filtered_image) { - gs_effect_set_texture(filter->param_filtered_image, filtered_texture); + if (filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, + filtered_texture); } - if(filter->param_mask_source_alpha_source) { - gs_effect_set_texture(filter->param_mask_source_alpha_source, alpha_texture); + if (filter->param_mask_source_alpha_source) { + gs_effect_set_texture(filter->param_mask_source_alpha_source, + alpha_texture); } - if(filter->param_mask_source_invert) { - gs_effect_set_bool(filter->param_mask_source_invert, filter->mask_source_invert); + if (filter->param_mask_source_invert) { + gs_effect_set_bool(filter->param_mask_source_invert, + filter->mask_source_invert); } // TODO- Move weights calculation to update. @@ -471,12 +497,14 @@ static void apply_effect_mask_source(composite_blur_filter_data_t *filter) weights.y = filter->mask_source_filter_green; weights.z = filter->mask_source_filter_blue; weights.w = filter->mask_source_filter_alpha; - if(filter->param_mask_source_rgba_weights) { - gs_effect_set_vec4(filter->param_mask_source_rgba_weights, &weights); + if (filter->param_mask_source_rgba_weights) { + gs_effect_set_vec4(filter->param_mask_source_rgba_weights, + &weights); } - if(filter->param_mask_source_multiplier) { - gs_effect_set_float(filter->param_mask_source_multiplier, filter->mask_source_multiplier); + if (filter->param_mask_source_multiplier) { + gs_effect_set_float(filter->param_mask_source_multiplier, + filter->mask_source_multiplier); } set_blending_parameters(); @@ -518,43 +546,111 @@ static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) } gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, texture); - - if(filter->param_filtered_image) { - gs_effect_set_texture(filter->param_filtered_image, filtered_texture); + + if (filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, + filtered_texture); } struct vec2 scale; - scale.x = 1.0f / fmax(1.0f - right - left, 1.e-6f); - scale.y = 1.0f / fmax(1.0f - bot - top, 1.e-6f); - if(filter->param_mask_crop_scale) { + scale.x = 1.0f / (float)fmax(1.0f - right - left, 1.e-6f); + scale.y = 1.0f / (float)fmax(1.0f - bot - top, 1.e-6f); + if (filter->param_mask_crop_scale) { gs_effect_set_vec2(filter->param_mask_crop_scale, &scale); } struct vec2 box_ar; box_ar.x = (1.0f - right - left) * filter->width / - fmin(filter->width, filter->height); + (float)fmin(filter->width, filter->height); box_ar.y = (1.0f - bot - top) * filter->height / - fmin(filter->width, filter->height); - if(filter->param_mask_crop_box_aspect_ratio) { - gs_effect_set_vec2(filter->param_mask_crop_box_aspect_ratio, &box_ar); + (float)fmin(filter->width, filter->height); + if (filter->param_mask_crop_box_aspect_ratio) { + gs_effect_set_vec2(filter->param_mask_crop_box_aspect_ratio, + &box_ar); } struct vec2 offset; offset.x = 1.0f - right - left > 0.0f ? left : -1000.0f; offset.y = 1.0f - bot - top > 0.0f ? top : -1000.0f; - if(filter->param_mask_crop_offset) { + if (filter->param_mask_crop_offset) { gs_effect_set_vec2(filter->param_mask_crop_offset, &offset); } bool invert_v = filter->mask_crop_invert; - if(filter->param_mask_crop_invert) { + if (filter->param_mask_crop_invert) { gs_effect_set_bool(filter->param_mask_crop_invert, invert_v); } float radius = filter->mask_crop_corner_radius / 100.0f * - fmin(box_ar.x, box_ar.y); - if(filter->param_mask_crop_corner_radius) { - gs_effect_set_float(filter->param_mask_crop_corner_radius, radius); + (float)fmin(box_ar.x, box_ar.y); + if (filter->param_mask_crop_corner_radius) { + gs_effect_set_float(filter->param_mask_crop_corner_radius, + radius); + } + set_blending_parameters(); + + filter->output_texrender = + create_or_reset_texrender(filter->output_texrender); + + if (gs_texrender_begin(filter->output_texrender, filter->width, + filter->height)) { + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(texture, 0, filter->width, + filter->height); + gs_texrender_end(filter->output_texrender); + } + texture = gs_texrender_get_texture(filter->output_texrender); + gs_blend_state_pop(); +} + +static void apply_effect_mask_circle(composite_blur_filter_data_t *filter) +{ + // Swap output with render + gs_texrender_t *tmp = filter->output_texrender; + filter->output_texrender = filter->render; + filter->render = tmp; + + gs_effect_t *effect = filter->effect_mask_effect; + gs_texture_t *texture = + gs_texrender_get_texture(filter->input_texrender); + gs_texture_t *filtered_texture = + gs_texrender_get_texture(filter->render); + + if (!effect || !texture || !filtered_texture) { + return; + } + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); + gs_effect_set_texture(image, texture); + + if (filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, + filtered_texture); + } + + struct vec2 center; + center.x = filter->mask_circle_center_x / 100.0f; + center.y = filter->mask_circle_center_y / 100.0f; + if (filter->param_mask_circle_center) { + gs_effect_set_vec2(filter->param_mask_circle_center, ¢er); + } + + struct vec2 uv_scale; + uv_scale.x = filter->width / (float)fmin(filter->width, filter->height); + uv_scale.y = + filter->height / (float)fmin(filter->width, filter->height); + if (filter->param_mask_circle_uv_scale) { + gs_effect_set_vec2(filter->param_mask_circle_uv_scale, + &uv_scale); + } + + bool invert = filter->mask_circle_inv; + if (filter->param_mask_circle_inv) { + gs_effect_set_bool(filter->param_mask_circle_inv, invert); + } + + float radius = filter->mask_circle_radius / 100.0f; + if (filter->param_mask_circle_radius) { + gs_effect_set_float(filter->param_mask_circle_radius, radius); } set_blending_parameters(); @@ -707,6 +803,10 @@ static obs_properties_t *composite_blur_properties(void *data) obs_property_list_add_int(effect_mask_list, obs_module_text(EFFECT_MASK_TYPE_CROP_LABEL), EFFECT_MASK_TYPE_CROP); + obs_property_list_add_int( + effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_CIRCLE_LABEL), + EFFECT_MASK_TYPE_CIRCLE); obs_property_list_add_int( effect_mask_list, obs_module_text(EFFECT_MASK_TYPE_SOURCE_LABEL), @@ -715,6 +815,35 @@ static obs_properties_t *composite_blur_properties(void *data) obs_property_set_modified_callback(effect_mask_list, setting_effect_mask_modified); + obs_properties_t *effect_mask_circle = obs_properties_create(); + + obs_properties_add_float_slider( + effect_mask_circle, "effect_mask_circle_center_x", + obs_module_text( + "CompositeBlurFilter.EffectMask.Circle.CenterX"), + -500.01, 600.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_circle, "effect_mask_circle_center_y", + obs_module_text( + "CompositeBlurFilter.EffectMask.Circle.CenterY"), + -500.01, 600.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_circle, "effect_mask_circle_radius", + obs_module_text("CompositeBlurFilter.EffectMask.Circle.Radius"), + 0.0, 500.01, 0.01); + + obs_properties_add_bool( + effect_mask_circle, "effect_mask_circle_invert", + obs_module_text("CompositeBlurFilter.EffectMask.Invert")); + + obs_properties_add_group( + props, "effect_mask_circle", + obs_module_text( + "CompositeBlurFilter.EffectMask.CircleParameters"), + OBS_GROUP_NORMAL, effect_mask_circle); + obs_properties_t *effect_mask_source = obs_properties_create(); obs_property_t *effect_mask_source_source = obs_properties_add_list( @@ -872,14 +1001,22 @@ static bool setting_effect_mask_modified(obs_properties_t *props, case EFFECT_MASK_TYPE_NONE: setting_visibility("effect_mask_crop", false, props); setting_visibility("effect_mask_source", false, props); + setting_visibility("effect_mask_circle", false, props); break; case EFFECT_MASK_TYPE_CROP: setting_visibility("effect_mask_crop", true, props); setting_visibility("effect_mask_source", false, props); + setting_visibility("effect_mask_circle", false, props); + break; + case EFFECT_MASK_TYPE_CIRCLE: + setting_visibility("effect_mask_crop", false, props); + setting_visibility("effect_mask_source", false, props); + setting_visibility("effect_mask_circle", true, props); break; case EFFECT_MASK_TYPE_SOURCE: setting_visibility("effect_mask_crop", false, props); setting_visibility("effect_mask_source", true, props); + setting_visibility("effect_mask_circle", false, props); break; } return true; @@ -894,6 +1031,9 @@ static void effect_mask_load_effect(composite_blur_filter_data_t *filter) case EFFECT_MASK_TYPE_SOURCE: load_source_mask_effect(filter); break; + case EFFECT_MASK_TYPE_CIRCLE: + load_circle_mask_effect(filter); + break; } } @@ -1157,15 +1297,16 @@ static void load_crop_mask_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "filtered_image") == 0) { filter->param_filtered_image = param; - } else if(strcmp(info.name, "scale") == 0) { + } else if (strcmp(info.name, "scale") == 0) { filter->param_mask_crop_scale = param; - } else if(strcmp(info.name, "offset") == 0) { + } else if (strcmp(info.name, "offset") == 0) { filter->param_mask_crop_offset = param; - } else if(strcmp(info.name, "box_aspect_ratio") == 0) { - filter->param_mask_crop_box_aspect_ratio = param; - } else if(strcmp(info.name, "corner_radius") == 0) { + } else if (strcmp(info.name, "box_aspect_ratio") == 0) { + filter->param_mask_crop_box_aspect_ratio = + param; + } else if (strcmp(info.name, "corner_radius") == 0) { filter->param_mask_crop_corner_radius = param; - } else if(strcmp(info.name, "inv") == 0) { + } else if (strcmp(info.name, "inv") == 0) { filter->param_mask_crop_invert = param; } } @@ -1211,19 +1352,72 @@ static void load_source_mask_effect(composite_blur_filter_data_t *filter) gs_effect_get_param_info(param, &info); if (strcmp(info.name, "filtered_image") == 0) { filter->param_filtered_image = param; - } else if(strcmp(info.name, "alpha_source") == 0) { + } else if (strcmp(info.name, "alpha_source") == 0) { filter->param_mask_source_alpha_source = param; - } else if(strcmp(info.name, "rgba_weights") == 0) { + } else if (strcmp(info.name, "rgba_weights") == 0) { filter->param_mask_source_rgba_weights = param; - } else if(strcmp(info.name, "multiplier") == 0) { - filter->param_mask_source_multiplier= param; - } else if(strcmp(info.name, "inv") == 0) { + } else if (strcmp(info.name, "multiplier") == 0) { + filter->param_mask_source_multiplier = param; + } else if (strcmp(info.name, "inv") == 0) { filter->param_mask_source_invert = param; } } } } +static void load_circle_mask_effect(composite_blur_filter_data_t *filter) +{ + blog(LOG_INFO, "==== LOAD CIRCLE MASK EFFECT ===="); + if (filter->effect_mask_effect != NULL) { + obs_enter_graphics(); + gs_effect_destroy(filter->effect_mask_effect); + filter->effect_mask_effect = NULL; + obs_leave_graphics(); + } + + char *shader_text = NULL; + struct dstr filename = {0}; + dstr_cat(&filename, obs_get_module_data_path(obs_current_module())); + dstr_cat(&filename, "/shaders/effect_mask_circle.effect"); + shader_text = load_shader_from_file(filename.array); + char *errors = NULL; + + obs_enter_graphics(); + filter->effect_mask_effect = + gs_effect_create(shader_text, NULL, &errors); + obs_leave_graphics(); + + bfree(shader_text); + if (filter->effect_mask_effect == NULL) { + blog(LOG_WARNING, + "[obs-composite-blur] Unable to load effect_mask_circle.effect file. Errors:\n%s", + (errors == NULL || strlen(errors) == 0 ? "(None)" + : errors)); + bfree(errors); + } else { + size_t effect_count = + gs_effect_get_num_params(filter->effect_mask_effect); + for (size_t effect_index = 0; effect_index < effect_count; + effect_index++) { + gs_eparam_t *param = gs_effect_get_param_by_idx( + filter->effect_mask_effect, effect_index); + struct gs_effect_param_info info; + gs_effect_get_param_info(param, &info); + if (strcmp(info.name, "filtered_image") == 0) { + filter->param_filtered_image = param; + } else if (strcmp(info.name, "inv") == 0) { + filter->param_mask_circle_inv = param; + } else if (strcmp(info.name, "center") == 0) { + filter->param_mask_circle_center = param; + } else if (strcmp(info.name, "circle_radius") == 0) { + filter->param_mask_circle_radius = param; + } else if (strcmp(info.name, "uv_scale") == 0) { + filter->param_mask_circle_uv_scale = param; + } + } + } +} + static void load_mix_effect(composite_blur_filter_data_t *filter) { if (filter->mix_effect != NULL) { @@ -1316,7 +1510,7 @@ gs_texture_t *blend_composite(gs_texture_t *texture, obs_source_release(source); gs_texture_t *tex = gs_texrender_get_texture(source_render); - if(data->param_background) { + if (data->param_background) { gs_effect_set_texture(data->param_background, tex); } gs_eparam_t *image = diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 7020c29..c61fafe 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -11,7 +11,7 @@ #include "version.h" #include "obs-utils.h" -#define PLUGIN_INFO \ +#define PLUGIN_INFO \ "Composite Blur (" PROJECT_VERSION \ ") by FiniteSingularity" @@ -204,6 +204,14 @@ struct composite_blur_filter_data { gs_eparam_t *param_mask_source_invert; bool mask_source_invert; obs_weak_source_t *mask_source_source; + gs_eparam_t *param_mask_circle_center; + float mask_circle_center_x; + float mask_circle_center_y; + gs_eparam_t *param_mask_circle_radius; + float mask_circle_radius; + gs_eparam_t *param_mask_circle_inv; + bool mask_circle_inv; + gs_eparam_t *param_mask_circle_uv_scale; bool rendering; bool reload; @@ -255,8 +263,10 @@ static bool settings_blur_tilt_shift(obs_properties_t *props); static void apply_effect_mask(composite_blur_filter_data_t *filter); static void apply_effect_mask_crop(composite_blur_filter_data_t *filter); static void apply_effect_mask_source(composite_blur_filter_data_t *filter); +static void apply_effect_mask_circle(composite_blur_filter_data_t *filter); static void load_crop_mask_effect(composite_blur_filter_data_t *filter); static void load_source_mask_effect(composite_blur_filter_data_t *filter); +static void load_circle_mask_effect(composite_blur_filter_data_t *filter); static void effect_mask_load_effect(composite_blur_filter_data_t *filter); static bool setting_effect_mask_source_filter_modified(obs_properties_t *props, From 3795808e165f6a7b16b7327de9669843522d0d7f Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 14:18:07 -0500 Subject: [PATCH 12/15] Fixes redefinitions in header after rebase --- src/obs-composite-blur-filter.h | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index c61fafe..4dc512a 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -102,26 +102,10 @@ struct composite_blur_filter_data { // Renderer for composite render step gs_texrender_t *composite_render; - gs_eparam_t *param_uv_size; - gs_eparam_t *param_dir; - gs_eparam_t *param_radius; - gs_eparam_t *param_background; - bool rendering; bool reload; bool rendered; - struct vec2 uv_size; - - float center_x; - float center_y; - - float radius; - float radius_last; - float angle; - float tilt_shift_center; - float tilt_shift_width; - float tilt_shift_angle; // Blur Filter Common int blur_algorithm; int blur_algorithm_last; @@ -213,9 +197,6 @@ struct composite_blur_filter_data { bool mask_circle_inv; gs_eparam_t *param_mask_circle_uv_scale; - bool rendering; - bool reload; - uint32_t width; uint32_t height; From 33c081f492f66feeb5bd677166ee8f623563e4a6 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 20:26:18 -0500 Subject: [PATCH 13/15] Working Image masking. --- data/locale/en-US.ini | 16 +- src/obs-composite-blur-filter.c | 342 +++++++++++++++++++++++++++----- src/obs-composite-blur-filter.h | 9 + 3 files changed, 315 insertions(+), 52 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index dca82b6..8e38968 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -48,14 +48,22 @@ CompositeBlurFilter.EffectMask.Crop.Right="Right (%)" CompositeBlurFilter.EffectMask.CornerRadius="Corner Radius (%)" CompositeBlurFilter.EffectMask.Invert="Invert Mask?" CompositeBlurFilter.EffectMask.SourceParameters="Source Mask Parameters" +CompositeBlurFilter.EffectMask.ImageParameters="Image Mask Parameters" CompositeBlurFilter.EffectMask.Source.Source="Source" +CompositeBlurFilter.EffectMask.Source.File="Image File" CompositeBlurFilter.EffectMask.Source.Filter="Mask Using" CompositeBlurFilter.EffectMask.Source.Alpha="Alpha Channel" CompositeBlurFilter.EffectMask.Source.Grayscale="Grayscale Value" CompositeBlurFilter.EffectMask.Source.Luminosity="Luminosity" CompositeBlurFilter.EffectMask.Source.Sliders="Manual RGBA Sliders" CompositeBlurFilter.EffectMask.Source.Multiplier="Multiplier" -CompositeBlurFilter.EffectMask.Circle.CenterX="Center X" -CompositeBlurFilter.EffectMask.Circle.CenterY="Center Y" -CompositeBlurFilter.EffectMask.Circle.Radius="Radius" -CompositeBlurFilter.EffectMask.CircleParameters="Circle Mask Parameters" \ No newline at end of file +CompositeBlurFilter.EffectMask.Circle.CenterX="Center X (%)" +CompositeBlurFilter.EffectMask.Circle.CenterY="Center Y (%)" +CompositeBlurFilter.EffectMask.Circle.Radius="Radius (%)" +CompositeBlurFilter.EffectMask.CircleParameters="Circle Mask Parameters" +CompositeBlurFilter.EffectMask.Rect.CenterX="Center X (%)" +CompositeBlurFilter.EffectMask.Rect.CenterY="Center Y (%)" +CompositeBlurFilter.EffectMask.Rect.Width="Width (%)" +CompositeBlurFilter.EffectMask.Rect.Height="Height (%)" +CompositeBlurFilter.EffectMask.Rect.CornerRadius="Corner Radius (%)" +CompositeBlurFilter.EffectMask.RectParameters="Rectangle Mask Parameters" diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index 8e95ed5..a84ca12 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -41,7 +41,19 @@ static void composite_blur_defaults(obs_data_t *settings) obs_data_set_default_double(settings, "effect_mask_circle_center_y", 50.0); obs_data_set_default_double(settings, "effect_mask_circle_radius", - 10.0); + 40.0); + + obs_data_set_default_double(settings, "effect_mask_rect_center_x", + 50.0); + obs_data_set_default_double(settings, "effect_mask_rect_center_y", + 50.0); + obs_data_set_default_double(settings, "effect_mask_rect_width", 50.0); + obs_data_set_default_double(settings, "effect_mask_rect_height", 50.0); + + obs_data_set_default_double(settings, "effect_mask_crop_top", 20.0); + obs_data_set_default_double(settings, "effect_mask_crop_bottom", 20.0); + obs_data_set_default_double(settings, "effect_mask_crop_left", 20.0); + obs_data_set_default_double(settings, "effect_mask_crop_right", 20.0); } static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) @@ -97,6 +109,8 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->param_mask_circle_inv = NULL; filter->param_mask_circle_uv_scale = NULL; + filter->mask_image = NULL; + filter->mask_crop_left = 0.0f; filter->mask_crop_right = 0.0f; filter->mask_crop_top = 0.0f; @@ -159,6 +173,10 @@ static void composite_blur_destroy(void *data) gs_texture_destroy(filter->kernel_texture); } + if (filter->mask_image) { + gs_image_file_free(filter->mask_image); + } + obs_leave_graphics(); bfree(filter); } @@ -268,6 +286,21 @@ static void composite_blur_update(void *data, obs_data_t *settings) filter->mask_source_source = NULL; } + const char *mask_image_file = + obs_data_get_string(settings, "effect_mask_source_file"); + + if (filter->mask_image == NULL) { + filter->mask_image = bzalloc(sizeof(gs_image_file_t)); + } else { + obs_enter_graphics(); + gs_image_file_free(filter->mask_image); + obs_leave_graphics(); + } + gs_image_file_init(filter->mask_image, mask_image_file); + obs_enter_graphics(); + gs_image_file_init_texture(filter->mask_image); + obs_leave_graphics(); + filter->mask_source_multiplier = (float)obs_data_get_double( settings, "effect_mask_source_filter_multiplier"); @@ -283,6 +316,19 @@ static void composite_blur_update(void *data, obs_data_t *settings) filter->mask_circle_inv = obs_data_get_bool(settings, "effect_mask_circle_invert"); + filter->mask_rect_center_x = (float)obs_data_get_double( + settings, "effect_mask_rect_center_x"); + filter->mask_rect_center_y = (float)obs_data_get_double( + settings, "effect_mask_rect_center_y"); + filter->mask_rect_width = + (float)obs_data_get_double(settings, "effect_mask_rect_width"); + filter->mask_rect_height = + (float)obs_data_get_double(settings, "effect_mask_rect_height"); + filter->mask_rect_corner_radius = (float)obs_data_get_double( + settings, "effect_mask_rect_corner_radius"); + filter->mask_rect_inv = + obs_data_get_bool(settings, "effect_mask_rect_invert"); + filter->radius = (float)obs_data_get_double(settings, "radius"); filter->passes = (int)obs_data_get_int(settings, "passes"); filter->kawase_passes = @@ -406,57 +452,76 @@ static void apply_effect_mask(composite_blur_filter_data_t *filter) break; case EFFECT_MASK_TYPE_CIRCLE: apply_effect_mask_circle(filter); + break; + case EFFECT_MASK_TYPE_RECT: + apply_effect_mask_rect(filter); + break; + case EFFECT_MASK_TYPE_IMAGE: + apply_effect_mask_source(filter); + break; } } static void apply_effect_mask_source(composite_blur_filter_data_t *filter) { // Get source - obs_source_t *source = - filter->mask_source_source - ? obs_weak_source_get_source(filter->mask_source_source) - : NULL; - if (!source) { - return; - } + gs_texture_t *alpha_texture = NULL; + gs_texrender_t *source_render = NULL; + if (filter->mask_type == EFFECT_MASK_TYPE_SOURCE) { + obs_source_t *source = + filter->mask_source_source + ? obs_weak_source_get_source( + filter->mask_source_source) + : NULL; + if (!source) { + return; + } + + const enum gs_color_space preferred_spaces[] = { + GS_CS_SRGB, + GS_CS_SRGB_16F, + GS_CS_709_EXTENDED, + }; + const enum gs_color_space space = obs_source_get_color_space( + source, OBS_COUNTOF(preferred_spaces), + preferred_spaces); + const enum gs_color_format format = + gs_get_format_from_space(space); - const enum gs_color_space preferred_spaces[] = { - GS_CS_SRGB, - GS_CS_SRGB_16F, - GS_CS_709_EXTENDED, - }; - const enum gs_color_space space = obs_source_get_color_space( - source, OBS_COUNTOF(preferred_spaces), preferred_spaces); - const enum gs_color_format format = gs_get_format_from_space(space); - - // Set up a tex renderer for source - gs_texrender_t *source_render = gs_texrender_create(format, GS_ZS_NONE); - uint32_t base_width = obs_source_get_base_width(source); - uint32_t base_height = obs_source_get_base_height(source); - gs_blend_state_push(); - gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); - if (gs_texrender_begin_with_color_space(source_render, base_width, - base_height, space)) { - const float w = (float)base_width; - const float h = (float)base_height; - uint32_t flags = obs_source_get_output_flags(source); - const bool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0; - const bool async = (flags & OBS_SOURCE_ASYNC) != 0; - struct vec4 clear_color; - - vec4_zero(&clear_color); - gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); - gs_ortho(0.0f, w, 0.0f, h, -100.0f, 100.0f); - - if (!custom_draw && !async) - obs_source_default_render(source); - else - obs_source_video_render(source); - gs_texrender_end(source_render); + // Set up a tex renderer for source + source_render = gs_texrender_create(format, GS_ZS_NONE); + uint32_t base_width = obs_source_get_base_width(source); + uint32_t base_height = obs_source_get_base_height(source); + gs_blend_state_push(); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + if (gs_texrender_begin_with_color_space( + source_render, base_width, base_height, space)) { + const float w = (float)base_width; + const float h = (float)base_height; + uint32_t flags = obs_source_get_output_flags(source); + const bool custom_draw = + (flags & OBS_SOURCE_CUSTOM_DRAW) != 0; + const bool async = (flags & OBS_SOURCE_ASYNC) != 0; + struct vec4 clear_color; + + vec4_zero(&clear_color); + gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); + gs_ortho(0.0f, w, 0.0f, h, -100.0f, 100.0f); + + if (!custom_draw && !async) + obs_source_default_render(source); + else + obs_source_video_render(source); + gs_texrender_end(source_render); + } + gs_blend_state_pop(); + obs_source_release(source); + alpha_texture = gs_texrender_get_texture(source_render); + } else if (filter->mask_type == EFFECT_MASK_TYPE_IMAGE && + filter->mask_image) { + blog(LOG_INFO, "IMAGE MASK EXISTS!"); + alpha_texture = filter->mask_image->texture; } - gs_blend_state_pop(); - obs_source_release(source); - gs_texture_t *alpha_texture = gs_texrender_get_texture(source_render); // Swap output with render gs_texrender_t *tmp = filter->output_texrender; @@ -523,6 +588,95 @@ static void apply_effect_mask_source(composite_blur_filter_data_t *filter) gs_blend_state_pop(); } +static void apply_effect_mask_rect(composite_blur_filter_data_t *filter) +{ + float right = (100.0f - filter->mask_rect_center_x - + filter->mask_rect_width / 2.0f) / + 100.0f; + float left = + (filter->mask_rect_center_x - filter->mask_rect_width / 2.0f) / + 100.0f; + float top = + (filter->mask_rect_center_y - filter->mask_rect_height / 2.0f) / + 100.0f; + float bot = (100.0f - filter->mask_rect_center_y - + filter->mask_rect_height / 2.0f) / + 100.0f; + //blog(LOG_INFO, "%f, %f, %f, %f", right, left, top, bot); + + // Swap output with render + gs_texrender_t *tmp = filter->output_texrender; + filter->output_texrender = filter->render; + filter->render = tmp; + + gs_effect_t *effect = filter->effect_mask_effect; + gs_texture_t *texture = + gs_texrender_get_texture(filter->input_texrender); + gs_texture_t *filtered_texture = + gs_texrender_get_texture(filter->render); + + if (!effect || !texture || !filtered_texture) { + return; + } + gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); + gs_effect_set_texture(image, texture); + + if (filter->param_filtered_image) { + gs_effect_set_texture(filter->param_filtered_image, + filtered_texture); + } + + struct vec2 scale; + scale.x = 1.0f / (float)fmax(1.0f - right - left, 1.e-6f); + scale.y = 1.0f / (float)fmax(1.0f - bot - top, 1.e-6f); + if (filter->param_mask_crop_scale) { + gs_effect_set_vec2(filter->param_mask_crop_scale, &scale); + } + + struct vec2 box_ar; + box_ar.x = (1.0f - right - left) * filter->width / + (float)fmin(filter->width, filter->height); + box_ar.y = (1.0f - bot - top) * filter->height / + (float)fmin(filter->width, filter->height); + if (filter->param_mask_crop_box_aspect_ratio) { + gs_effect_set_vec2(filter->param_mask_crop_box_aspect_ratio, + &box_ar); + } + + struct vec2 offset; + offset.x = 1.0f - right - left > 0.0f ? left : -1000.0f; + offset.y = 1.0f - bot - top > 0.0f ? top : -1000.0f; + if (filter->param_mask_crop_offset) { + gs_effect_set_vec2(filter->param_mask_crop_offset, &offset); + } + + bool invert_v = filter->mask_rect_inv; + if (filter->param_mask_crop_invert) { + gs_effect_set_bool(filter->param_mask_crop_invert, invert_v); + } + + float radius = filter->mask_rect_corner_radius / 100.0f * + (float)fmin(box_ar.x, box_ar.y); + if (filter->param_mask_crop_corner_radius) { + gs_effect_set_float(filter->param_mask_crop_corner_radius, + radius); + } + set_blending_parameters(); + + filter->output_texrender = + create_or_reset_texrender(filter->output_texrender); + + if (gs_texrender_begin(filter->output_texrender, filter->width, + filter->height)) { + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(texture, 0, filter->width, + filter->height); + gs_texrender_end(filter->output_texrender); + } + texture = gs_texrender_get_texture(filter->output_texrender); + gs_blend_state_pop(); +} + static void apply_effect_mask_crop(composite_blur_filter_data_t *filter) { float right = filter->mask_crop_right / 100.0f; @@ -803,14 +957,20 @@ static obs_properties_t *composite_blur_properties(void *data) obs_property_list_add_int(effect_mask_list, obs_module_text(EFFECT_MASK_TYPE_CROP_LABEL), EFFECT_MASK_TYPE_CROP); - obs_property_list_add_int( - effect_mask_list, - obs_module_text(EFFECT_MASK_TYPE_CIRCLE_LABEL), - EFFECT_MASK_TYPE_CIRCLE); obs_property_list_add_int( effect_mask_list, obs_module_text(EFFECT_MASK_TYPE_SOURCE_LABEL), EFFECT_MASK_TYPE_SOURCE); + obs_property_list_add_int(effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_IMAGE_LABEL), + EFFECT_MASK_TYPE_IMAGE); + obs_property_list_add_int(effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_RECT_LABEL), + EFFECT_MASK_TYPE_RECT); + obs_property_list_add_int( + effect_mask_list, + obs_module_text(EFFECT_MASK_TYPE_CIRCLE_LABEL), + EFFECT_MASK_TYPE_CIRCLE); obs_property_set_modified_callback(effect_mask_list, setting_effect_mask_modified); @@ -844,8 +1004,52 @@ static obs_properties_t *composite_blur_properties(void *data) "CompositeBlurFilter.EffectMask.CircleParameters"), OBS_GROUP_NORMAL, effect_mask_circle); + obs_properties_t *effect_mask_rect = obs_properties_create(); + + obs_properties_add_float_slider( + effect_mask_rect, "effect_mask_rect_center_x", + obs_module_text("CompositeBlurFilter.EffectMask.Rect.CenterX"), + -500.01, 600.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_rect, "effect_mask_rect_center_y", + obs_module_text("CompositeBlurFilter.EffectMask.Rect.CenterY"), + -500.01, 600.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_rect, "effect_mask_rect_width", + obs_module_text("CompositeBlurFilter.EffectMask.Rect.Width"), + 0.0, 500.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_rect, "effect_mask_rect_height", + obs_module_text("CompositeBlurFilter.EffectMask.Rect.Height"), + 0.0, 500.01, 0.01); + + obs_properties_add_float_slider( + effect_mask_rect, "effect_mask_rect_corner_radius", + obs_module_text( + "CompositeBlurFilter.EffectMask.Rect.CornerRadius"), + 0.0, 100.01, 0.01); + + obs_properties_add_bool( + effect_mask_rect, "effect_mask_rect_invert", + obs_module_text("CompositeBlurFilter.EffectMask.Invert")); + + obs_properties_add_group( + props, "effect_mask_rect", + obs_module_text( + "CompositeBlurFilter.EffectMask.RectParameters"), + OBS_GROUP_NORMAL, effect_mask_rect); + obs_properties_t *effect_mask_source = obs_properties_create(); + obs_property_t *effect_mask_source_image = obs_properties_add_path( + effect_mask_source, "effect_mask_source_file", + obs_module_text("CompositeBlurFilter.EffectMask.Source.File"), + OBS_PATH_FILE, + "Textures (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;", NULL); + obs_property_t *effect_mask_source_source = obs_properties_add_list( effect_mask_source, "effect_mask_source_source", obs_module_text("CompositeBlurFilter.EffectMask.Source.Source"), @@ -1002,21 +1206,57 @@ static bool setting_effect_mask_modified(obs_properties_t *props, setting_visibility("effect_mask_crop", false, props); setting_visibility("effect_mask_source", false, props); setting_visibility("effect_mask_circle", false, props); + setting_visibility("effect_mask_rect", false, props); break; case EFFECT_MASK_TYPE_CROP: setting_visibility("effect_mask_crop", true, props); setting_visibility("effect_mask_source", false, props); setting_visibility("effect_mask_circle", false, props); + setting_visibility("effect_mask_rect", false, props); break; case EFFECT_MASK_TYPE_CIRCLE: setting_visibility("effect_mask_crop", false, props); setting_visibility("effect_mask_source", false, props); setting_visibility("effect_mask_circle", true, props); + setting_visibility("effect_mask_rect", false, props); + break; + case EFFECT_MASK_TYPE_RECT: + setting_visibility("effect_mask_crop", false, props); + setting_visibility("effect_mask_source", false, props); + setting_visibility("effect_mask_circle", false, props); + setting_visibility("effect_mask_rect", true, props); break; case EFFECT_MASK_TYPE_SOURCE: setting_visibility("effect_mask_crop", false, props); setting_visibility("effect_mask_source", true, props); setting_visibility("effect_mask_circle", false, props); + setting_visibility("effect_mask_rect", false, props); + setting_visibility("effect_mask_source_file", false, props); + setting_visibility("effect_mask_source_source", true, props); + { + obs_property_t *prop = + obs_properties_get(props, "effect_mask_source"); + obs_property_set_description( + prop, + obs_module_text( + "CompositeBlurFilter.EffectMask.SourceParameters")); + } + break; + case EFFECT_MASK_TYPE_IMAGE: + setting_visibility("effect_mask_crop", false, props); + setting_visibility("effect_mask_source", true, props); + setting_visibility("effect_mask_circle", false, props); + setting_visibility("effect_mask_rect", false, props); + setting_visibility("effect_mask_source_file", true, props); + setting_visibility("effect_mask_source_source", false, props); + { + obs_property_t *prop = + obs_properties_get(props, "effect_mask_source"); + obs_property_set_description( + prop, + obs_module_text( + "CompositeBlurFilter.EffectMask.ImageParameters")); + } break; } return true; @@ -1034,6 +1274,12 @@ static void effect_mask_load_effect(composite_blur_filter_data_t *filter) case EFFECT_MASK_TYPE_CIRCLE: load_circle_mask_effect(filter); break; + case EFFECT_MASK_TYPE_RECT: + load_crop_mask_effect(filter); + break; + case EFFECT_MASK_TYPE_IMAGE: + load_source_mask_effect(filter); + break; } } diff --git a/src/obs-composite-blur-filter.h b/src/obs-composite-blur-filter.h index 4dc512a..f72b9d1 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -196,6 +197,13 @@ struct composite_blur_filter_data { gs_eparam_t *param_mask_circle_inv; bool mask_circle_inv; gs_eparam_t *param_mask_circle_uv_scale; + float mask_rect_center_x; + float mask_rect_center_y; + float mask_rect_width; + float mask_rect_height; + float mask_rect_corner_radius; + float mask_rect_inv; + gs_image_file_t *mask_image; uint32_t width; uint32_t height; @@ -245,6 +253,7 @@ static void apply_effect_mask(composite_blur_filter_data_t *filter); static void apply_effect_mask_crop(composite_blur_filter_data_t *filter); static void apply_effect_mask_source(composite_blur_filter_data_t *filter); static void apply_effect_mask_circle(composite_blur_filter_data_t *filter); +static void apply_effect_mask_rect(composite_blur_filter_data_t *filter); static void load_crop_mask_effect(composite_blur_filter_data_t *filter); static void load_source_mask_effect(composite_blur_filter_data_t *filter); static void load_circle_mask_effect(composite_blur_filter_data_t *filter); From 393b2255da7d9d6b6c795465f64dbbc76a7c0153 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 20:38:06 -0500 Subject: [PATCH 14/15] Update readme file. --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index c8d904d..0f448ba 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,21 @@ Zoom blur is applied away from a center zoom point, and increases the further fr ### Tilt-Shift Tilt-Shift blur defines an in-focus plane, specified by a location in the frame, and a thickness. All pixels outside of the in-focus plane have their blur value increased the further away from the plane they are. The resulting image gives a distorted sense of scale, making large objects look like mineature models. When applied to video scenes like a city street, the effect can be significant. Inputs are blur radius, focus plane angle, focus plane location, and focus plane thickness. + +## Effect Masking +OBS Composite Blur offers a variety of ways to mask where and how blur is applied to your source. For all mask options, the mask can also be inverted by checking the "Invert Mask" box. The following options are available. + +### Crop +Specify the percentage distance in from the top, bottom, left, and right edges of your source that you want masked. Additionally, the crop mask allows you to specify a corner radius for nice, smooth rounded corners. + +### Source +Use another OBS source or scene as a mask for your blur. Simply select the source or scene you want to use, and then specify if you want to use the source's alpha channel, grayscale value, luminosity, or a custom combination of the red, green, blue, and alpha channels to mask the blur effect. You can also multiply the resulting mask by a value. The multiply value comes in handy if you have a translucent source, but want everything behind the translucent source to be fully blurred. + +### Image +All of the same options as [Source](#source), but allows you to select an image file rather than a source. + +### Rectangle +Is the same as the [Crop](#crop) option, but instead of specifying the edges, you specify the center of the rectangle, the rectangle width, and rectangle height. This is easier to use with plugins like Move Transition if you want to animate the movement or size of the rectangular masked blur. + +### Circle +Similar to the [Rectangle](#rectangle) option, but lets you specify the center of a circle and its radius. Some nice sweep effects can be made by using a very large circle, and moving it from off the source (less than 0 or greater than 100 for the center coordinates) over the source. From a52a27bc50ea9f310b37f561bd591ddd674d46ac Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Wed, 30 Aug 2023 20:48:01 -0500 Subject: [PATCH 15/15] Small fix for MacOS build. --- src/obs-composite-blur-filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obs-composite-blur-filter.c b/src/obs-composite-blur-filter.c index a84ca12..99ae07a 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -1044,7 +1044,7 @@ static obs_properties_t *composite_blur_properties(void *data) obs_properties_t *effect_mask_source = obs_properties_create(); - obs_property_t *effect_mask_source_image = obs_properties_add_path( + obs_properties_add_path( effect_mask_source, "effect_mask_source_file", obs_module_text("CompositeBlurFilter.EffectMask.Source.File"), OBS_PATH_FILE,