Change Player menu and new Frame inspector

Frame menu is active when a single source is selected. The Frame menu include actions to capture frame and to enable Frame Inspector. Frame inspector zooms on the image at cursor coordinate. Previous Control menu actions are back to main menu.
This commit is contained in:
Bruno Herbelin
2022-07-23 22:45:14 +02:00
parent ae5ae24f6f
commit 5a2c0e15e9
3 changed files with 163 additions and 109 deletions

View File

@@ -173,6 +173,7 @@ void Settings::Save(uint64_t runtime)
SourceConfNode->SetAttribute("res", application.source.res);
SourceConfNode->SetAttribute("capture_naming", application.source.capture_naming);
SourceConfNode->SetAttribute("capture_path", application.source.capture_path.c_str());
SourceConfNode->SetAttribute("inspector_zoom", application.source.inspector_zoom);
pRoot->InsertEndChild(SourceConfNode);
// Brush
@@ -422,6 +423,7 @@ void Settings::Load()
application.source.capture_path = std::string(path_);
else
application.source.capture_path = SystemToolkit::home_path();
sourceconfnode->QueryFloatAttribute("inspector_zoom", &application.source.inspector_zoom);
}
// Transition

View File

@@ -169,12 +169,14 @@ struct SourceConfig
int res;
std::string capture_path;
int capture_naming;
float inspector_zoom;
SourceConfig() {
new_type = 0;
ratio = 3;
res = 1;
capture_naming = 0;
inspector_zoom = 8.f;
}
};

View File

@@ -2235,25 +2235,27 @@ void SourceController::Update()
Settings::application.source.capture_path = captureFolderDialog->path();
//
// Capture frame on current source
// Capture frame on current selection
//
Source *source = Mixer::manager().currentSource();
if (source != nullptr) {
Source *s = nullptr;
if ( selection_.size() == 1 )
s = selection_.front();
if ( s != nullptr) {
// back from capture of FBO: can save file
if ( capture.isFull() ){
std::string filename;
// if sequencial naming of file is selected
if (Settings::application.source.capture_naming == 0 )
filename = SystemToolkit::filename_sequential(Settings::application.source.capture_path, source->name(), "png");
filename = SystemToolkit::filename_sequential(Settings::application.source.capture_path, s->name(), "png");
else
filename = SystemToolkit::filename_dateprefix(Settings::application.source.capture_path, source->name(), "png");
filename = SystemToolkit::filename_dateprefix(Settings::application.source.capture_path, s->name(), "png");
// save capture and inform user
capture.save( filename );
Log::Notify("Frame saved in %s", filename.c_str() );
}
// request capture : initiate capture of FBO
if ( capture_request_ ) {
capture.captureFramebuffer(source->frame());
capture.captureFramebuffer( s->frame() );
capture_request_ = false;
}
}
@@ -2300,6 +2302,15 @@ void SourceController::Render()
}
if (ImGui::BeginMenu(IMGUI_TITLE_MEDIAPLAYER))
{
//
// Menu section for play control
//
if (ImGui::MenuItem( ICON_FA_FAST_BACKWARD " Restart", CTRL_MOD "Space", nullptr, !selection_.empty()))
replay_request_ = true;
if (ImGui::MenuItem( ICON_FA_PLAY " Play | Pause", "Space", nullptr, !selection_.empty()))
play_toggle_request_ = true;
ImGui::Separator();
//
// Menu section for display
//
@@ -2346,25 +2357,57 @@ void SourceController::Render()
ImGui::EndMenu();
}
if (ImGui::BeginMenu(ICON_FA_PLAY " Control"))
if (ImGui::BeginMenu(active_label_.c_str()))
{
//
// Menu section for play control
//
if (ImGui::MenuItem( ICON_FA_FAST_BACKWARD " Restart", CTRL_MOD "Space", nullptr, !selection_.empty()))
replay_request_ = true;
if (ImGui::MenuItem( ICON_FA_PLAY " Play | Pause", "Space", nullptr, !selection_.empty()))
play_toggle_request_ = true;
// info on selection status
size_t N = Mixer::manager().session()->numPlayGroups();
bool enabled = !selection_.empty() && active_selection_ < 0;
// Menu : Dynamic selection
if (ImGui::MenuItem(LABEL_AUTO_MEDIA_PLAYER))
resetActiveSelection();
// Menu : store selection
if (ImGui::MenuItem(ICON_FA_PLUS_SQUARE LABEL_STORE_SELECTION, NULL, false, enabled))
{
active_selection_ = N;
active_label_ = std::string(ICON_FA_CHECK_SQUARE " Selection #") + std::to_string(active_selection_);
Mixer::manager().session()->addPlayGroup( ids(playable_only(selection_)) );
info_.reset();
}
// Menu : list of selections
if (N>0) {
ImGui::Separator();
for (size_t i = 0 ; i < N; ++i)
{
std::string label = std::string(ICON_FA_CHECK_SQUARE " Selection #") + std::to_string(i);
if (ImGui::MenuItem( label.c_str() ))
{
active_selection_ = i;
active_label_ = label;
info_.reset();
}
}
}
ImGui::EndMenu();
}
//
// Menu for capture frame
//
if ( ImGui::BeginMenu(ICON_FA_PHOTO_VIDEO " Frame", selection_.size() == 1 ) )
{
bool inspect = Settings::application.source.inspector_zoom > 0.f;
if (ImGui::MenuItem( ICON_FA_SEARCH " Inspector", NULL, &inspect ))
Settings::application.source.inspector_zoom = inspect ? 8.f : 0.f;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_CAPTURE, 0.8f));
if (ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Capture frame", "F10", nullptr, Mixer::manager().currentSource() != nullptr ))
if (ImGui::MenuItem( MENU_CAPTUREFRAME, "F10" ))
capture_request_ = true;
ImGui::PopStyleColor(1);
// separator and hack for extending menu width
ImGui::Separator();
ImGui::MenuItem("Settings ", nullptr, false, false);
// path menu selection
@@ -2410,43 +2453,10 @@ void SourceController::Render()
ImGui::EndMenu();
}
if (ImGui::BeginMenu(active_label_.c_str()))
{
// info on selection status
size_t N = Mixer::manager().session()->numPlayGroups();
bool enabled = !selection_.empty() && active_selection_ < 0;
// Menu : Dynamic selection
if (ImGui::MenuItem(LABEL_AUTO_MEDIA_PLAYER))
resetActiveSelection();
// Menu : store selection
if (ImGui::MenuItem(ICON_FA_PLUS_SQUARE LABEL_STORE_SELECTION, NULL, false, enabled))
{
active_selection_ = N;
active_label_ = std::string(ICON_FA_CHECK_SQUARE " Selection #") + std::to_string(active_selection_);
Mixer::manager().session()->addPlayGroup( ids(playable_only(selection_)) );
info_.reset();
}
// Menu : list of selections
if (N>0) {
ImGui::Separator();
for (size_t i = 0 ; i < N; ++i)
{
std::string label = std::string(ICON_FA_CHECK_SQUARE " Selection #") + std::to_string(i);
if (ImGui::MenuItem( label.c_str() ))
{
active_selection_ = i;
active_label_ = label;
info_.reset();
}
}
}
ImGui::EndMenu();
}
if (mediaplayer_active_) {
if (ImGui::BeginMenu(ICON_FA_FILM " Video") )
//
// Menu for video Mediaplayer control
//
if (ImGui::BeginMenu(ICON_FA_FILM " Video", mediaplayer_active_) )
{
if (ImGui::MenuItem(ICON_FA_WINDOW_CLOSE " Reset timeline")){
mediaplayer_timeline_zoom_ = 1.f;
@@ -2503,11 +2513,6 @@ void SourceController::Render()
ImGui::EndMenu();
}
}
else {
ImGui::SameLine(0, 2.f * g.Style.ItemSpacing.x );
ImGui::TextDisabled(ICON_FA_FILM " Video");
}
ImGui::EndMenuBar();
}
@@ -3015,6 +3020,43 @@ bool SourceController::SourceButton(Source *s, ImVec2 framesize)
return ret;
}
void DrawInspector(uint texture, ImVec2 texturesize, ImVec2 origin)
{
if (Settings::application.source.inspector_zoom > 0 && ImGui::IsWindowFocused())
{
// region size is computed with zoom factor from settings
float region_sz = texturesize.x / Settings::application.source.inspector_zoom;
// get coordinates of area to zoom at mouse position
const ImGuiIO& io = ImGui::GetIO();
float region_x = io.MousePos.x - origin.x - region_sz * 0.5f;
if (region_x < 0.f)
region_x = 0.f;
else if (region_x > texturesize.x - region_sz)
region_x = texturesize.x - region_sz;
float region_y = io.MousePos.y - origin.y - region_sz * 0.5f;
if (region_y < 0.f)
region_y = 0.f;
else if (region_y > texturesize.y - region_sz)
region_y = texturesize.y - region_sz;
// Tooltip without border and 100% opaque
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f) );
ImGui::BeginTooltip();
// compute UV and display image in tooltip
ImVec2 uv0 = ImVec2((region_x) / texturesize.x, (region_y) / texturesize.y);
ImVec2 uv1 = ImVec2((region_x + region_sz) / texturesize.x, (region_y + region_sz) / texturesize.y);
ImGui::Image((void*)(intptr_t)texture, ImVec2(texturesize.x / 3.f, texturesize.x / 3.f), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
ImGui::EndTooltip();
ImGui::PopStyleVar(3);
}
}
ImRect DrawSourceWithSlider(Source *s, ImVec2 top, ImVec2 rendersize)
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
@@ -3049,6 +3091,8 @@ ImRect DrawSourceWithSlider(Source *s, ImVec2 top, ImVec2 rendersize)
//
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));
if ( ImGui::IsItemHovered() )
DrawInspector(s->texture(), framesize, top_image);
//
// RIGHT of slider : post-processed image (after crop and color correction)
@@ -3060,6 +3104,8 @@ ImRect DrawSourceWithSlider(Source *s, ImVec2 top, ImVec2 rendersize)
// 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));
if ( ImGui::IsItemHovered() )
DrawInspector(s->frame()->texture(), framesize, top_image);
}
// overlap of slider with cropped area (horizontally)
else if (slider.x < croptop.x + cropsize.x ) {
@@ -3071,6 +3117,8 @@ ImRect DrawSourceWithSlider(Source *s, ImVec2 top, ImVec2 rendersize)
// 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));
if ( ImGui::IsItemHovered() )
DrawInspector(s->frame()->texture(), framesize, top_image);
}
// else : no render of cropped area
@@ -3090,6 +3138,8 @@ ImRect DrawSourceWithSlider(Source *s, ImVec2 top, ImVec2 rendersize)
}
else {
ImGui::Image((void*)(uintptr_t) s->texture(), framesize);
if ( ImGui::IsItemHovered() )
DrawInspector(s->texture(), framesize, top_image);
}
return ImRect( top_image, top_image + framesize);