From 74a8d26a8907c6e00e29f054178a3425f44e38ed Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 16 Aug 2013 20:14:33 +0100 Subject: [PATCH] Very basics of colour conversion configuration. --- src/lib/colour_conversion.cc | 101 +++++++++++++++++ src/lib/colour_conversion.h | 45 ++++++++ src/lib/config.cc | 24 ++++- src/lib/config.h | 16 ++- src/lib/dcp_video_frame.cc | 2 +- src/lib/dcp_video_frame.h | 2 +- src/lib/encoder.cc | 8 +- src/lib/encoder.h | 2 +- src/lib/server.cc | 6 +- src/lib/server.h | 7 +- src/lib/wscript | 1 + src/tools/dcpomatic_cli.cc | 2 +- src/wx/colour_conversion_dialog.cc | 150 ++++++++++++++++++++++++++ src/wx/colour_conversion_dialog.h | 39 +++++++ src/wx/config_dialog.cc | 167 +++++++++-------------------- src/wx/config_dialog.h | 18 ++-- src/wx/editable_list.h | 156 +++++++++++++++++++++++++++ src/wx/server_dialog.cc | 8 +- src/wx/server_dialog.h | 6 +- src/wx/video_panel.cc | 40 +++++-- src/wx/video_panel.h | 3 + src/wx/wscript | 1 + test/client_server_test.cc | 6 +- test/test.cc | 2 +- 24 files changed, 651 insertions(+), 161 deletions(-) create mode 100644 src/lib/colour_conversion.cc create mode 100644 src/lib/colour_conversion.h create mode 100644 src/wx/colour_conversion_dialog.cc create mode 100644 src/wx/colour_conversion_dialog.h create mode 100644 src/wx/editable_list.h diff --git a/src/lib/colour_conversion.cc b/src/lib/colour_conversion.cc new file mode 100644 index 000000000..96dc0e2c9 --- /dev/null +++ b/src/lib/colour_conversion.cc @@ -0,0 +1,101 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include "colour_conversion.h" + +#include "i18n.h" + +using std::list; +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; + +ColourConversion::ColourConversion () + : name (_("Untitled")) + , input_gamma (2.4) + , input_gamma_linearised (true) + , matrix (3, 3) + , output_gamma (2.6) +{ + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + matrix (i, j) = libdcp::colour_matrix::xyz_to_rgb[i][j]; + } + } +} + +ColourConversion::ColourConversion (string n, float i, bool il, float const m[3][3], float o) + : name (n) + , input_gamma (i) + , input_gamma_linearised (il) + , matrix (3, 3) + , output_gamma (o) +{ + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + matrix (i, j) = m[i][j]; + } + } +} + +ColourConversion::ColourConversion (shared_ptr node) + : matrix (3, 3) +{ + name = node->string_child ("Name"); + input_gamma = node->number_child ("InputGamma"); + input_gamma_linearised = node->bool_child ("InputGammaLinearised"); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + matrix (i, j) = 0; + } + } + + list > m = node->node_children ("Matrix"); + for (list >::iterator i = m.begin(); i != m.end(); ++i) { + int const ti = (*i)->number_attribute ("i"); + int const tj = (*i)->number_attribute ("j"); + matrix(ti, tj) = lexical_cast ((*i)->content ()); + } + + output_gamma = node->number_child ("OutputGamma"); +} + +void +ColourConversion::as_xml (xmlpp::Node* node) const +{ + node->add_child("Name")->add_child_text (name); + node->add_child("InputGamma")->add_child_text (lexical_cast (input_gamma)); + node->add_child("InputGammaLinearised")->add_child_text (input_gamma_linearised ? "1" : "0"); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + xmlpp::Element* m = node->add_child("Matrix"); + m->set_attribute ("i", lexical_cast (i)); + m->set_attribute ("j", lexical_cast (j)); + m->add_child_text (lexical_cast (matrix (i, j))); + } + } + + node->add_child("OutputGamma")->add_child_text (lexical_cast (output_gamma)); +} diff --git a/src/lib/colour_conversion.h b/src/lib/colour_conversion.h new file mode 100644 index 000000000..ed89f8c63 --- /dev/null +++ b/src/lib/colour_conversion.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +namespace cxml { + class Node; +} + +namespace xmlpp { + class Node; +} + +class ColourConversion : public boost::noncopyable +{ +public: + ColourConversion (); + ColourConversion (std::string, float, bool, float const matrix[3][3], float); + ColourConversion (boost::shared_ptr); + + void as_xml (xmlpp::Node *) const; + + std::string name; + float input_gamma; + bool input_gamma_linearised; + boost::numeric::ublas::matrix matrix; + float output_gamma; +}; diff --git a/src/lib/config.cc b/src/lib/config.cc index 29b63b5c8..8f4e5aed0 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "config.h" #include "server.h" @@ -30,6 +31,7 @@ #include "ratio.h" #include "dcp_content_type.h" #include "sound_processor.h" +#include "colour_conversion.h" #include "i18n.h" @@ -62,6 +64,8 @@ Config::Config () _allowed_dcp_frame_rates.push_back (48); _allowed_dcp_frame_rates.push_back (50); _allowed_dcp_frame_rates.push_back (60); + + _colour_conversions.push_back (shared_ptr (new ColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::xyz_to_rgb, 2.6))); } void @@ -81,7 +85,7 @@ Config::read () list > servers = f.node_children ("Server"); for (list >::iterator i = servers.begin(); i != servers.end(); ++i) { - _servers.push_back (new ServerDescription (*i)); + _servers.push_back (shared_ptr (new ServerDescription (*i))); } _tms_ip = f.string_child ("TMSIP"); @@ -111,7 +115,17 @@ Config::read () _default_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata")); _default_still_length = f.optional_number_child("DefaultStillLength").get_value_or (10); - _default_j2k_bandwidth = f.optional_number_child("DefaultJ2KBandwidth").get_value_or (200000000); + _default_j2k_bandwidth = f.optional_number_child("DefaultJ2KBandwidth").get_value_or (200000000); + + list > cc = f.node_children ("ColourConversion"); + + if (!cc.empty ()) { + _colour_conversions.clear (); + } + + for (list >::iterator i = cc.begin(); i != cc.end(); ++i) { + _colour_conversions.push_back (shared_ptr (new ColourConversion (*i))); + } } void @@ -217,7 +231,7 @@ Config::write () const root->add_child("DefaultDirectory")->add_child_text (_default_directory); root->add_child("ServerPort")->add_child_text (lexical_cast (_server_port)); - for (vector::const_iterator i = _servers.begin(); i != _servers.end(); ++i) { + for (vector >::const_iterator i = _servers.begin(); i != _servers.end(); ++i) { (*i)->as_xml (root->add_child ("Server")); } @@ -245,6 +259,10 @@ Config::write () const root->add_child("DefaultStillLength")->add_child_text (lexical_cast (_default_still_length)); root->add_child("DefaultJ2KBandwidth")->add_child_text (lexical_cast (_default_j2k_bandwidth)); + for (vector >::const_iterator i = _colour_conversions.begin(); i != _colour_conversions.end(); ++i) { + (*i)->as_xml (root->add_child ("ColourConversion")); + } + doc.write_to_file_formatted (file (false)); } diff --git a/src/lib/config.h b/src/lib/config.h index 77287e686..40d06a172 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -36,6 +36,7 @@ class Filter; class SoundProcessor; class DCPContentType; class Ratio; +class ColourConversion; /** @class Config * @brief A singleton class holding configuration. @@ -61,7 +62,7 @@ public: } /** @return J2K encoding servers to use */ - std::vector servers () const { + std::vector > servers () const { return _servers; } @@ -122,6 +123,10 @@ public: return _default_j2k_bandwidth; } + std::vector > colour_conversions () const { + return _colour_conversions; + } + /** @param n New number of local encoding threads */ void set_num_local_encoding_threads (int n) { _num_local_encoding_threads = n; @@ -137,7 +142,7 @@ public: } /** @param s New list of servers */ - void set_servers (std::vector s) { + void set_servers (std::vector > s) { _servers = s; } @@ -204,6 +209,10 @@ public: void set_default_j2k_bandwidth (int b) { _default_j2k_bandwidth = b; } + + void set_colour_conversions (std::vector > const & c) { + _colour_conversions = c; + } void write () const; @@ -224,7 +233,7 @@ private: int _server_port; /** J2K encoding servers to use */ - std::vector _servers; + std::vector > _servers; /** Scaler to use for the "A" part of A/B comparisons */ Scaler const * _reference_scaler; /** Filters to use for the "A" part of A/B comparisons */ @@ -248,6 +257,7 @@ private: DCPContentType const * _default_dcp_content_type; libdcp::XMLMetadata _dcp_metadata; int _default_j2k_bandwidth; + std::vector > _colour_conversions; /** Singleton instance, or 0 */ static Config* _instance; diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index 4f6ff9987..53cf1753a 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -213,7 +213,7 @@ DCPVideoFrame::encode_locally () * @return Encoded data. */ shared_ptr -DCPVideoFrame::encode_remotely (ServerDescription const * serv) +DCPVideoFrame::encode_remotely (boost::shared_ptr serv) { boost::asio::io_service io_service; boost::asio::ip::tcp::resolver resolver (io_service); diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index 96a773a6f..18c8fe628 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -105,7 +105,7 @@ public: DCPVideoFrame (boost::shared_ptr, int, Eyes, int, int, boost::shared_ptr); boost::shared_ptr encode_locally (); - boost::shared_ptr encode_remotely (ServerDescription const *); + boost::shared_ptr encode_remotely (boost::shared_ptr); Eyes eyes () const { return _eyes; diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 0c7434220..a1c024799 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -71,12 +71,12 @@ void Encoder::process_begin () { for (int i = 0; i < Config::instance()->num_local_encoding_threads (); ++i) { - _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, (ServerDescription *) 0))); + _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, shared_ptr ()))); } - vector servers = Config::instance()->servers (); + vector > servers = Config::instance()->servers (); - for (vector::iterator i = servers.begin(); i != servers.end(); ++i) { + for (vector >::iterator i = servers.begin(); i != servers.end(); ++i) { for (int j = 0; j < (*i)->threads (); ++j) { _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, *i))); } @@ -244,7 +244,7 @@ Encoder::terminate_threads () } void -Encoder::encoder_thread (ServerDescription* server) +Encoder::encoder_thread (shared_ptr server) { /* Number of seconds that we currently wait between attempts to connect to the server; not relevant for localhost diff --git a/src/lib/encoder.h b/src/lib/encoder.h index a866a77f1..a4df202a2 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -81,7 +81,7 @@ private: void frame_done (); - void encoder_thread (ServerDescription *); + void encoder_thread (boost::shared_ptr); void terminate_threads (); /** Film that we are encoding */ diff --git a/src/lib/server.cc b/src/lib/server.cc index 37a076a54..54cffc077 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -68,17 +68,17 @@ ServerDescription::as_xml (xmlpp::Node* root) const * @param v Metadata. * @return ServerDescription, or 0. */ -ServerDescription * +shared_ptr ServerDescription::create_from_metadata (string v) { vector b; split (b, v, is_any_of (N_(" "))); if (b.size() != 2) { - return 0; + return shared_ptr (); } - return new ServerDescription (b[0], atoi (b[1].c_str ())); + return shared_ptr (new ServerDescription (b[0], atoi (b[1].c_str ()))); } Server::Server (shared_ptr log) diff --git a/src/lib/server.h b/src/lib/server.h index e6d374369..6307c1867 100644 --- a/src/lib/server.h +++ b/src/lib/server.h @@ -41,6 +41,11 @@ namespace cxml { class ServerDescription { public: + ServerDescription () + : _host_name ("") + , _threads (1) + {} + /** @param h Server host name or IP address in string form. * @param t Number of threads to use on the server. */ @@ -73,7 +78,7 @@ public: void as_xml (xmlpp::Node *) const; - static ServerDescription * create_from_metadata (std::string v); + static boost::shared_ptr create_from_metadata (std::string v); private: /** server's host name */ diff --git a/src/lib/wscript b/src/lib/wscript index 04dae7587..6c45d8b1e 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -8,6 +8,7 @@ sources = """ audio_content.cc audio_decoder.cc audio_mapping.cc + colour_conversion.cc config.cc content.cc content_factory.cc diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc index 7c5852fcc..ff7ab6ffe 100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@ -116,7 +116,7 @@ main (int argc, char* argv[]) dcpomatic_setup (); if (no_remote) { - Config::instance()->set_servers (vector ()); + Config::instance()->set_servers (vector > ()); } cout << "DCP-o-matic " << dcpomatic_version << " git " << dcpomatic_git_commit; diff --git a/src/wx/colour_conversion_dialog.cc b/src/wx/colour_conversion_dialog.cc new file mode 100644 index 000000000..976d295d5 --- /dev/null +++ b/src/wx/colour_conversion_dialog.cc @@ -0,0 +1,150 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include "lib/colour_conversion.h" +#include "wx_util.h" +#include "colour_conversion_dialog.h" + +using std::string; +using std::cout; +using boost::shared_ptr; +using boost::lexical_cast; + +ColourConversionDialog::ColourConversionDialog (wxWindow* parent, shared_ptr conversion) + : wxDialog (parent, wxID_ANY, _("Colour conversion")) + , _conversion (conversion) +{ + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + SetSizer (overall_sizer); + + wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + overall_sizer->Add (table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + int r = 0; + + add_label_to_grid_bag_sizer (table, this, _("Name"), true, wxGBPosition (r, 0)); + _name = new wxTextCtrl (this, wxID_ANY, wxT ("")); + table->Add (_name, wxGBPosition (r, 1)); + ++r; + + add_label_to_grid_bag_sizer (table, this, _("Input gamma"), true, wxGBPosition (r, 0)); + _input_gamma = new wxSpinCtrlDouble (this); + table->Add (_input_gamma, wxGBPosition (r, 1)); + ++r; + + _input_gamma_linearised = new wxCheckBox (this, wxID_ANY, _("Linearise input gamma curve for low values")); + table->Add (_input_gamma_linearised, wxGBPosition (r, 0), wxGBSpan (1, 2)); + ++r; + + wxClientDC dc (parent); + wxSize size = dc.GetTextExtent (wxT ("0.123456789012")); + size.SetHeight (-1); + + wxTextValidator validator (wxFILTER_INCLUDE_CHAR_LIST); + wxArrayString list; + + wxString n (wxT ("0123456789.-")); + for (size_t i = 0; i < n.Length(); ++i) { + list.Add (n[i]); + } + + validator.SetIncludes (list); + + add_label_to_grid_bag_sizer (table, this, _("Matrix"), true, wxGBPosition (r, 0)); + wxFlexGridSizer* matrix_sizer = new wxFlexGridSizer (3, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + for (int x = 0; x < 3; ++x) { + for (int y = 0; y < 3; ++y) { + _matrix[x][y] = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, size, 0, validator); + matrix_sizer->Add (_matrix[x][y]); + } + } + table->Add (matrix_sizer, wxGBPosition (r, 1)); + ++r; + + add_label_to_grid_bag_sizer (table, this, _("Output gamma"), true, wxGBPosition (r, 0)); + wxBoxSizer* output_sizer = new wxBoxSizer (wxHORIZONTAL); + /* TRANSLATORS: this means the mathematical reciprocal operation, i.e. we are dividing 1 by the control that + comes after it. + */ + add_label_to_sizer (output_sizer, this, _("1 / "), false); + _output_gamma = new wxSpinCtrlDouble (this); + output_sizer->Add (_output_gamma); + table->Add (output_sizer, wxGBPosition (r, 1)); + ++r; + + wxSizer* buttons = CreateSeparatedButtonSizer (wxOK); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + SetSizer (overall_sizer); + overall_sizer->Layout (); + overall_sizer->SetSizeHints (this); + + _input_gamma->SetRange(0.1, 4.0); + _input_gamma->SetDigits (1); + _input_gamma->SetIncrement (0.1); + _output_gamma->SetRange(0.1, 4.0); + _output_gamma->SetDigits (1); + _output_gamma->SetIncrement (0.1); + + _name->SetValue (std_to_wx (conversion->name)); + _input_gamma->SetValue (conversion->input_gamma); + _input_gamma_linearised->SetValue (conversion->input_gamma_linearised); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + _matrix[i][j]->SetValue (std_to_wx (lexical_cast (conversion->matrix(i, j)))); + } + } + _output_gamma->SetValue (conversion->output_gamma); + + _name->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ColourConversionDialog::changed, this)); + _input_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionDialog::changed, this)); + _input_gamma_linearised->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ColourConversionDialog::changed, this)); + _output_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionDialog::changed, this)); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + _matrix[i][j]->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ColourConversionDialog::changed, this)); + } + } +} + +void +ColourConversionDialog::changed () +{ + _conversion->name = wx_to_std (_name->GetValue ()); + _conversion->input_gamma = _input_gamma->GetValue (); + _conversion->input_gamma_linearised = _input_gamma_linearised->GetValue (); + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + string const v = wx_to_std (_matrix[i][j]->GetValue ()); + if (v.empty ()) { + _conversion->matrix (i, j) = 0; + } else { + _conversion->matrix (i, j) = lexical_cast (v); + } + } + } + _conversion->output_gamma = _output_gamma->GetValue (); +} + diff --git a/src/wx/colour_conversion_dialog.h b/src/wx/colour_conversion_dialog.h new file mode 100644 index 000000000..8da6df100 --- /dev/null +++ b/src/wx/colour_conversion_dialog.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +class wxSpinCtrlDouble; +class ColourConversion; + +class ColourConversionDialog : public wxDialog +{ +public: + ColourConversionDialog (wxWindow *, boost::shared_ptr); + +private: + void changed (); + + boost::shared_ptr _conversion; + wxTextCtrl* _name; + wxSpinCtrlDouble* _input_gamma; + wxCheckBox* _input_gamma_linearised; + wxTextCtrl* _matrix[3][3]; + wxSpinCtrlDouble* _output_gamma; +}; diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index cbcae78f8..66f0cd186 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -26,21 +26,28 @@ #include #include #include +#include #include "lib/config.h" #include "lib/server.h" #include "lib/ratio.h" #include "lib/scaler.h" #include "lib/filter.h" #include "lib/dcp_content_type.h" +#include "lib/colour_conversion.h" #include "config_dialog.h" #include "wx_util.h" #include "filter_dialog.h" #include "server_dialog.h" #include "dir_picker_ctrl.h" #include "dci_metadata_dialog.h" +#include "colour_conversion_dialog.h" -using namespace std; +using std::vector; +using std::string; +using std::list; using boost::bind; +using boost::shared_ptr; +using boost::lexical_cast; ConfigDialog::ConfigDialog (wxWindow* parent) : wxDialog (parent, wxID_ANY, _("DCP-o-matic Preferences"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) @@ -53,6 +60,8 @@ ConfigDialog::ConfigDialog (wxWindow* parent) _notebook->AddPage (_misc_panel, _("Miscellaneous"), true); make_servers_panel (); _notebook->AddPage (_servers_panel, _("Encoding servers"), false); + make_colour_conversions_panel (); + _notebook->AddPage (_colour_conversions_panel, _("Colour conversions"), false); make_metadata_panel (); _notebook->AddPage (_metadata_panel, _("Metadata"), false); make_tms_panel (); @@ -270,54 +279,32 @@ ConfigDialog::make_metadata_panel () _creator->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ConfigDialog::creator_changed, this)); } -void -ConfigDialog::make_servers_panel () +static std::string +server_column (shared_ptr s, int c) { - _servers_panel = new wxPanel (_notebook); - wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); - _servers_panel->SetSizer (s); - - wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); - table->AddGrowableCol (0, 1); - s->Add (table, 1, wxALL | wxEXPAND, 8); - - Config* config = Config::instance (); - - _servers = new wxListCtrl (_servers_panel, wxID_ANY, wxDefaultPosition, wxSize (220, 100), wxLC_REPORT | wxLC_SINGLE_SEL); - wxListItem ip; - ip.SetId (0); - ip.SetText (_("IP address")); - ip.SetWidth (120); - _servers->InsertColumn (0, ip); - ip.SetId (1); - ip.SetText (_("Threads")); - ip.SetWidth (80); - _servers->InsertColumn (1, ip); - table->Add (_servers, 1, wxEXPAND | wxALL); - - { - wxSizer* s = new wxBoxSizer (wxVERTICAL); - _add_server = new wxButton (_servers_panel, wxID_ANY, _("Add")); - s->Add (_add_server, 0, wxTOP | wxBOTTOM, 2); - _edit_server = new wxButton (_servers_panel, wxID_ANY, _("Edit")); - s->Add (_edit_server, 0, wxTOP | wxBOTTOM, 2); - _remove_server = new wxButton (_servers_panel, wxID_ANY, _("Remove")); - s->Add (_remove_server, 0, wxTOP | wxBOTTOM, 2); - table->Add (s, 0); + switch (c) { + case 0: + return s->host_name (); + case 1: + return lexical_cast (s->threads ()); } - vector servers = config->servers (); - for (vector::iterator i = servers.begin(); i != servers.end(); ++i) { - add_server_to_control (*i); - } - - _add_server->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ConfigDialog::add_server_clicked, this)); - _edit_server->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ConfigDialog::edit_server_clicked, this)); - _remove_server->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ConfigDialog::remove_server_clicked, this)); + return ""; +} - _servers->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&ConfigDialog::server_selection_changed, this)); - _servers->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&ConfigDialog::server_selection_changed, this)); - server_selection_changed (); +void +ConfigDialog::make_servers_panel () +{ + vector columns; + columns.push_back (wx_to_std (_("IP address"))); + columns.push_back (wx_to_std (_("Threads"))); + _servers_panel = new EditableList ( + _notebook, + columns, + boost::bind (&Config::servers, Config::instance()), + boost::bind (&Config::set_servers, Config::instance(), _1), + boost::bind (&server_column, _1, _2) + ); } void @@ -378,76 +365,6 @@ ConfigDialog::default_directory_changed () Config::instance()->set_default_directory (wx_to_std (_default_directory->GetPath ())); } -void -ConfigDialog::add_server_to_control (ServerDescription* s) -{ - wxListItem item; - int const n = _servers->GetItemCount (); - item.SetId (n); - _servers->InsertItem (item); - _servers->SetItem (n, 0, std_to_wx (s->host_name ())); - _servers->SetItem (n, 1, std_to_wx (boost::lexical_cast (s->threads ()))); -} - -void -ConfigDialog::add_server_clicked () -{ - ServerDialog* d = new ServerDialog (this, 0); - d->ShowModal (); - ServerDescription* s = d->server (); - d->Destroy (); - - add_server_to_control (s); - vector o = Config::instance()->servers (); - o.push_back (s); - Config::instance()->set_servers (o); -} - -void -ConfigDialog::edit_server_clicked () -{ - int i = _servers->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (i == -1) { - return; - } - - wxListItem item; - item.SetId (i); - item.SetColumn (0); - _servers->GetItem (item); - - vector servers = Config::instance()->servers (); - assert (i >= 0 && i < int (servers.size ())); - - ServerDialog* d = new ServerDialog (this, servers[i]); - d->ShowModal (); - d->Destroy (); - - _servers->SetItem (i, 0, std_to_wx (servers[i]->host_name ())); - _servers->SetItem (i, 1, std_to_wx (boost::lexical_cast (servers[i]->threads ()))); -} - -void -ConfigDialog::remove_server_clicked () -{ - int i = _servers->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (i >= 0) { - _servers->DeleteItem (i); - } - - vector o = Config::instance()->servers (); - o.erase (o.begin() + i); - Config::instance()->set_servers (o); -} - -void -ConfigDialog::server_selection_changed () -{ - int const i = _servers->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - _edit_server->Enable (i >= 0); - _remove_server->Enable (i >= 0); -} - void ConfigDialog::edit_default_dci_metadata_clicked () { @@ -515,3 +432,23 @@ ConfigDialog::default_j2k_bandwidth_changed () { Config::instance()->set_default_j2k_bandwidth (_default_j2k_bandwidth->GetValue() * 1e6); } + +static std::string +colour_conversion_column (shared_ptr c) +{ + return c->name; +} + +void +ConfigDialog::make_colour_conversions_panel () +{ + vector columns; + columns.push_back (wx_to_std (_("Name"))); + _colour_conversions_panel = new EditableList ( + _notebook, + columns, + boost::bind (&Config::colour_conversions, Config::instance()), + boost::bind (&Config::set_colour_conversions, Config::instance(), _1), + boost::bind (&colour_conversion_column, _1) + ); +} diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h index 444b886f4..60d9229c4 100644 --- a/src/wx/config_dialog.h +++ b/src/wx/config_dialog.h @@ -26,11 +26,14 @@ #include #include #include "wx_util.h" +#include "editable_list.h" class DirPickerCtrl; class wxNotebook; - class ServerDescription; +class ColourConversion; +class ColourConversionDialog; +class ServerDialog; /** @class ConfigDialog * @brief A dialogue to edit DCP-o-matic configuration. @@ -51,28 +54,25 @@ private: void default_still_length_changed (); void default_directory_changed (); void edit_default_dci_metadata_clicked (); - void add_server_clicked (); - void edit_server_clicked (); - void remove_server_clicked (); - void server_selection_changed (); void default_container_changed (); void default_dcp_content_type_changed (); void issuer_changed (); void creator_changed (); void default_j2k_bandwidth_changed (); - void add_server_to_control (ServerDescription *); void setup_language_sensitivity (); void make_misc_panel (); void make_tms_panel (); void make_metadata_panel (); void make_servers_panel (); + void make_colour_conversions_panel (); wxNotebook* _notebook; wxPanel* _misc_panel; wxPanel* _tms_panel; - wxPanel* _servers_panel; + EditableList* _colour_conversions_panel; + EditableList* _servers_panel; wxPanel* _metadata_panel; wxCheckBox* _set_language; wxChoice* _language; @@ -90,10 +90,6 @@ private: wxDirPickerCtrl* _default_directory; #endif wxButton* _default_dci_metadata_button; - wxListCtrl* _servers; - wxButton* _add_server; - wxButton* _edit_server; - wxButton* _remove_server; wxTextCtrl* _issuer; wxTextCtrl* _creator; wxSpinCtrl* _default_j2k_bandwidth; diff --git a/src/wx/editable_list.h b/src/wx/editable_list.h new file mode 100644 index 000000000..32cc326b6 --- /dev/null +++ b/src/wx/editable_list.h @@ -0,0 +1,156 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +template +class EditableList : public wxPanel +{ +public: + EditableList ( + wxWindow* parent, + std::vector columns, + boost::function > ()> get, + boost::function >)> set, + boost::function, int)> column + ) + : wxPanel (parent) + , _get (get) + , _set (set) + , _columns (columns.size ()) + , _column (column) + { + wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); + SetSizer (s); + + wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + table->AddGrowableCol (0, 1); + s->Add (table, 1, wxALL | wxEXPAND, 8); + + _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * 200, 100), wxLC_REPORT | wxLC_SINGLE_SEL); + + for (size_t i = 0; i < columns.size(); ++i) { + wxListItem ip; + ip.SetId (i); + ip.SetText (std_to_wx (columns[i])); + ip.SetWidth (200); + _list->InsertColumn (i, ip); + } + + table->Add (_list, 1, wxEXPAND | wxALL); + + { + wxSizer* s = new wxBoxSizer (wxVERTICAL); + _add = new wxButton (this, wxID_ANY, _("Add...")); + s->Add (_add, 0, wxTOP | wxBOTTOM, 2); + _edit = new wxButton (this, wxID_ANY, _("Edit...")); + s->Add (_edit, 0, wxTOP | wxBOTTOM, 2); + _remove = new wxButton (this, wxID_ANY, _("Remove")); + s->Add (_remove, 0, wxTOP | wxBOTTOM, 2); + table->Add (s, 0); + } + + std::vector > current = _get (); + for (typename std::vector >::iterator i = current.begin (); i != current.end(); ++i) { + add_to_control (*i); + } + + _add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::add_clicked, this)); + _edit->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::edit_clicked, this)); + _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&EditableList::remove_clicked, this)); + + _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&EditableList::selection_changed, this)); + _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&EditableList::selection_changed, this)); + selection_changed (); + } + + void add_to_control (boost::shared_ptr item) + { + wxListItem list_item; + int const n = _list->GetItemCount (); + list_item.SetId (n); + _list->InsertItem (list_item); + + for (int i = 0; i < _columns; ++i) { + _list->SetItem (n, i, std_to_wx (_column (item, i))); + } + } + + void selection_changed () + { + int const i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + _edit->Enable (i >= 0); + _remove->Enable (i >= 0); + } + + void add_clicked () + { + boost::shared_ptr new_item (new T); + S* dialog = new S (this, new_item); + dialog->ShowModal (); + dialog->Destroy (); + + add_to_control (new_item); + std::vector > all = _get (); + all.push_back (new_item); + _set (all); + } + + void edit_clicked () + { + int item = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) { + return; + } + + std::vector > all = _get (); + assert (item >= 0 && item < int (all.size ())); + + S* dialog = new S (this, all[item]); + dialog->ShowModal (); + dialog->Destroy (); + + for (int i = 0; i < _columns; ++i) { + _list->SetItem (item, i, std_to_wx (_column (all[item], i))); + } + } + + void remove_clicked () + { + int i = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (i >= 0) { + _list->DeleteItem (i); + } + + std::vector > all = _get (); + all.erase (all.begin() + i); + _set (all); + } + +private: + boost::function > ()> _get; + boost::function >)> _set; + int _columns; + boost::function, int)> _column; + + wxButton* _add; + wxButton* _edit; + wxButton* _remove; + wxListCtrl* _list; +}; diff --git a/src/wx/server_dialog.cc b/src/wx/server_dialog.cc index 0f41b5b90..a0f1f04ae 100644 --- a/src/wx/server_dialog.cc +++ b/src/wx/server_dialog.cc @@ -21,13 +21,15 @@ #include "server_dialog.h" #include "wx_util.h" -ServerDialog::ServerDialog (wxWindow* parent, ServerDescription* server) +using boost::shared_ptr; + +ServerDialog::ServerDialog (wxWindow* parent, shared_ptr server) : wxDialog (parent, wxID_ANY, _("Server")) { if (server) { _server = server; } else { - _server = new ServerDescription (wx_to_std (N_("localhost")), 1); + _server.reset (new ServerDescription (wx_to_std (N_("localhost")), 1)); } wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); @@ -73,7 +75,7 @@ ServerDialog::threads_changed () _server->set_threads (_threads->GetValue ()); } -ServerDescription * +shared_ptr ServerDialog::server () const { return _server; diff --git a/src/wx/server_dialog.h b/src/wx/server_dialog.h index f42236771..3f1fa1f73 100644 --- a/src/wx/server_dialog.h +++ b/src/wx/server_dialog.h @@ -25,15 +25,15 @@ class ServerDescription; class ServerDialog : public wxDialog { public: - ServerDialog (wxWindow *, ServerDescription *); + ServerDialog (wxWindow *, boost::shared_ptr); - ServerDescription* server () const; + boost::shared_ptr server () const; private: void host_changed (); void threads_changed (); - ServerDescription* _server; + boost::shared_ptr _server; wxTextCtrl* _host; wxSpinCtrl* _threads; }; diff --git a/src/wx/video_panel.cc b/src/wx/video_panel.cc index 0c2702913..5ba06b12d 100644 --- a/src/wx/video_panel.cc +++ b/src/wx/video_panel.cc @@ -21,6 +21,8 @@ #include "lib/ratio.h" #include "lib/filter.h" #include "lib/ffmpeg_content.h" +#include "lib/colour_conversion.h" +#include "lib/config.h" #include "filter_dialog.h" #include "video_panel.h" #include "wx_util.h" @@ -30,6 +32,7 @@ using std::vector; using std::string; using std::pair; using std::cout; +using std::list; using boost::shared_ptr; using boost::dynamic_pointer_cast; using boost::bind; @@ -83,6 +86,11 @@ VideoPanel::VideoPanel (FilmEditor* e) } ++r; + add_label_to_grid_bag_sizer (grid, this, _("Colour conversion"), true, wxGBPosition (r, 0)); + _colour_conversion = new wxChoice (this, wxID_ANY); + grid->Add (_colour_conversion, wxGBPosition (r, 1)); + ++r; + _description = new wxStaticText (this, wxID_ANY, wxT ("\n \n \n \n \n"), wxDefaultPosition, wxDefaultSize); grid->Add (_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6); wxFont font = _description->GetFont(); @@ -105,13 +113,16 @@ VideoPanel::VideoPanel (FilmEditor* e) _frame_type->Append (_("2D")); _frame_type->Append (_("3D left/right")); - _frame_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&VideoPanel::frame_type_changed, this)); - _left_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::left_crop_changed, this)); - _right_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::right_crop_changed, this)); - _top_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::top_crop_changed, this)); - _bottom_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::bottom_crop_changed, this)); - _ratio->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&VideoPanel::ratio_changed, this)); - _filters_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_filters_clicked, this)); + setup_colour_conversions (); + + _frame_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&VideoPanel::frame_type_changed, this)); + _left_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::left_crop_changed, this)); + _right_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::right_crop_changed, this)); + _top_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::top_crop_changed, this)); + _bottom_crop->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::bottom_crop_changed, this)); + _ratio->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&VideoPanel::ratio_changed, this)); + _filters_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_filters_clicked, this)); + _colour_conversion->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&VideoPanel::colour_conversion_changed, this)); } @@ -342,3 +353,18 @@ VideoPanel::frame_type_changed () vc->set_video_frame_type (static_cast (_frame_type->GetSelection ())); } } + +void +VideoPanel::setup_colour_conversions () +{ + vector > cc = Config::instance()->colour_conversions (); + for (vector >::iterator i = cc.begin(); i != cc.end(); ++i) { + _colour_conversion->Append (std_to_wx ((*i)->name)); + } +} + +void +VideoPanel::colour_conversion_changed () +{ + +} diff --git a/src/wx/video_panel.h b/src/wx/video_panel.h index 5a2281150..62332a052 100644 --- a/src/wx/video_panel.h +++ b/src/wx/video_panel.h @@ -41,8 +41,10 @@ private: void edit_filters_clicked (); void ratio_changed (); void frame_type_changed (); + void colour_conversion_changed (); void setup_description (); + void setup_colour_conversions (); wxChoice* _frame_type; wxSpinCtrl* _left_crop; @@ -54,4 +56,5 @@ private: wxStaticText* _description; wxStaticText* _filters; wxButton* _filters_button; + wxChoice* _colour_conversion; }; diff --git a/src/wx/wscript b/src/wx/wscript index 883304eff..e0566a6ed 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -9,6 +9,7 @@ sources = """ audio_mapping_view.cc audio_panel.cc audio_plot.cc + colour_conversion_dialog.cc config_dialog.cc content_menu.cc dci_metadata_dialog.cc diff --git a/test/client_server_test.cc b/test/client_server_test.cc index 2dc1545d6..d695f96ce 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -29,7 +29,7 @@ using boost::shared_ptr; using boost::thread; void -do_remote_encode (shared_ptr frame, ServerDescription* description, shared_ptr locally_encoded) +do_remote_encode (shared_ptr frame, shared_ptr description, shared_ptr locally_encoded) { shared_ptr remotely_encoded; BOOST_CHECK_NO_THROW (remotely_encoded = frame->encode_remotely (description)); @@ -92,11 +92,11 @@ BOOST_AUTO_TEST_CASE (client_server_test) /* Let the server get itself ready */ dcpomatic_sleep (1); - ServerDescription description ("localhost", 2); + shared_ptr description (new ServerDescription ("localhost", 2)); list threads; for (int i = 0; i < 8; ++i) { - threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded))); + threads.push_back (new thread (boost::bind (do_remote_encode, frame, description, locally_encoded))); } for (list::iterator i = threads.begin(); i != threads.end(); ++i) { diff --git a/test/test.cc b/test/test.cc index 2334523a1..21cf18c76 100644 --- a/test/test.cc +++ b/test/test.cc @@ -46,7 +46,7 @@ struct TestConfig dcpomatic_setup(); Config::instance()->set_num_local_encoding_threads (1); - Config::instance()->set_servers (vector ()); + Config::instance()->set_servers (vector > ()); Config::instance()->set_server_port (61920); Config::instance()->set_default_dci_metadata (DCIMetadata ()); Config::instance()->set_default_container (static_cast (0)); -- 2.30.2