From 3b932abd0c7634483911e1d5361e12b2d094ae6f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 8 Nov 2015 15:00:58 +0000 Subject: [PATCH] Add appearance dialog for SubRip subtitles. --- ChangeLog | 4 ++ src/lib/image.cc | 6 +-- src/lib/player.cc | 5 +- src/lib/subrip_content.cc | 70 ++++++++++++++++++++++++++++ src/lib/subrip_content.h | 33 +++++++++++++ src/lib/subrip_decoder.cc | 11 +++-- src/lib/subtitle_decoder.h | 3 +- src/wx/subtitle_appearance_dialog.cc | 58 +++++++++++++++++++++++ src/wx/subtitle_appearance_dialog.h | 40 ++++++++++++++++ src/wx/subtitle_panel.cc | 57 +++++++++++++++------- src/wx/subtitle_panel.h | 3 ++ src/wx/wscript | 1 + 12 files changed, 266 insertions(+), 25 deletions(-) create mode 100644 src/wx/subtitle_appearance_dialog.cc create mode 100644 src/wx/subtitle_appearance_dialog.h diff --git a/ChangeLog b/ChangeLog index afd9ff99a..713a25da3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2015-11-08 Carl Hetherington + + * Allow configuration of SubRip subtitle colour and outlines. + 2015-11-06 Carl Hetherington * Sort cinemas in KDM dialog and standalone creator (#726). diff --git a/src/lib/image.cc b/src/lib/image.cc index ed029ca2d..6835d0c26 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -464,10 +464,10 @@ Image::alpha_blend (shared_ptr other, Position position) uint8_t* op = other->data()[0] + oy * other->stride()[0]; for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) { float const alpha = float (op[3]) / 255; - /* Blend high bytes */ - tp[1] = op[0] * alpha + tp[1] * (1 - alpha); + /* Blend high bytes; the RGBA in op appears to be BGRA */ + tp[1] = op[2] * alpha + tp[1] * (1 - alpha); tp[3] = op[1] * alpha + tp[3] * (1 - alpha); - tp[5] = op[2] * alpha + tp[5] * (1 - alpha); + tp[5] = op[0] * alpha + tp[5] * (1 - alpha); tp += this_bpp; op += other_bpp; diff --git a/src/lib/player.cc b/src/lib/player.cc index 10e476862..ecf37b576 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -219,7 +219,10 @@ Player::playlist_content_changed (weak_ptr w, int property, bool freque property == ContentProperty::TRIM_END || property == ContentProperty::PATH || property == VideoContentProperty::VIDEO_FRAME_TYPE || - property == DCPContentProperty::CAN_BE_PLAYED + property == DCPContentProperty::CAN_BE_PLAYED || + property == SubRipContentProperty::SUBTITLE_COLOUR || + property == SubRipContentProperty::SUBTITLE_OUTLINE || + property == SubRipContentProperty::SUBTITLE_OUTLINE_COLOUR ) { _have_valid_pieces = false; diff --git a/src/lib/subrip_content.cc b/src/lib/subrip_content.cc index 1a7efc117..a6eb07629 100644 --- a/src/lib/subrip_content.cc +++ b/src/lib/subrip_content.cc @@ -35,9 +35,16 @@ using boost::lexical_cast; std::string const SubRipContent::font_id = "font"; +int const SubRipContentProperty::SUBTITLE_COLOUR = 300; +int const SubRipContentProperty::SUBTITLE_OUTLINE = 301; +int const SubRipContentProperty::SUBTITLE_OUTLINE_COLOUR = 302; + SubRipContent::SubRipContent (shared_ptr film, boost::filesystem::path path) : Content (film, path) , SubtitleContent (film, path) + , _colour (255, 255, 255) + , _outline (false) + , _outline_colour (0, 0, 0) { } @@ -47,6 +54,17 @@ SubRipContent::SubRipContent (shared_ptr film, cxml::ConstNodePtr no , SubtitleContent (film, node, version) , _length (node->number_child ("Length")) , _frame_rate (node->optional_number_child("SubtitleFrameRate")) + , _colour ( + node->optional_number_child("Red").get_value_or(255), + node->optional_number_child("Green").get_value_or(255), + node->optional_number_child("Blue").get_value_or(255) + ) + , _outline (node->optional_bool_child("Outline").get_value_or(false)) + , _outline_colour ( + node->optional_number_child("OutlineRed").get_value_or(255), + node->optional_number_child("OutlineGreen").get_value_or(255), + node->optional_number_child("OutlineBlue").get_value_or(255) + ) { } @@ -84,6 +102,13 @@ SubRipContent::as_xml (xmlpp::Node* node) const Content::as_xml (node); SubtitleContent::as_xml (node); node->add_child("Length")->add_child_text (raw_convert (_length.get ())); + node->add_child("Red")->add_child_text (raw_convert (_colour.r)); + node->add_child("Green")->add_child_text (raw_convert (_colour.g)); + node->add_child("Blue")->add_child_text (raw_convert (_colour.b)); + node->add_child("Outline")->add_child_text (raw_convert (_outline)); + node->add_child("OutlineRed")->add_child_text (raw_convert (_outline_colour.r)); + node->add_child("OutlineGreen")->add_child_text (raw_convert (_outline_colour.g)); + node->add_child("OutlineBlue")->add_child_text (raw_convert (_outline_colour.b)); } DCPTime @@ -119,3 +144,48 @@ SubRipContent::subtitle_video_frame_rate () const */ return film()->active_frame_rate_change(position()).source; } + +void +SubRipContent::set_colour (dcp::Colour colour) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_colour == colour) { + return; + } + + _colour = colour; + } + + signal_changed (SubRipContentProperty::SUBTITLE_COLOUR); +} + +void +SubRipContent::set_outline (bool o) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_outline == o) { + return; + } + + _outline = o; + } + + signal_changed (SubRipContentProperty::SUBTITLE_OUTLINE); +} + +void +SubRipContent::set_outline_colour (dcp::Colour colour) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_outline_colour == colour) { + return; + } + + _outline_colour = colour; + } + + signal_changed (SubRipContentProperty::SUBTITLE_OUTLINE_COLOUR); +} diff --git a/src/lib/subrip_content.h b/src/lib/subrip_content.h index e05063403..68864f58c 100644 --- a/src/lib/subrip_content.h +++ b/src/lib/subrip_content.h @@ -19,6 +19,15 @@ #include "subtitle_content.h" +class SubRipContentProperty +{ +public: + static int const SUBTITLE_COLOUR; + static int const SUBTITLE_OUTLINE; + static int const SUBTITLE_OUTLINE_COLOUR; +}; + + class SubRipContent : public SubtitleContent { public: @@ -49,10 +58,34 @@ public: double subtitle_video_frame_rate () const; void set_subtitle_video_frame_rate (int r); + void set_colour (dcp::Colour); + + dcp::Colour colour () const { + boost::mutex::scoped_lock lm (_mutex); + return _colour; + } + + void set_outline (bool); + + bool outline () const { + boost::mutex::scoped_lock lm (_mutex); + return _outline; + } + + void set_outline_colour (dcp::Colour); + + dcp::Colour outline_colour () const { + boost::mutex::scoped_lock lm (_mutex); + return _outline_colour; + } + static std::string const font_id; private: ContentTime _length; /** Video frame rate that this content has been prepared for, if known */ boost::optional _frame_rate; + dcp::Colour _colour; + bool _outline; + dcp::Colour _outline_colour; }; diff --git a/src/lib/subrip_decoder.cc b/src/lib/subrip_decoder.cc index 26a7ebcae..0bb543337 100644 --- a/src/lib/subrip_decoder.cc +++ b/src/lib/subrip_decoder.cc @@ -28,6 +28,7 @@ using std::string; using std::cout; using boost::shared_ptr; using boost::optional; +using boost::dynamic_pointer_cast; SubRipDecoder::SubRipDecoder (shared_ptr content) : SubtitleDecoder (content) @@ -57,6 +58,9 @@ SubRipDecoder::pass (PassReason, bool) /* XXX: we are ignoring positioning specified in the file */ + shared_ptr content = dynamic_pointer_cast (_subtitle_content); + DCPOMATIC_ASSERT (content); + list out; for (list::const_iterator i = _subtitles[_next].lines.begin(); i != _subtitles[_next].lines.end(); ++i) { for (list::const_iterator j = i->blocks.begin(); j != i->blocks.end(); ++j) { @@ -64,7 +68,8 @@ SubRipDecoder::pass (PassReason, bool) dcp::SubtitleString ( SubRipContent::font_id, j->italic, - dcp::Colour (j->colour.r * 255, j->colour.g * 255, j->colour.b * 255), + /* force the colour to whatever is configured */ + content->colour(), j->font_size.points (72 * 11), 1.0, dcp::Time (_subtitles[_next].from.all_as_seconds(), 1000), @@ -74,8 +79,8 @@ SubRipDecoder::pass (PassReason, bool) i->vertical_position.line.get() * (1.5 / 22) + 0.8, dcp::VALIGN_TOP, j->text, - dcp::NONE, - dcp::Colour (255, 255, 255), + content->outline() ? dcp::BORDER : dcp::NONE, + content->outline_colour(), dcp::Time (0, 1000), dcp::Time (0, 1000) ) diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h index dd9c024e6..ef62d8b88 100644 --- a/src/lib/subtitle_decoder.h +++ b/src/lib/subtitle_decoder.h @@ -44,6 +44,7 @@ protected: std::list _decoded_image_subtitles; std::list _decoded_text_subtitles; + boost::shared_ptr _subtitle_content; private: template @@ -54,8 +55,6 @@ private: */ virtual std::list image_subtitles_during (ContentTimePeriod period, bool starting) const = 0; virtual std::list text_subtitles_during (ContentTimePeriod period, bool starting) const = 0; - - boost::shared_ptr _subtitle_content; }; #endif diff --git a/src/wx/subtitle_appearance_dialog.cc b/src/wx/subtitle_appearance_dialog.cc new file mode 100644 index 000000000..0db55edfe --- /dev/null +++ b/src/wx/subtitle_appearance_dialog.cc @@ -0,0 +1,58 @@ +/* + Copyright (C) 2015 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 "subtitle_appearance_dialog.h" +#include "lib/subrip_content.h" +#include +#include + +using boost::shared_ptr; + +SubtitleAppearanceDialog::SubtitleAppearanceDialog (wxWindow* parent, shared_ptr content) + : TableDialog (parent, _("Subtitle appearance"), 2, 1, true) + , _content (content) +{ + add (_("Colour"), true); + _colour = new wxColourPickerCtrl (this, wxID_ANY); + add (_colour); + + _outline = new wxCheckBox (this, wxID_ANY, _("Outline")); + add (_outline); + add_spacer (); + + add (_("Outline colour"), true); + _outline_colour = new wxColourPickerCtrl (this, wxID_ANY); + add (_outline_colour); + + layout (); + + _colour->SetColour (wxColour (_content->colour().r, _content->colour().g, _content->colour().b)); + _outline->SetValue (_content->outline ()); + _outline_colour->SetColour (wxColour (_content->outline_colour().r, _content->outline_colour().g, _content->outline_colour().b)); +} + +void +SubtitleAppearanceDialog::apply () +{ + wxColour const c = _colour->GetColour (); + _content->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); + _content->set_outline (_outline->GetValue ()); + wxColour const oc = _outline_colour->GetColour (); + _content->set_outline_colour (dcp::Colour (oc.Red(), oc.Green(), oc.Blue())); +} diff --git a/src/wx/subtitle_appearance_dialog.h b/src/wx/subtitle_appearance_dialog.h new file mode 100644 index 000000000..f4fe307d2 --- /dev/null +++ b/src/wx/subtitle_appearance_dialog.h @@ -0,0 +1,40 @@ +/* + Copyright (C) 2015 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 "table_dialog.h" +#include + +class wxCheckBox; +class wxColourPickerCtrl; +class SubRipContent; + +class SubtitleAppearanceDialog : public TableDialog +{ +public: + SubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr content); + + void apply (); + +private: + wxColourPickerCtrl* _colour; + wxCheckBox* _outline; + wxColourPickerCtrl* _outline_colour; + + boost::shared_ptr _content; +}; diff --git a/src/wx/subtitle_panel.cc b/src/wx/subtitle_panel.cc index e9de4fccd..74a110349 100644 --- a/src/wx/subtitle_panel.cc +++ b/src/wx/subtitle_panel.cc @@ -23,6 +23,7 @@ #include "subtitle_view.h" #include "content_panel.h" #include "fonts_dialog.h" +#include "subtitle_appearance_dialog.h" #include "lib/ffmpeg_content.h" #include "lib/subrip_content.h" #include "lib/ffmpeg_subtitle_stream.h" @@ -119,6 +120,8 @@ SubtitlePanel::SubtitlePanel (ContentPanel* p) s->Add (_subtitle_view_button, 1, wxALL, DCPOMATIC_SIZER_GAP); _fonts_dialog_button = new wxButton (this, wxID_ANY, _("Fonts...")); s->Add (_fonts_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP); + _appearance_dialog_button = new wxButton (this, wxID_ANY, _("Appearance...")); + s->Add (_appearance_dialog_button, 1, wxALL, DCPOMATIC_SIZER_GAP); grid->Add (s, wxGBPosition (r, 0), wxGBSpan (1, 2)); ++r; @@ -129,17 +132,18 @@ SubtitlePanel::SubtitlePanel (ContentPanel* p) _x_scale->SetRange (10, 1000); _y_scale->SetRange (10, 1000); - _reference->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::reference_clicked, this)); - _use->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::use_toggled, this)); - _burn->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::burn_toggled, this)); - _x_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_offset_changed, this)); - _y_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this)); - _x_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_scale_changed, this)); - _y_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_scale_changed, this)); - _language->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&SubtitlePanel::language_changed, this)); - _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&SubtitlePanel::stream_changed, this)); - _subtitle_view_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::subtitle_view_clicked, this)); - _fonts_dialog_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::fonts_dialog_clicked, this)); + _reference->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::reference_clicked, this)); + _use->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::use_toggled, this)); + _burn->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::burn_toggled, this)); + _x_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_offset_changed, this)); + _y_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this)); + _x_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_scale_changed, this)); + _y_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_scale_changed, this)); + _language->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&SubtitlePanel::language_changed, this)); + _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&SubtitlePanel::stream_changed, this)); + _subtitle_view_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::subtitle_view_clicked, this)); + _fonts_dialog_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::fonts_dialog_clicked, this)); + _appearance_dialog_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::appearance_dialog_clicked, this)); } void @@ -229,7 +233,8 @@ SubtitlePanel::setup_sensitivity () { int any_subs = 0; int ffmpeg_subs = 0; - int subrip_or_dcp_subs = 0; + int subrip_subs = 0; + int dcp_subs = 0; int image_subs = 0; SubtitleContentList sel = _parent->selected_subtitle (); BOOST_FOREACH (shared_ptr i, sel) { @@ -241,8 +246,11 @@ SubtitlePanel::setup_sensitivity () ++ffmpeg_subs; ++any_subs; } - } else if (sc || dsc) { - ++subrip_or_dcp_subs; + } else if (sc) { + ++subrip_subs; + ++any_subs; + } else if (dsc) { + ++dcp_subs; ++any_subs; } else { ++any_subs; @@ -284,8 +292,9 @@ SubtitlePanel::setup_sensitivity () _y_scale->Enable (!reference && any_subs > 0 && use); _language->Enable (!reference && any_subs > 0 && use); _stream->Enable (!reference && ffmpeg_subs == 1); - _subtitle_view_button->Enable (!reference && subrip_or_dcp_subs == 1); - _fonts_dialog_button->Enable (!reference && subrip_or_dcp_subs == 1); + _subtitle_view_button->Enable (!reference && (subrip_subs == 1 || dcp_subs == 1)); + _fonts_dialog_button->Enable (!reference && (subrip_subs == 1 || dcp_subs == 1)); + _appearance_dialog_button->Enable (!reference && subrip_subs == 1); } void @@ -425,3 +434,19 @@ SubtitlePanel::reference_clicked () d->set_reference_subtitle (_reference->GetValue ()); } + +void +SubtitlePanel::appearance_dialog_clicked () +{ + SubtitleContentList c = _parent->selected_subtitle (); + DCPOMATIC_ASSERT (c.size() == 1); + + shared_ptr sr = dynamic_pointer_cast (c.front ()); + DCPOMATIC_ASSERT (sr); + + SubtitleAppearanceDialog* d = new SubtitleAppearanceDialog (this, sr); + if (d->ShowModal () == wxID_OK) { + d->apply (); + } + d->Destroy (); +} diff --git a/src/wx/subtitle_panel.h b/src/wx/subtitle_panel.h index 7eb9cd27e..e3fd7cd75 100644 --- a/src/wx/subtitle_panel.h +++ b/src/wx/subtitle_panel.h @@ -23,6 +23,7 @@ class wxCheckBox; class wxSpinCtrl; class SubtitleView; class FontsDialog; +class SubtitleAppearanceDialog; class SubtitlePanel : public ContentSubPanel { @@ -45,6 +46,7 @@ private: void subtitle_view_clicked (); void fonts_dialog_clicked (); void reference_clicked (); + void appearance_dialog_clicked (); void setup_sensitivity (); @@ -61,4 +63,5 @@ private: SubtitleView* _subtitle_view; wxButton* _fonts_dialog_button; FontsDialog* _fonts_dialog; + wxButton* _appearance_dialog_button; }; diff --git a/src/wx/wscript b/src/wx/wscript index d2bf33cd3..3b61eb5c6 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -70,6 +70,7 @@ sources = """ screens_panel.cc server_dialog.cc servers_list_dialog.cc + subtitle_appearance_dialog.cc subtitle_panel.cc subtitle_view.cc system_font_dialog.cc -- 2.30.2