User input unified and fixed for clone source

Fixed slider in player, show filtered image when disabled (outside mixing circle), correct timing for clone source (different for filters).
This commit is contained in:
Bruno Herbelin
2022-06-20 17:29:12 +02:00
parent 91f551c2d8
commit 452221daa5
13 changed files with 176 additions and 89 deletions

View File

@@ -111,6 +111,8 @@ void CloneSource::render()
void CloneSource::setActive (bool on)
{
bool was_active = active_;
// try to activate (may fail if source is cloned)
Source::setActive(on);
@@ -119,13 +121,9 @@ void CloneSource::setActive (bool on)
if ( mode_ > Source::UNINITIALIZED )
origin_->touch();
// change visibility of active surface (show preview of origin when inactive)
if (activesurface_) {
if (active_)
activesurface_->setTextureIndex(Resource::getTextureTransparent());
else
activesurface_->setTextureIndex(renderbuffer_->texture());
}
// enable / disable filtering
if ( active_ != was_active )
filter_->setEnabled( active_ );
}
}
@@ -190,8 +188,8 @@ void CloneSource::play (bool on)
// play / pause filter to suspend clone
filter_->setEnabled( on );
// restart clean if was paused
if (paused_)
// restart delay if was paused
if (paused_ && filter_->type() == FrameBufferFilter::FILTER_DELAY)
replay();
// toggle state
@@ -201,24 +199,19 @@ void CloneSource::play (bool on)
bool CloneSource::playable () const
{
if (filter_ && filter_->enabled())
return true;
if (origin_)
return origin_->playable();
return false;
return true;
}
void CloneSource::replay()
{
// TODO: add reset to Filter
// reset Filter
filter_->reset();
}
guint64 CloneSource::playtime () const
{
// TODO : get time of ImageFilter? Get Delay ?
if (filter_->type() != FrameBufferFilter::FILTER_PASSTHROUGH)
return guint64( filter_->updateTime() * GST_SECOND ) ;
return origin_->playtime();
}

View File

@@ -26,6 +26,29 @@ DelayFilter::~DelayFilter()
elapsed_.pop();
}
void DelayFilter::reset ()
{
// delete all frame buffers
while (!frames_.empty()) {
if (frames_.front() != nullptr)
delete frames_.front();
frames_.pop();
}
while (!elapsed_.empty())
elapsed_.pop();
now_ = 0.0;
}
double DelayFilter::updateTime ()
{
if (!elapsed_.empty())
return elapsed_.front();
return 0.;
}
void DelayFilter::update (float dt)
{
if (input_) {
@@ -96,7 +119,7 @@ void DelayFilter::draw (FrameBuffer *input)
{
input_ = input;
if ( enabled() ) // TODO TEST DISABLE
if ( enabled() )
{
// make sure the queue is not empty
if ( input_ && !frames_.empty() ) {

View File

@@ -24,6 +24,8 @@ public:
uint texture () const override;
glm::vec3 resolution () const override;
void update (float dt) override;
void reset () override;
double updateTime () override;
void draw (FrameBuffer *input) override;
void accept (Visitor& v) override;

View File

@@ -513,13 +513,6 @@ void DeviceSource::setActive (bool on)
}
// change visibility of active surface (show preview of stream when inactive)
if (activesurface_) {
if (active_)
activesurface_->setTextureIndex(Resource::getTextureTransparent());
else
activesurface_->setTextureIndex(stream_->texture());
}
}
}

View File

@@ -16,7 +16,8 @@ FrameBufferFilter::FrameBufferFilter() : enabled_(true), input_(nullptr)
void FrameBufferFilter::draw (FrameBuffer *input)
{
input_ = input;
if (input && ( enabled_ || input_ == nullptr ) )
input_ = input;
}
void FrameBufferFilter::accept(Visitor& v)

View File

@@ -50,8 +50,14 @@ public:
// get the resolution of the rendered filtered framebuffer
virtual glm::vec3 resolution () const = 0;
// perform update (non rendering)
virtual void update (float dt) {}
// perform update (non rendering), given dt in milisecond
virtual void update (float) {}
// reset update data and time
virtual void reset () {}
// total time of update, in second
virtual double updateTime () { return 0.; }
// draw the input framebuffer and apply the filter
virtual void draw (FrameBuffer *input);
@@ -60,8 +66,8 @@ public:
virtual void accept (Visitor& v);
// when enabled, draw is effective
inline void setEnabled (bool on) { enabled_ = on; }
inline bool enabled () const { return enabled_; }
inline void setEnabled (bool on) { enabled_ = on; }
inline bool enabled () const { return enabled_; }
protected:
FrameBuffer *input_;

View File

@@ -173,13 +173,12 @@ class ImageFilteringShader : public ImageShader
std::string shader_code_;
std::string code_;
public:
// for iTimedelta
GTimer *timer_;
double iTime_;
uint iFrame_;
public:
// list of uniforms to control shader
std::map< std::string, float > uniforms_;
@@ -323,6 +322,19 @@ ImageFilter::~ImageFilter ()
// NB: shaders_ are removed with surface
}
void ImageFilter::reset ()
{
shaders_.first->reset();
if ( program_.isTwoPass() )
shaders_.second->reset();
}
double ImageFilter::updateTime ()
{
return shaders_.first->iTime_;
}
void ImageFilter::update (float dt)
{
shaders_.first->update(dt);
@@ -355,6 +367,8 @@ glm::vec3 ImageFilter::resolution () const
void ImageFilter::draw (FrameBuffer *input)
{
bool forced = false;
// if input changed (typically on first draw)
if (input_ != input) {
// keep reference to input framebuffer
@@ -376,9 +390,11 @@ void ImageFilter::draw (FrameBuffer *input)
if (buffers_.second != nullptr)
delete buffers_.second;
buffers_.second = new FrameBuffer( buffers_.first->resolution(), buffers_.first->flags() );
// forced draw
forced = true;
}
if ( enabled() )
if ( enabled() || forced )
{
// FIRST PASS
// render input surface into frame buffer
@@ -482,6 +498,8 @@ void ResampleFilter::setFactor(int factor)
void ResampleFilter::draw (FrameBuffer *input)
{
bool forced = false;
// Default
if (factor_ == RESAMPLE_INVALID)
setFactor( RESAMPLE_DOUBLE );
@@ -524,9 +542,11 @@ void ResampleFilter::draw (FrameBuffer *input)
delete buffers_.second;
res /= 2.;
buffers_.second = new FrameBuffer( res, buffers_.first->flags() );
// forced draw
forced = true;
}
if ( enabled() )
if ( enabled() || forced )
{
// FIRST PASS
// render input surface into frame buffer
@@ -592,6 +612,8 @@ void BlurFilter::setMethod(int method)
void BlurFilter::draw (FrameBuffer *input)
{
bool forced = false;
// Default to Gaussian blur
if (method_ == BLUR_INVALID)
setMethod( BLUR_GAUSSIAN );
@@ -630,9 +652,11 @@ void BlurFilter::draw (FrameBuffer *input)
if (buffers_.second != nullptr)
delete buffers_.second;
buffers_.second = new FrameBuffer( input_->resolution(), f );
// forced draw
forced = true;
}
if ( enabled() )
if ( enabled() || forced )
{
// ZERO PASS
// render input surface into frame buffer with Mipmapping (Levels of Details)
@@ -676,7 +700,7 @@ const char* SharpenFilter::method_label[SharpenFilter::SHARPEN_INVALID] = {
std::vector< FilteringProgram > SharpenFilter::programs_ = {
FilteringProgram("UnsharpMask", "shaders/filters/sharpen_1.glsl", "shaders/filters/sharpen_2.glsl", { { "Amount", 0.5} }),
FilteringProgram("Sharpen", "shaders/filters/sharpen.glsl", "", { { "Amount", 0.5} }),
FilteringProgram("Sharp Edge", "shaders/filters/sharpenedge.glsl","", { { "Amount", 0.5} }),
FilteringProgram("Sharp Edge", "shaders/filters/sharpenedge.glsl","", { { "Amount", 0.25} }),
FilteringProgram("TopHat", "shaders/filters/erosion.glsl", "shaders/filters/tophat.glsl", { { "Radius", 0.5} }),
FilteringProgram("BlackHat", "shaders/filters/dilation.glsl", "shaders/filters/blackhat.glsl", { { "Radius", 0.5} }),
};
@@ -720,7 +744,7 @@ const char* SmoothFilter::method_label[SmoothFilter::SMOOTH_INVALID] = {
std::vector< FilteringProgram > SmoothFilter::programs_ = {
FilteringProgram("Bilateral","shaders/filters/bilinear.glsl", "", { { "Factor", 0.5} }),
FilteringProgram("Kuwahara", "shaders/filters/kuwahara.glsl", "", { { "Radius", 1.0} }),
FilteringProgram("Kuwahara", "shaders/filters/kuwahara.glsl", "", { { "Radius", 0.5} }),
FilteringProgram("Opening", "shaders/filters/erosion.glsl", "shaders/filters/dilation.glsl", { { "Radius", 0.5} }),
FilteringProgram("Closing", "shaders/filters/dilation.glsl", "shaders/filters/erosion.glsl", { { "Radius", 0.5} }),
FilteringProgram("Erosion", "shaders/filters/erosion.glsl", "", { { "Radius", 0.5} }),

View File

@@ -91,6 +91,8 @@ public:
uint texture () const override;
glm::vec3 resolution () const override;
void update (float dt) override;
double updateTime () override;
void reset () override;
void draw (FrameBuffer *input) override;
void accept (Visitor& v) override;

View File

@@ -142,13 +142,6 @@ void MediaSource::setActive (bool on)
if ( active_ != was_active )
mediaplayer_->enable(active_);
// change visibility of active surface (show preview of media when inactive)
if (activesurface_) {
if (active_)
activesurface_->setTextureIndex(Resource::getTextureTransparent());
else
activesurface_->setTextureIndex(mediaplayer_->texture());
}
}

View File

@@ -137,14 +137,6 @@ void SessionSource::setActive (bool on)
// change status of session (recursive change of internal sources)
if (session_) {
session_->setActive(active_);
// change visibility of active surface (show preview of session when inactive)
if (activesurface_) {
if (active_)
activesurface_->setTextureIndex(Resource::getTextureTransparent());
else
activesurface_->setTextureIndex(session_->frame()->texture());
}
}
}

View File

@@ -523,7 +523,7 @@ void Source::attach(FrameBuffer *renderbuffer)
if ( activesurface_ == nullptr) {
// for views showing a scaled mixing surface, a dedicated transparent surface allows grabbing
activesurface_ = new Surface();
activesurface_ = new Surface;
activesurface_->setTextureIndex(Resource::getTextureTransparent());
groups_[View::TEXTURE]->attach(activesurface_);
groups_[View::MIXING]->attach(activesurface_);

View File

@@ -154,13 +154,6 @@ void StreamSource::setActive (bool on)
if (active_ != was_active)
stream_->enable(active_);
// change visibility of active surface (show preview of stream when inactive)
if (activesurface_) {
if (active_)
activesurface_->setTextureIndex(Resource::getTextureTransparent());
else
activesurface_->setTextureIndex(stream_->texture());
}
}
}

View File

@@ -1130,10 +1130,10 @@ void UserInterface::showSourceEditor(Source *s)
outputcontrol.setVisible(true);
return;
}
CloneSource *cs = dynamic_cast<CloneSource *>(s);
if (cs != nullptr) {
shadercontrol.setVisible( cs );
}
// CloneSource *cs = dynamic_cast<CloneSource *>(s);
// if (cs != nullptr) {
// shadercontrol.setVisible( cs );
// }
if (s->playable()) {
sourcecontrol.setVisible(true);
sourcecontrol.resetActiveSelection();
@@ -3081,23 +3081,56 @@ void SourceController::RenderSingleSource(Source *s)
///
/// Image
///
top += corner;
ImGui::SetCursorScreenPos(top);
const ImVec2 top_image = top + corner;
ImGui::SetCursorScreenPos(top_image);
ImVec2 crop = ImVec2 ( s->frame()->projectionArea().x, s->frame()->projectionArea().y);
CloneSource *cloned = dynamic_cast<CloneSource *>(s);
if (s->imageProcessingEnabled() || cloned != nullptr) {
ImGui::Image((void*)(uintptr_t) s->texture(), framesize * ImVec2(Settings::application.widget.media_player_slider,1.f), ImVec2(0.f,0.f), ImVec2(Settings::application.widget.media_player_slider,1.f));
if (s->imageProcessingEnabled() || ImLengthSqr(crop) < 2 || cloned != nullptr) {
//
// LEFT of slider : original texture
//
ImVec2 slider = framesize * ImVec2(Settings::application.widget.media_player_slider,1.f);
ImGui::Image((void*)(uintptr_t) s->texture(), slider, ImVec2(0.f,0.f), ImVec2(Settings::application.widget.media_player_slider,1.f));
ImGui::SetCursorScreenPos(top + ImVec2(Settings::application.widget.media_player_slider * framesize.x, 0.f));
ImGui::Image((void*)(uintptr_t) s->frame()->texture(), framesize * ImVec2(1.f-Settings::application.widget.media_player_slider,1.f), ImVec2(Settings::application.widget.media_player_slider,0.f), ImVec2(1.f,1.f));
//
// RIGHT of slider : post-processed image (after crop and color correction)
//
ImVec2 cropsize = framesize * crop;
ImVec2 croptop = (framesize - cropsize) * 0.5f;
// no overlap of slider with cropped area
if (slider.x < croptop.x) {
// draw cropped area
ImGui::SetCursorScreenPos(top_image + croptop );
ImGui::Image((void*)(uintptr_t) s->frame()->texture(), cropsize, ImVec2(0.f, 0.f), ImVec2(1.f,1.f));
}
// overlap of slider with cropped area (horizontally)
else if (slider.x < croptop.x + cropsize.x ) {
// compute slider ratio of cropped area
float cropped_slider = (slider.x - croptop.x) / cropsize.x;
// top x moves with slider
croptop.x = slider.x;
ImGui::SetCursorScreenPos(top_image + croptop );
// size is reduced by slider
cropsize = cropsize * ImVec2(1.f -cropped_slider, 1.f);
ImGui::Image((void*)(uintptr_t) s->frame()->texture(), cropsize, ImVec2(cropped_slider, 0.f), ImVec2(1.f,1.f));
}
// else : no render of cropped area
draw_list->AddCircleFilled(top + framesize * ImVec2(Settings::application.widget.media_player_slider,0.5f), 20.f, IM_COL32(255, 255, 255, 150), 26);
draw_list->AddLine(top + framesize * ImVec2(Settings::application.widget.media_player_slider,0.0f), top + framesize * ImVec2(Settings::application.widget.media_player_slider,1.f), IM_COL32(255, 255, 255, 150), 1);
ImGui::SetCursorScreenPos(top + ImVec2(0.f, 0.5f * framesize.y - 20.0f));
ImGuiToolkit::InvisibleSliderFloat("#filter_slider", &Settings::application.widget.media_player_slider, 0.f, 1.f, ImVec2(framesize.x, 40.0f) );
//
// SLIDER
//
// graphical indication of slider
draw_list->AddCircleFilled(top_image + slider * ImVec2(1.f, 0.5f), 20.f, IM_COL32(255, 255, 255, 150), 26);
draw_list->AddLine(top_image + slider * ImVec2(1.f,0.0f), top_image + slider, IM_COL32(255, 255, 255, 150), 1);
// user input : move slider horizontally
ImGui::SetCursorScreenPos(top_image + ImVec2(0.f, 0.5f * framesize.y - 20.0f));
ImGuiToolkit::InvisibleSliderFloat("#Settings::application.widget.media_player_slider2", &Settings::application.widget.media_player_slider, 0.f, 1.f, ImVec2(framesize.x, 40.0f) );
// affordance: cursor change to horizontal arrows
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
else {
ImGui::Image((void*)(uintptr_t) s->texture(), framesize);
@@ -3106,20 +3139,20 @@ void SourceController::RenderSingleSource(Source *s)
///
/// Info overlays
///
ImGui::SetCursorScreenPos(top + ImVec2(framesize.x - ImGui::GetTextLineHeightWithSpacing(), v_space_));
ImGui::SetCursorScreenPos(top_image + ImVec2(framesize.x - ImGui::GetTextLineHeightWithSpacing(), v_space_));
ImGui::Text(ICON_FA_INFO_CIRCLE);
if (ImGui::IsItemHovered()){
// fill info string
s->accept(info_);
// draw overlay frame and text
float tooltip_height = 3.f * ImGui::GetTextLineHeightWithSpacing();
draw_list->AddRectFilled(top, top + ImVec2(framesize.x, tooltip_height), IMGUI_COLOR_OVERLAY);
ImGui::SetCursorScreenPos(top + ImVec2(h_space_, v_space_));
draw_list->AddRectFilled(top_image, top_image + ImVec2(framesize.x, tooltip_height), IMGUI_COLOR_OVERLAY);
ImGui::SetCursorScreenPos(top_image + ImVec2(h_space_, v_space_));
ImGui::Text("%s", info_.str().c_str());
// special case Streams: print framerate
StreamSource *sts = dynamic_cast<StreamSource*>(s);
if (sts && s->playing()) {
ImGui::SetCursorScreenPos(top + ImVec2( framesize.x - 1.5f * buttons_height_, 0.5f * tooltip_height));
ImGui::SetCursorScreenPos(top_image + ImVec2( framesize.x - 1.5f * buttons_height_, 0.5f * tooltip_height));
ImGui::Text("%.1f Hz", sts->stream()->updateFrameRate());
}
}
@@ -3178,20 +3211,53 @@ void SourceController::RenderMediaPlayer(MediaSource *ms)
///
const ImVec2 top_image = top + corner;
ImGui::SetCursorScreenPos(top_image);
ImVec2 crop = ImVec2 ( ms->frame()->projectionArea().x, ms->frame()->projectionArea().y);
if (ms->imageProcessingEnabled()) {
ImGui::Image((void*)(uintptr_t) ms->texture(), framesize * ImVec2(Settings::application.widget.media_player_slider,1.f), ImVec2(0.f,0.f), ImVec2(Settings::application.widget.media_player_slider,1.f));
if (ms->imageProcessingEnabled() || ImLengthSqr(crop) < 2) {
//
// LEFT of slider : original texture
//
ImVec2 slider = framesize * ImVec2(Settings::application.widget.media_player_slider,1.f);
ImGui::Image((void*)(uintptr_t) ms->texture(), slider, ImVec2(0.f,0.f), ImVec2(Settings::application.widget.media_player_slider,1.f));
ImGui::SetCursorScreenPos(top_image + ImVec2(Settings::application.widget.media_player_slider * framesize.x, 0.f));
ImGui::Image((void*)(uintptr_t) ms->frame()->texture(), framesize * ImVec2(1.f-Settings::application.widget.media_player_slider,1.f), ImVec2(Settings::application.widget.media_player_slider,0.f), ImVec2(1.f,1.f));
draw_list->AddCircleFilled(top_image + framesize * ImVec2(Settings::application.widget.media_player_slider,0.5f), 20.f, IM_COL32(255, 255, 255, 150), 26);
draw_list->AddLine(top_image + framesize * ImVec2(Settings::application.widget.media_player_slider,0.0f), top_image + framesize * ImVec2(Settings::application.widget.media_player_slider,1.f), IM_COL32(255, 255, 255, 150), 1);
//
// RIGHT of slider : post-processed image (after crop and color correction)
//
ImVec2 cropsize = framesize * crop;
ImVec2 croptop = (framesize - cropsize) * 0.5f;
// no overlap of slider with cropped area
if (slider.x < croptop.x) {
// draw cropped area
ImGui::SetCursorScreenPos(top_image + croptop );
ImGui::Image((void*)(uintptr_t) ms->frame()->texture(),
cropsize, ImVec2(0.f, 0.f), ImVec2(1.f,1.f));
}
// overlap of slider with cropped area (horizontally)
else if (slider.x < croptop.x + cropsize.x ) {
// compute slider ratio of cropped area
float cropped_slider = (slider.x - croptop.x) / cropsize.x;
// top x moves with slider
croptop.x = slider.x;
ImGui::SetCursorScreenPos(top_image + croptop );
// size is reduced by slider
cropsize = cropsize * ImVec2(1.f -cropped_slider, 1.f);
ImGui::Image((void*)(uintptr_t) ms->frame()->texture(), cropsize, ImVec2(cropped_slider, 0.f), ImVec2(1.f,1.f));
}
// else : no render of cropped area
//
// SLIDER
//
// graphical indication of slider
draw_list->AddCircleFilled(top_image + slider * ImVec2(1.f, 0.5f), 20.f, IM_COL32(255, 255, 255, 150), 26);
draw_list->AddLine(top_image + slider * ImVec2(1.f,0.0f), top_image + slider, IM_COL32(255, 255, 255, 150), 1);
// user input : move slider horizontally
ImGui::SetCursorScreenPos(top_image + ImVec2(0.f, 0.5f * framesize.y - 20.0f));
ImGuiToolkit::InvisibleSliderFloat("#Settings::application.widget.media_player_slider2", &Settings::application.widget.media_player_slider, 0.f, 1.f, ImVec2(framesize.x, 40.0f) );
// affordance: cursor change to horizontal arrows
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
}
else {
ImGui::Image((void*)(uintptr_t) mediaplayer_active_->texture(), framesize);
@@ -5397,7 +5463,6 @@ void ShaderEditor::setVisible(CloneSource *cs)
// if the filter is an Image Filter
if (f && f->type() == FrameBufferFilter::FILTER_IMAGE )
setVisible(true);
}
}