diff --git a/source/filter-blur.cpp b/source/filter-blur.cpp index ac677634..d925db92 100644 --- a/source/filter-blur.cpp +++ b/source/filter-blur.cpp @@ -18,396 +18,324 @@ */ #include "filter-blur.h" +#include +#include +#include #include "strings.h" #include "util-math.h" -#include -#include -#include extern "C" { -#pragma warning (push) -#pragma warning (disable: 4201) -#include "util/platform.h" +#pragma warning(push) +#pragma warning(disable : 4201) #include "graphics/graphics.h" #include "graphics/matrix4.h" -#pragma warning (pop) +#include "util/platform.h" +#pragma warning(pop) } -#define S_FILTER_BLUR "Filter.Blur" -#define S_TYPE "Filter.Blur.Type" -#define S_TYPE_BOX "Filter.Blur.Type.Box" -#define S_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian" -#define S_TYPE_BILATERAL "Filter.Blur.Type.Bilateral" -#define S_SIZE "Filter.Blur.Size" +// Translation Strings +#define SOURCE_NAME "Filter.Blur" -// Bilateral Blur -#define S_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing" -#define S_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness" - -// Region -#define S_REGION "Filter.Blur.Region" -#define S_REGION_LEFT "Filter.Blur.Region.Left" -#define S_REGION_TOP "Filter.Blur.Region.Top" -#define S_REGION_RIGHT "Filter.Blur.Region.Right" -#define S_REGION_BOTTOM "Filter.Blur.Region.Bottom" -#define S_REGION_FEATHER "Filter.Blur.Region.Feather" -#define S_REGION_FEATHER_SHIFT "Filter.Blur.Region.Feather.Shift" -#define S_REGION_INVERT "Filter.Blur.Region.Invert" - -// Advanced -#define S_FILTER_BLUR_COLORFORMAT "Filter.Blur.ColorFormat" +#define P_TYPE "Filter.Blur.Type" +#define P_TYPE_BOX "Filter.Blur.Type.Box" +#define P_TYPE_GAUSSIAN "Filter.Blur.Type.Gaussian" +#define P_TYPE_BILATERAL "Filter.Blur.Type.Bilateral" +#define P_SIZE "Filter.Blur.Size" +#define P_BILATERAL_SMOOTHING "Filter.Blur.Bilateral.Smoothing" +#define P_BILATERAL_SHARPNESS "Filter.Blur.Bilateral.Sharpness" +#define P_REGION "Filter.Blur.Region" +#define P_REGION_LEFT "Filter.Blur.Region.Left" +#define P_REGION_RIGHT "Filter.Blur.Region.Right" +#define P_REGION_TOP "Filter.Blur.Region.Top" +#define P_REGION_BOTTOM "Filter.Blur.Region.Bottom" +#define P_REGION_FEATHER "Filter.Blur.Region.Feather" +#define P_REGION_FEATHER_SHIFT "Filter.Blur.Region.Feather.Shift" +#define P_REGION_INVERT "Filter.Blur.Region.Invert" +#define P_COLORFORMAT "Filter.Blur.ColorFormat" // Initializer & Finalizer -static filter::Blur* filterBlurInstance; -INITIALIZER(FilterBlurInit) { - initializerFunctions.push_back([] { - filterBlurInstance = new filter::Blur(); - }); - finalizerFunctions.push_back([] { - delete filterBlurInstance; - }); +INITIALIZER(filterBlurFactoryInitializer) +{ + initializerFunctions.push_back([] { filter::blur::factory::initialize(); }); + finalizerFunctions.push_back([] { filter::blur::factory::finalize(); }); } -enum ColorFormat : uint64_t { +enum ColorFormat : uint64_t { // ToDo: Refactor into full class. RGB, YUV, // 701 }; -// Global Data -filter::Blur::Blur() { - memset(&m_sourceInfo, 0, sizeof(obs_source_info)); - m_sourceInfo.id = "obs-stream-effects-filter-blur"; - m_sourceInfo.type = OBS_SOURCE_TYPE_FILTER; - m_sourceInfo.output_flags = OBS_SOURCE_VIDEO; - m_sourceInfo.get_name = get_name; - m_sourceInfo.get_defaults = get_defaults; - m_sourceInfo.get_properties = get_properties; +static uint8_t const max_kernel_size = 25; - m_sourceInfo.create = create; - m_sourceInfo.destroy = destroy; - m_sourceInfo.update = update; - m_sourceInfo.activate = activate; - m_sourceInfo.deactivate = deactivate; - m_sourceInfo.video_tick = video_tick; - m_sourceInfo.video_render = video_render; +bool filter::blur::instance::apply_shared_param(gs_texture_t* input, float texelX, float texelY) +{ + bool result = true; - // Load effects once. - obs_enter_graphics(); - std::pair effects[] = { - { "Box Blur", obs_module_file("effects/box-blur.effect") }, - { "Gaussian Blur", obs_module_file("effects/gaussian-blur.effect") }, - { "Bilateral Blur", obs_module_file("effects/bilateral-blur.effect") }, - { "Color Conversion", obs_module_file("effects/color-conversion.effect") }, - }; - for (auto& kv : effects) { - try { - std::shared_ptr effect = std::make_shared(kv.second); - m_effects.insert(std::make_pair(kv.first, effect)); - } catch (std::runtime_error ex) { - P_LOG_ERROR(" Loading effect '%s' (path: '%s') failed with error(s): %s", - kv.first.c_str(), kv.second.c_str(), ex.what()); - obs_leave_graphics(); - return; + result = result && gs_set_param_texture(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); + + 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); + + 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_int(blur_effect->get_object(), "u_radius", (int)size); + result = result && gs_set_param_int(blur_effect->get_object(), "u_diameter", (int)(1 + (size * 2))); + + if (region.enabled) { + if (blur_effect->has_parameter("regionLeft")) { + blur_effect->get_parameter("regionLeft").set_float(region.left); + } + if (blur_effect->has_parameter("regionTop")) { + blur_effect->get_parameter("regionTop").set_float(region.top); + } + if (blur_effect->has_parameter("regionRight")) { + blur_effect->get_parameter("regionRight").set_float(region.right); + } + if (blur_effect->has_parameter("regionBottom")) { + blur_effect->get_parameter("regionBottom").set_float(region.bottom); + } + if (blur_effect->has_parameter("regionFeather")) { + blur_effect->get_parameter("regionFeather").set_float(region.feather); + } + if (blur_effect->has_parameter("regionFeatherShift")) { + blur_effect->get_parameter("regionFeatherShift").set_float(region.feather_shift); } } - generate_kernel_textures(); + return result; +} + +bool filter::blur::instance::apply_bilateral_param() +{ + gs_eparam_t* param; + + if (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 (blur_effect->has_parameter("bilateralSharpness")) { + blur_effect->get_parameter("bilateralSharpness").set_float((float)(1.0 - bilateral_sharpness)); + } + + return true; +} + +bool filter::blur::instance::apply_gaussian_param() +{ + std::shared_ptr kernel = filter::blur::factory::get()->get_kernel(filter::blur::type::Gaussian); + + if (blur_effect->has_parameter("kernel")) { + blur_effect->get_parameter("kernel").set_texture(kernel); + } else { + return false; + } + + if (blur_effect->has_parameter("kernelTexel")) { + float_t wb = 1.0f / kernel->get_width(); + float_t hb = 1.0f / kernel->get_height(); + blur_effect->get_parameter("kernelTexel").set_float2(wb, hb); + } + + return true; +} + +bool filter::blur::instance::modified_properties(void* ptr, obs_properties_t* props, obs_property* prop, + obs_data_t* settings) +{ + bool showBilateral = (obs_data_get_int(settings, P_TYPE) == type::Bilateral); + + // bilateral blur + obs_property_set_visible(obs_properties_get(props, P_BILATERAL_SMOOTHING), showBilateral); + obs_property_set_visible(obs_properties_get(props, P_BILATERAL_SHARPNESS), showBilateral); + + // region + bool showRegion = obs_data_get_bool(settings, P_REGION); + obs_property_set_visible(obs_properties_get(props, P_REGION_LEFT), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_TOP), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_RIGHT), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_BOTTOM), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_FEATHER), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_FEATHER_SHIFT), showRegion); + obs_property_set_visible(obs_properties_get(props, P_REGION_INVERT), showRegion); + + // advanced + bool showAdvanced = obs_data_get_bool(settings, S_ADVANCED); + obs_property_set_visible(obs_properties_get(props, P_COLORFORMAT), showAdvanced); + + return true; +} + +filter::blur::instance::instance(obs_data_t* settings, obs_source_t* parent) +{ + m_source = parent; + + obs_enter_graphics(); + blur_effect = filter::blur::factory::get()->get_effect(filter::blur::type::Box); + primary_rendertarget = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + secondary_rendertarget = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + horizontal_rendertarget = gs_texrender_create(GS_RGBA, GS_ZS_NONE); + vertical_rendertarget = gs_texrender_create(GS_RGBA, GS_ZS_NONE); obs_leave_graphics(); - obs_register_source(&m_sourceInfo); -} - -filter::Blur::~Blur() { - m_effects.clear(); -} - -void filter::Blur::generate_gaussian_kernels() { - // 2D texture, horizontal is value, vertical is kernel size. - size_t textureSizePOT = GetNearestPowerOfTwoAbove(max_kernel_size); - std::vector textureBuffer(textureSizePOT * textureSizePOT); - std::vector mathBuffer(textureSizePOT); - - for (size_t width = 1; width <= max_kernel_size; width++) { - size_t v = (width - 1) * textureSizePOT; - - // Calculate and normalize - float_t sum = 0; - for (size_t p = 0; p <= width; p++) { - mathBuffer[p] = float_t(Gaussian1D(double_t(p), double_t(width))); - sum += mathBuffer[p] * (p > 0 ? 2 : 1); - } - - // Normalize to Texture Buffer - double_t inverseSum = 1.0 / sum; - for (size_t p = 0; p <= width; p++) { - textureBuffer[v + p] = float_t(mathBuffer[p] * inverseSum); - } + if (!primary_rendertarget) { + P_LOG_ERROR(" Instance '%s' failed to create primary rendertarget.", + obs_source_get_name(m_source)); } - // Create Texture - try { - auto buf = reinterpret_cast(textureBuffer.data()); - auto rbuf = const_cast(&buf); - m_gaussianKernelTexture = std::make_shared( - uint32_t(textureSizePOT), uint32_t(textureSizePOT), GS_R32F, 1, rbuf, - gs::texture::flags::None); - } catch (std::runtime_error ex) { - P_LOG_ERROR(" Failed to create gaussian kernel texture."); + if (!secondary_rendertarget) { + P_LOG_ERROR(" Instance '%s' failed to create secondary rendertarget.", + obs_source_get_name(m_source)); } + + if (!horizontal_rendertarget) { + P_LOG_ERROR(" Instance '%s' failed to create horizontal rendertarget.", + obs_source_get_name(m_source)); + } + + if (!vertical_rendertarget) { + P_LOG_ERROR(" Instance '%s' failed to create vertical rendertarget.", + obs_source_get_name(m_source)); + } + + update(settings); } -void filter::Blur::generate_kernel_textures() { - generate_gaussian_kernels(); - +filter::blur::instance::~instance() +{ + obs_enter_graphics(); + gs_texrender_destroy(primary_rendertarget); + gs_texrender_destroy(secondary_rendertarget); + gs_texrender_destroy(horizontal_rendertarget); + gs_texrender_destroy(vertical_rendertarget); + obs_leave_graphics(); } -const char * filter::Blur::get_name(void *) { - return P_TRANSLATE(S_FILTER_BLUR); -} +obs_properties_t* filter::blur::instance::get_properties() +{ + obs_properties_t* pr = obs_properties_create(); + obs_property_t* p = NULL; -void filter::Blur::get_defaults(obs_data_t *data) { - obs_data_set_default_int(data, S_TYPE, filter::Blur::Type::Box); - obs_data_set_default_int(data, S_SIZE, 5); + p = obs_properties_add_list(pr, P_TYPE, P_TRANSLATE(P_TYPE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_TYPE))); + obs_property_set_modified_callback2(p, modified_properties, this); + obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BOX), filter::blur::type::Box); + obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_GAUSSIAN), filter::blur::type::Gaussian); + obs_property_list_add_int(p, P_TRANSLATE(P_TYPE_BILATERAL), filter::blur::type::Bilateral); - // Bilateral Only - obs_data_set_default_double(data, S_BILATERAL_SMOOTHING, 50.0); - obs_data_set_default_double(data, S_BILATERAL_SHARPNESS, 90.0); - - // Region - obs_data_set_default_bool(data, S_REGION, false); - obs_data_set_default_double(data, S_REGION_LEFT, 0.0f); - obs_data_set_default_double(data, S_REGION_TOP, 0.0f); - obs_data_set_default_double(data, S_REGION_RIGHT, 0.0f); - obs_data_set_default_double(data, S_REGION_BOTTOM, 0.0f); - obs_data_set_default_double(data, S_REGION_FEATHER, 0.0f); - obs_data_set_default_double(data, S_REGION_FEATHER_SHIFT, 0.0f); - obs_data_set_default_bool(data, S_REGION_INVERT, false); - - // Advanced - obs_data_set_default_bool(data, S_ADVANCED, false); - obs_data_set_default_int(data, S_FILTER_BLUR_COLORFORMAT, ColorFormat::RGB); -} - -obs_properties_t * filter::Blur::get_properties(void *) { - obs_properties_t *pr = obs_properties_create(); - obs_property_t* p = NULL; - - p = obs_properties_add_list(pr, S_TYPE, P_TRANSLATE(S_TYPE), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_TYPE))); - obs_property_set_modified_callback(p, modified_properties); - obs_property_list_add_int(p, P_TRANSLATE(S_TYPE_BOX), filter::Blur::Type::Box); - obs_property_list_add_int(p, P_TRANSLATE(S_TYPE_GAUSSIAN), filter::Blur::Type::Gaussian); - obs_property_list_add_int(p, P_TRANSLATE(S_TYPE_BILATERAL), filter::Blur::Type::Bilateral); - - p = obs_properties_add_int_slider(pr, S_SIZE, P_TRANSLATE(S_SIZE), 1, 25, 1); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_SIZE))); + p = obs_properties_add_int_slider(pr, P_SIZE, P_TRANSLATE(P_SIZE), 1, 25, 1); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_SIZE))); //obs_property_set_modified_callback(p, modified_properties); - // Bilateral Only - p = obs_properties_add_float_slider(pr, S_BILATERAL_SMOOTHING, P_TRANSLATE(S_BILATERAL_SMOOTHING), 0.01, 100.0, 0.01); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_BILATERAL_SMOOTHING))); - 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))); + // bilateral Only + p = obs_properties_add_float_slider(pr, P_BILATERAL_SMOOTHING, P_TRANSLATE(P_BILATERAL_SMOOTHING), 0.0, 100.0, + 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_BILATERAL_SMOOTHING))); + p = obs_properties_add_float_slider(pr, P_BILATERAL_SHARPNESS, P_TRANSLATE(P_BILATERAL_SHARPNESS), 0.0, 100.0, + 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_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))); + // region + p = obs_properties_add_bool(pr, P_REGION, P_TRANSLATE(P_REGION)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION))); + obs_property_set_modified_callback2(p, modified_properties, this); + p = obs_properties_add_float_slider(pr, P_REGION_LEFT, P_TRANSLATE(P_REGION_LEFT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_LEFT))); + p = obs_properties_add_float_slider(pr, P_REGION_TOP, P_TRANSLATE(P_REGION_TOP), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_TOP))); + p = obs_properties_add_float_slider(pr, P_REGION_RIGHT, P_TRANSLATE(P_REGION_RIGHT), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_RIGHT))); + p = obs_properties_add_float_slider(pr, P_REGION_BOTTOM, P_TRANSLATE(P_REGION_BOTTOM), 0.0, 100.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_BOTTOM))); + p = obs_properties_add_float_slider(pr, P_REGION_FEATHER, P_TRANSLATE(P_REGION_FEATHER), 0.0, 50.0, 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_FEATHER))); + p = obs_properties_add_float_slider(pr, P_REGION_FEATHER_SHIFT, P_TRANSLATE(P_REGION_FEATHER_SHIFT), -100.0, 100.0, + 0.01); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_FEATHER_SHIFT))); + p = obs_properties_add_bool(pr, P_REGION_INVERT, P_TRANSLATE(P_REGION_INVERT)); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_REGION_INVERT))); - // Advanced + // 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_modified_callback(p, modified_properties); + obs_property_set_modified_callback2(p, modified_properties, this); - p = obs_properties_add_list(pr, S_FILTER_BLUR_COLORFORMAT, P_TRANSLATE(S_FILTER_BLUR_COLORFORMAT), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_long_description(p, P_TRANSLATE(P_DESC(S_FILTER_BLUR_COLORFORMAT))); + p = obs_properties_add_list(pr, P_COLORFORMAT, P_TRANSLATE(P_COLORFORMAT), OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_long_description(p, P_TRANSLATE(P_DESC(P_COLORFORMAT))); obs_property_list_add_int(p, "RGB", ColorFormat::RGB); obs_property_list_add_int(p, "YUV", ColorFormat::YUV); return pr; } -bool filter::Blur::modified_properties(obs_properties_t *pr, obs_property_t *, obs_data_t *d) { - bool showBilateral = false; +void filter::blur::instance::update(obs_data_t* settings) +{ + type = (blur::type)obs_data_get_int(settings, P_TYPE); + blur_effect = factory::get()->get_effect(type); + size = (uint64_t)obs_data_get_int(settings, P_SIZE); - switch (obs_data_get_int(d, S_TYPE)) { - case filter::Blur::Type::Box: - break; - case filter::Blur::Type::Gaussian: - break; - case filter::Blur::Type::Bilateral: - showBilateral = true; - break; + // 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; + + // region + region.enabled = obs_data_get_bool(settings, P_REGION); + if (region.enabled) { + region.left = float_t(obs_data_get_double(settings, P_REGION_LEFT) / 100.0); + region.top = float_t(obs_data_get_double(settings, P_REGION_TOP) / 100.0); + region.right = 1.0 - float_t(obs_data_get_double(settings, P_REGION_RIGHT) / 100.0); + region.bottom = 1.0 - float_t(obs_data_get_double(settings, P_REGION_BOTTOM) / 100.0); + region.feather = float_t(obs_data_get_double(settings, P_REGION_FEATHER) / 100.0); + region.feather_shift = float_t(obs_data_get_double(settings, P_REGION_FEATHER_SHIFT) / 100.0); + region.invert = obs_data_get_bool(settings, P_REGION_INVERT); } - // Bilateral Blur - obs_property_set_visible(obs_properties_get(pr, S_BILATERAL_SMOOTHING), showBilateral); - obs_property_set_visible(obs_properties_get(pr, S_BILATERAL_SHARPNESS), showBilateral); - - // Region - bool showRegion = obs_data_get_bool(d, S_REGION); - obs_property_set_visible(obs_properties_get(pr, S_REGION_LEFT), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_TOP), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_RIGHT), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_BOTTOM), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_FEATHER), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_FEATHER_SHIFT), showRegion); - obs_property_set_visible(obs_properties_get(pr, S_REGION_INVERT), showRegion); - - // Advanced - bool showAdvanced = false; - if (obs_data_get_bool(d, S_ADVANCED)) - showAdvanced = true; - - obs_property_set_visible(obs_properties_get(pr, S_FILTER_BLUR_COLORFORMAT), - showAdvanced); - - return true; -} - -void * filter::Blur::create(obs_data_t *data, obs_source_t *source) { - return new Instance(data, source); -} - -void filter::Blur::destroy(void *ptr) { - delete reinterpret_cast(ptr); -} - -uint32_t filter::Blur::get_width(void *ptr) { - return reinterpret_cast(ptr)->get_width(); -} - -uint32_t filter::Blur::get_height(void *ptr) { - return reinterpret_cast(ptr)->get_height(); -} - -void filter::Blur::update(void *ptr, obs_data_t *data) { - reinterpret_cast(ptr)->update(data); -} - -void filter::Blur::activate(void *ptr) { - reinterpret_cast(ptr)->activate(); -} - -void filter::Blur::deactivate(void *ptr) { - reinterpret_cast(ptr)->deactivate(); -} - -void filter::Blur::video_tick(void *ptr, float time) { - reinterpret_cast(ptr)->video_tick(time); -} - -void filter::Blur::video_render(void *ptr, gs_effect_t *effect) { - reinterpret_cast(ptr)->video_render(effect); -} - -filter::Blur::Instance::Instance(obs_data_t *data, obs_source_t *context) : m_source(context) { - obs_enter_graphics(); - m_effect = filterBlurInstance->m_effects.at("Box Blur"); - m_primaryRT = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - m_secondaryRT = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - m_rtHorizontal = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - m_rtVertical = gs_texrender_create(GS_RGBA, GS_ZS_NONE); - obs_leave_graphics(); - - if (!m_primaryRT) - P_LOG_ERROR(" Instance '%s' failed to create primary rendertarget.", obs_source_get_name(m_source)); - if (!m_secondaryRT) - P_LOG_ERROR(" Instance '%s' failed to create secondary rendertarget.", obs_source_get_name(m_source)); - if (!m_rtHorizontal) - P_LOG_ERROR(" Instance '%s' failed to create horizontal rendertarget.", obs_source_get_name(m_source)); - if (!m_rtVertical) - P_LOG_ERROR(" Instance '%s' failed to create vertical rendertarget.", obs_source_get_name(m_source)); - - update(data); -} - -filter::Blur::Instance::~Instance() { - obs_enter_graphics(); - gs_texrender_destroy(m_primaryRT); - gs_texrender_destroy(m_secondaryRT); - gs_texrender_destroy(m_rtHorizontal); - gs_texrender_destroy(m_rtVertical); - obs_leave_graphics(); -} - -void filter::Blur::Instance::update(obs_data_t *data) { - m_type = (Type)obs_data_get_int(data, S_TYPE); - switch (m_type) { - case filter::Blur::Type::Box: - m_effect = filterBlurInstance->m_effects.at("Box Blur"); - break; - case filter::Blur::Type::Gaussian: - m_effect = filterBlurInstance->m_effects.at("Gaussian Blur"); - break; - case filter::Blur::Type::Bilateral: - m_effect = filterBlurInstance->m_effects.at("Bilateral Blur"); - break; - } - m_size = (uint64_t)obs_data_get_int(data, S_SIZE); - - // Bilateral Blur - m_bilateralSmoothing = obs_data_get_double(data, S_BILATERAL_SMOOTHING) / 100.0; - m_bilateralSharpness = obs_data_get_double(data, S_BILATERAL_SHARPNESS) / 100.0; - - // Region - m_region.enabled = obs_data_get_bool(data, S_REGION); - if (m_region.enabled) { - m_region.left = float_t(obs_data_get_double(data, S_REGION_LEFT) / 100.0); - m_region.top = float_t(obs_data_get_double(data, S_REGION_TOP) / 100.0); - m_region.right = 1.0 - float_t(obs_data_get_double(data, S_REGION_RIGHT) / 100.0); - m_region.bottom = 1.0 - float_t(obs_data_get_double(data, S_REGION_BOTTOM) / 100.0); - m_region.feather = float_t(obs_data_get_double(data, S_REGION_FEATHER) / 100.0); - m_region.feather_shift = float_t(obs_data_get_double(data, S_REGION_FEATHER_SHIFT) / 100.0); - m_region.invert = obs_data_get_bool(data, S_REGION_INVERT); - } - - // Advanced - if (obs_data_get_bool(data, S_ADVANCED)) { - m_colorFormat = obs_data_get_int(data, S_FILTER_BLUR_COLORFORMAT); + // advanced + if (obs_data_get_bool(settings, S_ADVANCED)) { + color_format = obs_data_get_int(settings, P_COLORFORMAT); } else { - m_colorFormat = obs_data_get_default_int(data, S_FILTER_BLUR_COLORFORMAT); + color_format = obs_data_get_default_int(settings, P_COLORFORMAT); } } -uint32_t filter::Blur::Instance::get_width() { - return 0; +uint32_t filter::blur::instance::get_width() +{ + return uint32_t(); } -uint32_t filter::Blur::Instance::get_height() { - return 0; +uint32_t filter::blur::instance::get_height() +{ + return uint32_t(); } -void filter::Blur::Instance::activate() {} +void filter::blur::instance::activate() {} -void filter::Blur::Instance::deactivate() {} +void filter::blur::instance::deactivate() {} -void filter::Blur::Instance::video_tick(float) {} +void filter::blur::instance::video_tick(float) {} + +void filter::blur::instance::video_render(gs_effect_t* effect) +{ + obs_source_t* parent = obs_filter_get_parent(m_source); + obs_source_t* target = obs_filter_get_target(m_source); + uint32_t baseW = obs_source_get_base_width(target); + uint32_t baseH = obs_source_get_base_height(target); + vec4 black = {0}; -void filter::Blur::Instance::video_render(gs_effect_t *effect) { bool failed = false; - vec4 black; vec4_zero(&black); - obs_source_t - *parent = obs_filter_get_parent(m_source), - *target = obs_filter_get_target(m_source); - uint32_t - baseW = obs_source_get_base_width(target), - baseH = obs_source_get_base_height(target); - gs_effect_t* colorConversionEffect = filterBlurInstance->m_effects.count("Color Conversion") ? filterBlurInstance->m_effects.at("Color Conversion")->get_object() : nullptr; + + std::shared_ptr colorConversionEffect = factory::get()->get_color_converter_effect(); // Skip rendering if our target, parent or context is not valid. if (!target || !parent || !m_source) { @@ -415,29 +343,29 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { return; } if ((baseW <= 0) || (baseH <= 0)) { - if (!m_errorLogged) - P_LOG_ERROR(" Instance '%s' has invalid size source '%s'.", - obs_source_get_name(m_source), obs_source_get_name(target)); - m_errorLogged = true; + if (!have_logged_error) + P_LOG_ERROR(" Instance '%s' has invalid size source '%s'.", obs_source_get_name(m_source), + obs_source_get_name(target)); + have_logged_error = true; obs_source_skip_video_filter(m_source); return; } - if (!m_primaryRT || !m_effect) { - if (!m_errorLogged) - P_LOG_ERROR(" Instance '%s' is unable to render.", - obs_source_get_name(m_source), obs_source_get_name(target)); - m_errorLogged = true; + if (!primary_rendertarget || !blur_effect) { + if (!have_logged_error) + P_LOG_ERROR(" Instance '%s' is unable to render.", obs_source_get_name(m_source), + obs_source_get_name(target)); + have_logged_error = true; obs_source_skip_video_filter(m_source); return; } - m_errorLogged = false; + have_logged_error = false; - gs_effect_t* defaultEffect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT); - gs_texture_t *sourceTexture = nullptr; + gs_effect_t* defaultEffect = obs_get_base_effect(obs_base_effect::OBS_EFFECT_DEFAULT); + gs_texture_t* sourceTexture = nullptr; #pragma region Source To Texture - gs_texrender_reset(m_primaryRT); - if (!gs_texrender_begin(m_primaryRT, baseW, baseH)) { + gs_texrender_reset(primary_rendertarget); + if (!gs_texrender_begin(primary_rendertarget, baseW, baseH)) { P_LOG_ERROR(" Failed to set up base texture."); obs_source_skip_video_filter(m_source); return; @@ -454,7 +382,7 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { P_LOG_ERROR(" Unable to render source."); failed = true; } - gs_texrender_end(m_primaryRT); + gs_texrender_end(primary_rendertarget); } if (failed) { @@ -462,7 +390,7 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { return; } - sourceTexture = gs_texrender_get_texture(m_primaryRT); + sourceTexture = gs_texrender_get_texture(primary_rendertarget); if (!sourceTexture) { P_LOG_ERROR(" Failed to get source texture."); obs_source_skip_video_filter(m_source); @@ -471,10 +399,10 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { #pragma endregion Source To Texture // Conversion -#pragma region RGB -> YUV - if ((m_colorFormat == ColorFormat::YUV) && colorConversionEffect) { - gs_texrender_reset(m_secondaryRT); - if (!gs_texrender_begin(m_secondaryRT, baseW, baseH)) { +#pragma region RGB->YUV + if ((color_format == ColorFormat::YUV) && colorConversionEffect) { + gs_texrender_reset(secondary_rendertarget); + if (!gs_texrender_begin(secondary_rendertarget, baseW, baseH)) { P_LOG_ERROR(" Failed to set up base texture."); obs_source_skip_video_filter(m_source); return; @@ -494,17 +422,13 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { gs_enable_stencil_write(false); gs_enable_color(true, true, true, true); - gs_eparam_t* param = gs_effect_get_param_by_name(colorConversionEffect, "image"); - if (!param) { - P_LOG_ERROR(" Failed to set image param."); - failed = true; - } else { - gs_effect_set_texture(param, sourceTexture); + if (colorConversionEffect->has_parameter("image")) { + colorConversionEffect->get_parameter("image").set_texture(sourceTexture); } - while (gs_effect_loop(colorConversionEffect, "RGBToYUV")) { + while (gs_effect_loop(colorConversionEffect->get_object(), "RGBToYUV")) { gs_draw_sprite(sourceTexture, 0, baseW, baseH); } - gs_texrender_end(m_secondaryRT); + gs_texrender_end(secondary_rendertarget); } if (failed) { @@ -512,16 +436,16 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { return; } - sourceTexture = gs_texrender_get_texture(m_secondaryRT); + sourceTexture = gs_texrender_get_texture(secondary_rendertarget); if (!sourceTexture) { P_LOG_ERROR(" Failed to get source texture."); obs_source_skip_video_filter(m_source); return; } } -#pragma endregion RGB -> YUV +#pragma endregion RGB->YUV -#pragma region Blur +#pragma region blur // Set up camera stuff gs_set_cull_mode(GS_NEITHER); gs_reset_blend_state(); @@ -532,37 +456,39 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { gs_enable_stencil_write(false); gs_enable_color(true, true, true, true); - gs_texture_t* blurred = nullptr, *intermediate = sourceTexture; + gs_texture_t* blurred = nullptr; + gs_texture_t* intermediate = sourceTexture; + std::tuple kvs[] = { - 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("Horizontal", horizontal_rendertarget, 1.0f / baseW, 0.0f), + std::make_tuple("Vertical", vertical_rendertarget, 0.0f, 1.0f / baseH), }; + std::string pass = "Draw"; - if (m_region.enabled) { - if (m_region.feather > 0) { + if (region.enabled) { + if (region.feather > 0) { pass = "DrawRegionFeather"; } else { pass = "DrawRegion"; } - if (m_region.invert) { + if (region.invert) { pass += "Invert"; } } for (auto v : kvs) { - const char* name = std::get<0>(v); - gs_texrender_t* rt = std::get<1>(v); - float xpel = std::get<2>(v), - ypel = std::get<3>(v); + const char* name = std::get<0>(v); + gs_texrender_t* rt = std::get<1>(v); + float xpel = std::get<2>(v), ypel = std::get<3>(v); if (!apply_shared_param(intermediate, xpel, ypel)) break; - switch (m_type) { - case Gaussian: - apply_gaussian_param(); - break; - case Bilateral: - apply_bilateral_param(); - break; + switch (type) { + case Gaussian: + apply_gaussian_param(); + break; + case Bilateral: + apply_bilateral_param(); + break; } gs_texrender_reset(rt); @@ -576,15 +502,14 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { gs_clear(GS_CLEAR_COLOR | GS_CLEAR_DEPTH, &black, 0, 0); // Render - while (gs_effect_loop(m_effect->get_object(), pass.c_str())) { + while (gs_effect_loop(blur_effect->get_object(), pass.c_str())) { gs_draw_sprite(intermediate, 0, baseW, baseH); } gs_texrender_end(rt); intermediate = gs_texrender_get_texture(rt); if (!intermediate) { - P_LOG_ERROR(" Failed to get intermediate texture.", - name); + P_LOG_ERROR(" Failed to get intermediate texture.", name); break; } blurred = intermediate; @@ -593,17 +518,17 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { obs_source_skip_video_filter(m_source); return; } -#pragma endregion Blur +#pragma endregion blur -#pragma region YUV -> RGB or straight draw +#pragma region YUV->RGB or straight draw // Draw final effect { gs_effect_t* finalEffect = defaultEffect; - const char* technique = "Draw"; + const char* technique = "Draw"; - if ((m_colorFormat == ColorFormat::YUV) && colorConversionEffect) { - finalEffect = colorConversionEffect; - technique = "YUVToRGB"; + if ((color_format == ColorFormat::YUV) && colorConversionEffect) { + finalEffect = colorConversionEffect->get_object(); + technique = "YUVToRGB"; } // Set up camera stuff @@ -627,7 +552,7 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { gs_draw_sprite(blurred, 0, baseW, baseH); } } -#pragma endregion YUV -> RGB or straight draw +#pragma endregion YUV->RGB or straight draw if (failed) { obs_source_skip_video_filter(m_source); @@ -635,92 +560,220 @@ void filter::Blur::Instance::video_render(gs_effect_t *effect) { } } -bool filter::Blur::Instance::apply_shared_param(gs_texture_t* input, float texelX, float texelY) { - bool result = true; +filter::blur::factory::factory() +{ + memset(&source_info, 0, sizeof(obs_source_info)); + source_info.id = "obs-stream-effects-filter-blur"; + source_info.type = OBS_SOURCE_TYPE_FILTER; + source_info.output_flags = OBS_SOURCE_VIDEO; + source_info.get_name = get_name; + source_info.get_defaults = get_defaults; + source_info.get_properties = get_properties; - result = result && gs_set_param_texture(m_effect->get_object(), "u_image", input); + source_info.create = create; + source_info.destroy = destroy; + source_info.update = update; + source_info.activate = activate; + source_info.deactivate = deactivate; + source_info.video_tick = video_tick; + source_info.video_render = video_render; - vec2 imageSize; - vec2_set(&imageSize, - (float)gs_texture_get_width(input), - (float)gs_texture_get_height(input)); - result = result && gs_set_param_float2(m_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(m_effect->get_object(), "u_imageTexel", &imageTexelDelta); - - vec2 texel; vec2_set(&texel, texelX, texelY); - result = result && gs_set_param_float2(m_effect->get_object(), "u_texelDelta", &texel); - - result = result && gs_set_param_int(m_effect->get_object(), "u_radius", (int)m_size); - result = result && gs_set_param_int(m_effect->get_object(), "u_diameter", (int)(1 + (m_size * 2))); - - if (m_region.enabled) { - if (m_effect->has_parameter("regionLeft")) { - m_effect->get_parameter("regionLeft").set_float(m_region.left); - } - if (m_effect->has_parameter("regionTop")) { - m_effect->get_parameter("regionTop").set_float(m_region.top); - } - if (m_effect->has_parameter("regionRight")) { - m_effect->get_parameter("regionRight").set_float(m_region.right); - } - if (m_effect->has_parameter("regionBottom")) { - m_effect->get_parameter("regionBottom").set_float(m_region.bottom); - } - if (m_effect->has_parameter("regionFeather")) { - m_effect->get_parameter("regionFeather").set_float(m_region.feather); - } - if (m_effect->has_parameter("regionFeatherShift")) { - m_effect->get_parameter("regionFeatherShift").set_float(m_region.feather_shift); - } - } - - return result; + obs_register_source(&source_info); } -bool filter::Blur::Instance::apply_bilateral_param() { - gs_eparam_t *param; +filter::blur::factory::~factory() {} - if (m_type != Type::Bilateral) - return false; - - // Bilateral Blur - param = gs_effect_get_param_by_name(m_effect->get_object(), "bilateralSmoothing"); - if (!param) { - P_LOG_ERROR(" Failed to set bilateralSmoothing param."); - return false; - } else { - gs_effect_set_float(param, - (float)(m_bilateralSmoothing * (1 + m_size * 2))); +void filter::blur::factory::on_list_fill() +{ + obs_enter_graphics(); + std::pair shader_list[] = { + {type::Box, obs_module_file("effects/box-blur.effect")}, + {type::Gaussian, obs_module_file("effects/gaussian-blur.effect")}, + {type::Bilateral, obs_module_file("effects/bilateral-blur.effect")}, + }; + for (auto& kv : shader_list) { + try { + std::shared_ptr effect = std::make_shared(kv.second); + effects.insert(std::make_pair(kv.first, effect)); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Loading effect '%s' failed with error(s): %s", kv.second.c_str(), ex.what()); + obs_leave_graphics(); + return; + } } + color_converter_effect = std::make_shared(obs_module_file("effects/color-conversion.effect")), - param = gs_effect_get_param_by_name(m_effect->get_object(), "bilateralSharpness"); - if (!param) { - P_LOG_ERROR(" Failed to set bilateralSmoothing param."); - return false; - } else { - gs_effect_set_float(param, (float)(1.0 - m_bilateralSharpness)); - } - - return true; + generate_kernel_textures(); + obs_leave_graphics(); } -bool filter::Blur::Instance::apply_gaussian_param() { - if (m_effect->has_parameter("kernel")) { - m_effect->get_parameter("kernel").set_texture(filterBlurInstance->m_gaussianKernelTexture); - } else { - return false; - } - - if (m_effect->has_parameter("kernelTexel")) { - auto tex = filterBlurInstance->m_gaussianKernelTexture->get_object(); - float_t wb = 1.0f / gs_texture_get_width(tex); - float_t hb = 1.0f / gs_texture_get_height(tex); - m_effect->get_parameter("kernelTexel").set_float2(wb, hb); - } - - return true; +void filter::blur::factory::on_list_empty() +{ + obs_enter_graphics(); + effects.clear(); + kernels.clear(); + obs_leave_graphics(); +} + +void filter::blur::factory::generate_gaussian_kernels() +{ + // 2D texture, horizontal is value, vertical is kernel size. + size_t size_power_of_two = pow(2, util::math::get_power_of_two_exponent_ceil(max_kernel_size)); + + std::vector texture_Data(size_power_of_two * size_power_of_two); + std::vector math_data(size_power_of_two); + + for (size_t width = 1; width <= max_kernel_size; width++) { + size_t v = (width - 1) * size_power_of_two; + + // Calculate and normalize + float_t sum = 0; + for (size_t p = 0; p <= width; p++) { + math_data[p] = float_t(Gaussian1D(double_t(p), double_t(width))); + sum += math_data[p] * (p > 0 ? 2 : 1); + } + + // Normalize to Texture Buffer + double_t inverse_sum = 1.0 / sum; + for (size_t p = 0; p <= width; p++) { + texture_Data[v + p] = float_t(math_data[p] * inverse_sum); + } + } + + // Create Texture + try { + auto texture_buffer = reinterpret_cast(texture_Data.data()); + auto unsafe_buffer = const_cast(&texture_buffer); + + kernels.insert_or_assign(filter::blur::type::Gaussian, + std::make_shared(uint32_t(size_power_of_two), uint32_t(size_power_of_two), + GS_R32F, 1, unsafe_buffer, gs::texture::flags::None)); + } catch (std::runtime_error ex) { + P_LOG_ERROR(" Failed to create gaussian kernel texture."); + } +} + +void filter::blur::factory::generate_kernel_textures() +{ + generate_gaussian_kernels(); +} + +void* filter::blur::factory::create(obs_data_t* data, obs_source_t* parent) +{ + if (get()->sources.empty()) { + get()->on_list_fill(); + } + filter::blur::instance* ptr = new filter::blur::instance(data, parent); + get()->sources.push_back(ptr); + return ptr; +} + +void filter::blur::factory::destroy(void* inptr) +{ + filter::blur::instance* ptr = reinterpret_cast(inptr); + get()->sources.remove(ptr); + if (get()->sources.empty()) { + get()->on_list_empty(); + } +} + +void filter::blur::factory::get_defaults(obs_data_t* data) +{ + obs_data_set_default_int(data, P_TYPE, filter::blur::type::Box); + obs_data_set_default_int(data, P_SIZE, 5); + + // bilateral Only + obs_data_set_default_double(data, P_BILATERAL_SMOOTHING, 50.0); + obs_data_set_default_double(data, P_BILATERAL_SHARPNESS, 90.0); + + // region + obs_data_set_default_bool(data, P_REGION, false); + obs_data_set_default_double(data, P_REGION_LEFT, 0.0f); + obs_data_set_default_double(data, P_REGION_TOP, 0.0f); + obs_data_set_default_double(data, P_REGION_RIGHT, 0.0f); + obs_data_set_default_double(data, P_REGION_BOTTOM, 0.0f); + obs_data_set_default_double(data, P_REGION_FEATHER, 0.0f); + obs_data_set_default_double(data, P_REGION_FEATHER_SHIFT, 0.0f); + obs_data_set_default_bool(data, P_REGION_INVERT, false); + + // advanced + obs_data_set_default_bool(data, S_ADVANCED, false); + obs_data_set_default_int(data, P_COLORFORMAT, ColorFormat::RGB); +} + +obs_properties_t* filter::blur::factory::get_properties(void* inptr) +{ + return reinterpret_cast(inptr)->get_properties(); +} + +void filter::blur::factory::update(void* inptr, obs_data_t* settings) +{ + reinterpret_cast(inptr)->update(settings); +} + +const char* filter::blur::factory::get_name(void* inptr) +{ + return P_TRANSLATE(SOURCE_NAME); +} + +uint32_t filter::blur::factory::get_width(void* inptr) +{ + return reinterpret_cast(inptr)->get_width(); +} + +uint32_t filter::blur::factory::get_height(void* inptr) +{ + return reinterpret_cast(inptr)->get_height(); +} + +void filter::blur::factory::activate(void* inptr) +{ + reinterpret_cast(inptr)->activate(); +} + +void filter::blur::factory::deactivate(void* inptr) +{ + reinterpret_cast(inptr)->deactivate(); +} + +void filter::blur::factory::video_tick(void* inptr, float delta) +{ + reinterpret_cast(inptr)->video_tick(delta); +} + +void filter::blur::factory::video_render(void* inptr, gs_effect_t* effect) +{ + reinterpret_cast(inptr)->video_render(effect); +} + +std::shared_ptr filter::blur::factory::get_effect(filter::blur::type type) +{ + return effects.at(type); +} + +std::shared_ptr filter::blur::factory::get_color_converter_effect() +{ + return color_converter_effect; +} + +std::shared_ptr filter::blur::factory::get_kernel(filter::blur::type type) +{ + return kernels.at(type); +} + +static filter::blur::factory* factory_instance = nullptr; + +void filter::blur::factory::initialize() +{ + factory_instance = new filter::blur::factory(); +} + +void filter::blur::factory::finalize() +{ + delete factory_instance; +} + +filter::blur::factory* filter::blur::factory::get() +{ + return factory_instance; } diff --git a/source/filter-blur.h b/source/filter-blur.h index 31b32239..8a37d714 100644 --- a/source/filter-blur.h +++ b/source/filter-blur.h @@ -18,101 +18,127 @@ */ #pragma once -#include "plugin.h" -#include "gs-helper.h" -#include "gs-effect.h" -#include "gs-texture.h" -#include +#include #include +#include +#include "gs-effect.h" +#include "gs-helper.h" +#include "gs-texture.h" +#include "plugin.h" namespace filter { - class Blur { - public: - Blur(); - ~Blur(); - - void generate_gaussian_kernels(); - void generate_kernel_textures(); - - public: - enum Type : int64_t { + namespace blur { + enum type : int64_t { Box, Gaussian, Bilateral, }; - std::shared_ptr m_gaussianKernelTexture; - std::map> m_effects; + class instance { + obs_source_t* m_source; + gs_texrender_t* primary_rendertarget; + gs_texrender_t* secondary_rendertarget; + gs_texrender_t* horizontal_rendertarget; + gs_texrender_t* vertical_rendertarget; - private: - obs_source_info m_sourceInfo; + // blur + std::shared_ptr blur_effect; + filter::blur::type type; + uint64_t size; - static const size_t max_kernel_size = 25; - - public /*static*/: - static const char *get_name(void *); - static void get_defaults(obs_data_t *); - static obs_properties_t *get_properties(void *); - static bool modified_properties(obs_properties_t *, obs_property_t *, obs_data_t *); - static void *create(obs_data_t *, obs_source_t *); - static void destroy(void *); - static uint32_t get_width(void *); - static uint32_t get_height(void *); - static void update(void *, obs_data_t *); - static void activate(void *); - static void deactivate(void *); - static void video_tick(void *, float); - static void video_render(void *, gs_effect_t *); - - private: - class Instance { - public: - Instance(obs_data_t*, obs_source_t*); - ~Instance(); - - void update(obs_data_t*); - uint32_t get_width(); - uint32_t get_height(); - void activate(); - void deactivate(); - void show(); - void hide(); - void video_tick(float); - void video_render(gs_effect_t*); - bool apply_shared_param(gs_texture_t* input, - float texelX, float texelY); - bool apply_bilateral_param(); - bool apply_gaussian_param(); - - private: - obs_source_t *m_source; - gs_texrender_t *m_primaryRT, *m_secondaryRT; - gs_texrender_t *m_rtHorizontal, *m_rtVertical; - std::shared_ptr m_effect; - - // Blur - Type m_type; - uint64_t m_size; - - // Bilateral - double_t m_bilateralSmoothing; - double_t m_bilateralSharpness; + // bilateral + double_t bilateral_smoothing; + double_t bilateral_sharpness; // Regional struct Region { - bool enabled; + 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; + bool invert; + } region; - // Advanced - bool m_errorLogged = false; - uint64_t m_colorFormat; + // advanced + bool have_logged_error = false; + uint64_t color_format; + + bool apply_shared_param(gs_texture_t* input, float texelX, float texelY); + bool apply_bilateral_param(); + bool apply_gaussian_param(); + + static bool modified_properties(void* ptr, obs_properties_t* props, obs_property* prop, + obs_data_t* settings); + + public: + instance(obs_data_t* settings, obs_source_t* parent); + ~instance(); + + obs_properties_t* get_properties(); + void update(obs_data_t*); + + uint32_t get_width(); + uint32_t get_height(); + + void activate(); + void deactivate(); + + void video_tick(float); + void video_render(gs_effect_t*); }; - }; -} + + class factory { + obs_source_info source_info; + std::list sources; + std::shared_ptr color_converter_effect; + + std::map> effects; + std::map> kernels; + + private: + factory(); + ~factory(); + + void on_list_fill(); + void on_list_empty(); + + void generate_gaussian_kernels(); + void generate_kernel_textures(); + + protected: + static void* create(obs_data_t* settings, obs_source_t* parent); + static void destroy(void* source); + + static void get_defaults(obs_data_t* settings); + static obs_properties_t* get_properties(void* source); + static void update(void* source, obs_data_t* settings); + + static const char* get_name(void* source); + static uint32_t get_width(void* source); + static uint32_t get_height(void* source); + + static void activate(void* source); + static void deactivate(void* source); + + static void video_tick(void* source, float delta); + static void video_render(void* source, gs_effect_t* effect); + + public: + std::shared_ptr get_effect(filter::blur::type type); + + std::shared_ptr get_color_converter_effect(); + + std::shared_ptr get_kernel(filter::blur::type type); + + public: // Singleton + static void initialize(); + static void finalize(); + static factory* get(); + }; + + } // namespace blur + +} // namespace filter