Very basics of colour conversion configuration.
[dcpomatic.git] / src / wx / video_panel.cc
1 /*
2     Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <wx/spinctrl.h>
21 #include "lib/ratio.h"
22 #include "lib/filter.h"
23 #include "lib/ffmpeg_content.h"
24 #include "lib/colour_conversion.h"
25 #include "lib/config.h"
26 #include "filter_dialog.h"
27 #include "video_panel.h"
28 #include "wx_util.h"
29 #include "film_editor.h"
30
31 using std::vector;
32 using std::string;
33 using std::pair;
34 using std::cout;
35 using std::list;
36 using boost::shared_ptr;
37 using boost::dynamic_pointer_cast;
38 using boost::bind;
39
40 VideoPanel::VideoPanel (FilmEditor* e)
41         : FilmEditorPanel (e, _("Video"))
42 {
43         wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
44         _sizer->Add (grid, 0, wxALL, 8);
45
46         int r = 0;
47
48         add_label_to_grid_bag_sizer (grid, this, _("Type"), true, wxGBPosition (r, 0));
49         _frame_type = new wxChoice (this, wxID_ANY);
50         grid->Add (_frame_type, wxGBPosition (r, 1));
51         ++r;
52         
53         add_label_to_grid_bag_sizer (grid, this, _("Left crop"), true, wxGBPosition (r, 0));
54         _left_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
55         grid->Add (_left_crop, wxGBPosition (r, 1));
56         ++r;
57
58         add_label_to_grid_bag_sizer (grid, this, _("Right crop"), true, wxGBPosition (r, 0));
59         _right_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
60         grid->Add (_right_crop, wxGBPosition (r, 1));
61         ++r;
62         
63         add_label_to_grid_bag_sizer (grid, this, _("Top crop"), true, wxGBPosition (r, 0));
64         _top_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
65         grid->Add (_top_crop, wxGBPosition (r, 1));
66         ++r;
67         
68         add_label_to_grid_bag_sizer (grid, this, _("Bottom crop"), true, wxGBPosition (r, 0));
69         _bottom_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
70         grid->Add (_bottom_crop, wxGBPosition (r, 1));
71         ++r;
72
73         add_label_to_grid_bag_sizer (grid, this, _("Scale to"), true, wxGBPosition (r, 0));
74         _ratio = new wxChoice (this, wxID_ANY);
75         grid->Add (_ratio, wxGBPosition (r, 1));
76         ++r;
77
78         {
79                 add_label_to_grid_bag_sizer (grid, this, _("Filters"), true, wxGBPosition (r, 0));
80                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
81                 _filters = new wxStaticText (this, wxID_ANY, _("None"));
82                 s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
83                 _filters_button = new wxButton (this, wxID_ANY, _("Edit..."));
84                 s->Add (_filters_button, 0, wxALIGN_CENTER_VERTICAL);
85                 grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
86         }
87         ++r;
88
89         add_label_to_grid_bag_sizer (grid, this, _("Colour conversion"), true, wxGBPosition (r, 0));
90         _colour_conversion = new wxChoice (this, wxID_ANY);
91         grid->Add (_colour_conversion, wxGBPosition (r, 1));
92         ++r;
93
94         _description = new wxStaticText (this, wxID_ANY, wxT ("\n \n \n \n \n"), wxDefaultPosition, wxDefaultSize);
95         grid->Add (_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
96         wxFont font = _description->GetFont();
97         font.SetStyle(wxFONTSTYLE_ITALIC);
98         font.SetPointSize(font.GetPointSize() - 1);
99         _description->SetFont(font);
100         ++r;
101
102         _left_crop->SetRange (0, 1024);
103         _top_crop->SetRange (0, 1024);
104         _right_crop->SetRange (0, 1024);
105         _bottom_crop->SetRange (0, 1024);
106
107         vector<Ratio const *> ratios = Ratio::all ();
108         _ratio->Clear ();
109         for (vector<Ratio const *>::iterator i = ratios.begin(); i != ratios.end(); ++i) {
110                 _ratio->Append (std_to_wx ((*i)->nickname ()));
111         }
112
113         _frame_type->Append (_("2D"));
114         _frame_type->Append (_("3D left/right"));
115
116         setup_colour_conversions ();
117
118         _frame_type->Bind        (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&VideoPanel::frame_type_changed, this));
119         _left_crop->Bind         (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::left_crop_changed, this));
120         _right_crop->Bind        (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::right_crop_changed, this));
121         _top_crop->Bind          (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::top_crop_changed, this));
122         _bottom_crop->Bind       (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&VideoPanel::bottom_crop_changed, this));
123         _ratio->Bind             (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&VideoPanel::ratio_changed, this));
124         _filters_button->Bind    (wxEVT_COMMAND_BUTTON_CLICKED,   boost::bind (&VideoPanel::edit_filters_clicked, this));
125         _colour_conversion->Bind (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&VideoPanel::colour_conversion_changed, this));
126 }
127
128
129 /** Called when the left crop widget has been changed */
130 void
131 VideoPanel::left_crop_changed ()
132 {
133         shared_ptr<VideoContent> c = _editor->selected_video_content ();
134         if (!c) {
135                 return;
136         }
137
138         c->set_left_crop (_left_crop->GetValue ());
139 }
140
141 /** Called when the right crop widget has been changed */
142 void
143 VideoPanel::right_crop_changed ()
144 {
145         shared_ptr<VideoContent> c = _editor->selected_video_content ();
146         if (!c) {
147                 return;
148         }
149
150         c->set_right_crop (_right_crop->GetValue ());
151 }
152
153 /** Called when the top crop widget has been changed */
154 void
155 VideoPanel::top_crop_changed ()
156 {
157         shared_ptr<VideoContent> c = _editor->selected_video_content ();
158         if (!c) {
159                 return;
160         }
161
162         c->set_top_crop (_top_crop->GetValue ());
163 }
164
165 /** Called when the bottom crop value has been changed */
166 void
167 VideoPanel::bottom_crop_changed ()
168 {
169         shared_ptr<VideoContent> c = _editor->selected_video_content ();
170         if (!c) {
171                 return;
172         }
173
174         c->set_bottom_crop (_bottom_crop->GetValue ());
175 }
176
177 void
178 VideoPanel::film_changed (Film::Property property)
179 {
180         switch (property) {
181         case Film::CONTAINER:
182         case Film::VIDEO_FRAME_RATE:
183                 setup_description ();
184                 break;
185         default:
186                 break;
187         }
188 }
189
190 void
191 VideoPanel::film_content_changed (shared_ptr<Content> c, int property)
192 {
193         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c);
194         shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c);
195
196         if (property == VideoContentProperty::VIDEO_FRAME_TYPE) {
197                 checked_set (_frame_type, vc ? vc->video_frame_type () : VIDEO_FRAME_TYPE_2D);
198                 setup_description ();
199         } else if (property == VideoContentProperty::VIDEO_CROP) {
200                 checked_set (_left_crop,   vc ? vc->crop().left : 0);
201                 checked_set (_right_crop,  vc ? vc->crop().right        : 0);
202                 checked_set (_top_crop,    vc ? vc->crop().top  : 0);
203                 checked_set (_bottom_crop, vc ? vc->crop().bottom : 0);
204                 setup_description ();
205         } else if (property == VideoContentProperty::VIDEO_RATIO) {
206                 if (vc) {
207                         int n = 0;
208                         vector<Ratio const *> ratios = Ratio::all ();
209                         vector<Ratio const *>::iterator i = ratios.begin ();
210                         while (i != ratios.end() && *i != vc->ratio()) {
211                                 ++i;
212                                 ++n;
213                         }
214
215                         if (i == ratios.end()) {
216                                 checked_set (_ratio, -1);
217                         } else {
218                                 checked_set (_ratio, n);
219                         }
220                 } else {
221                         checked_set (_ratio, -1);
222                 }
223                 setup_description ();
224         } else if (property == VideoContentProperty::VIDEO_FRAME_RATE) {
225                 setup_description ();
226         } else if (property == FFmpegContentProperty::FILTERS) {
227                 if (fc) {
228                         pair<string, string> p = Filter::ffmpeg_strings (fc->filters ());
229                         if (p.first.empty () && p.second.empty ()) {
230                                 _filters->SetLabel (_("None"));
231                         } else {
232                                 string const b = p.first + " " + p.second;
233                                 _filters->SetLabel (std_to_wx (b));
234                         }
235                 }
236         }
237 }
238
239 /** Called when the `Edit filters' button has been clicked */
240 void
241 VideoPanel::edit_filters_clicked ()
242 {
243         shared_ptr<Content> c = _editor->selected_content ();
244         if (!c) {
245                 return;
246         }
247
248         shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c);
249         if (!fc) {
250                 return;
251         }
252         
253         FilterDialog* d = new FilterDialog (this, fc->filters());
254         d->ActiveChanged.connect (bind (&FFmpegContent::set_filters, fc, _1));
255         d->ShowModal ();
256         d->Destroy ();
257 }
258
259 void
260 VideoPanel::setup_description ()
261 {
262         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
263         if (!vc) {
264                 _description->SetLabel ("");
265                 return;
266         }
267
268         wxString d;
269
270         int lines = 0;
271
272         if (vc->video_size().width && vc->video_size().height) {
273                 d << wxString::Format (
274                         _("Content video is %dx%d (%.2f:1)\n"),
275                         vc->video_size_after_3d_split().width, vc->video_size_after_3d_split().height,
276                         float (vc->video_size_after_3d_split().width) / vc->video_size_after_3d_split().height
277                         );
278                 ++lines;
279         }
280
281         Crop const crop = vc->crop ();
282         if ((crop.left || crop.right || crop.top || crop.bottom) && vc->video_size() != libdcp::Size (0, 0)) {
283                 libdcp::Size cropped = vc->video_size_after_3d_split ();
284                 cropped.width -= crop.left + crop.right;
285                 cropped.height -= crop.top + crop.bottom;
286                 d << wxString::Format (
287                         _("Cropped to %dx%d (%.2f:1)\n"),
288                         cropped.width, cropped.height,
289                         float (cropped.width) / cropped.height
290                         );
291                 ++lines;
292         }
293
294         Ratio const * ratio = vc->ratio ();
295         if (ratio) {
296                 libdcp::Size container_size = _editor->film()->container()->size (_editor->film()->full_frame ());
297                 
298                 libdcp::Size const scaled = ratio->size (container_size);
299                 d << wxString::Format (
300                         _("Scaled to %dx%d (%.2f:1)\n"),
301                         scaled.width, scaled.height,
302                         float (scaled.width) / scaled.height
303                         );
304                 ++lines;
305
306                 if (scaled != container_size) {
307                         d << wxString::Format (
308                                 _("Padded with black to %dx%d (%.2f:1)\n"),
309                                 container_size.width, container_size.height,
310                                 float (container_size.width) / container_size.height
311                                 );
312                         ++lines;
313                 }
314         }
315
316         d << wxString::Format (_("Content frame rate %.4f\n"), vc->video_frame_rate ());
317         ++lines;
318         FrameRateConversion frc (vc->video_frame_rate(), _editor->film()->video_frame_rate ());
319         d << frc.description << "\n";
320         ++lines;
321
322         for (int i = lines; i < 6; ++i) {
323                 d << wxT ("\n ");
324         }
325
326         _description->SetLabel (d);
327         _sizer->Layout ();
328 }
329
330
331 void
332 VideoPanel::ratio_changed ()
333 {
334         if (!_editor->film ()) {
335                 return;
336         }
337
338         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
339         
340         int const n = _ratio->GetSelection ();
341         if (n >= 0) {
342                 vector<Ratio const *> ratios = Ratio::all ();
343                 assert (n < int (ratios.size()));
344                 vc->set_ratio (ratios[n]);
345         }
346 }
347
348 void
349 VideoPanel::frame_type_changed ()
350 {
351         shared_ptr<VideoContent> vc = _editor->selected_video_content ();
352         if (vc) {
353                 vc->set_video_frame_type (static_cast<VideoFrameType> (_frame_type->GetSelection ()));
354         }
355 }
356
357 void
358 VideoPanel::setup_colour_conversions ()
359 {
360         vector<shared_ptr<ColourConversion> > cc = Config::instance()->colour_conversions ();
361         for (vector<shared_ptr<ColourConversion> >::iterator i = cc.begin(); i != cc.end(); ++i) {
362                 _colour_conversion->Append (std_to_wx ((*i)->name));
363         }
364 }
365
366 void
367 VideoPanel::colour_conversion_changed ()
368 {
369
370 }