From af71a7cc1d5e95e1697dd6dba91498eb12029be3 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 29 Apr 2018 00:14:29 +0200 Subject: [PATCH] filter-blur: Allow applying Blur to a sub-region of the source only The Blur Filter can now be applied to a region inside the source itself, the inverse of that region, and/or a feathered version of that region. This allows for easier scene setups where only some parts need to be blurred, but the rest can be left as is. Fixes #12 --- data/effects/bilateral-blur.effect | 143 ++++++++++++++++++++++++----- data/effects/box-blur.effect | 126 +++++++++++++++++++++++-- data/effects/gaussian-blur.effect | 122 ++++++++++++++++++++++-- data/locale/en-US.ini | 16 ++++ source/filter-blur.cpp | 102 +++++++++++++++++++- source/filter-blur.h | 12 +++ 6 files changed, 484 insertions(+), 37 deletions(-) diff --git a/data/effects/bilateral-blur.effect b/data/effects/bilateral-blur.effect index 7d94c8f3..9acf9c91 100644 --- a/data/effects/bilateral-blur.effect +++ b/data/effects/bilateral-blur.effect @@ -9,6 +9,14 @@ uniform int u_radius; uniform int u_diameter; uniform float2 u_texelDelta; +/// Region +uniform float regionLeft; +uniform float regionTop; +uniform float regionRight; +uniform float regionBottom; +uniform float regionFeather; +uniform float regionFeatherShift; + // Settings (Private) uniform float bilateralSmoothing; uniform float bilateralSharpness; @@ -17,6 +25,8 @@ sampler_state textureSampler { Filter = Point; AddressU = Clamp; AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; }; struct VertDataIn { @@ -38,33 +48,23 @@ VertDataOut VSDefault(VertDataIn v_in) } // Bilateral Blur -float Bilateral(float x, float sigma) -{ +float Bilateral(float x, float sigma) { return 0.39894 * exp(-0.5 * (x*x) / (sigma*sigma)) / sigma; } -float Bilateral3(float3 v, float sigma) -{ - // First part is Gaussian function (1.0 / (o * sqrt(2.0 * pivalue))) with o = 1 +float Bilateral3(float3 v, float sigma) { + // First part is Bilateral function (1.0 / (o * sqrt(2.0 * pivalue))) with o = 1 return 0.39894 * exp(-0.5 * dot(v,v) / (sigma*sigma)) / sigma; } -float4 BilateralBlur(float2 p_uv, float2 p_radius, - texture2d p_image, float2 p_imageTexel) { - float2 l_uvoffset = float2(0, 0); -} - -float4 PSBilateral(VertDataOut v_in) : TARGET -{ - float2 l_uv = float2(0, 0); +float4 BlurFunc(float2 uv, float4 rgba) { + float2 uvOffset = float2(0, 0); float Z = 0.0; float bZ = 1.0 / Bilateral(0.0, bilateralSharpness); - float4 source = u_image.Sample(textureSampler, v_in.uv); float3 color = float3(0, 0, 0); for (int k = 1; k <= u_radius; k++) { - // Advance UV by one texel. - l_uv += u_texelDelta; + uvOffset += u_texelDelta; // Bilateral Kernel float bKernel = Bilateral(abs(k), bilateralSmoothing); @@ -72,12 +72,12 @@ float4 PSBilateral(VertDataOut v_in) : TARGET float bZKernel = bZ * bKernel; // Sample Color - float3 l_p = u_image.Sample(textureSampler, v_in.uv + l_uv).rgb; - float3 l_n = u_image.Sample(textureSampler, v_in.uv - l_uv).rgb; + float3 l_p = u_image.SampleLevel(textureSampler, uv + uvOffset, 0).rgb; + float3 l_n = u_image.SampleLevel(textureSampler, uv - uvOffset, 0).rgb; // Bilateral Stuff - float l_factor_p = Bilateral3(l_p - source.rgb, bilateralSharpness) * bZKernel; - float l_factor_n = Bilateral3(l_n - source.rgb, bilateralSharpness) * bZKernel; + float l_factor_p = Bilateral3(l_p - rgba.rgb, bilateralSharpness) * bZKernel; + float l_factor_n = Bilateral3(l_n - rgba.rgb, bilateralSharpness) * bZKernel; Z = Z + l_factor_p + l_factor_n; // Store Color @@ -85,7 +85,74 @@ float4 PSBilateral(VertDataOut v_in) : TARGET color += l_n * l_factor_n; } - return float4(color.rgb / Z, source.a); + return float4(color.rgb / Z, rgba.a); +} + +float4 PSBilateral(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBilateralRegion(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x < regionLeft) + || (v_in.uv.x > regionRight) + || (v_in.uv.y < regionTop) + || (v_in.uv.y > regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBilateralRegionInvert(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x > regionLeft) + && (v_in.uv.x < regionRight) + && (v_in.uv.y > regionTop) + && (v_in.uv.y < regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBilateralRegionFeather(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); +} + +float4 PSBilateralRegionFeatherInvert(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = 1.0 - min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); } technique Draw @@ -96,3 +163,37 @@ technique Draw pixel_shader = PSBilateral(v_in); } } + +technique DrawRegion +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBilateralRegion(v_in); + } +} +technique DrawRegionInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBilateralRegionInvert(v_in); + } +} + +technique DrawRegionFeather +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBilateralRegionFeather(v_in); + } +} +technique DrawRegionFeatherInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBilateralRegionFeatherInvert(v_in); + } +} diff --git a/data/effects/box-blur.effect b/data/effects/box-blur.effect index b4a2f361..951420f6 100644 --- a/data/effects/box-blur.effect +++ b/data/effects/box-blur.effect @@ -9,10 +9,21 @@ uniform int u_radius; uniform int u_diameter; uniform float2 u_texelDelta; +/// Region +uniform float regionLeft; +uniform float regionTop; +uniform float regionRight; +uniform float regionBottom; +uniform float regionFeather; +uniform float regionFeatherShift; + +// Data sampler_state textureSampler { Filter = Point; AddressU = Clamp; AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; }; struct VertDataIn { @@ -34,15 +45,80 @@ VertDataOut VSDefault(VertDataIn v_in) } // Box Blur -float4 PSBox(VertDataOut v_in) : TARGET -{ - float4 rgba = u_image.Sample(textureSampler, v_in.uv); +float4 BlurFunc(float2 uv, float4 rgba) { + float4 final = rgba; for (int k = 1; k <= u_radius; k++) { - rgba += u_image.Sample(textureSampler, v_in.uv + (u_texelDelta * k)); - rgba += u_image.Sample(textureSampler, v_in.uv - (u_texelDelta * k)); + final += u_image.SampleLevel(textureSampler, uv + (u_texelDelta * k), 0); + final += u_image.SampleLevel(textureSampler, uv - (u_texelDelta * k), 0); } - rgba = rgba / u_diameter; - return rgba; + return final / u_diameter; +} + +float4 PSBox(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBoxRegion(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x < regionLeft) + || (v_in.uv.x > regionRight) + || (v_in.uv.y < regionTop) + || (v_in.uv.y > regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBoxRegionInvert(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x > regionLeft) + && (v_in.uv.x < regionRight) + && (v_in.uv.y > regionTop) + && (v_in.uv.y < regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSBoxRegionFeather(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); +} + +float4 PSBoxRegionFeatherInvert(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = 1.0 - min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); } technique Draw @@ -53,3 +129,39 @@ technique Draw pixel_shader = PSBox(v_in); } } + +technique DrawRegion +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBoxRegion(v_in); + } +} + +technique DrawRegionInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBoxRegionInvert(v_in); + } +} + +technique DrawRegionFeather +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBoxRegionFeather(v_in); + } +} + +technique DrawRegionFeatherInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSBoxRegionFeatherInvert(v_in); + } +} diff --git a/data/effects/gaussian-blur.effect b/data/effects/gaussian-blur.effect index aeaa38c4..078988de 100644 --- a/data/effects/gaussian-blur.effect +++ b/data/effects/gaussian-blur.effect @@ -9,6 +9,14 @@ uniform int u_radius; uniform int u_diameter; uniform float2 u_texelDelta; +/// Region +uniform float regionLeft; +uniform float regionTop; +uniform float regionRight; +uniform float regionBottom; +uniform float regionFeather; +uniform float regionFeatherShift; + // Settings (Private) //uniform float registerkernel[25]; uniform texture2d kernel; @@ -40,18 +48,84 @@ VertDataOut VSDefault(VertDataIn v_in) return vert_out; } -float4 PSGaussian(VertDataOut v_in) : TARGET { +float4 BlurFunc(float2 uv, float4 rgba) { float2 uvOffset = float2(0, 0); - float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0) - * kernel.SampleLevel(textureSampler, (float2(0, u_radius - 1) * kernelTexel), 0).r; + float4 final = rgba * kernel.SampleLevel(textureSampler, (float2(0, u_radius - 1) * kernelTexel), 0).r; for (int k = 1; k <= u_radius; k++) { uvOffset += u_texelDelta; float l_g = kernel.SampleLevel(textureSampler, (float2(k, u_radius - 1) * kernelTexel), 0).r; - float4 l_p = u_image.SampleLevel(textureSampler, v_in.uv + uvOffset, 0) * l_g; - float4 l_n = u_image.SampleLevel(textureSampler, v_in.uv - uvOffset, 0) * l_g; - rgba += l_p + l_n; + float4 l_p = u_image.SampleLevel(textureSampler, uv + uvOffset, 0); + float4 l_n = u_image.SampleLevel(textureSampler, uv - uvOffset, 0); + final += (l_p + l_n) * l_g; } - return rgba; + return final; +} + +float4 PSGaussian(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + return BlurFunc(v_in.uv, rgba); +} + +float4 PSGaussianRegion(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x < regionLeft) + || (v_in.uv.x > regionRight) + || (v_in.uv.y < regionTop) + || (v_in.uv.y > regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSGaussianRegionInvert(VertDataOut v_in) : TARGET { + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if ((v_in.uv.x > regionLeft) + && (v_in.uv.x < regionRight) + && (v_in.uv.y > regionTop) + && (v_in.uv.y < regionBottom)) { + return rgba; + } + + return BlurFunc(v_in.uv, rgba); +} + +float4 PSGaussianRegionFeather(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); +} + +float4 PSGaussianRegionFeatherInvert(VertDataOut v_in) : TARGET { + float halfFeather = (regionFeather / 2.0); + float feather = max(regionFeather, 0.00000001); + float leftFeather = clamp(((v_in.uv.x - regionLeft + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float rightFeather = clamp(((-(v_in.uv.x - regionRight) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float topFeather = clamp(((v_in.uv.y - regionTop + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float bottomFeather = clamp(((-(v_in.uv.y - regionBottom) + halfFeather) / feather) + regionFeatherShift, 0.0, 1.0); + float finalFeather = 1.0 - min(min(leftFeather, rightFeather), min(topFeather, bottomFeather)); + + float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + if (finalFeather <= 0.00001) { + return rgba; + } else if (finalFeather >= 0.99999) { + return BlurFunc(v_in.uv, rgba); + } + + return lerp(rgba, BlurFunc(v_in.uv, rgba), finalFeather); } technique Draw @@ -62,3 +136,37 @@ technique Draw pixel_shader = PSGaussian(v_in); } } + +technique DrawRegion +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSGaussianRegion(v_in); + } +} +technique DrawRegionInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSGaussianRegionInvert(v_in); + } +} + +technique DrawRegionFeather +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSGaussianRegionFeather(v_in); + } +} +technique DrawRegionFeatherInvert +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSGaussianRegionFeatherInvert(v_in); + } +} diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index bcbdf6db..31eab948 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -12,6 +12,22 @@ Filter.Blur.Size="Size (Pixel)" Filter.Blur.Size.Description="Area size of the blur, large sizes may cause:\n- Skipped frames\n- Frame loss or drops\n- Input lag\n- GPU overheating\n- or other issues." Filter.Blur.Bilateral.Smoothing="Smoothing" Filter.Blur.Bilateral.Sharpness="Sharpness" +Filter.Blur.Region="Apply to Region only" +Filter.Blur.Region.Description="Only apply the blur to a region inside the source." +Filter.Blur.Region.Left="Left Edge" +Filter.Blur.Region.Left.Description="Distance to left edge of the source in percent." +Filter.Blur.Region.Top="Top Edge" +Filter.Blur.Region.Top.Description="Distance to top edge of the source in percent." +Filter.Blur.Region.Right="Right Edge" +Filter.Blur.Region.Right.Description="Distance to right edge of the source in percent." +Filter.Blur.Region.Bottom="Bottom Edge" +Filter.Blur.Region.Bottom.Description="Distance to bottom edge of the source in percent." +Filter.Blur.Region.Feather="Feather Area" +Filter.Blur.Region.Feather.Description="Size of the smoothing area in percent, or 0 to turn off feather." +Filter.Blur.Region.Feather.Shift="Feather Shift" +Filter.Blur.Region.Feather.Shift.Description="Shift of the Feather area, positive is inwards, negative is outwards." +Filter.Blur.Region.Invert="Invert Region" +Filter.Blur.Region.Invert.Description="Invert the region so that everything but this area is blurred." Filter.Blur.ColorFormat="Color Format" # Filter - Custom Shader diff --git a/source/filter-blur.cpp b/source/filter-blur.cpp index 91497854..38ff7063 100644 --- a/source/filter-blur.cpp +++ b/source/filter-blur.cpp @@ -44,6 +44,16 @@ extern "C" { #define S_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing" #define S_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness" +// Region +#define S_REGION "Filter.Blur.Region" +#define S_REGION_LEFT "Filter.Blur.Region.Left" +#define S_REGION_TOP "Filter.Blur.Region.Top" +#define S_REGION_RIGHT "Filter.Blur.Region.Right" +#define S_REGION_BOTTOM "Filter.Blur.Region.Bottom" +#define S_REGION_FEATHER "Filter.Blur.Region.Feather" +#define S_REGION_FEATHER_SHIFT "Filter.Blur.Region.Feather.Shift" +#define S_REGION_INVERT "Filter.Blur.Region.Invert" + // Advanced #define S_FILTER_BLUR_COLORFORMAT "Filter.Blur.ColorFormat" @@ -96,6 +106,7 @@ Filter::Blur::Blur() { } catch (std::runtime_error ex) { P_LOG_ERROR(" Loading effect '%s' (path: '%s') failed with error(s): %s", kv.first.c_str(), kv.second.c_str(), ex.what()); + obs_leave_graphics(); return; } } @@ -160,6 +171,16 @@ void Filter::Blur::get_defaults(obs_data_t *data) { obs_data_set_default_double(data, S_BILATERAL_SMOOTHING, 50.0); obs_data_set_default_double(data, S_BILATERAL_SHARPNESS, 90.0); + // Region + obs_data_set_default_bool(data, S_REGION, false); + obs_data_set_default_double(data, S_REGION_LEFT, 0.0f); + obs_data_set_default_double(data, S_REGION_TOP, 0.0f); + obs_data_set_default_double(data, S_REGION_RIGHT, 0.0f); + obs_data_set_default_double(data, S_REGION_BOTTOM, 0.0f); + obs_data_set_default_double(data, S_REGION_FEATHER, 0.0f); + obs_data_set_default_double(data, S_REGION_FEATHER_SHIFT, 0.0f); + obs_data_set_default_bool(data, S_REGION_INVERT, false); + // Advanced obs_data_set_default_bool(data, S_ADVANCED, false); obs_data_set_default_int(data, S_FILTER_BLUR_COLORFORMAT, ColorFormat::RGB); @@ -186,6 +207,25 @@ obs_properties_t * Filter::Blur::get_properties(void *) { p = obs_properties_add_float_slider(pr, S_BILATERAL_SHARPNESS, P_TRANSLATE(S_BILATERAL_SHARPNESS), 0, 99.99, 0.01); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_BILATERAL_SHARPNESS))); + // Region + p = obs_properties_add_bool(pr, S_REGION, P_TRANSLATE(S_REGION)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION))); + obs_property_set_modified_callback(p, modified_properties); + p = obs_properties_add_float_slider(pr, S_REGION_LEFT, P_TRANSLATE(S_REGION_LEFT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_LEFT))); + p = obs_properties_add_float_slider(pr, S_REGION_TOP, P_TRANSLATE(S_REGION_TOP), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_TOP))); + p = obs_properties_add_float_slider(pr, S_REGION_RIGHT, P_TRANSLATE(S_REGION_RIGHT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_RIGHT))); + p = obs_properties_add_float_slider(pr, S_REGION_BOTTOM, P_TRANSLATE(S_REGION_BOTTOM), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_BOTTOM))); + p = obs_properties_add_float_slider(pr, S_REGION_FEATHER, P_TRANSLATE(S_REGION_FEATHER), 0.0, 50.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_FEATHER))); + p = obs_properties_add_float_slider(pr, S_REGION_FEATHER_SHIFT, P_TRANSLATE(S_REGION_FEATHER_SHIFT), -100.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_FEATHER_SHIFT))); + p = obs_properties_add_bool(pr, S_REGION_INVERT, P_TRANSLATE(S_REGION_INVERT)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_REGION_INVERT))); + // Advanced p = obs_properties_add_bool(pr, S_ADVANCED, P_TRANSLATE(S_ADVANCED)); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_ADVANCED))); @@ -216,6 +256,16 @@ bool Filter::Blur::modified_properties(obs_properties_t *pr, obs_property_t *, o obs_property_set_visible(obs_properties_get(pr, S_BILATERAL_SMOOTHING), showBilateral); obs_property_set_visible(obs_properties_get(pr, S_BILATERAL_SHARPNESS), showBilateral); + // Region + bool showRegion = obs_data_get_bool(d, S_REGION); + obs_property_set_visible(obs_properties_get(pr, S_REGION_LEFT), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_TOP), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_RIGHT), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_BOTTOM), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_FEATHER), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_FEATHER_SHIFT), showRegion); + obs_property_set_visible(obs_properties_get(pr, S_REGION_INVERT), showRegion); + // Advanced bool showAdvanced = false; if (obs_data_get_bool(d, S_ADVANCED)) @@ -312,8 +362,24 @@ void Filter::Blur::Instance::update(obs_data_t *data) { m_bilateralSmoothing = obs_data_get_double(data, S_BILATERAL_SMOOTHING) / 100.0; m_bilateralSharpness = obs_data_get_double(data, S_BILATERAL_SHARPNESS) / 100.0; + // Region + m_region.enabled = obs_data_get_bool(data, S_REGION); + if (m_region.enabled) { + m_region.left = float_t(obs_data_get_double(data, S_REGION_LEFT) / 100.0); + m_region.top = float_t(obs_data_get_double(data, S_REGION_TOP) / 100.0); + m_region.right = 1.0 - float_t(obs_data_get_double(data, S_REGION_RIGHT) / 100.0); + m_region.bottom = 1.0 - float_t(obs_data_get_double(data, S_REGION_BOTTOM) / 100.0); + m_region.feather = float_t(obs_data_get_double(data, S_REGION_FEATHER) / 100.0); + m_region.feather_shift = float_t(obs_data_get_double(data, S_REGION_FEATHER_SHIFT) / 100.0); + m_region.invert = obs_data_get_bool(data, S_REGION_INVERT); + } + // Advanced - m_colorFormat = obs_data_get_int(data, S_FILTER_BLUR_COLORFORMAT); + if (obs_data_get_bool(data, S_ADVANCED)) { + m_colorFormat = obs_data_get_int(data, S_FILTER_BLUR_COLORFORMAT); + } else { + m_colorFormat = obs_data_get_default_int(data, S_FILTER_BLUR_COLORFORMAT); + } } uint32_t Filter::Blur::Instance::get_width() { @@ -469,6 +535,17 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) { std::make_tuple("Horizontal", m_rtHorizontal, 1.0f / baseW, 0.0f), std::make_tuple("Vertical", m_rtVertical, 0.0f, 1.0f / baseH), }; + std::string pass = "Draw"; + if (m_region.enabled) { + if (m_region.feather > 0) { + pass = "DrawRegionFeather"; + } else { + pass = "DrawRegion"; + } + if (m_region.invert) { + pass += "Invert"; + } + } for (auto v : kvs) { const char* name = std::get<0>(v); gs_texrender_t* rt = std::get<1>(v); @@ -497,7 +574,7 @@ void Filter::Blur::Instance::video_render(gs_effect_t *effect) { gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); // Render - while (gs_effect_loop(m_effect->get_object(), "Draw")) { + while (gs_effect_loop(m_effect->get_object(), pass.c_str())) { gs_draw_sprite(intermediate, 0, baseW, baseH); } @@ -578,6 +655,27 @@ bool Filter::Blur::Instance::apply_shared_param(gs_texture_t* input, float texel result = result && gs_set_param_int(m_effect->get_object(), "u_radius", (int)m_size); result = result && gs_set_param_int(m_effect->get_object(), "u_diameter", (int)(1 + (m_size * 2))); + if (m_region.enabled) { + if (m_effect->has_parameter("regionLeft")) { + m_effect->get_parameter("regionLeft").set_float(m_region.left); + } + if (m_effect->has_parameter("regionTop")) { + m_effect->get_parameter("regionTop").set_float(m_region.top); + } + if (m_effect->has_parameter("regionRight")) { + m_effect->get_parameter("regionRight").set_float(m_region.right); + } + if (m_effect->has_parameter("regionBottom")) { + m_effect->get_parameter("regionBottom").set_float(m_region.bottom); + } + if (m_effect->has_parameter("regionFeather")) { + m_effect->get_parameter("regionFeather").set_float(m_region.feather); + } + if (m_effect->has_parameter("regionFeatherShift")) { + m_effect->get_parameter("regionFeatherShift").set_float(m_region.feather_shift); + } + } + return result; } diff --git a/source/filter-blur.h b/source/filter-blur.h index f79bc08c..cd99a1d5 100644 --- a/source/filter-blur.h +++ b/source/filter-blur.h @@ -98,6 +98,18 @@ namespace Filter { double_t m_bilateralSmoothing; double_t m_bilateralSharpness; + // Regional + struct Region { + bool enabled; + float_t left; + float_t top; + float_t right; + float_t bottom; + float_t feather; + float_t feather_shift; + bool invert; + } m_region; + // Advanced bool m_errorLogged = false; uint64_t m_colorFormat;