#include "lib/ffmpeg_content.h"
#include "lib/file_log.h"
#include "lib/film.h"
+#include "lib/image.h"
+#include "lib/image_jpeg.h"
+#include "lib/image_png.h"
#include "lib/internet.h"
#include "lib/job.h"
#include "lib/job_manager.h"
#include "lib/null_log.h"
+#include "lib/player.h"
+#include "lib/player_video.h"
#include "lib/ratio.h"
#include "lib/scoped_temporary.h"
#include "lib/server.h"
#include "lib/video_content.h"
#include <dcp/cpl.h>
#include <dcp/dcp.h>
-#include <dcp/raw_convert.h>
#include <dcp/exceptions.h>
+#include <dcp/raw_convert.h>
+#include <dcp/search.h>
+#include <dcp/warnings.h>
+LIBDCP_DISABLE_WARNINGS
#include <wx/cmdline.h>
#include <wx/display.h>
#include <wx/preferences.h>
#include <wx/splash.h>
#include <wx/stdpaths.h>
#include <wx/wx.h>
+LIBDCP_ENABLE_WARNINGS
#ifdef __WXGTK__
#include <X11/Xlib.h>
#endif
ID_file_open = 1,
ID_file_add_ov,
ID_file_add_kdm,
+ ID_file_save_frame,
ID_file_history,
/* Allow spare IDs after _history for the recent files list */
ID_file_close = 100,
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_open, this), ID_file_open);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_add_ov, this), ID_file_add_ov);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_add_kdm, this), ID_file_add_kdm);
+ Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_save_frame, this), ID_file_save_frame);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_history, this, _1), ID_file_history, ID_file_history + HISTORY_SIZE);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_close, this), ID_file_close);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
_stress.setup (this, _controls);
- wxAcceleratorEntry* accel = new wxAcceleratorEntry[accelerators];
+ std::vector<wxAcceleratorEntry> accel(accelerators);
accel[0].Set(wxACCEL_NORMAL, WXK_SPACE, ID_start_stop);
accel[1].Set(wxACCEL_NORMAL, WXK_LEFT, ID_go_back_frame);
accel[2].Set(wxACCEL_NORMAL, WXK_RIGHT, ID_go_forward_frame);
#ifdef __WXOSX__
accel[11].Set(wxACCEL_CTRL, static_cast<int>('W'), ID_file_close);
#endif
- wxAcceleratorTable accel_table (accelerators, accel);
+ wxAcceleratorTable accel_table (accelerators, accel.data());
SetAcceleratorTable (accel_table);
- delete[] accel;
Bind (wxEVT_MENU, boost::bind(&DOMFrame::start_stop_pressed, this), ID_start_stop);
Bind (wxEVT_MENU, boost::bind(&DOMFrame::go_back_frame, this), ID_go_back_frame);
/* Offer a CPL menu */
auto first = dynamic_pointer_cast<DCPContent>(_film->content().front());
if (first) {
- DCPExaminer ex (first, true);
int id = ID_view_cpl;
- for (auto i: ex.cpls()) {
+ for (auto i: dcp::find_and_resolve_cpls(first->directories(), true)) {
auto j = _cpl_menu->AppendRadioItem(
id,
wxString::Format("%s (%s)", std_to_wx(i->annotation_text().get_value_or("")).data(), std_to_wx(i->id()).data())
_file_menu->Append (ID_file_open, _("&Open...\tCtrl-O"));
_file_add_ov = _file_menu->Append (ID_file_add_ov, _("&Add OV..."));
_file_add_kdm = _file_menu->Append (ID_file_add_kdm, _("Add &KDM..."));
+ _file_menu->AppendSeparator ();
+ _file_save_frame = _file_menu->Append (ID_file_save_frame, _("&Save frame to file...\tCtrl-S"));
_history_position = _file_menu->GetMenuItems().GetCount();
_info->triggered_update ();
}
+ void file_save_frame ()
+ {
+ wxFileDialog dialog (this, _("Save frame to file"), "", "", "PNG files (*.png)|*.png|JPEG files (*.jpg,*.jpeg)|*.jpg,*.jpeg", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if (dialog.ShowModal() == wxID_CANCEL) {
+ return;
+ }
+
+ auto path = boost::filesystem::path (wx_to_std(dialog.GetPath()));
+
+ auto player = make_shared<Player>(_film, Image::Alignment::PADDED);
+ player->seek (_viewer->position(), true);
+
+ bool done = false;
+ player->Video.connect ([path, &done, this](shared_ptr<PlayerVideo> video, DCPTime) {
+ auto ext = boost::algorithm::to_lower_copy(path.extension().string());
+ if (ext == ".png") {
+ auto image = video->image(boost::bind(PlayerVideo::force, AV_PIX_FMT_RGBA), VideoRange::FULL, false);
+ image_as_png(image).write(path);
+ } else if (ext == ".jpg" || ext == ".jpeg") {
+ auto image = video->image(boost::bind(PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, false);
+ image_as_jpeg(image, 80).write(path);
+ } else {
+ error_dialog (this, _(wxString::Format("Unrecognised file extension %s (use .jpg, .jpeg or .png)", std_to_wx(ext))));
+ }
+ done = true;
+ });
+
+ int tries_left = 50;
+ while (!done && tries_left >= 0) {
+ player->pass();
+ --tries_left;
+ }
+
+ DCPOMATIC_ASSERT (tries_left >= 0);
+ }
+
void file_history (wxCommandEvent& event)
{
auto history = Config::instance()->player_history ();
{
auto dcp = std::dynamic_pointer_cast<DCPContent>(_film->content().front());
DCPOMATIC_ASSERT (dcp);
- DCPExaminer ex (dcp, true);
+ auto cpls = dcp::find_and_resolve_cpls (dcp->directories(), true);
int id = ev.GetId() - ID_view_cpl;
DCPOMATIC_ASSERT (id >= 0);
- DCPOMATIC_ASSERT (id < int(ex.cpls().size()));
- auto cpls = ex.cpls();
+ DCPOMATIC_ASSERT (id < int(cpls.size()));
auto i = cpls.begin();
while (id > 0) {
++i;
_dual_screen->SetBackgroundColour (wxColour(0, 0, 0));
_dual_screen->ShowFullScreen (true);
_viewer->panel()->Reparent (_dual_screen);
+ _viewer->panel()->SetFocus();
_dual_screen->Show ();
if (wxDisplay::GetCount() > 1) {
switch (Config::instance()->image_display()) {
break;
}
}
+ _dual_screen->Bind(wxEVT_CHAR_HOOK, boost::bind(&DOMFrame::dual_screen_key_press, this, _1));
} else {
if (_dual_screen) {
_viewer->panel()->Reparent (_overall_panel);
setup_main_sizer (_mode);
}
+ void dual_screen_key_press(wxKeyEvent& ev)
+ {
+ if (ev.GetKeyCode() == WXK_F11) {
+ if (ev.ShiftDown()) {
+ view_dual_screen();
+ } else if (!ev.HasAnyModifiers()) {
+ view_full_screen();
+ }
+ }
+ }
+
void view_closed_captions ()
{
_viewer->show_closed_captions ();
int pos = _history_position;
- /* Clear out non-existant history items before we re-build the menu */
+ /* Clear out non-existent history items before we re-build the menu */
Config::instance()->clean_player_history ();
auto history = Config::instance()->player_history ();
_tools_verify->Enable (static_cast<bool>(_film));
_file_add_ov->Enable (static_cast<bool>(_film));
_file_add_kdm->Enable (static_cast<bool>(_film));
+ _file_save_frame->Enable (static_cast<bool>(_film));
_view_cpl->Enable (static_cast<bool>(_film));
}
boost::signals2::scoped_connection _examine_job_connection;
wxMenuItem* _file_add_ov = nullptr;
wxMenuItem* _file_add_kdm = nullptr;
+ wxMenuItem* _file_save_frame = nullptr;
wxMenuItem* _tools_verify = nullptr;
wxMenuItem* _view_full_screen = nullptr;
wxMenuItem* _view_dual_screen = nullptr;