Add appearance dialog for SubRip subtitles.
authorCarl Hetherington <cth@carlh.net>
Sun, 8 Nov 2015 15:00:58 +0000 (15:00 +0000)
committerCarl Hetherington <cth@carlh.net>
Sun, 8 Nov 2015 15:00:58 +0000 (15:00 +0000)
12 files changed:
ChangeLog
src/lib/image.cc
src/lib/player.cc
src/lib/subrip_content.cc
src/lib/subrip_content.h
src/lib/subrip_decoder.cc
src/lib/subtitle_decoder.h
src/wx/subtitle_appearance_dialog.cc [new file with mode: 0644]
src/wx/subtitle_appearance_dialog.h [new file with mode: 0644]
src/wx/subtitle_panel.cc
src/wx/subtitle_panel.h
src/wx/wscript

index afd9ff99a0c4040d466a7a2cd2773872ccd4d082..713a25da3e6e4524595a39836f0416bcff75a5dd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2015-11-08  Carl Hetherington  <cth@carlh.net>
+
+       * Allow configuration of SubRip subtitle colour and outlines.
+
 2015-11-06  Carl Hetherington  <cth@carlh.net>
 
        * Sort cinemas in KDM dialog and standalone creator (#726).
index ed029ca2d3485fe587a305f0b99d35f527baf7b1..6835d0c26d967557df34fadfa06ba1e9680da53f 100644 (file)
@@ -464,10 +464,10 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> 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;
index 10e47686283d7df200c15e7ff88193909305fc06..ecf37b57662b6b89c198a15f3035626ba3b5dce7 100644 (file)
@@ -219,7 +219,10 @@ Player::playlist_content_changed (weak_ptr<Content> 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;
index 1a7efc117a345e6c6a836713eac731de9c4290f0..a6eb0762927cd908eeb728a167d4d0a546345c0d 100644 (file)
@@ -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<const Film> 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<const Film> film, cxml::ConstNodePtr no
        , SubtitleContent (film, node, version)
        , _length (node->number_child<ContentTime::Type> ("Length"))
        , _frame_rate (node->optional_number_child<double>("SubtitleFrameRate"))
+       , _colour (
+               node->optional_number_child<int>("Red").get_value_or(255),
+               node->optional_number_child<int>("Green").get_value_or(255),
+               node->optional_number_child<int>("Blue").get_value_or(255)
+               )
+       , _outline (node->optional_bool_child("Outline").get_value_or(false))
+       , _outline_colour (
+               node->optional_number_child<int>("OutlineRed").get_value_or(255),
+               node->optional_number_child<int>("OutlineGreen").get_value_or(255),
+               node->optional_number_child<int>("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<string> (_length.get ()));
+       node->add_child("Red")->add_child_text (raw_convert<string> (_colour.r));
+       node->add_child("Green")->add_child_text (raw_convert<string> (_colour.g));
+       node->add_child("Blue")->add_child_text (raw_convert<string> (_colour.b));
+       node->add_child("Outline")->add_child_text (raw_convert<string> (_outline));
+       node->add_child("OutlineRed")->add_child_text (raw_convert<string> (_outline_colour.r));
+       node->add_child("OutlineGreen")->add_child_text (raw_convert<string> (_outline_colour.g));
+       node->add_child("OutlineBlue")->add_child_text (raw_convert<string> (_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);
+}
index e050634030ed06db9d474e7bc33dacc8ba6cb002..68864f58c1a74830a4006e71f7387eceee5d03c7 100644 (file)
 
 #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<double> _frame_rate;
+       dcp::Colour _colour;
+       bool _outline;
+       dcp::Colour _outline_colour;
 };
index 26a7ebcae23634ce5837844b9477b2bedf54f101..0bb54333751007ad8fd1cd9c89e26aed4bde049e 100644 (file)
@@ -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<const SubRipContent> content)
        : SubtitleDecoder (content)
@@ -57,6 +58,9 @@ SubRipDecoder::pass (PassReason, bool)
 
        /* XXX: we are ignoring positioning specified in the file */
 
+       shared_ptr<const SubRipContent> content = dynamic_pointer_cast<const SubRipContent> (_subtitle_content);
+       DCPOMATIC_ASSERT (content);
+
        list<dcp::SubtitleString> out;
        for (list<sub::Line>::const_iterator i = _subtitles[_next].lines.begin(); i != _subtitles[_next].lines.end(); ++i) {
                for (list<sub::Block>::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)
                                        )
index dd9c024e6f9e1583a5af65d5350daabce3b852a9..ef62d8b88355f73c6a6f4e308e036664296fce1c 100644 (file)
@@ -44,6 +44,7 @@ protected:
 
        std::list<ContentImageSubtitle> _decoded_image_subtitles;
        std::list<ContentTextSubtitle> _decoded_text_subtitles;
+       boost::shared_ptr<const SubtitleContent> _subtitle_content;
 
 private:
        template <class T>
@@ -54,8 +55,6 @@ private:
         */
        virtual std::list<ContentTimePeriod> image_subtitles_during (ContentTimePeriod period, bool starting) const = 0;
        virtual std::list<ContentTimePeriod> text_subtitles_during (ContentTimePeriod period, bool starting) const = 0;
-
-       boost::shared_ptr<const SubtitleContent> _subtitle_content;
 };
 
 #endif
diff --git a/src/wx/subtitle_appearance_dialog.cc b/src/wx/subtitle_appearance_dialog.cc
new file mode 100644 (file)
index 0000000..0db55ed
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    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 <wx/wx.h>
+#include <wx/clrpicker.h>
+
+using boost::shared_ptr;
+
+SubtitleAppearanceDialog::SubtitleAppearanceDialog (wxWindow* parent, shared_ptr<SubRipContent> 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 (file)
index 0000000..f4fe307
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    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 <boost/shared_ptr.hpp>
+
+class wxCheckBox;
+class wxColourPickerCtrl;
+class SubRipContent;
+
+class SubtitleAppearanceDialog : public TableDialog
+{
+public:
+       SubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr<SubRipContent> content);
+
+       void apply ();
+
+private:
+       wxColourPickerCtrl* _colour;
+       wxCheckBox* _outline;
+       wxColourPickerCtrl* _outline_colour;
+
+       boost::shared_ptr<SubRipContent> _content;
+};
index e9de4fccd0bcca0e18c6761beea9974c3a21df84..74a1103499a656a97ef469ed9950ff11297b7f9b 100644 (file)
@@ -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<SubtitleContent> 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<SubRipContent> sr = dynamic_pointer_cast<SubRipContent> (c.front ());
+       DCPOMATIC_ASSERT (sr);
+
+       SubtitleAppearanceDialog* d = new SubtitleAppearanceDialog (this, sr);
+       if (d->ShowModal () == wxID_OK) {
+               d->apply ();
+       }
+       d->Destroy ();
+}
index 7eb9cd27e8aab4e6913048db6a97491e4e043f21..e3fd7cd75617e91c29a767cbfab3fd20a7b0a7d9 100644 (file)
@@ -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;
 };
index d2bf33cd35132e194d4253f17a2a6a95f1520cc2..3b61eb5c6e87428eaec9d6d8be49f66795790bf6 100644 (file)
@@ -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