obs-StreamFX/source/gfx/blur/gfx-blur-gaussian.cpp

633 lines
19 KiB
C++
Raw Normal View History

// Modern effects for a modern Streamer
// Copyright (C) 2019 Michael Fabian Dirks
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "gfx-blur-gaussian.hpp"
#include <algorithm>
#include <stdexcept>
#include "obs/gs/gs-helper.hpp"
#include "plugin.hpp"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201)
#endif
#include <obs.h>
#include <obs-module.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// TODO: It may be possible to optimize to run much faster: https://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
#define ST_KERNEL_SIZE 128u
#define ST_OVERSAMPLE_MULTIPLIER 2
#define ST_MAX_BLUR_SIZE ST_KERNEL_SIZE / ST_OVERSAMPLE_MULTIPLIER
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_data::gaussian_data()
{
using namespace streamfx::util;
std::vector<double> kernel_dbl(ST_KERNEL_SIZE);
std::vector<float> kernel(ST_KERNEL_SIZE);
{
auto gctx = streamfx::obs::gs::context();
{
auto file = streamfx::data_file_path("effects/blur/gaussian.effect");
try {
_effect = streamfx::obs::gs::effect::create(file);
} catch (const std::exception& ex) {
DLOG_ERROR("Error loading '%s': %s", file.generic_u8string().c_str(), ex.what());
}
}
}
//#define ST_USE_PASCAL_TRIANGLE
// Pre-calculate Kernel Information for all Kernel sizes
for (size_t size = 1; size <= ST_MAX_BLUR_SIZE; size++) {
#ifdef ST_USE_PASCAL_TRIANGLE
// The Pascal Triangle can be used to generate Gaussian Kernels, which is
// significantly faster than doing the same task with searching. It is also
// much more accurate at the same time, so it is a 2-in-1 solution.
// Generate the required row and sum.
size_t offset = size;
size_t row = size * 2;
auto triangle = math::pascal_triangle<double>(row);
double sum = pow(2, row);
// Convert all integers to floats.
double accum = 0.;
for (size_t idx = offset; idx < std::min<size_t>(triangle.size(), ST_KERNEL_SIZE); idx++) {
double v = static_cast<double>(triangle[idx]) / sum;
kernel_dbl[idx - offset] = v;
// Accumulator needed as we end up with float inaccuracies above a certain threshold.
accum += v * (idx > offset ? 2 : 1);
}
// Rescale all values back into useful ranges.
accum = 1. / accum;
for (size_t idx = offset; idx < ST_KERNEL_SIZE; idx++) {
kernel[idx - offset] = kernel_dbl[idx - offset] * accum;
}
#else
size_t oversample = size * ST_OVERSAMPLE_MULTIPLIER;
// Generate initial weights and calculate a total from them.
double total = 0.;
for (size_t idx = 0; (idx < oversample) && (idx < ST_KERNEL_SIZE); idx++) {
kernel_dbl[idx] = math::gaussian<double>(static_cast<double>(idx), static_cast<double>(size));
total += kernel_dbl[idx] * (idx > 0 ? 2 : 1);
}
// Scale the weights according to the total gathered, and convert to float.
for (size_t idx = 0; (idx < oversample) && (idx < ST_KERNEL_SIZE); idx++) {
kernel_dbl[idx] /= total;
kernel[idx] = static_cast<float>(kernel_dbl[idx]);
}
#endif
// Store Kernel
_kernels.insert_or_assign(size, kernel);
}
}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_data::~gaussian_data()
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
2019-08-04 14:20:26 +00:00
_effect.reset();
}
2021-06-08 02:47:04 +00:00
streamfx::obs::gs::effect streamfx::gfx::blur::gaussian_data::get_effect()
{
2019-08-04 14:20:26 +00:00
return _effect;
}
2021-06-08 02:47:04 +00:00
std::vector<float_t> const& streamfx::gfx::blur::gaussian_data::get_kernel(std::size_t width)
{
width = std::clamp<size_t>(width, 1, ST_MAX_BLUR_SIZE);
return _kernels.at(width);
}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_factory::gaussian_factory() {}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_factory::~gaussian_factory() {}
2021-06-08 02:47:04 +00:00
bool streamfx::gfx::blur::gaussian_factory::is_type_supported(::streamfx::gfx::blur::type v)
{
switch (v) {
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Area:
return true;
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Directional:
return true;
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Rotational:
return true;
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Zoom:
return true;
default:
return false;
}
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::gfx::blur::base>
streamfx::gfx::blur::gaussian_factory::create(::streamfx::gfx::blur::type v)
{
switch (v) {
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Area:
return std::make_shared<::streamfx::gfx::blur::gaussian>();
case ::streamfx::gfx::blur::type::Directional:
return std::static_pointer_cast<::streamfx::gfx::blur::gaussian>(
std::make_shared<::streamfx::gfx::blur::gaussian_directional>());
case ::streamfx::gfx::blur::type::Rotational:
return std::make_shared<::streamfx::gfx::blur::gaussian_rotational>();
case ::streamfx::gfx::blur::type::Zoom:
return std::make_shared<::streamfx::gfx::blur::gaussian_zoom>();
default:
throw std::runtime_error("Invalid type.");
}
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_min_size(::streamfx::gfx::blur::type)
{
return double_t(1.0);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_step_size(::streamfx::gfx::blur::type)
{
return double_t(1.0);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_max_size(::streamfx::gfx::blur::type)
{
2021-06-08 02:47:04 +00:00
return double_t(ST_MAX_BLUR_SIZE);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_min_angle(::streamfx::gfx::blur::type v)
{
switch (v) {
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Directional:
case ::streamfx::gfx::blur::type::Rotational:
return -180.0;
default:
return 0;
}
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_step_angle(::streamfx::gfx::blur::type)
{
return double_t(0.01);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_max_angle(::streamfx::gfx::blur::type v)
{
switch (v) {
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Directional:
case ::streamfx::gfx::blur::type::Rotational:
return 180.0;
default:
return 0;
}
}
2021-06-08 02:47:04 +00:00
bool streamfx::gfx::blur::gaussian_factory::is_step_scale_supported(::streamfx::gfx::blur::type v)
{
switch (v) {
2021-06-08 02:47:04 +00:00
case ::streamfx::gfx::blur::type::Area:
case ::streamfx::gfx::blur::type::Zoom:
case ::streamfx::gfx::blur::type::Directional:
return true;
default:
return false;
}
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_min_step_scale_x(::streamfx::gfx::blur::type)
{
return double_t(0.01);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_step_step_scale_x(::streamfx::gfx::blur::type)
{
return double_t(0.01);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_max_step_scale_x(::streamfx::gfx::blur::type)
{
return double_t(1000.0);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_min_step_scale_y(::streamfx::gfx::blur::type)
{
return double_t(0.01);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_step_step_scale_y(::streamfx::gfx::blur::type)
{
return double_t(0.01);
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_factory::get_max_step_scale_y(::streamfx::gfx::blur::type)
{
return double_t(1000.0);
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::gfx::blur::gaussian_data> streamfx::gfx::blur::gaussian_factory::data()
{
2021-06-08 02:47:04 +00:00
std::unique_lock<std::mutex> ulock(_data_lock);
std::shared_ptr<::streamfx::gfx::blur::gaussian_data> data = _data.lock();
if (!data) {
2021-06-08 02:47:04 +00:00
data = std::make_shared<::streamfx::gfx::blur::gaussian_data>();
2019-08-04 14:20:26 +00:00
_data = data;
}
return data;
}
2021-06-08 02:47:04 +00:00
::streamfx::gfx::blur::gaussian_factory& streamfx::gfx::blur::gaussian_factory::get()
{
2021-06-08 02:47:04 +00:00
static ::streamfx::gfx::blur::gaussian_factory instance;
return instance;
}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian::gaussian()
: _data(::streamfx::gfx::blur::gaussian_factory::get().data()), _size(1.), _step_scale({1., 1.})
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
_rendertarget = std::make_shared<streamfx::obs::gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
_rendertarget2 = std::make_shared<streamfx::obs::gs::rendertarget>(GS_RGBA, GS_ZS_NONE);
}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian::~gaussian() {}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian::set_input(std::shared_ptr<::streamfx::obs::gs::texture> texture)
{
2019-08-04 14:20:26 +00:00
_input_texture = texture;
}
2021-06-08 02:47:04 +00:00
::streamfx::gfx::blur::type streamfx::gfx::blur::gaussian::get_type()
{
2021-06-08 02:47:04 +00:00
return ::streamfx::gfx::blur::type::Area;
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian::get_size()
{
2019-08-04 14:20:26 +00:00
return _size;
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian::set_size(double_t width)
{
if (width < 1.)
width = 1.;
2021-06-08 02:47:04 +00:00
if (width > ST_MAX_BLUR_SIZE)
width = ST_MAX_BLUR_SIZE;
2019-08-04 14:20:26 +00:00
_size = width;
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian::set_step_scale(double_t x, double_t y)
{
2019-08-04 14:20:26 +00:00
_step_scale.first = x;
_step_scale.second = y;
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian::get_step_scale(double_t& x, double_t& y)
{
2019-08-04 14:20:26 +00:00
x = _step_scale.first;
y = _step_scale.second;
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian::get_step_scale_x()
{
2019-08-04 14:20:26 +00:00
return _step_scale.first;
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian::get_step_scale_y()
{
2019-08-04 14:20:26 +00:00
return _step_scale.second;
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::obs::gs::texture> streamfx::gfx::blur::gaussian::render()
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdmp = streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Gaussian Blur");
#endif
2021-06-08 02:38:24 +00:00
streamfx::obs::gs::effect effect = _data->get_effect();
2019-08-04 14:20:26 +00:00
if (!effect || ((_step_scale.first + _step_scale.second) < std::numeric_limits<double_t>::epsilon())) {
return _input_texture;
}
auto kernel = _data->get_kernel(size_t(_size));
2019-08-04 14:20:26 +00:00
float_t width = float_t(_input_texture->get_width());
float_t height = float_t(_input_texture->get_height());
// Setup
gs_set_cull_mode(GS_NEITHER);
gs_enable_color(true, true, true, true);
gs_enable_depth_test(false);
gs_depth_function(GS_ALWAYS);
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_blending(false);
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
gs_enable_stencil_test(false);
gs_enable_stencil_write(false);
gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS);
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
effect.get_parameter("pStepScale").set_float2(float_t(_step_scale.first), float_t(_step_scale.second));
effect.get_parameter("pSize").set_float(float_t(_size * ST_OVERSAMPLE_MULTIPLIER));
effect.get_parameter("pKernel").set_value(kernel.data(), ST_KERNEL_SIZE);
// First Pass
2019-08-04 14:20:26 +00:00
if (_step_scale.first > std::numeric_limits<double_t>::epsilon()) {
effect.get_parameter("pImage").set_texture(_input_texture);
effect.get_parameter("pImageTexel").set_float2(float_t(1.f / width), 0.f);
{
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdm = streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Horizontal");
#endif
auto op = _rendertarget2->render(uint32_t(width), uint32_t(height));
gs_ortho(0, 1., 0, 1., 0, 1.);
while (gs_effect_loop(effect.get_object(), "Draw")) {
streamfx::gs_draw_fullscreen_tri();
}
}
2019-08-04 14:20:26 +00:00
std::swap(_rendertarget, _rendertarget2);
}
// Second Pass
2019-08-04 14:20:26 +00:00
if (_step_scale.second > std::numeric_limits<double_t>::epsilon()) {
effect.get_parameter("pImage").set_texture(_rendertarget->get_texture());
effect.get_parameter("pImageTexel").set_float2(0.f, float_t(1.f / height));
{
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdm = streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Vertical");
#endif
auto op = _rendertarget2->render(uint32_t(width), uint32_t(height));
gs_ortho(0, 1., 0, 1., 0, 1.);
while (gs_effect_loop(effect.get_object(), "Draw")) {
streamfx::gs_draw_fullscreen_tri();
}
}
2019-08-04 14:20:26 +00:00
std::swap(_rendertarget, _rendertarget2);
}
gs_blend_state_pop();
return this->get();
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::obs::gs::texture> streamfx::gfx::blur::gaussian::get()
{
2019-08-04 14:20:26 +00:00
return _rendertarget->get_texture();
}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_directional::gaussian_directional() : m_angle(0.) {}
2021-06-08 02:47:04 +00:00
streamfx::gfx::blur::gaussian_directional::~gaussian_directional() {}
2021-06-08 02:47:04 +00:00
::streamfx::gfx::blur::type streamfx::gfx::blur::gaussian_directional::get_type()
{
2021-06-08 02:47:04 +00:00
return ::streamfx::gfx::blur::type::Directional;
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_directional::get_angle()
{
2019-08-04 14:20:26 +00:00
return D_RAD_TO_DEG(m_angle);
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_directional::set_angle(double_t angle)
{
2019-08-04 14:20:26 +00:00
m_angle = D_DEG_TO_RAD(angle);
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::obs::gs::texture> streamfx::gfx::blur::gaussian_directional::render()
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdmp =
streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Gaussian Directional Blur");
#endif
2021-06-08 02:38:24 +00:00
streamfx::obs::gs::effect effect = _data->get_effect();
2019-08-04 14:20:26 +00:00
if (!effect || ((_step_scale.first + _step_scale.second) < std::numeric_limits<double_t>::epsilon())) {
return _input_texture;
}
auto kernel = _data->get_kernel(size_t(_size));
2019-08-04 14:20:26 +00:00
float_t width = float_t(_input_texture->get_width());
float_t height = float_t(_input_texture->get_height());
// Setup
gs_set_cull_mode(GS_NEITHER);
gs_enable_color(true, true, true, true);
gs_enable_depth_test(false);
gs_depth_function(GS_ALWAYS);
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_blending(false);
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
gs_enable_stencil_test(false);
gs_enable_stencil_write(false);
gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS);
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
effect.get_parameter("pImage").set_texture(_input_texture);
effect.get_parameter("pImageTexel")
.set_float2(float_t(1.f / width * cos(m_angle)), float_t(1.f / height * sin(m_angle)));
effect.get_parameter("pStepScale").set_float2(float_t(_step_scale.first), float_t(_step_scale.second));
effect.get_parameter("pSize").set_float(float_t(_size * ST_OVERSAMPLE_MULTIPLIER));
effect.get_parameter("pKernel").set_value(kernel.data(), ST_KERNEL_SIZE);
{
2019-08-04 14:20:26 +00:00
auto op = _rendertarget->render(uint32_t(width), uint32_t(height));
gs_ortho(0, 1., 0, 1., 0, 1.);
while (gs_effect_loop(effect.get_object(), "Draw")) {
streamfx::gs_draw_fullscreen_tri();
}
}
gs_blend_state_pop();
return this->get();
}
2021-06-08 02:47:04 +00:00
::streamfx::gfx::blur::type streamfx::gfx::blur::gaussian_rotational::get_type()
{
2021-06-08 02:47:04 +00:00
return ::streamfx::gfx::blur::type::Rotational;
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::obs::gs::texture> streamfx::gfx::blur::gaussian_rotational::render()
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdmp =
streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Gaussian Rotational Blur");
#endif
2021-06-08 02:38:24 +00:00
streamfx::obs::gs::effect effect = _data->get_effect();
2019-08-04 14:20:26 +00:00
if (!effect || ((_step_scale.first + _step_scale.second) < std::numeric_limits<double_t>::epsilon())) {
return _input_texture;
}
auto kernel = _data->get_kernel(size_t(_size));
2019-08-04 14:20:26 +00:00
float_t width = float_t(_input_texture->get_width());
float_t height = float_t(_input_texture->get_height());
// Setup
gs_set_cull_mode(GS_NEITHER);
gs_enable_color(true, true, true, true);
gs_enable_depth_test(false);
gs_depth_function(GS_ALWAYS);
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_blending(false);
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
gs_enable_stencil_test(false);
gs_enable_stencil_write(false);
gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS);
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
effect.get_parameter("pImage").set_texture(_input_texture);
effect.get_parameter("pImageTexel").set_float2(float_t(1.f / width), float_t(1.f / height));
effect.get_parameter("pStepScale").set_float2(float_t(_step_scale.first), float_t(_step_scale.second));
effect.get_parameter("pSize").set_float(float_t(_size * ST_OVERSAMPLE_MULTIPLIER));
effect.get_parameter("pAngle").set_float(float_t(m_angle / _size));
effect.get_parameter("pCenter").set_float2(float_t(m_center.first), float_t(m_center.second));
effect.get_parameter("pKernel").set_value(kernel.data(), ST_KERNEL_SIZE);
// First Pass
{
2019-08-04 14:20:26 +00:00
auto op = _rendertarget->render(uint32_t(width), uint32_t(height));
gs_ortho(0, 1., 0, 1., 0, 1.);
while (gs_effect_loop(effect.get_object(), "Rotate")) {
streamfx::gs_draw_fullscreen_tri();
}
}
gs_blend_state_pop();
return this->get();
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_rotational::set_center(double_t x, double_t y)
{
m_center.first = x;
m_center.second = y;
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_rotational::get_center(double_t& x, double_t& y)
{
x = m_center.first;
y = m_center.second;
}
2021-06-08 02:47:04 +00:00
double_t streamfx::gfx::blur::gaussian_rotational::get_angle()
{
2019-08-04 14:20:26 +00:00
return double_t(D_RAD_TO_DEG(m_angle));
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_rotational::set_angle(double_t angle)
{
2019-08-04 14:20:26 +00:00
m_angle = D_DEG_TO_RAD(angle);
}
2021-06-08 02:47:04 +00:00
::streamfx::gfx::blur::type streamfx::gfx::blur::gaussian_zoom::get_type()
{
2021-06-08 02:47:04 +00:00
return ::streamfx::gfx::blur::type::Zoom;
}
2021-06-08 02:47:04 +00:00
std::shared_ptr<::streamfx::obs::gs::texture> streamfx::gfx::blur::gaussian_zoom::render()
{
2021-06-08 02:38:24 +00:00
auto gctx = streamfx::obs::gs::context();
#ifdef ENABLE_PROFILING
2021-06-08 02:38:24 +00:00
auto gdmp = streamfx::obs::gs::debug_marker(streamfx::obs::gs::debug_color_azure_radiance, "Gaussian Zoom Blur");
#endif
2021-06-08 02:38:24 +00:00
streamfx::obs::gs::effect effect = _data->get_effect();
auto kernel = _data->get_kernel(size_t(_size));
2019-08-04 14:20:26 +00:00
if (!effect || ((_step_scale.first + _step_scale.second) < std::numeric_limits<double_t>::epsilon())) {
return _input_texture;
}
2019-08-04 14:20:26 +00:00
float_t width = float_t(_input_texture->get_width());
float_t height = float_t(_input_texture->get_height());
// Setup
gs_set_cull_mode(GS_NEITHER);
gs_enable_color(true, true, true, true);
gs_enable_depth_test(false);
gs_depth_function(GS_ALWAYS);
gs_blend_state_push();
gs_reset_blend_state();
gs_enable_blending(false);
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
gs_enable_stencil_test(false);
gs_enable_stencil_write(false);
gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS);
gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO);
effect.get_parameter("pImage").set_texture(_input_texture);
effect.get_parameter("pImageTexel").set_float2(float_t(1.f / width), float_t(1.f / height));
effect.get_parameter("pStepScale").set_float2(float_t(_step_scale.first), float_t(_step_scale.second));
effect.get_parameter("pSize").set_float(float_t(_size));
effect.get_parameter("pCenter").set_float2(float_t(m_center.first), float_t(m_center.second));
effect.get_parameter("pKernel").set_value(kernel.data(), ST_KERNEL_SIZE);
// First Pass
{
2019-08-04 14:20:26 +00:00
auto op = _rendertarget->render(uint32_t(width), uint32_t(height));
gs_ortho(0, 1., 0, 1., 0, 1.);
while (gs_effect_loop(effect.get_object(), "Zoom")) {
streamfx::gs_draw_fullscreen_tri();
}
}
gs_blend_state_pop();
return this->get();
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_zoom::set_center(double_t x, double_t y)
{
m_center.first = x;
m_center.second = y;
}
2021-06-08 02:47:04 +00:00
void streamfx::gfx::blur::gaussian_zoom::get_center(double_t& x, double_t& y)
{
x = m_center.first;
y = m_center.second;
}