2 Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <cairomm/cairomm.h>
22 #include "render_subtitles.h"
31 using boost::shared_ptr;
32 using boost::optional;
35 calculate_position (dcp::VAlign v_align, double v_position, int target_height, int offset)
39 return (v_position / 100) * target_height - offset;
41 return (0.5 + v_position / 100) * target_height - offset;
43 return (1.0 - v_position / 100) * target_height - offset;
50 render_subtitles (list<dcp::SubtitleString> subtitles, dcp::Size target, shared_ptr<Image>& image, Position<int>& position)
52 if (subtitles.empty ()) {
57 /* Estimate height that the subtitle image needs to be */
60 for (list<dcp::SubtitleString>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
61 int const b = calculate_position (i->v_align(), i->v_position(), target.height, 0);
62 int const t = b - i->size() * target.height / (11 * 72);
64 top = min (top.get_value_or (t), t);
65 bottom = max (bottom.get_value_or (b), b);
69 bottom = bottom.get() + 32;
71 image.reset (new Image (PIX_FMT_RGBA, dcp::Size (target.width, bottom.get() - top.get ()), false));
74 Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create (
79 Cairo::ImageSurface::format_stride_for_width (Cairo::FORMAT_ARGB32, image->size().width)
82 Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (surface);
83 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
85 layout->set_width (image->size().width * PANGO_SCALE);
86 layout->set_alignment (Pango::ALIGN_CENTER);
88 context->set_line_width (1);
90 for (list<dcp::SubtitleString>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
91 string f = i->font ();
95 Pango::FontDescription font (f);
96 font.set_absolute_size (i->size_in_pixels (target.height) * PANGO_SCALE);
98 font.set_style (Pango::STYLE_ITALIC);
100 layout->set_font_description (font);
101 layout->set_text (i->text ());
103 /* Compute fade factor */
105 float fade_factor = 1;
107 dcp::Time now (time * 1000 / (4 * TIME_HZ));
108 dcp::Time end_fade_up = i->in() + i->fade_up_time ();
109 dcp::Time start_fade_down = i->out() - i->fade_down_time ();
110 if (now < end_fade_up) {
111 fade_factor = (now - i->in()) / i->fade_up_time();
112 } else if (now > start_fade_down) {
113 fade_factor = 1.0 - ((now - start_fade_down) / i->fade_down_time ());
117 layout->update_from_cairo_context (context);
119 /* Work out position */
122 int const y = calculate_position (i->v_align (), i->v_position (), target.height, (layout->get_baseline() / PANGO_SCALE) + top.get ());
124 if (i->effect() == dcp::SHADOW) {
125 /* Drop-shadow effect */
126 dcp::Color const ec = i->effect_color ();
127 context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
128 context->move_to (x + 4, y + 4);
129 layout->add_to_cairo_context (context);
133 /* The actual subtitle */
134 context->move_to (x, y);
135 dcp::Color const c = i->color ();
136 context->set_source_rgba (float(c.r) / 255, float(c.g) / 255, float(c.b) / 255, fade_factor);
137 layout->add_to_cairo_context (context);
140 if (i->effect() == dcp::BORDER) {
142 context->move_to (x, y);
143 dcp::Color ec = i->effect_color ();
144 context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
145 layout->add_to_cairo_context (context);