From 8c5dc3c6308713b234173fc660c5e4d8919c76b7 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Tue, 2 Apr 2019 02:46:55 +0200 Subject: [PATCH] gfx/blur/box: Refactor Box Blur Box Blur is the first to be on the new system and has received all the new features and optimizations available. The maximum Blur size has been increased to 128, Rotational and Zoom Blur are supported and a small optimization has been done to the shader. Related: #45, #6 --- data/effects/blur/box.effect | 137 +++++++++ source/gfx/blur/gfx-blur-box.cpp | 506 +++++++++++++++++++++++++++++++ source/gfx/blur/gfx-blur-box.hpp | 159 ++++++++++ 3 files changed, 802 insertions(+) create mode 100644 data/effects/blur/box.effect create mode 100644 source/gfx/blur/gfx-blur-box.cpp create mode 100644 source/gfx/blur/gfx-blur-box.hpp diff --git a/data/effects/blur/box.effect b/data/effects/blur/box.effect new file mode 100644 index 00000000..a7ff6392 --- /dev/null +++ b/data/effects/blur/box.effect @@ -0,0 +1,137 @@ +// Parameters: +/// OBS Default +uniform float4x4 ViewProj; +/// Texture +uniform texture2d pImage; +uniform float2 pImageTexel; +/// Blur +uniform float pSize; +uniform float pSizeInverseMul; +uniform float pAngle; +uniform float2 pCenter; +uniform float2 pStepScale; + +#define MAX_BLUR_SIZE 128 + +// Sampler +sampler_state linearSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; + MinLOD = 0; + MaxLOD = 0; +}; + +// Default Vertex Shader and Data +struct VertDataIn { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertDataOut { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +VertDataOut VSDefault(VertDataIn vtx) { + VertDataOut vert_out; + vert_out.pos = mul(float4(vtx.pos.xyz, 1.0), ViewProj); + vert_out.uv = vtx.uv; + return vert_out; +} + +// Blur 1 Dimensional +float4 PSBlur1D(VertDataOut vtx) : TARGET { + float4 final = pImage.Sample(linearSampler, vtx.uv); + + // Loop unrolling is only possible with a fixed known maximum. + // Some compilers may unroll up to x iterations, but most will not. + for (int n = 1; n <= MAX_BLUR_SIZE; n++) { + float2 nstep = (pImageTexel * pStepScale) * n; + final += pImage.Sample(linearSampler, vtx.uv + nstep); + final += pImage.Sample(linearSampler, vtx.uv - nstep); + + if (n >= pSize) { + break; + } + } + + final *= pSizeInverseMul; + return final; +} + +technique Draw { + pass { + vertex_shader = VSDefault(vtx); + pixel_shader = PSBlur1D(vtx); + } +} + +// Blur Rotation +float2 rotate(float2 pt, float angle) { + float cp = cos(angle); + float sp = sin(angle); + float sn = -sp; + return float2((pt.x * cp) + (pt.y * sn), (pt.x * sp) + (pt.y * cp)); +} + +float2 rotateAround(float2 pt, float2 cpt, float angle) { + return rotate(pt - cpt, angle) + cpt; +} + +float4 PSRotate(VertDataOut vtx) : TARGET { + float4 final = pImage.Sample(linearSampler, vtx.uv); + + float angstep = pAngle * pStepScale.x; + + // Loop unrolling is only possible with a fixed known maximum. + // Some compilers may unroll up to x iterations, but most will not. + for (int n = 1; n <= MAX_BLUR_SIZE; n++) { + final += pImage.Sample(linearSampler, rotateAround(vtx.uv, pCenter, angstep * n)); + final += pImage.Sample(linearSampler, rotateAround(vtx.uv, pCenter, angstep * -n)); + + if (n >= pSize) { + break; + } + } + + final *= pSizeInverseMul; + return final; +} + +technique Rotate { + pass { + vertex_shader = VSDefault(vtx); + pixel_shader = PSRotate(vtx); + } +} + +// Blur Zoom +float4 PSZoom(VertDataOut vtx) : TARGET { + float4 final = pImage.Sample(linearSampler, vtx.uv); + + // step is calculated from the direction relative to the center + float2 dir = normalize(vtx.uv - pCenter) * pStepScale * pImageTexel; + float dist = distance(vtx.uv, pCenter); + + // Loop unrolling is only possible with a fixed known maximum. + // Some compilers may unroll up to x iterations, but most will not. + for (int n = 1; n <= MAX_BLUR_SIZE; n++) { + final += pImage.Sample(linearSampler, vtx.uv + (dir * n) * dist); + final += pImage.Sample(linearSampler, vtx.uv + (dir * -n) * dist); + + if (n >= pSize) { + break; + } + } + + final *= pSizeInverseMul; + return final; +} + +technique Zoom { + pass { + vertex_shader = VSDefault(vtx); + pixel_shader = PSZoom(vtx); + } +} diff --git a/source/gfx/blur/gfx-blur-box.cpp b/source/gfx/blur/gfx-blur-box.cpp new file mode 100644 index 00000000..ce724de4 --- /dev/null +++ b/source/gfx/blur/gfx-blur-box.cpp @@ -0,0 +1,506 @@ +// 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-box.hpp" +#include +#include +#include "plugin.hpp" +#include "util-math.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) +#endif +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#define MAX_BLUR_SIZE 128 // Also change this in box.effect if modified. + +gfx::blur::box_data::box_data() +{ + try { + char* file = obs_module_file("effects/blur/box.effect"); + m_effect = std::make_shared<::gs::effect>(file); + bfree(file); + } catch (...) { + P_LOG_ERROR(" Failed to load effect."); + } +} + +gfx::blur::box_data::~box_data() +{ + m_effect.reset(); +} + +std::shared_ptr<::gs::effect> gfx::blur::box_data::get_effect() +{ + return m_effect; +} + +gfx::blur::box_factory::box_factory() {} + +gfx::blur::box_factory::~box_factory() {} + +bool gfx::blur::box_factory::is_type_supported(::gfx::blur::type type) +{ + switch (type) { + case ::gfx::blur::type::Area: + return true; + case ::gfx::blur::type::Directional: + return true; + case ::gfx::blur::type::Rotational: + return true; + case ::gfx::blur::type::Zoom: + return true; + default: + return false; + } +} + +std::shared_ptr<::gfx::blur::ibase> gfx::blur::box_factory::create(::gfx::blur::type type) +{ + switch (type) { + case ::gfx::blur::type::Area: + return std::make_shared<::gfx::blur::box>(); + case ::gfx::blur::type::Directional: + return std::static_pointer_cast<::gfx::blur::box>(std::make_shared<::gfx::blur::box_directional>()); + case ::gfx::blur::type::Rotational: + return std::make_shared<::gfx::blur::box_rotational>(); + case ::gfx::blur::type::Zoom: + return std::make_shared<::gfx::blur::box_zoom>(); + default: + throw std::runtime_error("Invalid type."); + } +} + +double_t gfx::blur::box_factory::get_min_size(::gfx::blur::type) +{ + return double_t(1.0); +} + +double_t gfx::blur::box_factory::get_step_size(::gfx::blur::type) +{ + return double_t(1.0); +} + +double_t gfx::blur::box_factory::get_max_size(::gfx::blur::type) +{ + return double_t(MAX_BLUR_SIZE); +} + +double_t gfx::blur::box_factory::get_min_angle(::gfx::blur::type v) +{ + switch (v) { + case ::gfx::blur::type::Directional: + case ::gfx::blur::type::Rotational: + return -180.0; + default: + return 0; + } +} + +double_t gfx::blur::box_factory::get_step_angle(::gfx::blur::type) +{ + return double_t(0.01); +} + +double_t gfx::blur::box_factory::get_max_angle(::gfx::blur::type v) +{ + switch (v) { + case ::gfx::blur::type::Directional: + case ::gfx::blur::type::Rotational: + return 180.0; + default: + return 0; + } +} + +bool gfx::blur::box_factory::is_step_scale_supported(::gfx::blur::type v) +{ + switch (v) { + case ::gfx::blur::type::Area: + case ::gfx::blur::type::Zoom: + case ::gfx::blur::type::Directional: + return true; + default: + return false; + } +} + +double_t gfx::blur::box_factory::get_min_step_scale_x(::gfx::blur::type) +{ + return double_t(0.01); +} + +double_t gfx::blur::box_factory::get_step_step_scale_x(::gfx::blur::type) +{ + return double_t(0.01); +} + +double_t gfx::blur::box_factory::get_max_step_scale_x(::gfx::blur::type) +{ + return double_t(1000.0); +} + +double_t gfx::blur::box_factory::get_min_step_scale_y(::gfx::blur::type) +{ + return double_t(0.01); +} + +double_t gfx::blur::box_factory::get_step_step_scale_y(::gfx::blur::type) +{ + return double_t(0.01); +} + +double_t gfx::blur::box_factory::get_max_step_scale_y(::gfx::blur::type) +{ + return double_t(1000.0); +} + +std::shared_ptr<::gfx::blur::box_data> gfx::blur::box_factory::data() +{ + std::unique_lock ulock(m_data_lock); + std::shared_ptr<::gfx::blur::box_data> data = m_data.lock(); + if (!data) { + data = std::make_shared<::gfx::blur::box_data>(); + m_data = data; + } + return data; +} + +::gfx::blur::box_factory& gfx::blur::box_factory::get() +{ + static ::gfx::blur::box_factory instance; + return instance; +} + +gfx::blur::box::box() : m_size(1.), m_step_scale({1., 1.}), m_data(::gfx::blur::box_factory::get().data()) +{ + m_rendertarget = std::make_shared<::gs::rendertarget>(GS_RGBA, GS_ZS_NONE); + m_rendertarget2 = std::make_shared<::gs::rendertarget>(GS_RGBA, GS_ZS_NONE); +} + +gfx::blur::box::~box() {} + +void gfx::blur::box::set_input(std::shared_ptr<::gs::texture> texture) +{ + m_input_texture = texture; +} + +::gfx::blur::type gfx::blur::box::get_type() +{ + return ::gfx::blur::type::Area; +} + +double_t gfx::blur::box::get_size() +{ + return m_size; +} + +void gfx::blur::box::set_size(double_t width) +{ + m_size = width; + if (m_size < 1.0) { + m_size = 1.0; + } + if (m_size > MAX_BLUR_SIZE) { + m_size = MAX_BLUR_SIZE; + } +} + +void gfx::blur::box::set_step_scale(double_t x, double_t y) +{ + m_step_scale = {x, y}; +} + +void gfx::blur::box::get_step_scale(double_t& x, double_t& y) +{ + x = m_step_scale.first; + y = m_step_scale.second; +} + +double_t gfx::blur::box::get_step_scale_x() +{ + return m_step_scale.first; +} + +double_t gfx::blur::box::get_step_scale_y() +{ + return m_step_scale.second; +} + +std::shared_ptr<::gs::texture> gfx::blur::box::render() +{ + float_t width, height; + width = float_t(m_input_texture->get_width()); + height = float_t(m_input_texture->get_height()); + + obs_enter_graphics(); + 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); + + // Two Pass Blur + std::shared_ptr<::gs::effect> effect = m_data->get_effect(); + if (effect) { + // Pass 1 + effect->get_parameter("pImage").set_texture(m_input_texture); + effect->get_parameter("pImageTexel").set_float2(float_t(1. / width), 0.); + effect->get_parameter("pStepScale").set_float2(float_t(m_step_scale.first), float_t(m_step_scale.second)); + effect->get_parameter("pSize").set_float(float_t(m_size)); + effect->get_parameter("pSizeInverseMul").set_float(float_t(1.0 / (float_t(m_size) * 2.0 + 1.0))); + + { + auto op = m_rendertarget2->render(uint32_t(width), uint32_t(height)); + gs_ortho(0, 1., 0, 1., 0, 1.); + while (gs_effect_loop(effect->get_object(), "Draw")) { + gs_draw_sprite(0, 0, 1, 1); + } + } + + // Pass 2 + effect->get_parameter("pImage").set_texture(m_rendertarget2->get_texture()); + effect->get_parameter("pImageTexel").set_float2(0., float_t(1. / height)); + + { + auto op = m_rendertarget->render(uint32_t(width), uint32_t(height)); + gs_ortho(0, 1., 0, 1., 0, 1.); + while (gs_effect_loop(effect->get_object(), "Draw")) { + gs_draw_sprite(0, 0, 1, 1); + } + } + } + + gs_blend_state_pop(); + obs_leave_graphics(); + + return m_rendertarget->get_texture(); +} + +std::shared_ptr<::gs::texture> gfx::blur::box::get() +{ + return m_rendertarget->get_texture(); +} + +gfx::blur::box_directional::box_directional() : m_angle(0) {} + +::gfx::blur::type gfx::blur::box_directional::get_type() +{ + return ::gfx::blur::type::Directional; +} + +double_t gfx::blur::box_directional::get_angle() +{ + return RAD_TO_DEG(m_angle); +} + +void gfx::blur::box_directional::set_angle(double_t angle) +{ + m_angle = DEG_TO_RAD(angle); +} + +std::shared_ptr<::gs::texture> gfx::blur::box_directional::render() +{ + float_t width, height; + width = float_t(m_input_texture->get_width()); + height = float_t(m_input_texture->get_height()); + + obs_enter_graphics(); + gs_blend_state_push(); + gs_reset_blend_state(); + gs_enable_color(true, true, true, true); + gs_enable_blending(false); + gs_enable_depth_test(false); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_set_cull_mode(GS_NEITHER); + gs_depth_function(GS_ALWAYS); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS); + gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO); + + // One Pass Blur + std::shared_ptr<::gs::effect> effect = m_data->get_effect(); + if (effect) { + effect->get_parameter("pImage").set_texture(m_input_texture); + effect->get_parameter("pImageTexel") + .set_float2(float_t(1. / width * cos(m_angle)), float_t(1. / height * sin(m_angle))); + effect->get_parameter("pStepScale").set_float2(float_t(m_step_scale.first), float_t(m_step_scale.second)); + effect->get_parameter("pSize").set_float(float_t(m_size)); + effect->get_parameter("pSizeInverseMul").set_float(float_t(1.0 / (float_t(m_size) * 2.0 + 1.0))); + + { + auto op = m_rendertarget->render(uint32_t(width), uint32_t(height)); + gs_ortho(0, 1., 0, 1., 0, 1.); + while (gs_effect_loop(effect->get_object(), "Draw")) { + gs_draw_sprite(0, 0, 1, 1); + } + } + } + + gs_blend_state_pop(); + obs_leave_graphics(); + + return m_rendertarget->get_texture(); +} + +::gfx::blur::type gfx::blur::box_rotational::get_type() +{ + return ::gfx::blur::type::Rotational; +} + +void gfx::blur::box_rotational::set_center(double_t x, double_t y) +{ + m_center.first = x; + m_center.second = y; +} + +void gfx::blur::box_rotational::get_center(double_t& x, double_t& y) +{ + x = m_center.first; + y = m_center.second; +} + +double_t gfx::blur::box_rotational::get_angle() +{ + return RAD_TO_DEG(m_angle); +} + +void gfx::blur::box_rotational::set_angle(double_t angle) +{ + m_angle = DEG_TO_RAD(angle); +} + +std::shared_ptr<::gs::texture> gfx::blur::box_rotational::render() +{ + float_t width, height; + width = float_t(m_input_texture->get_width()); + height = float_t(m_input_texture->get_height()); + + obs_enter_graphics(); + gs_blend_state_push(); + gs_reset_blend_state(); + gs_enable_color(true, true, true, true); + gs_enable_blending(false); + gs_enable_depth_test(false); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_set_cull_mode(GS_NEITHER); + gs_depth_function(GS_ALWAYS); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS); + gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO); + + // One Pass Blur + std::shared_ptr<::gs::effect> effect = m_data->get_effect(); + if (effect) { + effect->get_parameter("pImage").set_texture(m_input_texture); + effect->get_parameter("pImageTexel").set_float2(float_t(1. / width), float_t(1. / height)); + effect->get_parameter("pStepScale").set_float2(float_t(m_step_scale.first), float_t(m_step_scale.second)); + effect->get_parameter("pSize").set_float(float_t(m_size)); + effect->get_parameter("pSizeInverseMul").set_float(float_t(1.0 / (float_t(m_size) * 2.0 + 1.0))); + effect->get_parameter("pAngle").set_float(float_t(m_angle / m_size)); + effect->get_parameter("pCenter").set_float2(float_t(m_center.first), float_t(m_center.second)); + + { + auto op = m_rendertarget->render(uint32_t(width), uint32_t(height)); + gs_ortho(0, 1., 0, 1., 0, 1.); + while (gs_effect_loop(effect->get_object(), "Rotate")) { + gs_draw_sprite(0, 0, 1, 1); + } + } + } + + gs_blend_state_pop(); + obs_leave_graphics(); + + return m_rendertarget->get_texture(); +} + +::gfx::blur::type gfx::blur::box_zoom::get_type() +{ + return ::gfx::blur::type::Zoom; +} + +void gfx::blur::box_zoom::set_center(double_t x, double_t y) +{ + m_center.first = x; + m_center.second = y; +} + +void gfx::blur::box_zoom::get_center(double_t& x, double_t& y) +{ + x = m_center.first; + y = m_center.second; +} + +std::shared_ptr<::gs::texture> gfx::blur::box_zoom::render() +{ + float_t width, height; + width = float_t(m_input_texture->get_width()); + height = float_t(m_input_texture->get_height()); + + obs_enter_graphics(); + gs_blend_state_push(); + gs_reset_blend_state(); + gs_enable_color(true, true, true, true); + gs_enable_blending(false); + gs_enable_depth_test(false); + gs_enable_stencil_test(false); + gs_enable_stencil_write(false); + gs_set_cull_mode(GS_NEITHER); + gs_depth_function(GS_ALWAYS); + gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + gs_stencil_function(GS_STENCIL_BOTH, GS_ALWAYS); + gs_stencil_op(GS_STENCIL_BOTH, GS_ZERO, GS_ZERO, GS_ZERO); + + // One Pass Blur + std::shared_ptr<::gs::effect> effect = m_data->get_effect(); + if (effect) { + effect->get_parameter("pImage").set_texture(m_input_texture); + effect->get_parameter("pImageTexel").set_float2(float_t(1. / width), float_t(1. / height)); + effect->get_parameter("pStepScale").set_float2(float_t(m_step_scale.first), float_t(m_step_scale.second)); + effect->get_parameter("pSize").set_float(float_t(m_size)); + effect->get_parameter("pSizeInverseMul").set_float(float_t(1.0 / (float_t(m_size) * 2.0 + 1.0))); + effect->get_parameter("pCenter").set_float2(float_t(m_center.first), float_t(m_center.second)); + + { + auto op = m_rendertarget->render(uint32_t(width), uint32_t(height)); + gs_ortho(0, 1., 0, 1., 0, 1.); + while (gs_effect_loop(effect->get_object(), "Zoom")) { + gs_draw_sprite(0, 0, 1, 1); + } + } + } + + gs_blend_state_pop(); + obs_leave_graphics(); + + return m_rendertarget->get_texture(); +} diff --git a/source/gfx/blur/gfx-blur-box.hpp b/source/gfx/blur/gfx-blur-box.hpp new file mode 100644 index 00000000..e78fafcc --- /dev/null +++ b/source/gfx/blur/gfx-blur-box.hpp @@ -0,0 +1,159 @@ +// 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 + +#pragma once +#include +#include +#include +#include "gfx-blur-base.hpp" +#include "obs/gs/gs-effect.hpp" +#include "obs/gs/gs-rendertarget.hpp" +#include "obs/gs/gs-texture.hpp" + +namespace gfx { + namespace blur { + class box_data { + std::shared_ptr<::gs::effect> m_effect; + + public: + box_data(); + ~box_data(); + + std::shared_ptr<::gs::effect> get_effect(); + }; + + class box_factory : public ::gfx::blur::ifactory { + std::mutex m_data_lock; + std::weak_ptr<::gfx::blur::box_data> m_data; + + public: + box_factory(); + virtual ~box_factory(); + + virtual bool is_type_supported(::gfx::blur::type type) override; + + virtual std::shared_ptr<::gfx::blur::ibase> create(::gfx::blur::type type) override; + + virtual double_t get_min_size(::gfx::blur::type type) override; + + virtual double_t get_step_size(::gfx::blur::type type) override; + + virtual double_t get_max_size(::gfx::blur::type type) override; + + virtual double_t get_min_angle(::gfx::blur::type type) override; + + virtual double_t get_step_angle(::gfx::blur::type type) override; + + virtual double_t get_max_angle(::gfx::blur::type type) override; + + virtual bool is_step_scale_supported(::gfx::blur::type type) override; + + virtual double_t get_min_step_scale_x(::gfx::blur::type type) override; + + virtual double_t get_step_step_scale_x(::gfx::blur::type type) override; + + virtual double_t get_max_step_scale_x(::gfx::blur::type type) override; + + virtual double_t get_min_step_scale_y(::gfx::blur::type type) override; + + virtual double_t get_step_step_scale_y(::gfx::blur::type type) override; + + virtual double_t get_max_step_scale_y(::gfx::blur::type type) override; + + std::shared_ptr<::gfx::blur::box_data> data(); + + public: // Singleton + static ::gfx::blur::box_factory& get(); + }; + + class box : public ::gfx::blur::ibase { + protected: + std::shared_ptr<::gfx::blur::box_data> m_data; + + double_t m_size; + std::pair m_step_scale; + std::shared_ptr<::gs::texture> m_input_texture; + std::shared_ptr<::gs::rendertarget> m_rendertarget; + + private: + std::shared_ptr<::gs::rendertarget> m_rendertarget2; + + public: + box(); + ~box(); + + virtual void set_input(std::shared_ptr<::gs::texture> texture) override; + + virtual ::gfx::blur::type get_type() override; + + virtual double_t get_size() override; + virtual void set_size(double_t width) override; + + virtual void set_step_scale(double_t x, double_t y) override; + virtual void get_step_scale(double_t& x, double_t& y) override; + virtual double_t get_step_scale_x() override; + virtual double_t get_step_scale_y() override; + + virtual std::shared_ptr<::gs::texture> render() override; + virtual std::shared_ptr<::gs::texture> get() override; + }; + + class box_directional : public ::gfx::blur::box, public ::gfx::blur::ibase_angle { + double_t m_angle; + + public: + box_directional(); + + virtual ::gfx::blur::type get_type() override; + + virtual double_t get_angle() override; + virtual void set_angle(double_t angle) override; + + virtual std::shared_ptr<::gs::texture> render() override; + }; + + class box_rotational : public ::gfx::blur::box, + public ::gfx::blur::ibase_angle, + public ::gfx::blur::ibase_center { + std::pair m_center; + double_t m_angle; + + public: + virtual ::gfx::blur::type get_type() override; + + virtual void set_center(double_t x, double_t y) override; + virtual void get_center(double_t& x, double_t& y) override; + + virtual double_t get_angle() override; + virtual void set_angle(double_t angle) override; + + virtual std::shared_ptr<::gs::texture> render() override; + }; + + class box_zoom : public ::gfx::blur::box, public ::gfx::blur::ibase_center { + std::pair m_center; + + public: + virtual ::gfx::blur::type get_type() override; + + virtual void set_center(double_t x, double_t y) override; + virtual void get_center(double_t& x, double_t& y) override; + + virtual std::shared_ptr<::gs::texture> render() override; + }; + } // namespace blur +} // namespace gfx