/* Copyright (C) 2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "render_subtitles.h" #include "types.h" #include "image.h" using std::list; using std::cout; using std::string; using std::min; using std::max; using std::pair; using boost::shared_ptr; using boost::optional; static int calculate_position (dcp::VAlign v_align, double v_position, int target_height, int offset) { switch (v_align) { case dcp::TOP: return v_position * target_height - offset; case dcp::CENTER: return (0.5 + v_position) * target_height - offset; case dcp::BOTTOM: return (1.0 - v_position) * target_height - offset; } return 0; } PositionImage render_subtitles (list subtitles, dcp::Size target) { if (subtitles.empty ()) { return PositionImage (); } /* Estimate height that the subtitle image needs to be */ optional top; optional bottom; for (list::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) { int const b = calculate_position (i->v_align(), i->v_position(), target.height, 0); int const t = b - i->size() * target.height / (11 * 72); top = min (top.get_value_or (t), t); bottom = max (bottom.get_value_or (b), b); } top = top.get() - 32; bottom = bottom.get() + 32; shared_ptr image (new Image (PIX_FMT_RGBA, dcp::Size (target.width, bottom.get() - top.get ()), false)); image->make_black (); Cairo::RefPtr surface = Cairo::ImageSurface::create ( image->data()[0], Cairo::FORMAT_ARGB32, image->size().width, image->size().height, Cairo::ImageSurface::format_stride_for_width (Cairo::FORMAT_ARGB32, image->size().width) ); Cairo::RefPtr context = Cairo::Context::create (surface); Glib::RefPtr layout = Pango::Layout::create (context); layout->set_width (image->size().width * PANGO_SCALE); layout->set_alignment (Pango::ALIGN_CENTER); context->set_line_width (1); for (list::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) { string f = i->font (); if (f.empty ()) { f = "Arial"; } Pango::FontDescription font (f); font.set_absolute_size (i->size_in_pixels (target.height) * PANGO_SCALE); if (i->italic ()) { font.set_style (Pango::STYLE_ITALIC); } layout->set_font_description (font); layout->set_text (i->text ()); /* Compute fade factor */ /* XXX */ float fade_factor = 1; #if 0 dcp::Time now (time * 1000 / (4 * TIME_HZ)); dcp::Time end_fade_up = i->in() + i->fade_up_time (); dcp::Time start_fade_down = i->out() - i->fade_down_time (); if (now < end_fade_up) { fade_factor = (now - i->in()) / i->fade_up_time(); } else if (now > start_fade_down) { fade_factor = 1.0 - ((now - start_fade_down) / i->fade_down_time ()); } #endif layout->update_from_cairo_context (context); /* Work out position */ int const x = 0; int const y = calculate_position (i->v_align (), i->v_position (), target.height, (layout->get_baseline() / PANGO_SCALE) + top.get ()); if (i->effect() == dcp::SHADOW) { /* Drop-shadow effect */ dcp::Color const ec = i->effect_color (); context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor); context->move_to (x + 4, y + 4); layout->add_to_cairo_context (context); context->fill (); } /* The actual subtitle */ context->move_to (x, y); dcp::Color const c = i->color (); context->set_source_rgba (float(c.r) / 255, float(c.g) / 255, float(c.b) / 255, fade_factor); layout->add_to_cairo_context (context); context->fill (); if (i->effect() == dcp::BORDER) { /* Border effect */ context->move_to (x, y); dcp::Color ec = i->effect_color (); context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor); layout->add_to_cairo_context (context); context->stroke (); } } return PositionImage (image, Position (0, top.get ())); }