diff --git a/src/accelerator/ogl/image/image_kernel.cpp b/src/accelerator/ogl/image/image_kernel.cpp index 155d574e8d..691fac5a04 100644 --- a/src/accelerator/ogl/image/image_kernel.cpp +++ b/src/accelerator/ogl/image/image_kernel.cpp @@ -265,6 +265,20 @@ struct image_kernel::impl shader_->set("chroma", false); } + if (params.transform.edgeblend.left > epsilon || params.transform.edgeblend.right > epsilon || + params.transform.edgeblend.top > epsilon || params.transform.edgeblend.bottom > epsilon) { + shader_->set("edgeblend", true); + shader_->set("edgeblend_left", params.transform.edgeblend.left); + shader_->set("edgeblend_right", params.transform.edgeblend.right); + shader_->set("edgeblend_top", params.transform.edgeblend.top); + shader_->set("edgeblend_bottom", params.transform.edgeblend.bottom); + shader_->set("edgeblend_g", params.transform.edgeblend.g); + shader_->set("edgeblend_p", params.transform.edgeblend.p); + shader_->set("edgeblend_a", params.transform.edgeblend.a); + } else { + shader_->set("edgeblend", false); + } + // Setup blend_func if (params.transform.is_key) { diff --git a/src/accelerator/ogl/image/shader.frag b/src/accelerator/ogl/image/shader.frag index 93d31d13f2..bbab9f3730 100644 --- a/src/accelerator/ogl/image/shader.frag +++ b/src/accelerator/ogl/image/shader.frag @@ -39,6 +39,15 @@ uniform float chroma_softness; uniform float chroma_spill_suppress; uniform float chroma_spill_suppress_saturation; +uniform bool edgeblend; +uniform float edgeblend_left; +uniform float edgeblend_right; +uniform float edgeblend_top; +uniform float edgeblend_bottom; +uniform float edgeblend_g; +uniform float edgeblend_p; +uniform float edgeblend_a; + /* ** Contrast, saturation, brightness ** Code of this function is from TGM's shader pack @@ -520,6 +529,36 @@ vec4 get_rgba_color() return vec4(0.0, 0.0, 0.0, 0.0); } +float edgeblend_value(float pos, float boundary, float G, float P, float A) +{ + if (pos > boundary) + return 1.0; + float x = pos / boundary; + bool flipped = x >= 0.5; + if (flipped) + x = 1.0 - x; + float a = A; + if (flipped) + a = 1.0 - a; + float v = a * pow(2.0 * x, P); + if (flipped) + v = 1.0 - v; + return pow(v, 1.0 / G); +} + +vec3 Edgeblend(vec3 color, float left, float right, float top, float bottom, float G, float P, float A) +{ + vec2 pos = TexCoord2.st; + vec2 size = vec2(1.0, 1.0); + vec2 edge = vec2(0.0, 0.0); + float blend = 1.0; + blend *= edgeblend_value(pos.x, left, G, P, A); + blend *= edgeblend_value(1.0 - pos.x, right, G, P, A); + blend *= edgeblend_value(pos.y, top, G, P, A); + blend *= edgeblend_value(1.0 - pos.y, bottom, G, P, A); + return color * blend; +} + void main() { vec4 color = get_rgba_color(); @@ -538,5 +577,8 @@ void main() color = 1.0 - color; if (blend_mode >= 0) color = blend(color); + if (edgeblend) + color.rgb = Edgeblend(color.rgb, edgeblend_left, edgeblend_right, edgeblend_top, edgeblend_bottom, edgeblend_g, edgeblend_p, edgeblend_a); + fragColor = color.bgra; } diff --git a/src/core/frame/frame_transform.cpp b/src/core/frame/frame_transform.cpp index d858792fbb..34273fe777 100644 --- a/src/core/frame/frame_transform.cpp +++ b/src/core/frame/frame_transform.cpp @@ -99,6 +99,13 @@ image_transform& image_transform::operator*=(const image_transform& other) chroma.spill_suppress = std::max(other.chroma.spill_suppress, chroma.spill_suppress); chroma.spill_suppress_saturation = std::min(other.chroma.spill_suppress_saturation, chroma.spill_suppress_saturation); + edgeblend.left = std::max(edgeblend.left, other.edgeblend.left); + edgeblend.right = std::max(edgeblend.right, other.edgeblend.right); + edgeblend.top = std::max(edgeblend.top, other.edgeblend.top); + edgeblend.bottom = std::max(edgeblend.bottom, other.edgeblend.bottom); + edgeblend.g = std::max(edgeblend.g, other.edgeblend.g); + edgeblend.p = std::max(edgeblend.p, other.edgeblend.p); + edgeblend.a = std::max(edgeblend.a, other.edgeblend.a); is_key |= other.is_key; invert |= other.invert; is_mix |= other.is_mix; @@ -175,6 +182,13 @@ image_transform image_transform::tween(double time, result.levels.max_output = do_tween(time, source.levels.max_output, dest.levels.max_output, duration, tween); result.levels.min_output = do_tween(time, source.levels.min_output, dest.levels.min_output, duration, tween); result.levels.gamma = do_tween(time, source.levels.gamma, dest.levels.gamma, duration, tween); + result.edgeblend.bottom = do_tween(time, source.edgeblend.bottom, dest.edgeblend.bottom, duration, tween); + result.edgeblend.top = do_tween(time, source.edgeblend.top, dest.edgeblend.top, duration, tween); + result.edgeblend.right = do_tween(time, source.edgeblend.right, dest.edgeblend.right, duration, tween); + result.edgeblend.left = do_tween(time, source.edgeblend.left, dest.edgeblend.left, duration, tween); + result.edgeblend.g = do_tween(time, source.edgeblend.g, dest.edgeblend.g, duration, tween); + result.edgeblend.p = do_tween(time, source.edgeblend.p, dest.edgeblend.p, duration, tween); + result.edgeblend.a = do_tween(time, source.edgeblend.a, dest.edgeblend.a, duration, tween); result.chroma.target_hue = do_tween(time, source.chroma.target_hue, dest.chroma.target_hue, duration, tween); result.chroma.hue_width = do_tween(time, source.chroma.hue_width, dest.chroma.hue_width, duration, tween); result.chroma.min_saturation = @@ -228,7 +242,15 @@ bool operator==(const image_transform& lhs, const image_transform& rhs) eq(lhs.chroma.min_saturation, rhs.chroma.min_saturation) && eq(lhs.chroma.min_brightness, rhs.chroma.min_brightness) && eq(lhs.chroma.softness, rhs.chroma.softness) && eq(lhs.chroma.spill_suppress, rhs.chroma.spill_suppress) && - eq(lhs.chroma.spill_suppress_saturation, rhs.chroma.spill_suppress_saturation) && lhs.crop == rhs.crop && + eq(lhs.chroma.spill_suppress_saturation, rhs.chroma.spill_suppress_saturation) && + eq(lhs.edgeblend.bottom, rhs.edgeblend.bottom) && + eq(lhs.edgeblend.right, rhs.edgeblend.right) && + eq(lhs.edgeblend.left, rhs.edgeblend.left) && + eq(lhs.edgeblend.top, rhs.edgeblend.top) && + eq(lhs.edgeblend.g, rhs.edgeblend.g) && + eq(lhs.edgeblend.p, rhs.edgeblend.p) && + eq(lhs.edgeblend.a, rhs.edgeblend.a) && + lhs.crop == rhs.crop && lhs.perspective == rhs.perspective; } diff --git a/src/core/frame/frame_transform.h b/src/core/frame/frame_transform.h index fcad4ba840..b752437100 100644 --- a/src/core/frame/frame_transform.h +++ b/src/core/frame/frame_transform.h @@ -50,6 +50,17 @@ struct chroma double spill_suppress_saturation = 1.0; }; +struct edgeblend { + double left = 0.0; + double right = 0.0; + double top = 0.0; + double bottom = 0.0; + + double g = 1.8; + double p = 3.0; + double a = 0.5; +}; + struct levels final { double min_input = 0.0; @@ -90,6 +101,7 @@ struct image_transform final corners perspective; core::levels levels; core::chroma chroma; + core::edgeblend edgeblend; bool is_key = false; bool invert = false; diff --git a/src/modules/artnet/consumer/artnet_consumer.cpp b/src/modules/artnet/consumer/artnet_consumer.cpp index c38b1df6dc..735e300635 100644 --- a/src/modules/artnet/consumer/artnet_consumer.cpp +++ b/src/modules/artnet/consumer/artnet_consumer.cpp @@ -95,7 +95,7 @@ struct artnet_consumer : public core::frame_consumer long long elapsed_ms = std::chrono::duration_cast(elapsed_seconds).count(); - long long sleep_time = time - elapsed_ms * 1000; + long long sleep_time = time - elapsed_ms; if (sleep_time > 0) std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); diff --git a/src/protocol/amcp/AMCPCommandsImpl.cpp b/src/protocol/amcp/AMCPCommandsImpl.cpp index 0a8c812eca..beb53aa64d 100644 --- a/src/protocol/amcp/AMCPCommandsImpl.cpp +++ b/src/protocol/amcp/AMCPCommandsImpl.cpp @@ -979,6 +979,45 @@ std::future mixer_chroma_command(command_context& ctx) return make_ready_future(L"202 MIXER OK\r\n"); } +std::future mixer_edgeblend_command(command_context& ctx) +{ + if (ctx.parameters.empty()) { + auto edgeblend2 = get_current_transform(ctx).share(); + + return std::async(std::launch::deferred, [edgeblend2]() -> std::wstring { + auto edgeblend = edgeblend2.get().image_transform.edgeblend; + return L"201 MIXER OK\r\n" + + std::to_wstring(edgeblend.left) + L" " + std::to_wstring(edgeblend.right) + L" " + + std::to_wstring(edgeblend.top) + L" " + std::to_wstring(edgeblend.bottom) + L" " + + std::to_wstring(edgeblend.g) + L" " + std::to_wstring(edgeblend.p) + L" " + + std::to_wstring(edgeblend.a) + L"\r\n"; + }); + } + + transforms_applier transforms(ctx); + core::edgeblend edgeblend; + + edgeblend.left = std::stod(ctx.parameters.at(0)); + edgeblend.right = std::stod(ctx.parameters.at(1)); + edgeblend.top = std::stod(ctx.parameters.at(2)); + edgeblend.bottom = std::stod(ctx.parameters.at(3)); + edgeblend.g = std::stod(ctx.parameters.at(4)); + edgeblend.p = std::stod(ctx.parameters.at(5)); + edgeblend.a = std::stod(ctx.parameters.at(6)); + + transforms.add(stage::transform_tuple_t( + ctx.layer_index(), + [=](frame_transform transform) -> frame_transform { + transform.image_transform.edgeblend = edgeblend; + return transform; + }, + 0, + L"linear")); + transforms.apply(); + + return make_ready_future(L"202 MIXER OK\r\n"); +} + std::future mixer_blend_command(command_context& ctx) { if (ctx.parameters.empty()) @@ -1731,6 +1770,7 @@ void register_commands(std::shared_ptr& repo) repo->register_channel_command(L"Mixer Commands", L"MIXER KEYER", mixer_keyer_command, 0); repo->register_channel_command(L"Mixer Commands", L"MIXER INVERT", mixer_invert_command, 0); repo->register_channel_command(L"Mixer Commands", L"MIXER CHROMA", mixer_chroma_command, 0); + repo->register_channel_command(L"Mixer Commands", L"MIXER EDGEBLEND", mixer_edgeblend_command, 0); repo->register_channel_command(L"Mixer Commands", L"MIXER BLEND", mixer_blend_command, 0); repo->register_channel_command(L"Mixer Commands", L"MIXER OPACITY", mixer_opacity_command, 0); repo->register_channel_command(L"Mixer Commands", L"MIXER BRIGHTNESS", mixer_brightness_command, 0);