From 94b3924ed3cbf4fbfa2445ca1007f25c53dc8b60 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 16 Aug 2016 08:41:25 +0100 Subject: Basic support for fading subtitles in and out (#923). --- src/lib/player.cc | 2 +- src/lib/render_subtitles.cc | 28 ++++++++++++++++++------- src/lib/render_subtitles.h | 5 ++++- src/lib/subtitle_content.cc | 28 ++++++++++++++++++++++++- src/lib/subtitle_content.h | 34 +++++++++++++++++++++---------- src/lib/subtitle_decoder.cc | 4 ++-- src/wx/subtitle_panel.cc | 2 +- src/wx/text_subtitle_appearance_dialog.cc | 30 +++++++++++++++++++-------- src/wx/text_subtitle_appearance_dialog.h | 9 +++++--- 9 files changed, 106 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/lib/player.cc b/src/lib/player.cc index 7dcaf68b7..07bb097c2 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -337,7 +337,7 @@ Player::get_video (DCPTime time, bool accurate) /* Text subtitles (rendered to an image) */ if (!ps.text.empty ()) { - list s = render_subtitles (ps.text, ps.fonts, _video_container_size); + list s = render_subtitles (ps.text, ps.fonts, _video_container_size, time); copy (s.begin (), s.end (), back_inserter (sub_images)); } diff --git a/src/lib/render_subtitles.cc b/src/lib/render_subtitles.cc index 5c8cf13ee..6e4dcf46a 100644 --- a/src/lib/render_subtitles.cc +++ b/src/lib/render_subtitles.cc @@ -92,9 +92,11 @@ marked_up (list subtitles) return out; } -/** @param subtitles A list of subtitles that are all on the same line */ +/** @param subtitles A list of subtitles that are all on the same line, + * at the same time and with the same fade in/out. + */ static PositionImage -render_line (list subtitles, list > fonts, dcp::Size target) +render_line (list subtitles, list > fonts, dcp::Size target, DCPTime time) { /* XXX: this method can only handle italic / bold changes mid-line, nothing else yet. @@ -205,7 +207,9 @@ render_line (list subtitles, list > fonts, } } - FcPattern* pattern = FcPatternBuild (0, FC_FILE, FcTypeString, font_files.get(FontFiles::NORMAL).get().string().c_str(), static_cast (0)); + FcPattern* pattern = FcPatternBuild ( + 0, FC_FILE, FcTypeString, font_files.get(FontFiles::NORMAL).get().string().c_str(), static_cast (0) + ); FcObjectSet* object_set = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, static_cast (0)); FcFontSet* font_set = FcFontList (fc_config, pattern, object_set); if (font_set) { @@ -248,9 +252,18 @@ render_line (list subtitles, list > fonts, layout->set_markup (marked_up (subtitles)); /* Compute fade factor */ - /* XXX */ float fade_factor = 1; + DCPTime const fade_in_start = DCPTime::from_seconds (subtitles.front().in().as_seconds ()); + DCPTime const fade_in_end = fade_in_start + DCPTime::from_seconds (subtitles.front().fade_up_time().as_seconds ()); + DCPTime const fade_out_end = DCPTime::from_seconds (subtitles.front().out().as_seconds ()); + DCPTime const fade_out_start = fade_out_end - DCPTime::from_seconds (subtitles.front().fade_down_time().as_seconds ()); + if (fade_in_start <= time && time <= fade_in_end && fade_in_start != fade_in_end) { + fade_factor = DCPTime(time - fade_in_start).seconds() / DCPTime(fade_in_end - fade_in_start).seconds(); + } else if (fade_out_start <= time && time <= fade_out_end && fade_out_start != fade_out_end) { + fade_factor = 1 - DCPTime(time - fade_out_start).seconds() / DCPTime(fade_out_end - fade_out_start).seconds(); + } + context->scale (xscale, yscale); layout->update_from_cairo_context (context); @@ -335,22 +348,23 @@ render_line (list subtitles, list > fonts, return PositionImage (image, Position (max (0, x), max (0, y))); } +/** @param time Time of the frame that these subtitles are going on */ list -render_subtitles (list subtitles, list > fonts, dcp::Size target) +render_subtitles (list subtitles, list > fonts, dcp::Size target, DCPTime time) { list pending; list images; BOOST_FOREACH (dcp::SubtitleString const & i, subtitles) { if (!pending.empty() && fabs (i.v_position() - pending.back().v_position()) > 1e-4) { - images.push_back (render_line (pending, fonts, target)); + images.push_back (render_line (pending, fonts, target, time)); pending.clear (); } pending.push_back (i); } if (!pending.empty ()) { - images.push_back (render_line (pending, fonts, target)); + images.push_back (render_line (pending, fonts, target, time)); } return images; diff --git a/src/lib/render_subtitles.h b/src/lib/render_subtitles.h index 281efe97a..79194568a 100644 --- a/src/lib/render_subtitles.h +++ b/src/lib/render_subtitles.h @@ -19,10 +19,13 @@ */ #include "position_image.h" +#include "dcpomatic_time.h" #include #include class Font; std::string marked_up (std::list subtitles); -std::list render_subtitles (std::list, std::list > fonts, dcp::Size); +std::list render_subtitles ( + std::list, std::list > fonts, dcp::Size, DCPTime + ); diff --git a/src/lib/subtitle_content.cc b/src/lib/subtitle_content.cc index 21bfd8a2d..c57c72fb1 100644 --- a/src/lib/subtitle_content.cc +++ b/src/lib/subtitle_content.cc @@ -52,6 +52,8 @@ int const SubtitleContentProperty::OUTLINE = 509; int const SubtitleContentProperty::SHADOW = 510; int const SubtitleContentProperty::EFFECT_COLOUR = 511; int const SubtitleContentProperty::LINE_SPACING = 512; +int const SubtitleContentProperty::FADE_IN = 513; +int const SubtitleContentProperty::FADE_OUT = 514; SubtitleContent::SubtitleContent (Content* parent) : ContentPart (parent) @@ -107,6 +109,8 @@ SubtitleContent::SubtitleContent (Content* parent, cxml::ConstNodePtr node, int , _outline (node->optional_bool_child("Outline").get_value_or(false)) , _shadow (node->optional_bool_child("Shadow").get_value_or(false)) , _line_spacing (node->optional_number_child("LineSpacing").get_value_or (1)) + , _fade_in (node->optional_number_child("SubtitleFadeIn").get_value_or (0)) + , _fade_out (node->optional_number_child("SubtitleFadeOut").get_value_or (0)) { if (version >= 32) { _use = node->bool_child ("UseSubtitles"); @@ -188,6 +192,10 @@ SubtitleContent::SubtitleContent (Content* parent, vector > throw JoinError (_("Content to be joined must have the same subtitle line spacing.")); } + if ((c[i]->subtitle->fade_in() != ref->fade_in()) || (c[i]->subtitle->fade_out() != ref->fade_out())) { + throw JoinError (_("Content to be joined must have the same subtitle fades.")); + } + list > fonts = c[i]->subtitle->fonts (); if (fonts.size() != ref_fonts.size()) { throw JoinError (_("Content to be joined must use the same fonts.")); @@ -214,6 +222,8 @@ SubtitleContent::SubtitleContent (Content* parent, vector > _language = ref->language (); _fonts = ref_fonts; _line_spacing = ref->line_spacing (); + _fade_in = ref->fade_in (); + _fade_out = ref->fade_out (); connect_to_fonts (); } @@ -240,6 +250,8 @@ SubtitleContent::as_xml (xmlpp::Node* root) const root->add_child("EffectGreen")->add_child_text (raw_convert (_effect_colour.g)); root->add_child("EffectBlue")->add_child_text (raw_convert (_effect_colour.b)); root->add_child("LineSpacing")->add_child_text (raw_convert (_line_spacing)); + root->add_child("SubtitleFadeIn")->add_child_text (raw_convert (_fade_in.get())); + root->add_child("SubtitleFadeOut")->add_child_text (raw_convert (_fade_out.get())); for (list >::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) { (*i)->as_xml (root->add_child("Font")); @@ -253,7 +265,9 @@ SubtitleContent::identifier () const + "_" + raw_convert (y_scale()) + "_" + raw_convert (x_offset()) + "_" + raw_convert (y_offset()) - + "_" + raw_convert (line_spacing()); + + "_" + raw_convert (line_spacing()) + + "_" + raw_convert (fade_in().get()) + + "_" + raw_convert (fade_out().get()); /* XXX: I suppose really _fonts shouldn't be in here, since not all types of subtitle content involve fonts. @@ -369,3 +383,15 @@ SubtitleContent::set_line_spacing (double s) { maybe_set (_line_spacing, s, SubtitleContentProperty::LINE_SPACING); } + +void +SubtitleContent::set_fade_in (ContentTime t) +{ + maybe_set (_fade_in, t, SubtitleContentProperty::FADE_IN); +} + +void +SubtitleContent::set_fade_out (ContentTime t) +{ + maybe_set (_fade_out, t, SubtitleContentProperty::FADE_OUT); +} diff --git a/src/lib/subtitle_content.h b/src/lib/subtitle_content.h index c8492b91a..fd02d032a 100644 --- a/src/lib/subtitle_content.h +++ b/src/lib/subtitle_content.h @@ -44,6 +44,8 @@ public: static int const SHADOW; static int const EFFECT_COLOUR; static int const LINE_SPACING; + static int const FADE_IN; + static int const FADE_OUT; }; /** @class SubtitleContent @@ -70,6 +72,13 @@ public: void set_x_scale (double); void set_y_scale (double); void set_language (std::string language); + void set_colour (dcp::Colour); + void set_outline (bool); + void set_shadow (bool); + void set_effect_colour (dcp::Colour); + void set_line_spacing (double s); + void set_fade_in (ContentTime); + void set_fade_out (ContentTime); bool use () const { boost::mutex::scoped_lock lm (_mutex); @@ -111,41 +120,41 @@ public: return _language; } - 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_shadow (bool); - bool shadow () const { boost::mutex::scoped_lock lm (_mutex); return _shadow; } - void set_effect_colour (dcp::Colour); - dcp::Colour effect_colour () const { boost::mutex::scoped_lock lm (_mutex); return _effect_colour; } - void set_line_spacing (double s); - double line_spacing () const { boost::mutex::scoped_lock lm (_mutex); return _line_spacing; } + ContentTime fade_in () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_in; + } + + ContentTime fade_out () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_out; + } + static boost::shared_ptr from_xml (Content* parent, cxml::ConstNodePtr, int version); protected: @@ -159,6 +168,8 @@ private: void font_changed (); void connect_to_fonts (); + std::list _font_connections; + bool _use; bool _burn; /** x offset for placing subtitles, as a proportion of the container width; @@ -178,9 +189,10 @@ private: bool _outline; bool _shadow; dcp::Colour _effect_colour; - std::list _font_connections; /** scaling factor for line spacing; 1 is "standard", < 1 is closer together, > 1 is further apart */ double _line_spacing; + ContentTime _fade_in; + ContentTime _fade_out; }; #endif diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index 5ae1a703e..ba6fe4600 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.cc @@ -245,8 +245,8 @@ SubtitleDecoder::give_text (ContentTimePeriod period, sub::Subtitle const & subt j.text, effect, content()->effect_colour(), - dcp::Time (0, 1000), - dcp::Time (0, 1000) + dcp::Time (content()->fade_in().seconds(), 1000), + dcp::Time (content()->fade_out().seconds(), 1000) ) ); } diff --git a/src/wx/subtitle_panel.cc b/src/wx/subtitle_panel.cc index 3db50104f..e4db3560e 100644 --- a/src/wx/subtitle_panel.cc +++ b/src/wx/subtitle_panel.cc @@ -477,7 +477,7 @@ SubtitlePanel::appearance_dialog_clicked () } if (text) { - TextSubtitleAppearanceDialog* d = new TextSubtitleAppearanceDialog (this, c.front()->subtitle); + TextSubtitleAppearanceDialog* d = new TextSubtitleAppearanceDialog (this, c.front()); if (d->ShowModal () == wxID_OK) { d->apply (); } diff --git a/src/wx/text_subtitle_appearance_dialog.cc b/src/wx/text_subtitle_appearance_dialog.cc index a826cbc66..49c8920f6 100644 --- a/src/wx/text_subtitle_appearance_dialog.cc +++ b/src/wx/text_subtitle_appearance_dialog.cc @@ -26,7 +26,7 @@ using boost::shared_ptr; -TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, shared_ptr content) +TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, shared_ptr content) : TableDialog (parent, _("Subtitle appearance"), 2, 1, true) , _content (content) { @@ -50,23 +50,35 @@ TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, sh _effect_colour = new wxColourPickerCtrl (this, wxID_ANY); add (_effect_colour); + add (_("Fade in time"), true); + _fade_in = new Timecode (this); + add (_fade_in); + + add (_("Fade out time"), true); + _fade_out = new Timecode (this); + add (_fade_out); + layout (); - _colour->SetColour (wxColour (_content->colour().r, _content->colour().g, _content->colour().b)); - _outline->SetValue (_content->outline ()); - _shadow->SetValue (_content->shadow ()); + _colour->SetColour (wxColour (_content->subtitle->colour().r, _content->subtitle->colour().g, _content->subtitle->colour().b)); + _outline->SetValue (_content->subtitle->outline ()); + _shadow->SetValue (_content->subtitle->shadow ()); _effect_colour->SetColour ( - wxColour (_content->effect_colour().r, _content->effect_colour().g, _content->effect_colour().b) + wxColour (_content->subtitle->effect_colour().r, _content->subtitle->effect_colour().g, _content->subtitle->effect_colour().b) ); + _fade_in->set (_content->subtitle->fade_in(), _content->active_video_frame_rate ()); + _fade_out->set (_content->subtitle->fade_out(), _content->active_video_frame_rate ()); } void TextSubtitleAppearanceDialog::apply () { wxColour const c = _colour->GetColour (); - _content->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); - _content->set_outline (_outline->GetValue ()); - _content->set_shadow (_shadow->GetValue ()); + _content->subtitle->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); + _content->subtitle->set_outline (_outline->GetValue ()); + _content->subtitle->set_shadow (_shadow->GetValue ()); wxColour const ec = _effect_colour->GetColour (); - _content->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue())); + _content->subtitle->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue())); + _content->subtitle->set_fade_in (_fade_in->get (_content->active_video_frame_rate ())); + _content->subtitle->set_fade_out (_fade_out->get (_content->active_video_frame_rate ())); } diff --git a/src/wx/text_subtitle_appearance_dialog.h b/src/wx/text_subtitle_appearance_dialog.h index c67ec8756..55fe51066 100644 --- a/src/wx/text_subtitle_appearance_dialog.h +++ b/src/wx/text_subtitle_appearance_dialog.h @@ -19,16 +19,17 @@ */ #include "table_dialog.h" +#include "timecode.h" #include class wxRadioButton; class wxColourPickerCtrl; -class SubtitleContent; +class Content; class TextSubtitleAppearanceDialog : public TableDialog { public: - TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr content); + TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr content); void apply (); @@ -37,6 +38,8 @@ private: wxRadioButton* _outline; wxRadioButton* _shadow; wxColourPickerCtrl* _effect_colour; + Timecode* _fade_in; + Timecode* _fade_out; - boost::shared_ptr _content; + boost::shared_ptr _content; }; -- cgit v1.2.3