diff --git a/CloneSource.cpp b/CloneSource.cpp index 741487d..401df4c 100644 --- a/CloneSource.cpp +++ b/CloneSource.cpp @@ -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(); } diff --git a/DelayFilter.cpp b/DelayFilter.cpp index ad720c0..4d4af86 100644 --- a/DelayFilter.cpp +++ b/DelayFilter.cpp @@ -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() ) { diff --git a/DelayFilter.h b/DelayFilter.h index f094cf4..05c4321 100644 --- a/DelayFilter.h +++ b/DelayFilter.h @@ -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; diff --git a/DeviceSource.cpp b/DeviceSource.cpp index 68ad7f1..b610e17 100644 --- a/DeviceSource.cpp +++ b/DeviceSource.cpp @@ -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()); - } } } diff --git a/FrameBufferFilter.cpp b/FrameBufferFilter.cpp index 276bb6a..055cf21 100644 --- a/FrameBufferFilter.cpp +++ b/FrameBufferFilter.cpp @@ -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) diff --git a/FrameBufferFilter.h b/FrameBufferFilter.h index b5ae301..73ef0c5 100644 --- a/FrameBufferFilter.h +++ b/FrameBufferFilter.h @@ -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_; diff --git a/ImageFilter.cpp b/ImageFilter.cpp index 06780d5..9a2ddfb 100644 --- a/ImageFilter.cpp +++ b/ImageFilter.cpp @@ -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} }), diff --git a/ImageFilter.h b/ImageFilter.h index 439a66c..73659d9 100644 --- a/ImageFilter.h +++ b/ImageFilter.h @@ -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; diff --git a/MediaSource.cpp b/MediaSource.cpp index ccabf7e..789c0d5 100644 --- a/MediaSource.cpp +++ b/MediaSource.cpp @@ -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()); - } } diff --git a/SessionSource.cpp b/SessionSource.cpp index 6a64126..ef890fc 100644 --- a/SessionSource.cpp +++ b/SessionSource.cpp @@ -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()); - } } } diff --git a/Source.cpp b/Source.cpp index 19c7bca..077aefa 100644 --- a/Source.cpp +++ b/Source.cpp @@ -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_); diff --git a/StreamSource.cpp b/StreamSource.cpp index 7f0c0d7..93361f7 100644 --- a/StreamSource.cpp +++ b/StreamSource.cpp @@ -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()); - } } } diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 4d723eb..37405b8 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1130,10 +1130,10 @@ void UserInterface::showSourceEditor(Source *s) outputcontrol.setVisible(true); return; } - CloneSource *cs = dynamic_cast(s); - if (cs != nullptr) { - shadercontrol.setVisible( cs ); - } +// CloneSource *cs = dynamic_cast(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(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(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); - } }