_audio_mapping = boost::none;
_custom_languages.clear ();
_add_files_path = boost::none;
+ _auto_crop_threshold = 0.1;
_allowed_dcp_frame_rates.clear ();
_allowed_dcp_frame_rates.push_back (24);
}
_add_files_path = f.optional_string_child("AddFilesPath");
+ _auto_crop_threshold = f.optional_number_child<double>("AutoCropThreshold").get_value_or(0.1);
if (boost::filesystem::exists (_cinemas_file)) {
cxml::Document f ("Cinemas");
/* [XML] AddFilesPath The default path that will be offered in the picker when adding files to a film. */
root->add_child("AddFilesPath")->add_child_text(_add_files_path->string());
}
+ root->add_child("AutoCropThreshold")->add_child_text(raw_convert<string>(_auto_crop_threshold));
auto target = config_write_file();
HISTORY,
SHOW_EXPERIMENTAL_AUDIO_PROCESSORS,
AUDIO_MAPPING,
+ AUTO_CROP_THRESHOLD,
OTHER
};
return _add_files_path;
}
+ double auto_crop_threshold () const {
+ return _auto_crop_threshold;
+ }
+
/* SET (mostly) */
void set_master_encoding_threads (int n) {
changed ();
}
+ void set_auto_crop_threshold (double threshold) {
+ maybe_set (_auto_crop_threshold, threshold, AUTO_CROP_THRESHOLD);
+ }
+
void changed (Property p = OTHER);
boost::signals2::signal<void (Property)> Changed;
/** Emitted if read() failed on an existing Config file. There is nothing
boost::optional<AudioMapping> _audio_mapping;
std::vector<dcp::LanguageTag> _custom_languages;
boost::optional<boost::filesystem::path> _add_files_path;
+ double _auto_crop_threshold;
static int const _current_version;
optional<DCPTime>
-Player::content_time_to_dcp (shared_ptr<Content> content, ContentTime t)
+Player::content_time_to_dcp (shared_ptr<const Content> content, ContentTime t)
{
boost::mutex::scoped_lock lm (_mutex);
}
+optional<ContentTime>
+Player::dcp_to_content_time (shared_ptr<const Content> content, DCPTime t)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+
+ for (auto i: _pieces) {
+ if (i->content == content) {
+ return dcp_to_content_time (i, t);
+ }
+ }
+
+ /* We couldn't find this content; perhaps things are being changed over */
+ return {};
+}
+
+
shared_ptr<const Playlist>
Player::playlist () const
{
void set_play_referenced ();
void set_dcp_decode_reduction (boost::optional<int> reduction);
- boost::optional<dcpomatic::DCPTime> content_time_to_dcp (std::shared_ptr<Content> content, dcpomatic::ContentTime t);
+ boost::optional<dcpomatic::DCPTime> content_time_to_dcp (std::shared_ptr<const Content> content, dcpomatic::ContentTime t);
+ boost::optional<dcpomatic::ContentTime> dcp_to_content_time (std::shared_ptr<const Content> content, dcpomatic::DCPTime t);
boost::signals2::signal<void (ChangeType, int, bool)> Change;
};
+template <class T>
+bool operator== (Rect<T> const& a, Rect<T> const& b)
+{
+ return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height;
+}
+
+
}
}
+void
+VideoContent::set_crop (Crop c)
+{
+ maybe_set (_crop, c, VideoContentProperty::CROP);
+}
+
void
VideoContent::set_left_crop (int c)
{
void set_frame_type (VideoFrameType);
+ void set_crop (Crop crop);
void set_left_crop (int);
void set_right_crop (int);
void set_top_crop (int);
--- /dev/null
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "auto_crop_dialog.h"
+#include "dcpomatic_spin_ctrl.h"
+#include "wx_util.h"
+#include "lib/config.h"
+#include "lib/types.h"
+
+
+AutoCropDialog::AutoCropDialog (wxWindow* parent, Crop crop)
+ : TableDialog (parent, _("Auto crop"), 2, 1, true)
+{
+ add (_("Left"), true);
+ _left = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH));
+ add (_("Right"), true);
+ _right = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH));
+ add (_("Top"), true);
+ _top = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH));
+ add (_("Bottom"), true);
+ _bottom = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH));
+ add (_("Threshold"), true);
+ _threshold = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH));
+
+ _left->SetRange(0, 4096);
+ _right->SetRange(0, 4096);
+ _top->SetRange(0, 4096);
+ _bottom->SetRange(0, 4096);
+
+ set (crop);
+ _threshold->SetValue (std::round(Config::instance()->auto_crop_threshold() * 100));
+
+ layout ();
+
+ _left->Bind (wxEVT_SPINCTRL, [this](wxSpinEvent&) { Changed(get()); });
+ _right->Bind (wxEVT_SPINCTRL, [this](wxSpinEvent&) { Changed(get()); });
+ _top->Bind (wxEVT_SPINCTRL, [this](wxSpinEvent&) { Changed(get()); });
+ _bottom->Bind (wxEVT_SPINCTRL, [this](wxSpinEvent&) { Changed(get()); });
+ _threshold->Bind (wxEVT_SPINCTRL, [](wxSpinEvent& ev) { Config::instance()->set_auto_crop_threshold(ev.GetPosition() / 100.0); });
+}
+
+
+Crop
+AutoCropDialog::get () const
+{
+ return Crop(_left->GetValue(), _right->GetValue(), _top->GetValue(), _bottom->GetValue());
+}
+
+
+void
+AutoCropDialog::set (Crop crop)
+{
+ _left->SetValue (crop.left);
+ _right->SetValue (crop.right);
+ _top->SetValue (crop.top);
+ _bottom->SetValue (crop.bottom);
+}
+
--- /dev/null
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic 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.
+
+ DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "table_dialog.h"
+#include "lib/types.h"
+#include <boost/signals2.hpp>
+
+
+class SpinCtrl;
+
+
+class AutoCropDialog : public TableDialog
+{
+public:
+ AutoCropDialog (wxWindow* parent, Crop crop);
+
+ Crop get () const;
+ void set (Crop crop);
+
+ boost::signals2::signal<void (Crop)> Changed;
+
+private:
+ SpinCtrl* _left;
+ SpinCtrl* _right;
+ SpinCtrl* _top;
+ SpinCtrl* _bottom;
+ SpinCtrl* _threshold;
+};
+
*/
+#include "auto_crop_dialog.h"
#include "content_advanced_dialog.h"
#include "content_menu.h"
#include "content_properties_dialog.h"
+#include "film_viewer.h"
#include "repeat_dialog.h"
#include "timeline_audio_content_view.h"
#include "timeline_video_content_view.h"
#include "lib/ffmpeg_content.h"
#include "lib/film.h"
#include "lib/find_missing.h"
+#include "lib/guess_crop.h"
#include "lib/image_content.h"
#include "lib/job_manager.h"
#include "lib/playlist.h"
+#include "lib/video_content.h"
#include <dcp/cpl.h>
#include <dcp/decrypted_kdm.h>
#include <dcp/exceptions.h>
#if BOOST_VERSION >= 106100
using namespace boost::placeholders;
#endif
+using namespace dcpomatic;
enum {
ID_properties,
ID_advanced,
ID_re_examine,
+ ID_auto_crop,
ID_kdm,
ID_ov,
ID_choose_cpl,
};
-ContentMenu::ContentMenu (wxWindow* p)
+ContentMenu::ContentMenu (wxWindow* p, weak_ptr<FilmViewer> viewer)
: _menu (new wxMenu)
, _parent (p)
, _pop_up_open (false)
+ , _viewer (viewer)
{
_repeat = _menu->Append (ID_repeat, _("Repeat..."));
_join = _menu->Append (ID_join, _("Join"));
_find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
_re_examine = _menu->Append (ID_re_examine, _("Re-examine..."));
+ _auto_crop = _menu->Append (ID_auto_crop, _("Auto-crop..."));
_properties = _menu->Append (ID_properties, _("Properties..."));
_advanced = _menu->Append (ID_advanced, _("Advanced settings..."));
_menu->AppendSeparator ();
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::properties, this), ID_properties);
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::advanced, this), ID_advanced);
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::re_examine, this), ID_re_examine);
+ _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::auto_crop, this), ID_auto_crop);
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::kdm, this), ID_kdm);
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::ov, this), ID_ov);
_parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::set_dcp_settings, this), ID_set_dcp_settings);
_properties->Enable (_content.size() == 1);
_advanced->Enable (_content.size() == 1);
_re_examine->Enable (!_content.empty ());
+ _auto_crop->Enable (_content.size() == 1);
if (_content.size() == 1) {
auto dcp = dynamic_pointer_cast<DCPContent> (_content.front());
DCPOMATIC_ASSERT (film);
JobManager::instance()->add (make_shared<ExamineContentJob>(film, dcp));
}
+
+
+void
+ContentMenu::auto_crop ()
+{
+ DCPOMATIC_ASSERT (_content.size() == 1);
+
+ auto film = _film.lock ();
+ DCPOMATIC_ASSERT (film);
+ auto viewer = _viewer.lock ();
+ DCPOMATIC_ASSERT (viewer);
+
+ auto update_viewer = [this](Crop crop) {
+ auto film = _film.lock();
+ DCPOMATIC_ASSERT (film);
+ auto viewer = _viewer.lock ();
+ DCPOMATIC_ASSERT (viewer);
+ auto const content = _content.front();
+ auto const current_crop = content->video->actual_crop();
+ viewer->set_crop_guess (
+ Rect<float>(
+ static_cast<float>(std::max(0, crop.left - current_crop.left)) / content->video->size().width,
+ static_cast<float>(std::max(0, crop.top - current_crop.top)) / content->video->size().height,
+ 1.0f - (static_cast<float>(std::max(0, crop.left - current_crop.left + crop.right - current_crop.right)) / content->video->size().width),
+ 1.0f - (static_cast<float>(std::max(0, crop.top - current_crop.top + crop.bottom - current_crop.bottom)) / content->video->size().height)
+ ));
+ };
+
+ auto guess_crop_for_content = [this, film, viewer]() {
+ auto position = viewer->position_in_content(_content.front()).get_value_or(
+ ContentTime::from_frames(_content.front()->video->length(), _content.front()->video_frame_rate().get_value_or(24))
+ );
+ return guess_crop(film, _content.front(), Config::instance()->auto_crop_threshold(), position);
+ };
+
+ /* Make an initial guess in the view and open the dialog */
+
+ auto const crop = guess_crop_for_content ();
+ update_viewer (crop);
+
+ if (_auto_crop_dialog) {
+ _auto_crop_dialog->Destroy();
+ _auto_crop_dialog = nullptr;
+ }
+ _auto_crop_dialog = new AutoCropDialog (_parent, crop);
+ _auto_crop_dialog->Show ();
+
+ /* Update the dialog and view when the crop threshold changes */
+ _auto_crop_config_connection = Config::instance()->Changed.connect([this, guess_crop_for_content, update_viewer](Config::Property property) {
+ auto film = _film.lock();
+ DCPOMATIC_ASSERT (film);
+ if (property == Config::AUTO_CROP_THRESHOLD) {
+ auto const crop = guess_crop_for_content();
+ _auto_crop_dialog->set(crop);
+ update_viewer(crop);
+ }
+ });
+
+ /* Also update the dialog and view when we're looking at a different frame */
+ _auto_crop_viewer_connection = viewer->ImageChanged.connect([this, guess_crop_for_content, update_viewer](shared_ptr<PlayerVideo>) {
+ auto const crop = guess_crop_for_content();
+ _auto_crop_dialog->set(crop);
+ update_viewer(crop);
+ });
+
+ /* Handle the user closing the dialog (with OK or cancel) */
+ _auto_crop_dialog->Bind (wxEVT_BUTTON, [this, viewer](wxCommandEvent& ev) {
+ if (ev.GetId() == wxID_OK) {
+ _content.front()->video->set_crop(_auto_crop_dialog->get());
+ }
+ _auto_crop_dialog->Show (false);
+ viewer->unset_crop_guess ();
+ _auto_crop_config_connection.disconnect ();
+ _auto_crop_viewer_connection.disconnect ();
+ });
+
+ /* Update the view when something in the dialog is changed */
+ _auto_crop_dialog->Changed.connect([this, update_viewer](Crop crop) {
+ update_viewer (crop);
+ });
+}
*/
+
#ifndef DCPOMATIC_CONTENT_MENU_H
#define DCPOMATIC_CONTENT_MENU_H
+
#include "timeline_content_view.h"
#include "lib/types.h"
#include <wx/wx.h>
#include <memory>
+
+class AutoCropDialog;
+class DCPContent;
class Film;
+class FilmViewer;
class Job;
-class DCPContent;
+
class ContentMenu
{
public:
- explicit ContentMenu (wxWindow* p);
+ ContentMenu (wxWindow* parent, std::weak_ptr<FilmViewer> viewer);
ContentMenu (ContentMenu const &) = delete;
ContentMenu& operator= (ContentMenu const &) = delete;
void properties ();
void advanced ();
void re_examine ();
+ void auto_crop ();
void kdm ();
void ov ();
void set_dcp_settings ();
std::weak_ptr<Film> _film;
wxWindow* _parent;
bool _pop_up_open;
+ std::weak_ptr<FilmViewer> _viewer;
ContentList _content;
TimelineContentViewList _views;
wxMenuItem* _repeat;
wxMenuItem* _properties;
wxMenuItem* _advanced;
wxMenuItem* _re_examine;
+ wxMenuItem* _auto_crop;
wxMenuItem* _kdm;
wxMenuItem* _ov;
wxMenuItem* _choose_cpl;
wxMenuItem* _set_dcp_settings;
wxMenuItem* _remove;
+
+ AutoCropDialog* _auto_crop_dialog = nullptr;
+ boost::signals2::scoped_connection _auto_crop_config_connection;
+ boost::signals2::scoped_connection _auto_crop_viewer_connection;
};
+
#endif
_splitter = new LimitedSplitter (n);
_top_panel = new wxPanel (_splitter);
- _menu = new ContentMenu (_splitter);
+ _menu = new ContentMenu (_splitter, _film_viewer);
{
auto s = new wxBoxSizer (wxHORIZONTAL);
}
-/** Ask for ::get() to be called next time we are idle */
+/** Ask for ::idle_handler() to be called next time we are idle */
void
FilmViewer::request_idle_display_next_frame ()
{
}
+optional<ContentTime>
+FilmViewer::position_in_content (shared_ptr<const Content> content) const
+{
+ return _player->dcp_to_content_time (content, position());
+}
+
+
DCPTime
FilmViewer::one_video_frame () const
{
_video_view->set_optimise_for_j2k (o);
}
+
+void
+FilmViewer::set_crop_guess (Rect<float> crop)
+{
+ if (crop != _crop_guess) {
+ _crop_guess = crop;
+ _video_view->update ();
+ }
+}
+
+
+void
+FilmViewer::unset_crop_guess ()
+{
+ _crop_guess = {};
+ _video_view->update ();
+}
+
dcpomatic::DCPTime position () const {
return _video_view->position();
}
+ boost::optional<dcpomatic::ContentTime> position_in_content (std::shared_ptr<const Content> content) const;
dcpomatic::DCPTime one_video_frame () const;
void start ();
void set_eyes (Eyes e);
void set_pad_black (bool p);
void set_optimise_for_j2k (bool o);
+ void set_crop_guess (dcpomatic::Rect<float> crop);
+ void unset_crop_guess ();
void slow_refresh ();
}
void finished ();
void image_changed (std::shared_ptr<PlayerVideo> video);
+ boost::optional<dcpomatic::Rect<float>> crop_guess () const {
+ return _crop_guess;
+ }
bool pending_idle_get () const {
return _idle_get;
/** true if an get() is required next time we are idle */
bool _idle_get = false;
+ boost::optional<dcpomatic::Rect<float>> _crop_guess;
+
boost::signals2::scoped_connection _config_changed_connection;
};
"\n"
"uniform sampler2D texture_sampler;\n"
/* type = 0: draw outline content rectangle
- * type = 1: draw XYZ image
- * type = 2: draw RGB image
+ * type = 1: draw crop guess rectangle
+ * type = 2: draw XYZ image
+ * type = 3: draw RGB image
* See FragmentType enum below.
*/
"uniform int type = 0;\n"
"uniform vec4 outline_content_colour;\n"
+"uniform vec4 crop_guess_colour;\n"
"uniform mat4 colour_conversion;\n"
"\n"
"out vec4 FragColor;\n"
" FragColor = outline_content_colour;\n"
" break;\n"
" case 1:\n"
+" FragColor = crop_guess_colour;\n"
+" break;\n"
+" case 2:\n"
" FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
" FragColor.x = pow(FragColor.x, IN_GAMMA) / DCI_COEFFICIENT;\n"
" FragColor.y = pow(FragColor.y, IN_GAMMA) / DCI_COEFFICIENT;\n"
" FragColor.y = pow(FragColor.y, OUT_GAMMA);\n"
" FragColor.z = pow(FragColor.z, OUT_GAMMA);\n"
" break;\n"
-" case 2:\n"
+" case 3:\n"
" FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
" break;\n"
" }\n"
enum class FragmentType
{
OUTLINE_CONTENT = 0,
- XYZ_IMAGE = 1,
- RGB_IMAGE = 2,
+ CROP_GUESS = 1,
+ XYZ_IMAGE = 2,
+ RGB_IMAGE = 3,
};
static constexpr int indices_subtitle_texture_number = 6;
static constexpr int indices_outline_content_offset = indices_subtitle_texture_offset + indices_subtitle_texture_number;
static constexpr int indices_outline_content_number = 8;
+static constexpr int indices_crop_guess_offset = indices_outline_content_offset + indices_outline_content_number;
+static constexpr int indices_crop_guess_number = 8;
static constexpr unsigned int indices[] = {
0, 1, 3, // video texture triangle #1
9, 10, // outline content line #2
10, 11, // outline content line #3
11, 8, // outline content line #4
+ 12, 13, // crop guess line #1
+ 13, 14, // crop guess line #2
+ 14, 15, // crop guess line #3
+ 15, 12, // crop guess line #4
};
/* Offsets of things in the GL_ARRAY_BUFFER */
static constexpr int array_buffer_video_offset = 0;
static constexpr int array_buffer_subtitle_offset = array_buffer_video_offset + 4 * 5 * sizeof(float);
static constexpr int array_buffer_outline_content_offset = array_buffer_subtitle_offset + 4 * 5 * sizeof(float);
+static constexpr int array_buffer_crop_guess_offset = array_buffer_outline_content_offset + 4 * 5 * sizeof(float);
void
_fragment_type = glGetUniformLocation (program, "type");
check_gl_error ("glGetUniformLocation");
set_outline_content_colour (program);
+ set_crop_guess_colour (program);
auto conversion = dcp::ColourConversion::rec709_to_xyz();
boost::numeric::ublas::matrix<double> matrix = conversion.xyz_to_rgb ();
check_gl_error ("glGetUniformLocation");
glUniformMatrix4fv (colour_conversion, 1, GL_TRUE, gl_matrix);
- glLineWidth (1.0f);
+ glLineWidth (2.0f);
check_gl_error ("glLineWidth");
glEnable (GL_BLEND);
check_gl_error ("glEnable");
check_gl_error ("glBlendFunc");
/* Reserve space for the GL_ARRAY_BUFFER */
- glBufferData(GL_ARRAY_BUFFER, 12 * 5 * sizeof(float), nullptr, GL_STATIC_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, 16 * 5 * sizeof(float), nullptr, GL_STATIC_DRAW);
check_gl_error ("glBufferData");
}
}
+void
+GLVideoView::set_crop_guess_colour (GLuint program)
+{
+ auto uniform = glGetUniformLocation (program, "crop_guess_colour");
+ check_gl_error ("glGetUniformLocation");
+ auto colour = crop_guess_colour ();
+ glUniform4f (uniform, colour.Red() / 255.0f, colour.Green() / 255.0f, colour.Blue() / 255.0f, 1.0f);
+ check_gl_error ("glUniform4f");
+}
+
+
void
GLVideoView::draw ()
{
glDrawElements (GL_LINES, indices_outline_content_number, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_outline_content_offset * sizeof(int)));
check_gl_error ("glDrawElements");
}
+ if (auto guess = _viewer->crop_guess()) {
+ glUniform1i(_fragment_type, 1);
+ glDrawElements (GL_LINES, indices_crop_guess_number, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_crop_guess_offset * sizeof(int)));
+ check_gl_error ("glDrawElements");
+ }
glFlush();
check_gl_error ("glFlush");
auto const inter_position = player_video().first->inter_position();
auto const inter_size = player_video().first->inter_size();
auto const out_size = player_video().first->out_size();
+ auto const crop_guess = _viewer->crop_guess();
auto x_offset = std::max(0, (canvas_width - out_size.width) / 2);
auto y_offset = std::max(0, (canvas_height - out_size.height) / 2);
_last_inter_position.set_next (inter_position);
_last_inter_size.set_next (inter_size);
_last_out_size.set_next (out_size);
+ _last_crop_guess.set_next (crop_guess);
class Rectangle
{
float _vertices[20];
};
- if (_last_canvas_size.changed() || _last_inter_position.changed() || _last_inter_size.changed() || _last_out_size.changed()) {
+ auto const sizing_changed = _last_canvas_size.changed() || _last_inter_position.changed() || _last_inter_size.changed() || _last_out_size.changed();
+ if (sizing_changed) {
const auto video = _optimise_for_j2k ?
Rectangle(canvas_size, inter_position.x + x_offset, inter_position.y + y_offset, inter_size)
: Rectangle(canvas_size, x_offset, y_offset, out_size);
check_gl_error ("glBufferSubData (outline_content)");
}
+ if ((sizing_changed || _last_crop_guess.changed()) && crop_guess) {
+ auto const crop_guess_rectangle = Rectangle(
+ canvas_size,
+ inter_position.x + x_offset + inter_size.width * crop_guess->x,
+ inter_position.y + y_offset + inter_size.height * crop_guess->y,
+ dcp::Size(inter_size.width * crop_guess->width, inter_size.height * crop_guess->height)
+ );
+ glBufferSubData (GL_ARRAY_BUFFER, array_buffer_crop_guess_offset, crop_guess_rectangle.size(), crop_guess_rectangle.vertices());
+ check_gl_error ("glBufferSubData (crop_guess_rectangle)");
+ }
+
if (_have_subtitle_to_render) {
const auto subtitle = Rectangle(canvas_size, inter_position.x + x_offset + text->position.x, inter_position.y + y_offset + text->position.y, text->image->size());
glBufferSubData (GL_ARRAY_BUFFER, array_buffer_subtitle_offset, subtitle.size(), subtitle.vertices());
void size_changed (wxSizeEvent const &);
void setup_shaders ();
void set_outline_content_colour (GLuint program);
+ void set_crop_guess_colour (GLuint program);
wxGLCanvas* _canvas;
wxGLContext* _context;
Last<Position<int>> _last_inter_position;
Last<dcp::Size> _last_inter_size;
Last<dcp::Size> _last_out_size;
+ Last<boost::optional<dcpomatic::Rect<float>>> _last_crop_guess;
boost::atomic<wxSize> _canvas_size;
std::unique_ptr<Texture> _video_texture;
dc.DrawRectangle (subs->x * out_size.width, subs->y * out_size.height, subs->width * out_size.width, subs->height * out_size.height);
}
+ if (_viewer->crop_guess()) {
+ wxPen p (crop_guess_colour(), 2);
+ dc.SetPen (p);
+ dc.SetBrush (*wxTRANSPARENT_BRUSH);
+ auto const crop_guess = _viewer->crop_guess().get();
+ dc.DrawRectangle (
+ _inter_position.x + _inter_size.width * crop_guess.x,
+ _inter_position.y + _inter_size.height * crop_guess.y,
+ _inter_size.width * crop_guess.width,
+ _inter_size.height * crop_guess.height
+ );
+ }
+
_state_timer.unset();
}
, _left_down (false)
, _down_view_position (0)
, _first_move (false)
- , _menu (this)
+ , _menu (this, viewer)
, _snap (true)
, _tool (SELECT)
, _x_scroll_rate (16)
return wxColour(240, 240, 240);
}
}
-
return wxColour(0, 255, 0);
}
+ wxColour crop_guess_colour () const {
+ return wxColour(0, 0, 255);
+ }
+
int video_frame_rate () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_frame_rate;
audio_mapping_view.cc
audio_panel.cc
audio_plot.cc
+ auto_crop_dialog.cc
barco_alchemy_certificate_panel.cc
batch_job_view.cc
check_box.cc