filter-blur: Refactoring

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2019-01-28 01:08:13 +01:00
parent 4eb3197720
commit a56cbf89a9
2 changed files with 187 additions and 227 deletions

View file

@ -376,42 +376,73 @@ std::shared_ptr<std::vector<float_t>> filter::blur::blur_factory::get_gaussian_k
return gaussian_kernels.at(size);
}
filter::blur::blur_instance::blur_instance(obs_data_t* settings, obs_source_t* parent)
: m_self(parent), m_source_rendered(false), m_output_rendered(false)
{
m_self = parent;
// Create RenderTargets
try {
this->m_source_rt = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->m_output_rt1 = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->m_output_rt2 = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
} catch (std::exception ex) {
P_LOG_ERROR("<filter-blur:%s> Failed to create rendertargets, error %s.", obs_source_get_name(m_self),
ex.what());
}
// Get initial Blur effect.
m_blur_effect = filter::blur::blur_factory::get()->get_effect(filter::blur::type::Box);
update(settings);
}
filter::blur::blur_instance::~blur_instance()
{
this->m_mask.source.source_texture.reset();
this->m_output_rt1.reset();
this->m_output_rt2.reset();
this->m_source_rt.reset();
this->m_blur_effect.reset();
this->m_output_texture.reset();
}
bool filter::blur::blur_instance::apply_shared_param(gs_texture_t* input, float texelX, float texelY)
{
bool result = true;
result = result && gs_set_param_texture(blur_effect->get_object(), "u_image", input);
result = result && gs_set_param_texture(m_blur_effect->get_object(), "u_image", input);
vec2 imageSize;
vec2_set(&imageSize, (float)gs_texture_get_width(input), (float)gs_texture_get_height(input));
result = result && gs_set_param_float2(blur_effect->get_object(), "u_imageSize", &imageSize);
result = result && gs_set_param_float2(m_blur_effect->get_object(), "u_imageSize", &imageSize);
vec2 imageTexelDelta;
vec2_set(&imageTexelDelta, 1.0f, 1.0f);
vec2_div(&imageTexelDelta, &imageTexelDelta, &imageSize);
result = result && gs_set_param_float2(blur_effect->get_object(), "u_imageTexel", &imageTexelDelta);
result = result && gs_set_param_float2(m_blur_effect->get_object(), "u_imageTexel", &imageTexelDelta);
vec2 texel;
vec2_set(&texel, texelX, texelY);
result = result && gs_set_param_float2(blur_effect->get_object(), "u_texelDelta", &texel);
result = result && gs_set_param_float2(m_blur_effect->get_object(), "u_texelDelta", &texel);
result = result && gs_set_param_int(blur_effect->get_object(), "u_radius", (int)size);
result = result && gs_set_param_int(blur_effect->get_object(), "u_diameter", (int)(1 + (size * 2)));
result = result && gs_set_param_int(m_blur_effect->get_object(), "u_radius", (int)m_blur_size);
result = result && gs_set_param_int(m_blur_effect->get_object(), "u_diameter", (int)(1 + (m_blur_size * 2)));
return result;
}
bool filter::blur::blur_instance::apply_bilateral_param()
{
if (type != type::Bilateral)
if (m_blur_type != type::Bilateral)
return false;
if (blur_effect->has_parameter("bilateralSmoothing")) {
blur_effect->get_parameter("bilateralSmoothing").set_float((float)(bilateral_smoothing * (1 + size * 2)));
if (m_blur_effect->has_parameter("bilateralSmoothing")) {
m_blur_effect->get_parameter("bilateralSmoothing").set_float((float)(m_blur_bilateral_smoothing * (1 + m_blur_size * 2)));
}
if (blur_effect->has_parameter("bilateralSharpness")) {
blur_effect->get_parameter("bilateralSharpness").set_float((float)(1.0 - bilateral_sharpness));
if (m_blur_effect->has_parameter("bilateralSharpness")) {
m_blur_effect->get_parameter("bilateralSharpness").set_float((float)(1.0 - m_blur_bilateral_sharpness));
}
return true;
@ -421,8 +452,8 @@ bool filter::blur::blur_instance::apply_gaussian_param(uint8_t width)
{
auto kernel = filter::blur::blur_factory::get()->get_gaussian_kernel(width);
if (blur_effect->has_parameter("kernel")) {
blur_effect->get_parameter("kernel").set_float_array(&(kernel->front()), kernel->size());
if (m_blur_effect->has_parameter("kernel")) {
m_blur_effect->get_parameter("kernel").set_float_array(&(kernel->front()), kernel->size());
}
return true;
@ -439,32 +470,32 @@ bool filter::blur::blur_instance::apply_mask_parameters(std::shared_ptr<gs::effe
}
// Region
if (mask.type == mask_type::Region) {
if (m_mask.type == mask_type::Region) {
if (effect->has_parameter("mask_region_left")) {
effect->get_parameter("mask_region_left").set_float(mask.region.left);
effect->get_parameter("mask_region_left").set_float(m_mask.region.left);
}
if (effect->has_parameter("mask_region_right")) {
effect->get_parameter("mask_region_right").set_float(mask.region.right);
effect->get_parameter("mask_region_right").set_float(m_mask.region.right);
}
if (effect->has_parameter("mask_region_top")) {
effect->get_parameter("mask_region_top").set_float(mask.region.top);
effect->get_parameter("mask_region_top").set_float(m_mask.region.top);
}
if (effect->has_parameter("mask_region_bottom")) {
effect->get_parameter("mask_region_bottom").set_float(mask.region.bottom);
effect->get_parameter("mask_region_bottom").set_float(m_mask.region.bottom);
}
if (effect->has_parameter("mask_region_feather")) {
effect->get_parameter("mask_region_feather").set_float(mask.region.feather);
effect->get_parameter("mask_region_feather").set_float(m_mask.region.feather);
}
if (effect->has_parameter("mask_region_feather_shift")) {
effect->get_parameter("mask_region_feather_shift").set_float(mask.region.feather_shift);
effect->get_parameter("mask_region_feather_shift").set_float(m_mask.region.feather_shift);
}
}
// Image
if (mask.type == mask_type::Image) {
if (m_mask.type == mask_type::Image) {
if (effect->has_parameter("mask_image")) {
if (mask.image.texture) {
effect->get_parameter("mask_image").set_texture(mask.image.texture);
if (m_mask.image.texture) {
effect->get_parameter("mask_image").set_texture(m_mask.image.texture);
} else {
effect->get_parameter("mask_image").set_texture(nullptr);
}
@ -472,10 +503,10 @@ bool filter::blur::blur_instance::apply_mask_parameters(std::shared_ptr<gs::effe
}
// Source
if (mask.type == mask_type::Source) {
if (m_mask.type == mask_type::Source) {
if (effect->has_parameter("mask_image")) {
if (mask.source.texture) {
effect->get_parameter("mask_image").set_texture(mask.source.texture);
if (m_mask.source.texture) {
effect->get_parameter("mask_image").set_texture(m_mask.source.texture);
} else {
effect->get_parameter("mask_image").set_texture(nullptr);
}
@ -484,10 +515,10 @@ bool filter::blur::blur_instance::apply_mask_parameters(std::shared_ptr<gs::effe
// Shared
if (effect->has_parameter("mask_color")) {
effect->get_parameter("mask_color").set_float4(mask.color.r, mask.color.g, mask.color.b, mask.color.a);
effect->get_parameter("mask_color").set_float4(m_mask.color.r, m_mask.color.g, m_mask.color.b, m_mask.color.a);
}
if (effect->has_parameter("mask_multiplier")) {
effect->get_parameter("mask_multiplier").set_float(mask.multiplier);
effect->get_parameter("mask_multiplier").set_float(m_mask.multiplier);
}
return true;
@ -537,44 +568,6 @@ bool filter::blur::blur_instance::modified_properties(void*, obs_properties_t* p
return true;
}
bool filter::blur::blur_instance::can_log()
{
// Only allow logging errors every 200ms.
auto now = std::chrono::high_resolution_clock::now();
auto delta = (now - this->last_log);
std::swap(this->last_log, now);
return std::chrono::duration_cast<std::chrono::milliseconds>(delta) > std::chrono::milliseconds(200);
}
filter::blur::blur_instance::blur_instance(obs_data_t* settings, obs_source_t* parent)
: m_self(parent), m_source_rendered(false)
{
m_self = parent;
// Create RenderTargets
try {
this->rt_source = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->rt_primary = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
this->rt_secondary = std::make_shared<gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
} catch (std::exception ex) {
P_LOG_ERROR("<filter-blur:%s> Failed to create rendertargets, error %s.", obs_source_get_name(m_self),
ex.what());
}
// Get initial Blur effect.
blur_effect = filter::blur::blur_factory::get()->get_effect(filter::blur::type::Box);
}
filter::blur::blur_instance::~blur_instance()
{
this->mask.source.source_texture.reset();
this->rt_primary.reset();
this->rt_secondary.reset();
this->rt_source.reset();
this->blur_effect.reset();
this->m_source_texture.reset();
}
obs_properties_t* filter::blur::blur_instance::get_properties()
{
obs_properties_t* pr = obs_properties_create();
@ -685,60 +678,60 @@ obs_properties_t* filter::blur::blur_instance::get_properties()
void filter::blur::blur_instance::update(obs_data_t* settings)
{
type = (blur::type)obs_data_get_int(settings, P_TYPE);
blur_effect = blur_factory::get()->get_effect(type);
blur_technique = blur_factory::get()->get_technique(type);
size = (uint64_t)obs_data_get_int(settings, P_SIZE);
m_blur_type = (blur::type)obs_data_get_int(settings, P_TYPE);
m_blur_effect = blur_factory::get()->get_effect(m_blur_type);
m_blur_technique = blur_factory::get()->get_technique(m_blur_type);
m_blur_size = (uint64_t)obs_data_get_int(settings, P_SIZE);
// bilateral blur
bilateral_smoothing = obs_data_get_double(settings, P_BILATERAL_SMOOTHING) / 100.0;
bilateral_sharpness = obs_data_get_double(settings, P_BILATERAL_SHARPNESS) / 100.0;
m_blur_bilateral_smoothing = obs_data_get_double(settings, P_BILATERAL_SMOOTHING) / 100.0;
m_blur_bilateral_sharpness = obs_data_get_double(settings, P_BILATERAL_SHARPNESS) / 100.0;
// region
mask.enabled = obs_data_get_bool(settings, P_MASK);
if (mask.enabled) {
mask.type = static_cast<mask_type>(obs_data_get_int(settings, P_MASK_TYPE));
switch (mask.type) {
m_mask.enabled = obs_data_get_bool(settings, P_MASK);
if (m_mask.enabled) {
m_mask.type = static_cast<mask_type>(obs_data_get_int(settings, P_MASK_TYPE));
switch (m_mask.type) {
case mask_type::Region:
mask.region.left = float_t(obs_data_get_double(settings, P_MASK_REGION_LEFT) / 100.0);
mask.region.top = float_t(obs_data_get_double(settings, P_MASK_REGION_TOP) / 100.0);
mask.region.right = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_RIGHT) / 100.0);
mask.region.bottom = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_BOTTOM) / 100.0);
mask.region.feather = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER) / 100.0);
mask.region.feather_shift = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER_SHIFT) / 100.0);
mask.region.invert = obs_data_get_bool(settings, P_MASK_REGION_INVERT);
m_mask.region.left = float_t(obs_data_get_double(settings, P_MASK_REGION_LEFT) / 100.0);
m_mask.region.top = float_t(obs_data_get_double(settings, P_MASK_REGION_TOP) / 100.0);
m_mask.region.right = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_RIGHT) / 100.0);
m_mask.region.bottom = 1.0f - float_t(obs_data_get_double(settings, P_MASK_REGION_BOTTOM) / 100.0);
m_mask.region.feather = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER) / 100.0);
m_mask.region.feather_shift = float_t(obs_data_get_double(settings, P_MASK_REGION_FEATHER_SHIFT) / 100.0);
m_mask.region.invert = obs_data_get_bool(settings, P_MASK_REGION_INVERT);
break;
case mask_type::Image:
mask.image.path = obs_data_get_string(settings, P_MASK_IMAGE);
m_mask.image.path = obs_data_get_string(settings, P_MASK_IMAGE);
break;
case mask_type::Source:
mask.source.name = obs_data_get_string(settings, P_MASK_SOURCE);
m_mask.source.name = obs_data_get_string(settings, P_MASK_SOURCE);
break;
}
if ((mask.type == mask_type::Image) || (mask.type == mask_type::Source)) {
if ((m_mask.type == mask_type::Image) || (m_mask.type == mask_type::Source)) {
uint32_t color = static_cast<uint32_t>(obs_data_get_int(settings, P_MASK_COLOR));
mask.color.r = ((color >> 0) & 0xFF) / 255.0f;
mask.color.g = ((color >> 8) & 0xFF) / 255.0f;
mask.color.b = ((color >> 16) & 0xFF) / 255.0f;
mask.color.a = static_cast<float_t>(obs_data_get_double(settings, P_MASK_ALPHA));
mask.multiplier = float_t(obs_data_get_double(settings, P_MASK_MULTIPLIER));
m_mask.color.r = ((color >> 0) & 0xFF) / 255.0f;
m_mask.color.g = ((color >> 8) & 0xFF) / 255.0f;
m_mask.color.b = ((color >> 16) & 0xFF) / 255.0f;
m_mask.color.a = static_cast<float_t>(obs_data_get_double(settings, P_MASK_ALPHA));
m_mask.multiplier = float_t(obs_data_get_double(settings, P_MASK_MULTIPLIER));
}
}
// Directional Blur
this->directional = obs_data_get_bool(settings, P_DIRECTIONAL);
this->angle = obs_data_get_double(settings, P_DIRECTIONAL_ANGLE);
this->m_blur_directional = obs_data_get_bool(settings, P_DIRECTIONAL);
this->m_blur_angle = obs_data_get_double(settings, P_DIRECTIONAL_ANGLE);
// Scaling
this->scaling = obs_data_get_bool(settings, P_STEPSCALE);
this->scale.first = obs_data_get_double(settings, P_STEPSCALE_X) / 100.0;
this->scale.second = obs_data_get_double(settings, P_STEPSCALE_Y) / 100.0;
this->m_blur_step_scaling = obs_data_get_bool(settings, P_STEPSCALE);
this->m_blur_step_scale.first = obs_data_get_double(settings, P_STEPSCALE_X) / 100.0;
this->m_blur_step_scale.second = obs_data_get_double(settings, P_STEPSCALE_Y) / 100.0;
// advanced
if (obs_data_get_bool(settings, S_ADVANCED)) {
color_format = obs_data_get_int(settings, P_COLORFORMAT);
m_color_format = obs_data_get_int(settings, P_COLORFORMAT);
} else {
color_format = obs_data_get_default_int(settings, P_COLORFORMAT);
m_color_format = obs_data_get_default_int(settings, P_COLORFORMAT);
}
}
@ -764,30 +757,31 @@ void filter::blur::blur_instance::deactivate() {}
void filter::blur::blur_instance::video_tick(float)
{
// Load Mask
if (mask.type == mask_type::Image) {
if (mask.image.path_old != mask.image.path) {
if (m_mask.type == mask_type::Image) {
if (m_mask.image.path_old != m_mask.image.path) {
try {
mask.image.texture = std::make_shared<gs::texture>(mask.image.path);
mask.image.path_old = mask.image.path;
m_mask.image.texture = std::make_shared<gs::texture>(m_mask.image.path);
m_mask.image.path_old = m_mask.image.path;
} catch (...) {
P_LOG_ERROR("<filter-blur> Instance '%s' failed to load image '%s'.", obs_source_get_name(m_self),
mask.image.path.c_str());
m_mask.image.path.c_str());
}
}
} else if (mask.type == mask_type::Source) {
if (mask.source.name_old != mask.source.name) {
} else if (m_mask.type == mask_type::Source) {
if (m_mask.source.name_old != m_mask.source.name) {
try {
mask.source.source_texture = std::make_shared<gfx::source_texture>(mask.source.name, m_self);
mask.source.is_scene = (obs_scene_from_source(mask.source.source_texture->get_object()) != nullptr);
mask.source.name_old = mask.source.name;
m_mask.source.source_texture = std::make_shared<gfx::source_texture>(m_mask.source.name, m_self);
m_mask.source.is_scene = (obs_scene_from_source(m_mask.source.source_texture->get_object()) != nullptr);
m_mask.source.name_old = m_mask.source.name;
} catch (...) {
P_LOG_ERROR("<filter-blur> Instance '%s' failed to grab source '%s'.", obs_source_get_name(m_self),
mask.source.name.c_str());
m_mask.source.name.c_str());
}
}
}
m_source_rendered = false;
m_output_rendered = false;
}
void filter::blur::blur_instance::video_render(gs_effect_t* effect)
@ -797,43 +791,28 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
uint32_t baseW = obs_source_get_base_width(target);
uint32_t baseH = obs_source_get_base_height(target);
vec4 black;
vec4_set(&black, 0, 0, 0, 0);
bool failed = false;
bool failed = false;
std::shared_ptr<gs::effect> colorConversionEffect = blur_factory::get()->get_color_converter_effect();
gs_effect_t* defaultEffect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT);
vec4_set(&black, 0, 0, 0, 0);
// Verify that we can actually run first.
if (!target || !parent || !this->m_self) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Invalid context, memory corruption may have happened.",
obs_source_get_name(this->m_self));
}
obs_source_skip_video_filter(this->m_self);
return;
}
if ((baseW == 0) || (baseH == 0)) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Invalid base size from '%s': %" PRIu32 "x%" PRIu32 ".",
obs_source_get_name(this->m_self), obs_source_get_name(target), baseW, baseH);
}
obs_source_skip_video_filter(this->m_self);
return;
}
if (!this->rt_primary || !this->rt_secondary || !this->blur_effect) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Missing RenderTarget or Effect.", obs_source_get_name(this->m_self));
}
if (!this->m_output_rt1 || !this->m_output_rt2 || !this->m_blur_effect) {
obs_source_skip_video_filter(this->m_self);
return;
}
gs_effect_t* defaultEffect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT);
if (!m_source_rendered) {
std::shared_ptr<gs::texture> tex_source;
std::shared_ptr<gs::texture> tex_intermediate;
// Source To Texture
{
gs_blend_state_push();
@ -850,7 +829,7 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
try {
auto op = this->rt_source->render(baseW, baseH);
auto op = this->m_source_rt->render(baseW, baseH);
// Orthographic Camera and clear RenderTarget.
gs_ortho(0, (float)baseW, 0, (float)baseH, -1., 1.);
@ -863,27 +842,26 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
throw std::runtime_error("Failed to render source");
}
} catch (std::exception ex) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Rendering parent source to texture failed with error: %s.",
obs_source_get_name(this->m_self), ex.what());
}
gs_blend_state_pop();
obs_source_skip_video_filter(this->m_self);
return;
}
gs_blend_state_pop();
if (!(tex_source = this->rt_source->get_texture())) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur> Failed to get source texture.");
}
if (!(m_source_texture = this->m_source_rt->get_texture())) {
obs_source_skip_video_filter(m_self);
return;
}
}
m_source_rendered = true;
}
if (!m_output_rendered) {
m_output_texture = m_source_texture;
// Color Conversion RGB-YUV
if ((color_format == ColorFormat::YUV) && colorConversionEffect) {
if ((m_color_format != ColorFormat::RGB) && colorConversionEffect) {
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_color(true, true, true, true);
@ -898,37 +876,29 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
try {
auto op = this->rt_primary->render(baseW, baseH);
auto op = this->m_output_rt1->render(baseW, baseH);
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
if (colorConversionEffect->has_parameter("image")) {
colorConversionEffect->get_parameter("image").set_texture(tex_source->get_object());
colorConversionEffect->get_parameter("image").set_texture(m_source_texture->get_object());
}
while (gs_effect_loop(colorConversionEffect->get_object(), "RGBToYUV")) {
gs_draw_sprite(tex_source->get_object(), 0, baseW, baseH);
gs_draw_sprite(m_source_texture->get_object(), 0, baseW, baseH);
}
} catch (std::exception ex) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> RGB-YUV conversion failed with error: %s.",
obs_source_get_name(this->m_self), ex.what());
}
gs_blend_state_pop();
obs_source_skip_video_filter(m_self);
return;
}
gs_blend_state_pop();
if (!(tex_source = this->rt_primary->get_texture())) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Failed to get color conversion texture.",
obs_source_get_name(this->m_self));
}
if (!(m_output_texture = this->m_output_rt1->get_texture())) {
obs_source_skip_video_filter(m_self);
return;
}
// Swap RTs for further rendering.
std::swap(this->rt_primary, this->rt_secondary);
std::swap(this->m_output_rt1, this->m_output_rt2);
}
// Blur
@ -946,11 +916,10 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS);
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
std::pair<float, float> kvs[] = {{1.0f / baseW, 0.0f}, {0.0f, 1.0f / baseH}};
tex_intermediate = tex_source; // We need the original to work.
std::pair<float, float> kvs[] = {{1.0f / baseW, 0.0f}, {0.0f, 1.0f / baseH}};
// Directional Blur
if (this->directional) {
if (this->m_blur_directional) {
// Directional Blur changes how
kvs[0].first = (1.0f / baseW);
@ -958,14 +927,14 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
kvs[1].first = kvs[0].first;
kvs[1].second = kvs[0].second;
double_t rad = this->angle * PI / 180.0;
double_t rad = this->m_blur_angle * PI / 180.0;
double_t c0 = cos(rad);
double_t s0 = sin(rad);
kvs[0].first *= c0;
kvs[0].second *= s0;
if (!this->scaling) {
if (!this->m_blur_step_scaling) {
kvs[1].first *= 0.0;
kvs[1].second *= 0.0;
} else {
@ -975,18 +944,18 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
}
// Apply scaling
if (this->scaling) {
if (!this->directional) {
kvs[0].first *= float_t(this->scale.first);
kvs[0].second *= float_t(this->scale.second);
kvs[1].first *= float_t(this->scale.first);
kvs[1].second *= float_t(this->scale.second);
if (this->m_blur_step_scaling) {
if (!this->m_blur_directional) {
kvs[0].first *= float_t(this->m_blur_step_scale.first);
kvs[0].second *= float_t(this->m_blur_step_scale.second);
kvs[1].first *= float_t(this->m_blur_step_scale.first);
kvs[1].second *= float_t(this->m_blur_step_scale.second);
} else {
// Directional Blur changes how scaling works as it rotates and needs to be relative to the axis of rotation.
kvs[0].first *= float_t(this->scale.first);
kvs[0].second *= float_t(this->scale.first);
kvs[1].first *= float_t(this->scale.second);
kvs[1].second *= float_t(this->scale.second);
kvs[0].first *= float_t(this->m_blur_step_scale.first);
kvs[0].second *= float_t(this->m_blur_step_scale.first);
kvs[1].first *= float_t(this->m_blur_step_scale.second);
kvs[1].second *= float_t(this->m_blur_step_scale.second);
}
}
@ -1000,31 +969,27 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
}
{
auto op = this->rt_primary->render(baseW, baseH);
auto op = this->m_output_rt1->render(baseW, baseH);
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
apply_shared_param(tex_intermediate->get_object(), xpel, ypel);
apply_gaussian_param(uint8_t(this->size));
apply_shared_param(m_output_texture->get_object(), xpel, ypel);
apply_gaussian_param(uint8_t(this->m_blur_size));
apply_bilateral_param();
// Render
while (gs_effect_loop(this->blur_effect->get_object(), this->blur_technique.c_str())) {
gs_draw_sprite(tex_intermediate->get_object(), 0, baseW, baseH);
while (gs_effect_loop(this->m_blur_effect->get_object(), this->m_blur_technique.c_str())) {
gs_draw_sprite(m_output_texture->get_object(), 0, baseW, baseH);
}
}
if (!(tex_intermediate = this->rt_primary->get_texture())) {
if (!(m_output_texture = this->m_output_rt1->get_texture())) {
throw("Failed to get blur texture.");
}
// Swap RTs for further rendering.
std::swap(this->rt_primary, this->rt_secondary);
std::swap(this->m_output_rt1, this->m_output_rt2);
}
} catch (std::exception ex) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Blur failed with error: %s.", obs_source_get_name(this->m_self),
ex.what());
}
gs_blend_state_pop();
obs_source_skip_video_filter(this->m_self);
return;
@ -1033,7 +998,7 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
}
// Mask
if (mask.enabled) {
if (m_mask.enabled) {
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_color(true, true, true, true);
@ -1048,16 +1013,16 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
std::string technique = "";
switch (this->mask.type) {
switch (this->m_mask.type) {
case Region:
if (this->mask.region.feather > 0.001) {
if (this->mask.region.invert) {
if (this->m_mask.region.feather > 0.001) {
if (this->m_mask.region.invert) {
technique = "RegionFeatherInverted";
} else {
technique = "RegionFeather";
}
} else {
if (this->mask.region.invert) {
if (this->m_mask.region.invert) {
technique = "RegionInverted";
} else {
technique = "Region";
@ -1070,9 +1035,9 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
break;
}
if (mask.source.source_texture) {
uint32_t source_width = obs_source_get_width(this->mask.source.source_texture->get_object());
uint32_t source_height = obs_source_get_height(this->mask.source.source_texture->get_object());
if (m_mask.source.source_texture) {
uint32_t source_width = obs_source_get_width(this->m_mask.source.source_texture->get_object());
uint32_t source_height = obs_source_get_height(this->m_mask.source.source_texture->get_object());
if (source_width == 0) {
source_width = baseW;
@ -1080,7 +1045,7 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
if (source_height == 0) {
source_height = baseH;
}
if (this->mask.source.is_scene) {
if (this->m_mask.source.is_scene) {
obs_video_info ovi;
if (obs_get_video_info(&ovi)) {
source_width = ovi.base_width;
@ -1088,45 +1053,38 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
}
}
this->mask.source.texture = this->mask.source.source_texture->render(source_width, source_height);
this->m_mask.source.texture = this->m_mask.source.source_texture->render(source_width, source_height);
}
std::shared_ptr<gs::effect> mask_effect = blur_factory::get()->get_mask_effect();
apply_mask_parameters(mask_effect, tex_source->get_object(), tex_intermediate->get_object());
apply_mask_parameters(mask_effect, m_source_texture->get_object(), m_output_texture->get_object());
try {
auto op = this->rt_primary->render(baseW, baseH);
auto op = this->m_output_rt1->render(baseW, baseH);
gs_ortho(0, (float)baseW, 0, (float)baseH, -1, 1);
// Render
while (gs_effect_loop(mask_effect->get_object(), technique.c_str())) {
gs_draw_sprite(tex_intermediate->get_object(), 0, baseW, baseH);
gs_draw_sprite(m_output_texture->get_object(), 0, baseW, baseH);
}
} catch (std::exception ex) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Masking failed with error: %s.", obs_source_get_name(this->m_self),
ex.what());
}
gs_blend_state_pop();
obs_source_skip_video_filter(this->m_self);
return;
}
gs_blend_state_pop();
if (!(tex_intermediate = this->rt_primary->get_texture())) {
if (this->can_log()) {
P_LOG_ERROR("<filter-blur:%s> Failed to get masked texture.", obs_source_get_name(this->m_self));
}
if (!(m_output_texture = this->m_output_rt1->get_texture())) {
obs_source_skip_video_filter(this->m_self);
return;
}
// Swap RTs for further rendering.
std::swap(this->rt_primary, this->rt_secondary);
std::swap(this->m_output_rt1, this->m_output_rt2);
}
m_source_texture = tex_intermediate;
m_output_rendered = true;
}
// Color Conversion RGB-YUV or Straight Draw
@ -1144,7 +1102,7 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
gs_effect_t* finalEffect = effect ? effect : defaultEffect;
const char* technique = "Draw";
if ((color_format == ColorFormat::YUV) && colorConversionEffect) {
if ((m_color_format == ColorFormat::YUV) && colorConversionEffect) {
finalEffect = colorConversionEffect->get_object();
technique = "YUVToRGB";
}
@ -1154,10 +1112,10 @@ void filter::blur::blur_instance::video_render(gs_effect_t* effect)
P_LOG_ERROR("<filter-blur:%s> Failed to set image param.", obs_source_get_name(this->m_self));
failed = true;
} else {
gs_effect_set_texture(param, m_source_texture->get_object());
gs_effect_set_texture(param, m_output_texture->get_object());
}
while (gs_effect_loop(finalEffect, technique)) {
gs_draw_sprite(m_source_texture->get_object(), 0, baseW, baseH);
gs_draw_sprite(m_output_texture->get_object(), 0, baseW, baseH);
}
}

View file

@ -120,27 +120,31 @@ namespace filter {
class blur_instance {
obs_source_t* m_self;
// Rendering
std::shared_ptr<gs::rendertarget> rt_source;
std::shared_ptr<gs::rendertarget> rt_primary;
std::shared_ptr<gs::rendertarget> rt_secondary;
// Input
std::shared_ptr<gs::rendertarget> m_source_rt;
std::shared_ptr<gs::texture> m_source_texture;
bool m_source_rendered;
// Rendering
std::shared_ptr<gs::rendertarget> m_output_rt1;
std::shared_ptr<gs::rendertarget> m_output_rt2;
std::shared_ptr<gs::texture> m_output_texture;
bool m_output_rendered;
// Blur
std::shared_ptr<gs::effect> blur_effect;
std::string blur_technique;
filter::blur::type type;
uint64_t size;
std::shared_ptr<gs::effect> m_blur_effect;
std::string m_blur_technique;
filter::blur::type m_blur_type;
uint64_t m_blur_size;
/// Bilateral Blur
double_t bilateral_smoothing;
double_t bilateral_sharpness;
double_t m_blur_bilateral_smoothing;
double_t m_blur_bilateral_sharpness;
/// Directional Blur
bool directional;
double_t angle;
bool m_blur_directional;
double_t m_blur_angle;
/// Step Scaling
bool scaling;
std::pair<double_t, double_t> scale;
bool m_blur_step_scaling;
std::pair<double_t, double_t> m_blur_step_scale;
// Masking
struct {
@ -174,11 +178,16 @@ namespace filter {
float_t a;
} color;
float_t multiplier;
} mask;
} m_mask;
// advanced
uint64_t color_format;
uint64_t m_color_format;
public:
blur_instance(obs_data_t* settings, obs_source_t* self);
~blur_instance();
private:
bool apply_shared_param(gs_texture_t* input, float texelX, float texelY);
bool apply_bilateral_param();
bool apply_gaussian_param(uint8_t width);
@ -188,14 +197,7 @@ namespace filter {
static bool modified_properties(void* ptr, obs_properties_t* props, obs_property* prop,
obs_data_t* settings);
// Logging
std::chrono::high_resolution_clock::time_point last_log;
bool can_log();
public:
blur_instance(obs_data_t* settings, obs_source_t* self);
~blur_instance();
obs_properties_t* get_properties();
void update(obs_data_t*);
void load(obs_data_t*);