From 40d3f837198c0972831f9d6b79bb63cbadd016a6 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Wed, 1 Jan 2025 23:55:39 +0100 Subject: [PATCH] BugFix Important fix of Bundle Session source premultiply alpha should NOT be applied to render session in framebuffer with alpha on surface : added a uniform to image shader to allow this. Fixed other problems related to creation and expand of bundle sources. Added a button to bundle a single source. --- rsc/shaders/image.fs | 3 ++- src/ImageShader.cpp | 3 ++- src/ImageShader.h | 1 + src/Mixer.cpp | 37 ++++++++++++++++++++++++++++-------- src/Mixer.h | 6 ++++-- src/SessionSource.cpp | 5 +++++ src/Source.cpp | 3 ++- src/UserInterfaceManager.cpp | 30 +++++++++++++++++++++-------- 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/rsc/shaders/image.fs b/rsc/shaders/image.fs index ee8831c..4382c32 100644 --- a/rsc/shaders/image.fs +++ b/rsc/shaders/image.fs @@ -14,6 +14,7 @@ uniform vec4 color; uniform sampler2D iChannel0; // input channel (texture id). uniform sampler2D iChannel1; // input mask uniform float stipple; +uniform float premultiply; void main() { @@ -33,5 +34,5 @@ void main() A = clamp(A, 0.0, 1.0); // output RGB with Alpha pre-multiplied - FragColor = vec4(RGB * A, A); + FragColor = vec4(RGB * mix(1.0 / max(A, 0.001), A, premultiply), A); } diff --git a/src/ImageShader.cpp b/src/ImageShader.cpp index dc8bc1f..be7b720 100644 --- a/src/ImageShader.cpp +++ b/src/ImageShader.cpp @@ -43,7 +43,7 @@ const char* MaskShader::mask_icons[4] = { ICON_FA_WINDOW_CLOSE, ICON_FA_EDIT, I const char* MaskShader::mask_names[4] = { "Mask None", "Mask Paint", "Mask Shape", "Mask Source" }; const char* MaskShader::mask_shapes[5] = { "Ellipse", "Oblong", "Rectangle", "Horizontal", "Vertical" }; -ImageShader::ImageShader(): Shader(), secondary_texture(0), stipple(0.f) +ImageShader::ImageShader(): Shader(), secondary_texture(0), stipple(0.f), premultiply(1.f) { // static program shader program_ = &imageShadingProgram; @@ -57,6 +57,7 @@ void ImageShader::use() // set stippling program_->setUniform("stipple", stipple); + program_->setUniform("premultiply", premultiply); program_->setUniform("iNodes", iNodes); // default mask diff --git a/src/ImageShader.h b/src/ImageShader.h index f2c497c..57f2ceb 100644 --- a/src/ImageShader.h +++ b/src/ImageShader.h @@ -20,6 +20,7 @@ public: // uniforms float stipple; + float premultiply; glm::mat4 iNodes; }; diff --git a/src/Mixer.cpp b/src/Mixer.cpp index ee44b7a..2bc1dcf 100644 --- a/src/Mixer.cpp +++ b/src/Mixer.cpp @@ -771,8 +771,27 @@ void Mixer::groupSelection() if (selection().empty()) return; - // work on non-empty selection of sources - auto _selection = selection().getCopy(); + // group the current selection + group(selection().getCopy()); +} + +void Mixer::groupCurrent() +{ + if ( current_source_ != session_->end() ) { + + SourceList L; + L.emplace_front( *current_source_ ); + + // group the current selection + group( L ); + } +} + +void Mixer::group(SourceList sourcelist) +{ + // work on non-empty list of sources + if (sourcelist.empty()) + return; // create session group where to transfer sources into SessionGroupSource *sessiongroup = new SessionGroupSource; @@ -781,25 +800,25 @@ void Mixer::groupSelection() // prepare for new session group name std::string name; // prepare for depth to place the group source - float d = _selection.front()->depth(); + float d = sourcelist.front()->depth(); // remember groups before emptying the session std::list allgroups = session_->getMixingGroups(); std::list selectgroups; for (auto git = allgroups.begin(); git != allgroups.end(); ++git){ - selectgroups.push_back( intersect( *git, _selection)); + selectgroups.push_back( intersect( *git, sourcelist)); } - // browse the selection - for (auto sit = _selection.begin(); sit != _selection.end(); ++sit) { + // browse the list + for (auto sit = sourcelist.begin(); sit != sourcelist.end(); ++sit) { // import source into group if ( sessiongroup->import(*sit) ) { - // find lower depth in _selection + // find lower depth in list d = MIN( (*sit)->depth(), d); // generate name from intials of all sources name += (*sit)->initials(); - // detach & remove element from selection() + // detach & remove element from list detachSource (*sit); // remove source from session session_->removeSource(*sit); @@ -813,6 +832,7 @@ void Mixer::groupSelection() // set depth at given location sessiongroup->group(View::LAYER)->translation_.z = d; + sessiongroup->group(View::RENDERING)->translation_.z = d; // set alpha to full opacity sessiongroup->group(View::MIXING)->translation_.x = 0.f; @@ -1498,6 +1518,7 @@ void Mixer::merge(SessionSource *source) // avoid display issues current_view_->update(0.f); + unsetCurrentSource(); // new state in history manager Action::manager().store(info.str()); diff --git a/src/Mixer.h b/src/Mixer.h index 178d8aa..6712708 100644 --- a/src/Mixer.h +++ b/src/Mixer.h @@ -69,8 +69,9 @@ public: int numSource () const; // operations on selection - void deleteSelection (); - void groupSelection (); + void deleteSelection (); + void groupSelection (); + void groupCurrent (); void groupAll (bool only_active = false); void ungroupAll (); void groupSession (); @@ -148,6 +149,7 @@ protected: void attachSource (Source *s); void detachSource (Source *s); bool attached (Source *s) const; + void group (SourceList sources); void setCurrentSource(SourceList::iterator it); SourceList::iterator current_source_; diff --git a/src/SessionSource.cpp b/src/SessionSource.cpp index c752823..b862736 100644 --- a/src/SessionSource.cpp +++ b/src/SessionSource.cpp @@ -447,6 +447,11 @@ void SessionGroupSource::init() // get the texture index from framebuffer of session, apply it to the surface texturesurface_->setTextureIndex( session_->frame()->texture() ); + // special case for session group using FrameBuffer_alpha + // shader should not pre-multiply alpha + ImageShader *ish = static_cast(texturesurface_->shader()); + ish->premultiply = 0.f; + // create Frame buffer matching size of session FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution(), FrameBuffer::FrameBuffer_alpha ); diff --git a/src/Source.cpp b/src/Source.cpp index 4045c00..bbc549d 100644 --- a/src/Source.cpp +++ b/src/Source.cpp @@ -930,6 +930,7 @@ void Source::update(float dt) static glm::mat4 UVtoScene = GlmToolkit::transform(glm::vec3(1.f, -1.f, 0.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(-2.f, 2.f, 1.f)); + static glm::mat4 ScenetoUV = glm::inverse(UVtoScene); // Aspect Ratio correction transform : coordinates of Appearance Frame are scaled by render buffer width glm::mat4 Ar = glm::scale(glm::identity(), glm::vec3(renderbuffer_->aspectRatio(), 1.f, 1.f) ); // Translation : same as Appearance Frame (modified by Ar) @@ -948,7 +949,7 @@ void Source::update(float dt) // 5. Revert aspect ration correction // 6. Apply the Scaling (independent of aspect ratio) // 7. switch back to UV coordinate system - texturesurface_->shader()->iTransform = glm::inverse(UVtoScene) * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene; + texturesurface_->shader()->iTransform = ScenetoUV * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene; // inform mixing group if (mixinggroup_) diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index d5fdf9f..311e855 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -3464,6 +3464,9 @@ void Navigator::RenderSourcePannel(Source *s, const ImVec2 &iconsize) // index indicator ImGui::SetCursorPos(ImVec2(pannel_width_ - 2.8f * ImGui::GetTextLineHeightWithSpacing(), IMGUI_TOP_ALIGN)); + + if (Mixer::manager().indexCurrentSource() < 0) + Mixer::manager().setCurrentIndex(selected_index); ImGui::TextDisabled("#%d", Mixer::manager().indexCurrentSource()); ImGui::PopFont(); @@ -3564,17 +3567,27 @@ void Navigator::RenderSourcePannel(Source *s, const ImVec2 &iconsize) ImGui::Text(" "); if (s->ready() || s->failed()) { + ImVec2 size = ImVec2(ImGui::GetContentRegionAvail().x, 0); + // clone button - if ( s->failed() ) { - ImGuiToolkit::ButtonDisabled( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)); - } - else if ( ImGui::Button( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + if ( s->failed() ) + ImGuiToolkit::ButtonDisabled( ICON_FA_SHARE_SQUARE " Clone & Filter", size); + else if ( ImGui::Button( ICON_FA_SHARE_SQUARE " Clone & Filter", size) ) { Mixer::manager().addSource ( (Source *) Mixer::manager().createSourceClone() ); UserInterface::manager().showPannel( Mixer::manager().numSource() ); } + // bundle button + if ( s->failed() ) + ImGuiToolkit::ButtonDisabled( ICON_FA_SIGN_IN_ALT " Bundle", ImVec2((size.x - IMGUI_SAME_LINE)/2.f, 0)); + else if ( ImGui::Button( ICON_FA_SIGN_IN_ALT " Bundle", ImVec2((size.x - IMGUI_SAME_LINE)/2.f, 0)) ) { + Mixer::manager().groupCurrent(); + UserInterface::manager().showPannel( Mixer::manager().numSource() ); + } + // replace button - if ( ImGui::Button( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + ImGui::SameLine(0, IMGUI_SAME_LINE); + if ( ImGui::Button( ICON_FA_PLUS_SQUARE " Replace", ImVec2((size.x - IMGUI_SAME_LINE)/2.f, 0)) ) { // prepare panel for new source of same type MediaSource *file = dynamic_cast(s); MultiFileSource *sequence = dynamic_cast(s); @@ -3587,20 +3600,21 @@ void Navigator::RenderSourcePannel(Source *s, const ImVec2 &iconsize) Settings::application.source.new_type = SOURCE_GENERATED; else Settings::application.source.new_type = SOURCE_CONNECTED; - // switch to panel new source showPannelSource(NAV_NEW); // set source to be replaced source_to_replace = s; } + // delete button - if ( ImGui::Button( ACTION_DELETE, ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + if ( ImGui::Button( ACTION_DELETE, size) ) { Mixer::manager().deleteSource(s); Action::manager().store(sname + std::string(": Deleted")); } + // delete all button if ( Mixer::manager().session()->failedSources().size() > 1 ) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_FAILED, 1.)); - if ( ImGui::Button( ICON_FA_BACKSPACE " Delete all failed", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + if ( ImGui::Button( ICON_FA_BACKSPACE " Delete all failed", size) ) { auto failedsources = Mixer::manager().session()->failedSources(); for (auto sit = failedsources.cbegin(); sit != failedsources.cend(); ++sit) { Mixer::manager().deleteSource( Mixer::manager().findSource( (*sit)->id() ) );