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