+static float
+calculate_fade_factor (StringText const& first, DCPTime time, int frame_rate)
+{
+ float fade_factor = 1;
+
+ /* Round the fade start/end to the nearest frame start. Otherwise if a subtitle starts just after
+ the start of a frame it will be faded out.
+ */
+ 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) {
+ fade_factor = 0;
+ }
+
+ return fade_factor;
+}
+
+
+static int
+x_position (StringText const& first, int target_width, int layout_width)
+{
+ int x = 0;
+ switch (first.h_align()) {
+ case dcp::HAlign::LEFT:
+ /* h_position is distance between left of frame and left of subtitle */
+ x = first.h_position() * target_width;
+ break;
+ case dcp::HAlign::CENTER:
+ /* h_position is distance between centre of frame and centre of subtitle */
+ x = (0.5 + first.h_position()) * target_width - layout_width / 2;
+ break;
+ case dcp::HAlign::RIGHT:
+ /* h_position is distance between right of frame and right of subtitle */
+ x = (1.0 - first.h_position()) * target_width - layout_width;
+ break;
+ }
+
+ return x;
+}
+
+
+static int
+y_position (StringText const& first, int target_height, int layout_height)
+{
+ int y = 0;
+ switch (first.v_align()) {
+ case dcp::VAlign::TOP:
+ /* SMPTE says that v_position is the distance between top
+ of frame and top of subtitle, but this doesn't always seem to be
+ the case in practice; Gunnar Ásgeirsson's Dolby server appears
+ to put VAlign::TOP subs with v_position as the distance between top
+ of frame and bottom of subtitle.
+ */
+ y = first.v_position() * target_height - layout_height;
+ break;
+ case dcp::VAlign::CENTER:
+ /* v_position is distance between centre of frame and centre of subtitle */
+ y = (0.5 + first.v_position()) * target_height - layout_height / 2;
+ break;
+ case dcp::VAlign::BOTTOM:
+ /* v_position is distance between bottom of frame and bottom of subtitle */
+ y = (1.0 - first.v_position()) * target_height - layout_height;
+ break;
+ }
+
+ return y;
+}
+
+
+static void
+setup_layout (Glib::RefPtr<Pango::Layout> layout, string font_name, string markup)
+{
+ layout->set_alignment (Pango::ALIGN_LEFT);
+ Pango::FontDescription font (font_name);
+ layout->set_font_description (font);
+ layout->set_markup (markup);
+}
+
+
+/** Create a Pango layout using a dummy context which we can use to calculate the size
+ * of the text we will render. Then we can transfer the layout over to the real context
+ * for the actual render.
+ */
+static Glib::RefPtr<Pango::Layout>
+create_layout()
+{
+ auto c_font_map = pango_cairo_font_map_new ();
+ DCPOMATIC_ASSERT (c_font_map);
+ auto font_map = Glib::wrap (c_font_map);
+ auto c_context = pango_font_map_create_context (c_font_map);
+ DCPOMATIC_ASSERT (c_context);
+ auto context = Glib::wrap (c_context);
+ return Pango::Layout::create (context);
+}
+
+