summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2016-08-16 08:41:25 +0100
committerCarl Hetherington <cth@carlh.net>2016-08-16 16:56:32 +0100
commit94b3924ed3cbf4fbfa2445ca1007f25c53dc8b60 (patch)
tree40abbdf14043edf4d6592b82acf3eccb960ea71f
parent03c5a8155043613c01e0e151735a7fcf8ab84415 (diff)
Basic support for fading subtitles in and out (#923).
-rw-r--r--ChangeLog2
-rw-r--r--src/lib/player.cc2
-rw-r--r--src/lib/render_subtitles.cc28
-rw-r--r--src/lib/render_subtitles.h5
-rw-r--r--src/lib/subtitle_content.cc28
-rw-r--r--src/lib/subtitle_content.h34
-rw-r--r--src/lib/subtitle_decoder.cc4
-rw-r--r--src/wx/subtitle_panel.cc2
-rw-r--r--src/wx/text_subtitle_appearance_dialog.cc30
-rw-r--r--src/wx/text_subtitle_appearance_dialog.h9
10 files changed, 108 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index c6d270897..60e3de29c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
2016-08-16 c.hetherington <cth@carlh.net>
+ * Basic support for fading subtitles in and out (#923).
+
* Fix error on seeking through imported mulit-reel DCPs.
* Simple information on mouse position in the video waveform (part of #932).
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<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
+ list<PositionImage> 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<dcp::SubtitleString> 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<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target)
+render_line (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > 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<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts,
}
}
- FcPattern* pattern = FcPatternBuild (0, FC_FILE, FcTypeString, font_files.get(FontFiles::NORMAL).get().string().c_str(), static_cast<char *> (0));
+ FcPattern* pattern = FcPatternBuild (
+ 0, FC_FILE, FcTypeString, font_files.get(FontFiles::NORMAL).get().string().c_str(), static_cast<char *> (0)
+ );
FcObjectSet* object_set = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, static_cast<char *> (0));
FcFontSet* font_set = FcFontList (fc_config, pattern, object_set);
if (font_set) {
@@ -248,9 +252,18 @@ render_line (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > 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<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts,
return PositionImage (image, Position<int> (max (0, x), max (0, y)));
}
+/** @param time Time of the frame that these subtitles are going on */
list<PositionImage>
-render_subtitles (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target)
+render_subtitles (list<dcp::SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time)
{
list<dcp::SubtitleString> pending;
list<PositionImage> 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 <dcp/subtitle_string.h>
#include <dcp/util.h>
class Font;
std::string marked_up (std::list<dcp::SubtitleString> subtitles);
-std::list<PositionImage> render_subtitles (std::list<dcp::SubtitleString>, std::list<boost::shared_ptr<Font> > fonts, dcp::Size);
+std::list<PositionImage> render_subtitles (
+ std::list<dcp::SubtitleString>, std::list<boost::shared_ptr<Font> > 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<double>("LineSpacing").get_value_or (1))
+ , _fade_in (node->optional_number_child<Frame>("SubtitleFadeIn").get_value_or (0))
+ , _fade_out (node->optional_number_child<Frame>("SubtitleFadeOut").get_value_or (0))
{
if (version >= 32) {
_use = node->bool_child ("UseSubtitles");
@@ -188,6 +192,10 @@ SubtitleContent::SubtitleContent (Content* parent, vector<shared_ptr<Content> >
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<shared_ptr<Font> > 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<shared_ptr<Content> >
_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<string> (_effect_colour.g));
root->add_child("EffectBlue")->add_child_text (raw_convert<string> (_effect_colour.b));
root->add_child("LineSpacing")->add_child_text (raw_convert<string> (_line_spacing));
+ root->add_child("SubtitleFadeIn")->add_child_text (raw_convert<string> (_fade_in.get()));
+ root->add_child("SubtitleFadeOut")->add_child_text (raw_convert<string> (_fade_out.get()));
for (list<shared_ptr<Font> >::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<string> (y_scale())
+ "_" + raw_convert<string> (x_offset())
+ "_" + raw_convert<string> (y_offset())
- + "_" + raw_convert<string> (line_spacing());
+ + "_" + raw_convert<string> (line_spacing())
+ + "_" + raw_convert<string> (fade_in().get())
+ + "_" + raw_convert<string> (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<SubtitleContent> from_xml (Content* parent, cxml::ConstNodePtr, int version);
protected:
@@ -159,6 +168,8 @@ private:
void font_changed ();
void connect_to_fonts ();
+ std::list<boost::signals2::connection> _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<boost::signals2::connection> _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<SubtitleContent> content)
+TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, shared_ptr<Content> 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<ContentTime> (this);
+ add (_fade_in);
+
+ add (_("Fade out time"), true);
+ _fade_out = new Timecode<ContentTime> (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 <boost/shared_ptr.hpp>
class wxRadioButton;
class wxColourPickerCtrl;
-class SubtitleContent;
+class Content;
class TextSubtitleAppearanceDialog : public TableDialog
{
public:
- TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr<SubtitleContent> content);
+ TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr<Content> content);
void apply ();
@@ -37,6 +38,8 @@ private:
wxRadioButton* _outline;
wxRadioButton* _shadow;
wxColourPickerCtrl* _effect_colour;
+ Timecode<ContentTime>* _fade_in;
+ Timecode<ContentTime>* _fade_out;
- boost::shared_ptr<SubtitleContent> _content;
+ boost::shared_ptr<Content> _content;
};