From 757e83d8d8a15f4333048f0f7bf97dcbb32e2b5d Mon Sep 17 00:00:00 2001 From: FiniteSingularity <72580859+FiniteSingularity@users.noreply.github.com> Date: Wed, 30 Aug 2023 20:59:08 -0500 Subject: [PATCH] Feature/effect masking (#18) * Adds crop mask, with optional rounded corners. * Adds source masking, with options to mask by alpha, grayscale, luminosity, or manual * Refactor param data into data struct. Change mask functions to use new struct. * Adds defaults callback for filter settings, and sets up some sane defaults. * Switches box blur to using data param pointers. * Switches gaussian blur to using data param pointers. * Switches pixelate blur to using data param pointers. * Adds plugin version info to filter parameters panel. * Adds circle masking. * Adds Image masking. * Updated readme file with masking information --- CMakeLists.txt | 2 +- README.md | 18 + buildspec.json | 160 +-- data/locale/en-US.ini | 40 +- data/shaders/effect_mask_circle.effect | 47 + data/shaders/effect_mask_crop.effect | 87 ++ data/shaders/effect_mask_source.effect | 48 + data/shaders/gaussian_1d_texture.effect | 2 +- data/shaders/gaussian_radial_texture.effect | 2 +- 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/box.c | 183 ++- src/blur/dual_kawase.c | 5 +- src/blur/gaussian.c | 244 ++-- src/blur/pixelate.c | 33 +- src/obs-composite-blur-filter.c | 1099 ++++++++++++++++++- src/obs-composite-blur-filter.h | 168 ++- src/version.h | 4 +- 20 files changed, 1792 insertions(+), 358 deletions(-) create mode 100644 data/shaders/effect_mask_circle.effect create mode 100644 data/shaders/effect_mask_crop.effect create mode 100644 data/shaders/effect_mask_source.effect 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/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. 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/data/locale/en-US.ini b/data/locale/en-US.ini index 2c626d0..8e38968 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" @@ -28,4 +32,38 @@ 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 Mask 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="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" +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/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/data/shaders/effect_mask_crop.effect b/data/shaders/effect_mask_crop.effect new file mode 100644 index 0000000..a28bd08 --- /dev/null +++ b/data/shaders/effect_mask_crop.effect @@ -0,0 +1,87 @@ +uniform float4x4 ViewProj; +uniform texture2d image; +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{ + 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 - 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); + } + 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 +{ + pass + { + vertex_shader = mainTransform(v_in); + pixel_shader = mainImage(v_in); + } +} \ No newline at end of file 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/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/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/box.c b/src/blur/box.c index e366f7d..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,10 +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; - } else if (strcmp(info.name, "dir") == 0) { - filter->param_dir = param; + if (strcmp(info.name, "texel_step") == 0) { + filter->param_texel_step = param; + } else if(strcmp(info.name, "radius") == 0) { + filter->param_radius = param; } } } @@ -402,8 +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, "dir") == 0) { - filter->param_dir = 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; } } } @@ -423,8 +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, "dir") == 0) { - filter->param_dir = 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/blur/dual_kawase.c b/src/blur/dual_kawase.c index e1359fa..26d0eac 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; @@ -207,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; } } } @@ -229,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..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,8 +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, "dir") == 0) { - filter->param_dir = 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; } } } @@ -406,8 +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, "dir") == 0) { - filter->param_dir = 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; } } } @@ -432,8 +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, "dir") == 0) { - filter->param_dir = 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; } } } diff --git a/src/blur/pixelate.c b/src/blur/pixelate.c index 39e22ec..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,8 +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, "dir") == 0) { - filter->param_dir = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -115,8 +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, "dir") == 0) { - filter->param_dir = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -136,8 +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, "dir") == 0) { - filter->param_dir = param; + } else if (strcmp(info.name, "pixel_size") == 0) { + filter->param_pixel_size = param; } } } @@ -157,8 +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, "dir") == 0) { - filter->param_dir = 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 207c498..99ae07a 100644 --- a/src/obs-composite-blur-filter.c +++ b/src/obs-composite-blur-filter.c @@ -17,7 +17,8 @@ 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 +26,36 @@ 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", 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", 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", + 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) { composite_blur_filter_data_t *filter = @@ -43,10 +74,6 @@ 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; @@ -54,10 +81,57 @@ static void *composite_blur_create(obs_data_t *settings, obs_source_t *source) filter->pixelate_type = 1; filter->pixelate_type_last = -1; - da_init(filter->kernel); + // 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_pixel_size = 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->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_image = NULL; + + 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; + + 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); + //composite_blur_defaults(settings); obs_source_update(source, settings); - return filter; } @@ -78,6 +152,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); } @@ -96,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); } @@ -139,6 +220,115 @@ 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"); + 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); + } + + 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; + } + + 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"); + + 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->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 = @@ -190,7 +380,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); @@ -232,12 +421,18 @@ 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 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 +441,387 @@ 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; + case EFFECT_MASK_TYPE_SOURCE: + apply_effect_mask_source(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 + 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); + + // 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; + } + + // 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); + } + + 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); + } + + // 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; + 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 = + 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_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; + 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; + 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_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 * + (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(); + + 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 +946,343 @@ 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_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); + + 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_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_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"), + 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( + effect_mask_crop, "effect_mask_crop_top", + obs_module_text("CompositeBlurFilter.EffectMask.Crop.Top"), 0.0, + 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.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.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.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.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", + obs_module_text( + "CompositeBlurFilter.EffectMask.CropParameters"), + OBS_GROUP_NORMAL, effect_mask_crop); + + obs_properties_add_text(props, "plugin_info", PLUGIN_INFO, + OBS_TEXT_INFO); + 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) +{ + 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); + 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; +} + +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; + case EFFECT_MASK_TYPE_SOURCE: + load_source_mask_effect(filter); + break; + 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; + } +} + static bool setting_blur_algorithm_modified(void *data, obs_properties_t *props, obs_property_t *p, obs_data_t *settings) @@ -594,6 +1504,166 @@ 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->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_crop.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); + 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; + } + } + } +} + +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); + 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; + } + } + } +} + +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) { @@ -617,7 +1687,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); @@ -630,9 +1700,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; - } } } } @@ -689,9 +1756,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 3e8a4d8..f72b9d1 100644 --- a/src/obs-composite-blur-filter.h +++ b/src/obs-composite-blur-filter.h @@ -5,11 +5,17 @@ #include #include #include +#include #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 @@ -45,6 +51,32 @@ #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" + +#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; @@ -58,55 +90,124 @@ 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; 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; - 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; 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 + gs_eparam_t *param_pixel_size; 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; + 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; + 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; - // 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); @@ -115,6 +216,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); @@ -131,7 +233,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 +247,18 @@ 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 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); +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 diff --git a/src/version.h b/src/version.h index 3d15f1b..99d962e 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.7" #define PROJECT_VERSION_MAJOR 0 #define PROJECT_VERSION_MINOR 0 -#define PROJECT_VERSION_PATCH 3 +#define PROJECT_VERSION_PATCH 7