From d56f4f9eacaa27a87d165aa835de96f792d3e20c Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 30 Sep 2018 22:48:47 +0200 Subject: [PATCH] filter-blur: Add proper mask options * Removes the old 'Region' fields and places them under a 'Mask' option that can do much more. * Supported Mask types: Region, Image, Source. * Image and Source mask types allow for a color filter and multiplier. --- data/effects/bilateral-blur.effect | 104 -------- data/effects/box-blur.effect | 120 +-------- data/effects/gaussian-blur.effect | 104 -------- data/locale/en-US.ini | 30 +++ source/filter-blur.cpp | 382 +++++++++++++++++++++-------- source/filter-blur.h | 46 +++- 6 files changed, 359 insertions(+), 427 deletions(-) diff --git a/data/effects/bilateral-blur.effect b/data/effects/bilateral-blur.effect index 9acf9c91..19d1541b 100644 --- a/data/effects/bilateral-blur.effect +++ b/data/effects/bilateral-blur.effect @@ -9,14 +9,6 @@ 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; @@ -93,68 +85,6 @@ float4 PSBilateral(VertDataOut v_in) : TARGET { 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 { pass @@ -163,37 +93,3 @@ 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 951420f6..45be7d1b 100644 --- a/data/effects/box-blur.effect +++ b/data/effects/box-blur.effect @@ -1,7 +1,7 @@ -// OBS Default +// Parameters +/// OBS uniform float4x4 ViewProj; - -// Settings (Shared) +/// Blur uniform texture2d u_image; uniform float2 u_imageSize; uniform float2 u_imageTexel; @@ -9,16 +9,8 @@ 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 { +sampler_state pointSampler { Filter = Point; AddressU = Clamp; AddressV = Clamp; @@ -48,79 +40,17 @@ VertDataOut VSDefault(VertDataIn v_in) float4 BlurFunc(float2 uv, float4 rgba) { float4 final = rgba; for (int k = 1; k <= u_radius; k++) { - final += u_image.SampleLevel(textureSampler, uv + (u_texelDelta * k), 0); - final += u_image.SampleLevel(textureSampler, uv - (u_texelDelta * k), 0); + final += u_image.SampleLevel(pointSampler, uv + (u_texelDelta * k), 0); + final += u_image.SampleLevel(pointSampler, uv - (u_texelDelta * k), 0); } return final / u_diameter; } float4 PSBox(VertDataOut v_in) : TARGET { - float4 rgba = u_image.SampleLevel(textureSampler, v_in.uv, 0); + float4 rgba = u_image.SampleLevel(pointSampler, 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 { pass @@ -129,39 +59,3 @@ 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 078988de..58196f2a 100644 --- a/data/effects/gaussian-blur.effect +++ b/data/effects/gaussian-blur.effect @@ -9,14 +9,6 @@ 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; @@ -66,68 +58,6 @@ float4 PSGaussian(VertDataOut v_in) : TARGET { 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 { pass @@ -136,37 +66,3 @@ 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 9ba8688f..7cba0bd3 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -53,6 +53,36 @@ Filter.Blur.Region.Feather.Shift.Description="Shift of the Feather area, positiv 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.Blur.Mask="Apply a Mask" +Filter.Blur.Mask.Description="Apply a mask to the area that needs to be blurred, which allows for more control over the blurred area." +Filter.Blur.Mask.Type="Mask Type" +Filter.Blur.Mask.Type.Description="What kind of mask to you want to apply?" +Filter.Blur.Mask.Type.Region="Region" +Filter.Blur.Mask.Type.Image="Image" +Filter.Blur.Mask.Type.Source="Source" +Filter.Blur.Mask.Region.Left="Left Edge" +Filter.Blur.Mask.Region.Left.Description="Distance to left edge of the source in percent." +Filter.Blur.Mask.Region.Top="Top Edge" +Filter.Blur.Mask.Region.Top.Description="Distance to top edge of the source in percent." +Filter.Blur.Mask.Region.Right="Right Edge" +Filter.Blur.Mask.Region.Right.Description="Distance to right edge of the source in percent." +Filter.Blur.Mask.Region.Bottom="Bottom Edge" +Filter.Blur.Mask.Region.Bottom.Description="Distance to bottom edge of the source in percent." +Filter.Blur.Mask.Region.Feather="Feather Area" +Filter.Blur.Mask.Region.Feather.Description="Size of the smoothing area in percent, or 0 to turn off feather." +Filter.Blur.Mask.Region.Feather.Shift="Feather Shift" +Filter.Blur.Mask.Region.Feather.Shift.Description="Shift of the Feather area, positive is inwards, negative is outwards." +Filter.Blur.Mask.Region.Invert="Invert Region" +Filter.Blur.Mask.Region.Invert.Description="Invert the region so that everything but this area is blurred." +Filter.Blur.Mask.Image="Image Mask" +Filter.Blur.Mask.Image.Description="Image to use for the mask." +Filter.Blur.Mask.Source="Source Mask" +Filter.Blur.Mask.Source.Description="Source to use for the mask." +Filter.Blur.Mask.Color="Mask Color Filter" +Filter.Blur.Mask.Color.Description="Filter the mask by this color before applying it." +Filter.Blur.Mask.Multiplier="Mask Multiplier" +Filter.Blur.Mask.Multiplier.Description="Multiply the final mask value by this value." + # Filter - Custom Shader Filter.CustomShader="Custom Shader" diff --git a/source/filter-blur.cpp b/source/filter-blur.cpp index d925db92..98011ffe 100644 --- a/source/filter-blur.cpp +++ b/source/filter-blur.cpp @@ -43,15 +43,23 @@ extern "C" { #define P_SIZE "Filter.Blur.Size" #define P_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing" #define P_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness" -#define P_REGION "Filter.Blur.Region" -#define P_REGION_LEFT "Filter.Blur.Region.Left" -#define P_REGION_RIGHT "Filter.Blur.Region.Right" -#define P_REGION_TOP "Filter.Blur.Region.Top" -#define P_REGION_BOTTOM "Filter.Blur.Region.Bottom" -#define P_REGION_FEATHER "Filter.Blur.Region.Feather" -#define P_REGION_FEATHER_SHIFT "Filter.Blur.Region.Feather.Shift" -#define P_REGION_INVERT "Filter.Blur.Region.Invert" #define P_COLORFORMAT "Filter.Blur.ColorFormat" +#define P_MASK "Filter.Blur.Mask" +#define P_MASK_TYPE "Filter.Blur.Mask.Type" +#define P_MASK_TYPE_REGION "Filter.Blur.Mask.Type.Region" +#define P_MASK_TYPE_IMAGE "Filter.Blur.Mask.Type.Image" +#define P_MASK_TYPE_SOURCE "Filter.Blur.Mask.Type.Source" +#define P_MASK_REGION_LEFT "Filter.Blur.Mask.Region.Left" +#define P_MASK_REGION_RIGHT "Filter.Blur.Mask.Region.Right" +#define P_MASK_REGION_TOP "Filter.Blur.Mask.Region.Top" +#define P_MASK_REGION_BOTTOM "Filter.Blur.Mask.Region.Bottom" +#define P_MASK_REGION_FEATHER "Filter.Blur.Mask.Region.Feather" +#define P_MASK_REGION_FEATHER_SHIFT "Filter.Blur.Mask.Region.Feather.Shift" +#define P_MASK_REGION_INVERT "Filter.Blur.Mask.Region.Invert" +#define P_MASK_IMAGE "Filter.Blur.Mask.Image" +#define P_MASK_SOURCE "Filter.Blur.Mask.Source" +#define P_MASK_COLOR "Filter.Blur.Mask.Color" +#define P_MASK_MULTIPLIER "Filter.Blur.Mask.Multiplier" // Initializer & Finalizer INITIALIZER(filterBlurFactoryInitializer) @@ -89,34 +97,12 @@ bool filter::blur::instance::apply_shared_param(gs_texture_t* input, float texel result = result && gs_set_param_int(blur_effect->get_object(), "u_radius", (int)size); result = result && gs_set_param_int(blur_effect->get_object(), "u_diameter", (int)(1 + (size * 2))); - if (region.enabled) { - if (blur_effect->has_parameter("regionLeft")) { - blur_effect->get_parameter("regionLeft").set_float(region.left); - } - if (blur_effect->has_parameter("regionTop")) { - blur_effect->get_parameter("regionTop").set_float(region.top); - } - if (blur_effect->has_parameter("regionRight")) { - blur_effect->get_parameter("regionRight").set_float(region.right); - } - if (blur_effect->has_parameter("regionBottom")) { - blur_effect->get_parameter("regionBottom").set_float(region.bottom); - } - if (blur_effect->has_parameter("regionFeather")) { - blur_effect->get_parameter("regionFeather").set_float(region.feather); - } - if (blur_effect->has_parameter("regionFeatherShift")) { - blur_effect->get_parameter("regionFeatherShift").set_float(region.feather_shift); - } - } return result; } bool filter::blur::instance::apply_bilateral_param() { - gs_eparam_t* param; - if (type != type::Bilateral) return false; @@ -150,24 +136,103 @@ bool filter::blur::instance::apply_gaussian_param() return true; } +bool filter::blur::instance::apply_mask_parameters(std::shared_ptr effect, gs_texture_t * original_texture, gs_texture_t* blurred_texture) +{ + if (effect->has_parameter("image_orig")) { + effect->get_parameter("image_orig").set_texture(original_texture); + } + if (effect->has_parameter("image_blur")) { + effect->get_parameter("image_blur").set_texture(blurred_texture); + } + + // Region + if (mask.type == mask_type::Region) { + if (effect->has_parameter("mask_region_left")) { + effect->get_parameter("mask_region_left").set_float(mask.region.left); + } + if (effect->has_parameter("mask_region_right")) { + effect->get_parameter("mask_region_right").set_float(mask.region.right); + } + if (effect->has_parameter("mask_region_top")) { + effect->get_parameter("mask_region_top").set_float(mask.region.top); + } + if (effect->has_parameter("mask_region_bottom")) { + effect->get_parameter("mask_region_bottom").set_float(mask.region.bottom); + } + if (effect->has_parameter("mask_region_feather")) { + effect->get_parameter("mask_region_feather").set_float(mask.region.feather); + } + if (effect->has_parameter("mask_region_feather_shift")) { + effect->get_parameter("mask_region_feather_shift").set_float(mask.region.feather_shift); + } + } + + // Image + if (mask.type == mask_type::Image) { + if (effect->has_parameter("mask_image")) { + if (mask.image.texture) { + effect->get_parameter("mask_image").set_texture(mask.image.texture); + } else { + effect->get_parameter("mask_image").set_texture(nullptr); + } + } + } + + // Source + if (mask.type == mask_type::Source) { + if (effect->has_parameter("mask_image")) { + if (mask.source.texture) { + effect->get_parameter("mask_image").set_texture(mask.source.texture); + } else { + effect->get_parameter("mask_image").set_texture(nullptr); + } + } + } + + // Shared + if (effect->has_parameter("mask_color")) { + effect->get_parameter("mask_color") + .set_float4((mask.color & 0xFF) / 255.0f, ((mask.color >> 8) & 0xFF) / 255.0f, + ((mask.color >> 16) & 0xFF) / 255.0f, ((mask.color >> 24) & 0xFF) / 255.0f); + } + if (effect->has_parameter("mask_multiplier")) { + effect->get_parameter("mask_multiplier").set_float(mask.multiplier); + } + + return true; +} + bool filter::blur::instance::modified_properties(void* ptr, obs_properties_t* props, obs_property* prop, obs_data_t* settings) { bool showBilateral = (obs_data_get_int(settings, P_TYPE) == type::Bilateral); + prop; + ptr; + // bilateral blur obs_property_set_visible(obs_properties_get(props, P_BILATERAL_SMOOTHING), showBilateral); obs_property_set_visible(obs_properties_get(props, P_BILATERAL_SHARPNESS), showBilateral); // region - bool showRegion = obs_data_get_bool(settings, P_REGION); - obs_property_set_visible(obs_properties_get(props, P_REGION_LEFT), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_TOP), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_RIGHT), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_BOTTOM), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_FEATHER), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_FEATHER_SHIFT), showRegion); - obs_property_set_visible(obs_properties_get(props, P_REGION_INVERT), showRegion); + bool show_mask = obs_data_get_bool(settings, P_MASK); + mask_type mtype = static_cast(obs_data_get_int(settings, P_MASK_TYPE)); + bool show_region = (mtype == mask_type::Region) && show_mask; + bool show_image = (mtype == mask_type::Image) && show_mask; + bool show_source = (mtype == mask_type::Source) && show_mask; + + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_LEFT), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_TOP), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_RIGHT), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_BOTTOM), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_FEATHER), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_FEATHER_SHIFT), show_region); + obs_property_set_visible(obs_properties_get(props, P_MASK_REGION_INVERT), show_region); + + obs_property_set_visible(obs_properties_get(props, P_MASK_IMAGE), show_image); + obs_property_set_visible(obs_properties_get(props, P_MASK_SOURCE), show_source); + obs_property_set_visible(obs_properties_get(props, P_MASK_COLOR), show_image || show_source); + obs_property_set_visible(obs_properties_get(props, P_MASK_MULTIPLIER), show_image || show_source); // advanced bool showAdvanced = obs_data_get_bool(settings, S_ADVANCED); @@ -245,25 +310,52 @@ obs_properties_t* filter::blur::instance::get_properties() 0.01); obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_BILATERAL_SHARPNESS))); - // region - p = obs_properties_add_bool(pr, P_REGION, P_TRANSLATE(P_REGION)); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION))); + // Mask + p = obs_properties_add_bool(pr, P_MASK, P_TRANSLATE(P_MASK)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK))); obs_property_set_modified_callback2(p, modified_properties, this); - p = obs_properties_add_float_slider(pr, P_REGION_LEFT, P_TRANSLATE(P_REGION_LEFT), 0.0, 100.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_LEFT))); - p = obs_properties_add_float_slider(pr, P_REGION_TOP, P_TRANSLATE(P_REGION_TOP), 0.0, 100.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_TOP))); - p = obs_properties_add_float_slider(pr, P_REGION_RIGHT, P_TRANSLATE(P_REGION_RIGHT), 0.0, 100.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_RIGHT))); - p = obs_properties_add_float_slider(pr, P_REGION_BOTTOM, P_TRANSLATE(P_REGION_BOTTOM), 0.0, 100.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_BOTTOM))); - p = obs_properties_add_float_slider(pr, P_REGION_FEATHER, P_TRANSLATE(P_REGION_FEATHER), 0.0, 50.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_FEATHER))); - p = obs_properties_add_float_slider(pr, P_REGION_FEATHER_SHIFT, P_TRANSLATE(P_REGION_FEATHER_SHIFT), -100.0, 100.0, - 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_FEATHER_SHIFT))); - p = obs_properties_add_bool(pr, P_REGION_INVERT, P_TRANSLATE(P_REGION_INVERT)); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_INVERT))); + p = obs_properties_add_list(pr, P_MASK_TYPE, P_TRANSLATE(P_MASK_TYPE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_TYPE))); + obs_property_set_modified_callback2(p, modified_properties, this); + obs_property_list_add_int(p, P_TRANSLATE(P_MASK_TYPE_REGION), mask_type::Region); + obs_property_list_add_int(p, P_TRANSLATE(P_MASK_TYPE_IMAGE), mask_type::Image); + obs_property_list_add_int(p, P_TRANSLATE(P_MASK_TYPE_SOURCE), mask_type::Source); + /// Region + p = obs_properties_add_float_slider(pr, P_MASK_REGION_LEFT, P_TRANSLATE(P_MASK_REGION_LEFT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_LEFT))); + p = obs_properties_add_float_slider(pr, P_MASK_REGION_TOP, P_TRANSLATE(P_MASK_REGION_TOP), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_TOP))); + p = obs_properties_add_float_slider(pr, P_MASK_REGION_RIGHT, P_TRANSLATE(P_MASK_REGION_RIGHT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_RIGHT))); + p = obs_properties_add_float_slider(pr, P_MASK_REGION_BOTTOM, P_TRANSLATE(P_MASK_REGION_BOTTOM), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_BOTTOM))); + p = obs_properties_add_float_slider(pr, P_MASK_REGION_FEATHER, P_TRANSLATE(P_MASK_REGION_FEATHER), 0.0, 50.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_FEATHER))); + p = obs_properties_add_float_slider(pr, P_MASK_REGION_FEATHER_SHIFT, P_TRANSLATE(P_MASK_REGION_FEATHER_SHIFT), + -100.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_FEATHER_SHIFT))); + p = obs_properties_add_bool(pr, P_MASK_REGION_INVERT, P_TRANSLATE(P_MASK_REGION_INVERT)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_REGION_INVERT))); + /// Image + p = obs_properties_add_path(pr, P_MASK_IMAGE, P_TRANSLATE(P_MASK_IMAGE), OBS_PATH_FILE, + P_TRANSLATE(S_FILEFILTERS_IMAGES), nullptr); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_IMAGE))); + /// Source + p = obs_properties_add_list(pr, P_MASK_SOURCE, P_TRANSLATE(P_MASK_SOURCE), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_SOURCE))); + obs_enum_sources( + [](void* ptr, obs_source_t* source) { + obs_property_t* p = reinterpret_cast(ptr); + obs_property_list_add_string(p, obs_source_get_name(source), obs_source_get_name(source)); + return true; + }, + p); + /// Shared + p = obs_properties_add_color(pr, P_MASK_COLOR, P_TRANSLATE(P_MASK_COLOR)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_COLOR))); + p = obs_properties_add_float_slider(pr, P_MASK_MULTIPLIER, P_TRANSLATE(P_MASK_MULTIPLIER), 0.0, 10.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_MASK_MULTIPLIER))); // advanced p = obs_properties_add_bool(pr, S_ADVANCED, P_TRANSLATE(S_ADVANCED)); @@ -290,15 +382,30 @@ void filter::blur::instance::update(obs_data_t* settings) bilateral_sharpness = obs_data_get_double(settings, P_BILATERAL_SHARPNESS) / 100.0; // region - region.enabled = obs_data_get_bool(settings, P_REGION); - if (region.enabled) { - region.left = float_t(obs_data_get_double(settings, P_REGION_LEFT) / 100.0); - region.top = float_t(obs_data_get_double(settings, P_REGION_TOP) / 100.0); - region.right = 1.0 - float_t(obs_data_get_double(settings, P_REGION_RIGHT) / 100.0); - region.bottom = 1.0 - float_t(obs_data_get_double(settings, P_REGION_BOTTOM) / 100.0); - region.feather = float_t(obs_data_get_double(settings, P_REGION_FEATHER) / 100.0); - region.feather_shift = float_t(obs_data_get_double(settings, P_REGION_FEATHER_SHIFT) / 100.0); - region.invert = obs_data_get_bool(settings, P_REGION_INVERT); + mask.enabled = obs_data_get_bool(settings, P_MASK); + if (mask.enabled) { + mask.type = static_cast(obs_data_get_int(settings, P_MASK_TYPE)); + switch (mask.type) { + case mask_type::Region: + mask.region.left = float_t(obs_data_get_double(settings, P_MASK_REGION_LEFT) / 100.0); + mask.region.top = float_t(obs_data_get_double(settings, P_MASK_REGION_TOP) / 100.0); + mask.region.right = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_RIGHT) / 100.0); + mask.region.bottom = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_BOTTOM) / 100.0); + mask.region.feather = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER) / 100.0); + mask.region.feather_shift = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER_SHIFT) / 100.0); + mask.region.invert = obs_data_get_bool(settings, P_MASK_REGION_INVERT); + break; + case mask_type::Image: + mask.image.path = obs_data_get_string(settings, P_MASK_IMAGE); + break; + case mask_type::Source: + mask.source.name = obs_data_get_string(settings, P_MASK_SOURCE); + break; + } + if ((mask.type == mask_type::Image) || (mask.type == mask_type::Source)) { + mask.color = static_cast(obs_data_get_int(settings, P_MASK_COLOR)); + mask.multiplier = float_t(obs_data_get_double(settings, P_MASK_MULTIPLIER)); + } } // advanced @@ -465,16 +572,7 @@ void filter::blur::instance::video_render(gs_effect_t* effect) }; std::string pass = "Draw"; - if (region.enabled) { - if (region.feather > 0) { - pass = "DrawRegionFeather"; - } else { - pass = "DrawRegion"; - } - if (region.invert) { - pass += "Invert"; - } - } + for (auto v : kvs) { const char* name = std::get<0>(v); gs_texrender_t* rt = std::get<1>(v); @@ -520,6 +618,53 @@ void filter::blur::instance::video_render(gs_effect_t* effect) } #pragma endregion blur +#pragma region Mask + if (mask.enabled) { + std::string technique = ""; + switch (mask.type) { + case Region: + if (mask.region.feather > 0.001) { + if (mask.region.invert) { + technique = "RegionFeatherInverted"; + } else { + technique = "RegionFeather"; + } + } else { + if (mask.region.invert) { + technique = "RegionInverted"; + } else { + technique = "Region"; + } + } + break; + case Image: + case Source: + technique = "Image"; + break; + } + + gs_texrender_reset(horizontal_rendertarget); + if (gs_texrender_begin(horizontal_rendertarget, baseW, baseH)) { + std::shared_ptr mask_effect = factory::get()->get_mask_effect(); + apply_mask_parameters(mask_effect, sourceTexture, blurred); + + // Camera + gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1); + gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); + + // Render + while (gs_effect_loop(mask_effect->get_object(), technique.c_str())) { + gs_draw_sprite(blurred, 0, baseW, baseH); + } + + gs_texrender_end(horizontal_rendertarget); + + blurred = gs_texrender_get_texture(horizontal_rendertarget); + } + } + +#pragma endregion + #pragma region YUV->RGB or straight draw // Draw final effect { @@ -586,22 +731,52 @@ filter::blur::factory::~factory() {} void filter::blur::factory::on_list_fill() { obs_enter_graphics(); - std::pair shader_list[] = { - {type::Box, obs_module_file("effects/box-blur.effect")}, - {type::Gaussian, obs_module_file("effects/gaussian-blur.effect")}, - {type::Bilateral, obs_module_file("effects/bilateral-blur.effect")}, - }; - for (auto& kv : shader_list) { + + { + char* file = obs_module_file("effects/box-blur.effect"); try { - std::shared_ptr effect = std::make_shared(kv.second); - effects.insert(std::make_pair(kv.first, effect)); + effects.insert_or_assign(type::Box, std::make_shared(file)); } catch (std::runtime_error ex) { - P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", kv.second.c_str(), ex.what()); - obs_leave_graphics(); - return; + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", file, ex.what()); } + bfree(file); + } + { + char* file = obs_module_file("effects/gaussian-blur.effect"); + try { + effects.insert_or_assign(type::Gaussian, std::make_shared(file)); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", file, ex.what()); + } + bfree(file); + } + { + char* file = obs_module_file("effects/bilateral-blur.effect"); + try { + effects.insert_or_assign(type::Bilateral, std::make_shared(file)); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", file, ex.what()); + } + bfree(file); + } + { + char* file = obs_module_file("effects/color-conversion.effect"); + try { + color_converter_effect = std::make_shared(file); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", file, ex.what()); + } + bfree(file); + } + { + char* file = obs_module_file("effects/mask.effect"); + try { + mask_effect = std::make_shared(file); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", file, ex.what()); + } + bfree(file); } - color_converter_effect = std::make_shared(obs_module_file("effects/color-conversion.effect")), generate_kernel_textures(); obs_leave_graphics(); @@ -612,13 +787,15 @@ void filter::blur::factory::on_list_empty() obs_enter_graphics(); effects.clear(); kernels.clear(); + color_converter_effect.reset(); + mask_effect.reset(); obs_leave_graphics(); } void filter::blur::factory::generate_gaussian_kernels() { // 2D texture, horizontal is value, vertical is kernel size. - size_t size_power_of_two = pow(2, util::math::get_power_of_two_exponent_ceil(max_kernel_size)); + size_t size_power_of_two = size_t(pow(2, util::math::get_power_of_two_exponent_ceil(max_kernel_size))); std::vector texture_Data(size_power_of_two * size_power_of_two); std::vector math_data(size_power_of_two); @@ -687,14 +864,21 @@ void filter::blur::factory::get_defaults(obs_data_t* data) obs_data_set_default_double(data, P_BILATERAL_SHARPNESS, 90.0); // region - obs_data_set_default_bool(data, P_REGION, false); - obs_data_set_default_double(data, P_REGION_LEFT, 0.0f); - obs_data_set_default_double(data, P_REGION_TOP, 0.0f); - obs_data_set_default_double(data, P_REGION_RIGHT, 0.0f); - obs_data_set_default_double(data, P_REGION_BOTTOM, 0.0f); - obs_data_set_default_double(data, P_REGION_FEATHER, 0.0f); - obs_data_set_default_double(data, P_REGION_FEATHER_SHIFT, 0.0f); - obs_data_set_default_bool(data, P_REGION_INVERT, false); + obs_data_set_default_bool(data, P_MASK, false); + obs_data_set_default_int(data, P_MASK_TYPE, mask_type::Region); + obs_data_set_default_double(data, P_MASK_REGION_LEFT, 0.0f); + obs_data_set_default_double(data, P_MASK_REGION_RIGHT, 0.0f); + obs_data_set_default_double(data, P_MASK_REGION_TOP, 0.0f); + obs_data_set_default_double(data, P_MASK_REGION_BOTTOM, 0.0f); + obs_data_set_default_double(data, P_MASK_REGION_FEATHER, 0.0f); + obs_data_set_default_double(data, P_MASK_REGION_FEATHER_SHIFT, 0.0f); + obs_data_set_default_bool(data, P_MASK_REGION_INVERT, false); + char* default_file = obs_module_file("white.png"); + obs_data_set_default_string(data, P_MASK_IMAGE, default_file); + bfree(default_file); + obs_data_set_default_string(data, P_MASK_SOURCE, ""); + obs_data_set_default_int(data, P_MASK_COLOR, 0xFFFFFFFF); + obs_data_set_default_double(data, P_MASK_MULTIPLIER, 1.0); // advanced obs_data_set_default_bool(data, S_ADVANCED, false); @@ -713,6 +897,7 @@ void filter::blur::factory::update(void* inptr, obs_data_t* settings) const char* filter::blur::factory::get_name(void* inptr) { + inptr; return P_TRANSLATE(SOURCE_NAME); } @@ -756,6 +941,11 @@ std::shared_ptr filter::blur::factory::get_color_converter_effect() return color_converter_effect; } +std::shared_ptr filter::blur::factory::get_mask_effect() +{ + return mask_effect; +} + std::shared_ptr filter::blur::factory::get_kernel(filter::blur::type type) { return kernels.at(type); diff --git a/source/filter-blur.h b/source/filter-blur.h index 8a37d714..f06e037b 100644 --- a/source/filter-blur.h +++ b/source/filter-blur.h @@ -21,6 +21,7 @@ #include #include #include +#include "gfx-source-texture.h" #include "gs-effect.h" #include "gs-helper.h" #include "gs-texture.h" @@ -34,6 +35,12 @@ namespace filter { Bilateral, }; + enum mask_type : int64_t { + Region, + Image, + Source, + }; + class instance { obs_source_t* m_source; gs_texrender_t* primary_rendertarget; @@ -51,16 +58,31 @@ namespace filter { double_t bilateral_sharpness; // 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; - } region; + struct { + bool enabled; + mask_type type; + struct { + float_t left; + float_t top; + float_t right; + float_t bottom; + float_t feather; + float_t feather_shift; + bool invert; + } region; + struct { + std::string path; + std::shared_ptr texture; + } image; + struct { + std::string name; + obs_source_t* object; + std::shared_ptr source_texture; + std::shared_ptr texture; + } source; + uint32_t color; + float_t multiplier; + } mask; // advanced bool have_logged_error = false; @@ -69,6 +91,7 @@ namespace filter { bool apply_shared_param(gs_texture_t* input, float texelX, float texelY); bool apply_bilateral_param(); bool apply_gaussian_param(); + bool apply_mask_parameters(std::shared_ptr effect, gs_texture_t * original_texture, gs_texture_t* blurred_texture); static bool modified_properties(void* ptr, obs_properties_t* props, obs_property* prop, obs_data_t* settings); @@ -94,6 +117,7 @@ namespace filter { obs_source_info source_info; std::list sources; std::shared_ptr color_converter_effect; + std::shared_ptr mask_effect; std::map> effects; std::map> kernels; @@ -131,6 +155,8 @@ namespace filter { std::shared_ptr get_color_converter_effect(); + std::shared_ptr get_mask_effect(); + std::shared_ptr get_kernel(filter::blur::type type); public: // Singleton