Fix fade factor calculation when there is no end time.
[dcpomatic.git] / src / lib / render_text.cc
index 989bc7dfea7d33e83c575057d700c3d9f482fcc6..6f7614d36f5050b4bde1f9c553c59d5827110f7f 100644 (file)
@@ -83,27 +83,53 @@ setup_layout (Glib::RefPtr<Pango::Layout> layout, string font_name, string marku
 
 
 string
-marked_up (list<StringText> subtitles, int target_height, float fade_factor)
+marked_up (list<StringText> subtitles, int target_height, float fade_factor, string font_name)
 {
-       string out;
+       auto constexpr pixels_to_1024ths_point = 72 * 1024 / 96;
 
-       for (auto const& i: subtitles) {
-               out += "<span ";
-               if (i.italic()) {
-                       out += "style=\"italic\" ";
+       auto make_span = [target_height, fade_factor](StringText const& subtitle, string text, string extra_attribute) {
+               string span;
+               span += "<span ";
+               if (subtitle.italic()) {
+                       span += "style=\"italic\" ";
                }
-               if (i.bold()) {
-                       out += "weight=\"bold\" ";
+               if (subtitle.bold()) {
+                       span += "weight=\"bold\" ";
                }
-               if (i.underline()) {
-                       out += "underline=\"single\" ";
+               if (subtitle.underline()) {
+                       span += "underline=\"single\" ";
                }
-               out += "size=\"" + dcp::raw_convert<string>(i.size_in_pixels(target_height) * 72 * 1024 / 96) + "\" ";
+               span += "size=\"" + dcp::raw_convert<string>(subtitle.size_in_pixels(target_height) * pixels_to_1024ths_point) + "\" ";
                /* Between 1-65535 inclusive, apparently... */
-               out += "alpha=\"" + dcp::raw_convert<string>(int(floor(fade_factor * 65534)) + 1) + "\" ";
-               out += "color=\"#" + i.colour().to_rgb_string() + "\">";
-               out += i.text();
-               out += "</span>";
+               span += "alpha=\"" + dcp::raw_convert<string>(int(floor(fade_factor * 65534)) + 1) + "\" ";
+               span += "color=\"#" + subtitle.colour().to_rgb_string() + "\"";
+               if (!extra_attribute.empty()) {
+                       span += " " + extra_attribute;
+               }
+               span += ">";
+               span += text;
+               span += "</span>";
+               return span;
+       };
+
+       string out;
+       for (auto const& i: subtitles) {
+               if (std::abs(i.space_before()) > dcp::SPACE_BEFORE_EPSILON) {
+                       /* We need to insert some horizontal space into the layout.  The only way I can find to do this
+                        * is to write a " " with some special letter_spacing.  As far as I can see, such a space will
+                        * be written with letter_spacing either side.  This means that to get a horizontal space x we
+                        * need to write a " " with letter spacing (x - s) / 2, where s is the width of the " ".
+                        */
+                       auto layout = create_layout();
+                       setup_layout(layout, font_name, make_span(i, " ", {}));
+                       int space_width;
+                       int dummy;
+                       layout->get_pixel_size(space_width, dummy);
+                       auto spacing = ((i.space_before() * i.size_in_pixels(target_height) - space_width) / 2) * pixels_to_1024ths_point;
+                       out += make_span(i, " ", "letter_spacing=\"" + dcp::raw_convert<string>(spacing) + "\"");
+               }
+
+               out += make_span(i, i.text(), {});
        }
 
        return out;
@@ -218,19 +244,30 @@ calculate_fade_factor (StringText const& first, DCPTime time, int frame_rate)
        */
        auto const fade_in_start = DCPTime::from_seconds(first.in().as_seconds()).round(frame_rate);
        auto const fade_in_end = fade_in_start + DCPTime::from_seconds (first.fade_up_time().as_seconds ());
-       auto const fade_out_end =  DCPTime::from_seconds (first.out().as_seconds()).round(frame_rate);
-       auto const fade_out_start = fade_out_end - DCPTime::from_seconds (first.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();
        }
-       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();
-       }
-       if (time < fade_in_start || time > fade_out_end) {
+
+       if (time < fade_in_start) {
                fade_factor = 0;
        }
 
+       /* first.out() may be zero if we don't know when this subtitle will finish.  We can only think about
+        * fading out if we _do_ know when it will finish.
+        */
+       if (first.out() != dcp::Time()) {
+               auto const fade_out_end = DCPTime::from_seconds (first.out().as_seconds()).round(frame_rate);
+               auto const fade_out_start = fade_out_end - DCPTime::from_seconds(first.fade_down_time().as_seconds());
+
+               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();
+               }
+               if (time > fade_out_end) {
+                       fade_factor = 0;
+               }
+       }
+
        return fade_factor;
 }
 
@@ -301,7 +338,7 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font>> fonts, dcp::Size
 
        auto const font_name = setup_font (first, fonts);
        auto const fade_factor = calculate_fade_factor (first, time, frame_rate);
-       auto const markup = marked_up (subtitles, target.height, fade_factor);
+       auto const markup = marked_up (subtitles, target.height, fade_factor, font_name);
        auto layout = create_layout ();
        setup_layout (layout, font_name, markup);
        dcp::Size size;