Hack: delay decisions about subtitle position late enough that we can refresh the... attic/hack-faster-subtitle-moving
authorCarl Hetherington <cth@carlh.net>
Thu, 19 Dec 2019 01:13:12 +0000 (02:13 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 19 Dec 2019 01:13:12 +0000 (02:13 +0100)
14 files changed:
src/lib/bitmap_text.h
src/lib/player.cc
src/lib/player.h
src/lib/player_text.cc
src/lib/player_text.h
src/lib/player_video.cc
src/lib/player_video.h
src/lib/rect.h
src/lib/render_text.cc
src/lib/render_text.h
src/lib/string_text.h
src/lib/wscript
src/wx/film_viewer.cc
test/client_server_test.cc

index 2314c2db0cec2ad1a521c24fcc008bd640e20cca..9324e1d34d0df5add088d8bc68eeea84e295be6c 100644 (file)
@@ -45,4 +45,6 @@ public:
        dcpomatic::Rect<double> rectangle;
 };
 
+extern bool operator== (BitmapText const & a, BitmapText const & b);
+
 #endif
index c88be58266251da86ecb85a91ea3d2e96b4426f7..51c89b36210a987965073c4a0dc0516f15087738 100644 (file)
@@ -345,7 +345,8 @@ Player::black_player_video_frame (Eyes eyes) const
                        PresetColourConversion::all().front().conversion,
                        VIDEO_RANGE_FULL,
                        boost::weak_ptr<Content>(),
-                       boost::optional<Frame>()
+                       boost::optional<DCPTime>(),
+                       _film->video_frame_rate()
                )
        );
 }
@@ -713,52 +714,6 @@ Player::pass ()
        return done;
 }
 
-/** @return Open subtitles for the frame at the given time, converted to images */
-optional<PositionImage>
-Player::open_subtitles_for_frame (DCPTime time) const
-{
-       list<PositionImage> captions;
-       int const vfr = _film->video_frame_rate();
-
-       BOOST_FOREACH (
-               PlayerText j,
-               _active_texts[TEXT_OPEN_SUBTITLE].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_subtitles)
-               ) {
-
-               /* Bitmap subtitles */
-               BOOST_FOREACH (BitmapText i, j.bitmap) {
-                       if (!i.image) {
-                               continue;
-                       }
-
-                       /* i.image will already have been scaled to fit _video_container_size */
-                       dcp::Size scaled_size (i.rectangle.width * _video_container_size.width, i.rectangle.height * _video_container_size.height);
-
-                       captions.push_back (
-                               PositionImage (
-                                       i.image,
-                                       Position<int> (
-                                               lrint (_video_container_size.width * i.rectangle.x),
-                                               lrint (_video_container_size.height * i.rectangle.y)
-                                               )
-                                       )
-                               );
-               }
-
-               /* String subtitles (rendered to an image) */
-               if (!j.string.empty ()) {
-                       list<PositionImage> s = render_text (j.string, j.fonts, _video_container_size, time, vfr);
-                       copy (s.begin(), s.end(), back_inserter (captions));
-               }
-       }
-
-       if (captions.empty ()) {
-               return optional<PositionImage> ();
-       }
-
-       return merge (captions);
-}
-
 void
 Player::video (weak_ptr<Piece> wp, ContentVideo video)
 {
@@ -847,7 +802,8 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        piece->content->video->colour_conversion(),
                        piece->content->video->range(),
                        piece->content,
-                       video.frame
+                       time,
+                       _film->video_frame_rate()
                        )
                );
 
@@ -950,10 +906,11 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, C
        subtitle.sub.rectangle.width *= text->x_scale ();
        subtitle.sub.rectangle.height *= text->y_scale ();
 
-       PlayerText ps;
        shared_ptr<Image> image = subtitle.sub.image;
        /* We will scale the subtitle up to fit _video_container_size */
        dcp::Size scaled_size (subtitle.sub.rectangle.width * _video_container_size.width, subtitle.sub.rectangle.height * _video_container_size.height);
+       PlayerText ps;
+       ps.content = text;
        ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUV_TO_RGB_REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle));
        DCPTime from (content_time_to_dcp (piece, subtitle.from()));
 
@@ -970,6 +927,7 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, Co
        }
 
        PlayerText ps;
+       ps.content = text;
        DCPTime const from (content_time_to_dcp (piece, subtitle.from()));
 
        if (from > piece->content->end(_film)) {
@@ -977,8 +935,8 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, Co
        }
 
        BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
-               s.set_h_position (s.h_position() + text->x_offset ());
-               s.set_v_position (s.v_position() + text->y_offset ());
+//             s.set_h_position (s.h_position() + text->x_offset ());
+//             s.set_v_position (s.v_position() + text->y_offset ());
                float const xs = text->x_scale();
                float const ys = text->y_scale();
                float size = s.size();
@@ -1126,10 +1084,9 @@ Player::do_emit_video (shared_ptr<PlayerVideo> pv, DCPTime time)
                }
        }
 
-       optional<PositionImage> subtitles = open_subtitles_for_frame (time);
-       if (subtitles) {
-               pv->set_text (subtitles.get ());
-       }
+       int const vfr = _film->video_frame_rate();
+       list<PlayerText> subtitles = _active_texts[TEXT_OPEN_SUBTITLE].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_subtitles);
+       pv->set_text (subtitles);
 
        Video (pv, time);
 }
index e99c345bb211ea99d683fcb5b3d32513305ad4c9..37b3f6c1b5237015f8135561aa968a973d024752 100644 (file)
@@ -135,7 +135,6 @@ private:
        std::pair<boost::shared_ptr<AudioBuffers>, dcpomatic::DCPTime> discard_audio (
                boost::shared_ptr<const AudioBuffers> audio, dcpomatic::DCPTime time, dcpomatic::DCPTime discard_to
                ) const;
-       boost::optional<PositionImage> open_subtitles_for_frame (dcpomatic::DCPTime time) const;
        void emit_video (boost::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time);
        void do_emit_video (boost::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time);
        void emit_audio (boost::shared_ptr<AudioBuffers> data, dcpomatic::DCPTime time);
index d31c7d02467b0507e83fc93e1485a5abcda4fa61..ab28f922151a377b554a50ce7bdaf0e30ee735c5 100644 (file)
@@ -41,3 +41,31 @@ PlayerText::add_fonts (list<shared_ptr<Font> > fonts_)
                }
        }
 }
+
+bool
+operator== (PlayerText const & a, PlayerText const & b)
+{
+       if (a.fonts.size() != b.fonts.size()) {
+               return false;
+       }
+
+       {
+               list<shared_ptr<Font> >::const_iterator i = a.fonts.begin();
+               list<shared_ptr<Font> >::const_iterator j = b.fonts.begin();
+               while (i != a.fonts.end()) {
+                       if (**i != **j) {
+                               return false;
+                       }
+                       ++i;
+                       ++j;
+               }
+       }
+
+       return a.bitmap == b.bitmap && a.string == b.string;
+}
+
+bool
+operator!= (PlayerText const & a, PlayerText const & b)
+{
+       return !(a == b);
+}
index fb1d846d8217d2c4f85af36c7b81270deff45db4..37c55db4ae7e9c9baa80bcd9087259170482a541 100644 (file)
 
 */
 
-#ifndef DCPOMATIC_PLAYER_CAPTION_H
-#define DCPOMATIC_PLAYER_CAPTION_H
+#ifndef DCPOMATIC_PLAYER_TEXT_H
+#define DCPOMATIC_PLAYER_TEXT_H
 
 #include "bitmap_text.h"
 #include "dcpomatic_time.h"
 #include "string_text.h"
+#include <boost/weak_ptr.hpp>
 
 namespace dcpomatic {
        class Font;
 }
 
+class TextContent;
+
 /** A set of text (subtitle/CCAP) which span the same time period */
 class PlayerText
 {
@@ -36,9 +39,13 @@ public:
        void add_fonts (std::list<boost::shared_ptr<dcpomatic::Font> > fonts_);
        std::list<boost::shared_ptr<dcpomatic::Font> > fonts;
 
+       boost::weak_ptr<const TextContent> content;
        /** BitmapTexts, with their rectangles transformed as specified by their content */
        std::list<BitmapText> bitmap;
        std::list<StringText> string;
 };
 
+extern bool operator== (PlayerText const & a, PlayerText const & b);
+extern bool operator!= (PlayerText const & a, PlayerText const & b);
+
 #endif
index 75479136f0682bea3be97bf9e1debc8bf4bb390b..fa550f346a06e9330996bccf4ea4165173e25242 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -25,6 +25,8 @@
 #include "image_proxy.h"
 #include "j2k_image_proxy.h"
 #include "film.h"
+#include "render_text.h"
+#include "text_content.h"
 #include <dcp/raw_convert.h>
 extern "C" {
 #include <libavutil/pixfmt.h>
@@ -35,6 +37,7 @@ extern "C" {
 using std::string;
 using std::cout;
 using std::pair;
+using std::list;
 using boost::shared_ptr;
 using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
@@ -54,7 +57,8 @@ PlayerVideo::PlayerVideo (
        optional<ColourConversion> colour_conversion,
        VideoRange video_range,
        weak_ptr<Content> content,
-       optional<Frame> video_frame
+       optional<dcpomatic::DCPTime> time,
+       int video_frame_rate
        )
        : _in (in)
        , _crop (crop)
@@ -66,12 +70,15 @@ PlayerVideo::PlayerVideo (
        , _colour_conversion (colour_conversion)
        , _video_range (video_range)
        , _content (content)
-       , _video_frame (video_frame)
+       , _time (time)
+       , _video_frame_rate (video_frame_rate)
+       , _image_dirty (false)
 {
 
 }
 
 PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket)
+       : _image_dirty (false)
 {
        _crop = Crop (node);
        _fade = node->optional_number_child<double> ("Fade");
@@ -95,14 +102,16 @@ PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket
 
                image->read_from_socket (socket);
 
+               /* XXX_b
                _text = PositionImage (image, Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY")));
+               */
        }
 }
 
 void
-PlayerVideo::set_text (PositionImage image)
+PlayerVideo::set_text (list<PlayerText> text)
 {
-       _text = image;
+       _text = text;
 }
 
 shared_ptr<Image>
@@ -111,7 +120,7 @@ PlayerVideo::image (function<AVPixelFormat (AVPixelFormat)> pixel_format, bool a
        /* XXX: this assumes that image() and prepare() are only ever called with the same parameters (except crop, inter size, out size, fade) */
 
        boost::mutex::scoped_lock lm (_mutex);
-       if (!_image || _crop != _image_crop || _inter_size != _image_inter_size || _out_size != _image_out_size || _fade != _image_fade) {
+       if (!_image || _image_dirty) {
                make_image (pixel_format, aligned, fast);
        }
        return _image;
@@ -172,13 +181,52 @@ PlayerVideo::make_image (function<AVPixelFormat (AVPixelFormat)> pixel_format, b
                total_crop, _inter_size, _out_size, yuv_to_rgb, _video_range, pixel_format (im->pixel_format()), aligned, fast
                );
 
-       if (_text) {
-               _image->alpha_blend (Image::ensure_aligned (_text->image), _text->position);
+       list<PositionImage> subtitles;
+
+       BOOST_FOREACH (PlayerText i, _text) {
+
+               /* Bitmap subtitles */
+               BOOST_FOREACH (BitmapText j, i.bitmap) {
+                       if (!j.image) {
+                               continue;
+                       }
+
+                       /* j.image will already have been scaled to fit _out_size */
+                       dcp::Size scaled_size (j.rectangle.width * _out_size.width, j.rectangle.height * _out_size.height);
+
+                       subtitles.push_back (
+                               PositionImage (
+                                       j.image,
+                                       Position<int> (
+                                               lrint (_out_size.width * j.rectangle.x),
+                                               lrint (_out_size.height * j.rectangle.y)
+                                               )
+                                       )
+                               );
+               }
+
+               /* String subtitles (rendered to an image) */
+               if (!i.string.empty ()) {
+                       DCPOMATIC_ASSERT (_time);
+                       shared_ptr<const TextContent> content = i.content.lock ();
+                       DCPOMATIC_ASSERT (content);
+                       list<PositionImage> s = render_text (
+                               i.string, i.fonts, _out_size, *_time, _video_frame_rate, content->x_offset(), content->y_offset()
+                               );
+                       copy (s.begin(), s.end(), back_inserter (subtitles));
+               }
+       }
+
+       if (!subtitles.empty()) {
+               PositionImage pi = merge (subtitles);
+               _image->alpha_blend (Image::ensure_aligned(pi.image), pi.position);
        }
 
        if (_fade) {
                _image->fade (_fade.get ());
        }
+
+       _image_dirty = false;
 }
 
 void
@@ -199,21 +247,25 @@ PlayerVideo::add_metadata (xmlpp::Node* node) const
        if (_colour_conversion) {
                _colour_conversion.get().as_xml (node);
        }
+       /* XXX_b
        if (_text) {
                node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_text->image->size().width));
                node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_text->image->size().height));
                node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_text->position.x));
                node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_text->position.y));
        }
+       */
 }
 
 void
 PlayerVideo::send_binary (shared_ptr<Socket> socket) const
 {
        _in->send_binary (socket);
+       /* XXX_b
        if (_text) {
                _text->image->write_to_socket (socket);
        }
+       */
 }
 
 bool
@@ -226,7 +278,7 @@ PlayerVideo::has_j2k () const
                return false;
        }
 
-       return _crop == Crop () && _out_size == j2k->size() && !_text && !_fade && !_colour_conversion;
+       return _crop == Crop() && _out_size == j2k->size() && _text.empty() && !_fade && !_colour_conversion;
 }
 
 Data
@@ -258,18 +310,22 @@ PlayerVideo::same (shared_ptr<const PlayerVideo> other) const
                return false;
        }
 
-       if ((!_text && other->_text) || (_text && !other->_text)) {
-               /* One has a text and the other doesn't */
+       if (_text.size() != other->_text.size()) {
+               /* Different text counts */
                return false;
        }
 
-       if (_text && other->_text && !_text->same (other->_text.get ())) {
-               /* They both have texts but they are different */
-               return false;
+       list<PlayerText>::const_iterator i = _text.begin();
+       list<PlayerText>::const_iterator j = other->_text.begin();
+       while (i != _text.end()) {
+               if (*i != *j) {
+                       /* Same number of texts but one differs */
+                       return false;
+               }
+               ++i;
+               ++j;
        }
 
-       /* Now neither has subtitles */
-
        return _in->same (other->_in);
 }
 
@@ -317,7 +373,8 @@ PlayerVideo::shallow_copy () const
                        _colour_conversion,
                        _video_range,
                        _content,
-                       _video_frame
+                       _time,
+                       _video_frame_rate
                        )
                );
 }
@@ -329,16 +386,23 @@ bool
 PlayerVideo::reset_metadata (shared_ptr<const Film> film, dcp::Size video_container_size, dcp::Size film_frame_size)
 {
        shared_ptr<Content> content = _content.lock();
-       if (!content || !_video_frame) {
+       if (!content || !_time) {
                return false;
        }
 
        _crop = content->video->crop();
-       _fade = content->video->fade(film, _video_frame.get());
+       _fade = content->video->fade(film, _time->frames_round(_video_frame_rate));
        _inter_size = content->video->scale().size(content->video, video_container_size, film_frame_size);
        _out_size = video_container_size;
        _colour_conversion = content->video->colour_conversion();
        _video_range = content->video->range();
 
+       /* XXX_b _text looks into content directly itself, as maybe the other parameters should; we could just
+          have a pointer to the content and get stuff from there.
+       */
+
+       /* XXX_b thread safety of _image_dirty? */
+       _image_dirty = true;
+
        return true;
 }
index 3cd5594097baa42e7a3727ba59c83d6ae0544d18..f62287a64c58c84d54e950a88aa4e73845ebd347 100644 (file)
@@ -26,6 +26,7 @@
 #include "dcpomatic_time.h"
 #include "colour_conversion.h"
 #include "position_image.h"
+#include "player_text.h"
 extern "C" {
 #include <libavutil/pixfmt.h>
 }
@@ -56,14 +57,15 @@ public:
                boost::optional<ColourConversion>,
                VideoRange video_range,
                boost::weak_ptr<Content>,
-               boost::optional<Frame>
+               boost::optional<dcpomatic::DCPTime>,
+               int video_frame_rate
                );
 
        PlayerVideo (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>);
 
        boost::shared_ptr<PlayerVideo> shallow_copy () const;
 
-       void set_text (PositionImage);
+       void set_text (std::list<PlayerText>);
 
        void prepare (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast);
        boost::shared_ptr<Image> image (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
@@ -119,13 +121,14 @@ private:
        Part _part;
        boost::optional<ColourConversion> _colour_conversion;
        VideoRange _video_range;
-       boost::optional<PositionImage> _text;
+       std::list<PlayerText> _text;
        /** Content that we came from.  This is so that reset_metadata() can work, and also
         *  for variant:swaroop's non-skippable ads.
         */
        boost::weak_ptr<Content> _content;
-       /** Video frame that we came from.  Again, this is for reset_metadata() */
-       boost::optional<Frame> _video_frame;
+       /** Time that we came from.  Again, this is for reset_metadata() */
+       boost::optional<dcpomatic::DCPTime> _time;
+       int _video_frame_rate;
 
        mutable boost::mutex _mutex;
        mutable boost::shared_ptr<Image> _image;
@@ -137,6 +140,7 @@ private:
        mutable dcp::Size _image_out_size;
        /** _fade that was used to make _image */
        mutable boost::optional<double> _image_fade;
+       mutable bool _image_dirty;
 };
 
 #endif
index 4851ad007fe11c6ce7b7469c2cb6f7367dd2eccd..07fb22dc99d63e52afbe3371a20bbb4b0af622d5 100644 (file)
@@ -24,6 +24,7 @@
 #include "position.h"
 #include <boost/optional.hpp>
 #include <algorithm>
+#include <cmath>
 
 /* Put this inside a namespace as Apple put a Rect in the global namespace */
 
@@ -114,6 +115,8 @@ public:
        }
 };
 
+extern bool operator== (Rect<double> const & a, Rect<double> const & b);
+
 }
 
 #endif
index 8b9d93423d390062e53b710fba9feaf06b098a92..dff702b780a1010c8b77857cc6ee50fa15fcbf37 100644 (file)
@@ -92,7 +92,7 @@ set_source_rgba (Cairo::RefPtr<Cairo::Context> context, dcp::Colour colour, floa
  *  at the same time and with the same fade in/out.
  */
 static PositionImage
-render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate, double x_offset, double y_offset)
 {
        /* XXX: this method can only handle italic / bold changes mid-line,
           nothing else yet.
@@ -263,14 +263,14 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Siz
        /* Shuffle the subtitle over very slightly if it has a border so that the left-hand
           side of the first character's border is not cut off.
        */
-       int const x_offset = subtitles.front().effect() == dcp::BORDER ? (target.width / 600.0) : 0;
+       int const xoff = subtitles.front().effect() == dcp::BORDER ? (target.width / 600.0) : 0;
        /* Move down a bit so that accents on capital letters can be seen */
-       int const y_offset = target.height / 100.0;
+       int const yoff = target.height / 100.0;
 
        if (subtitles.front().effect() == dcp::SHADOW) {
                /* Drop-shadow effect */
                set_source_rgba (context, subtitles.front().effect_colour(), fade_factor);
-               context->move_to (x_offset + 4, y_offset + 4);
+               context->move_to (xoff + 4, yoff + 4);
                layout->add_to_cairo_context (context);
                context->fill ();
        }
@@ -280,7 +280,7 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Siz
                set_source_rgba (context, subtitles.front().effect_colour(), fade_factor);
                context->set_line_width (subtitles.front().outline_width * target.width / 2048.0);
                context->set_line_join (Cairo::LINE_JOIN_ROUND);
-               context->move_to (x_offset, y_offset);
+               context->move_to (xoff, yoff);
                layout->add_to_cairo_context (context);
                context->stroke ();
        }
@@ -301,65 +301,68 @@ render_line (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Siz
        layout_width *= xscale;
        layout_height *= yscale;
 
+       double const h_pos = subtitles.front().h_position() + x_offset;
+       double const v_pos = subtitles.front().v_position() + y_offset;
+
        int x = 0;
-       switch (subtitles.front().h_align ()) {
+       switch (subtitles.front().h_align()) {
        case dcp::HALIGN_LEFT:
-               /* h_position is distance between left of frame and left of subtitle */
-               x = subtitles.front().h_position() * target.width;
+               /* h_pos is distance between left of frame and left of subtitle */
+               x = h_pos * target.width;
                break;
        case dcp::HALIGN_CENTER:
-               /* h_position is distance between centre of frame and centre of subtitle */
-               x = (0.5 + subtitles.front().h_position()) * target.width - layout_width / 2;
+               /* h_pos is distance between centre of frame and centre of subtitle */
+               x = (0.5 + h_pos) * 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 - subtitles.front().h_position()) * target.width - layout_width;
+               /* h_pos is distance between right of frame and right of subtitle */
+               x = (1.0 - h_pos) * target.width - layout_width;
                break;
        }
 
        int y = 0;
-       switch (subtitles.front().v_align ()) {
+       switch (subtitles.front().v_align()) {
        case dcp::VALIGN_TOP:
-               /* SMPTE says that v_position is the distance between top
+               /* SMPTE says that v_pos 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 = subtitles.front().v_position() * target.height - layout_height;
+               y = v_pos * target.height - layout_height;
                break;
        case dcp::VALIGN_CENTER:
                /* v_position is distance between centre of frame and centre of subtitle */
-               y = (0.5 + subtitles.front().v_position()) * target.height - layout_height / 2;
+               y = (0.5 + v_pos) * 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 - subtitles.front().v_position()) * target.height - layout_height;
+               y = (1.0 - v_pos) * target.height - layout_height;
                break;
        }
 
-       return PositionImage (image, Position<int> (max (0, x), max (0, y)));
+       return PositionImage (image, Position<int>(max (0, x), max(0, y)));
 }
 
 /** @param time Time of the frame that these subtitles are going on.
  *  @param frame_rate DCP frame rate.
  */
 list<PositionImage>
-render_text (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate)
+render_text (list<StringText> subtitles, list<shared_ptr<Font> > fonts, dcp::Size target, DCPTime time, int frame_rate, double x_offset, double y_offset)
 {
        list<StringText> pending;
        list<PositionImage> images;
 
        BOOST_FOREACH (StringText const & i, subtitles) {
                if (!pending.empty() && (i.v_align() != pending.back().v_align() || fabs(i.v_position() - pending.back().v_position()) > 1e-4)) {
-                       images.push_back (render_line (pending, fonts, target, time, frame_rate));
+                       images.push_back (render_line(pending, fonts, target, time, frame_rate, x_offset, y_offset));
                        pending.clear ();
                }
                pending.push_back (i);
        }
 
        if (!pending.empty ()) {
-               images.push_back (render_line (pending, fonts, target, time, frame_rate));
+               images.push_back (render_line(pending, fonts, target, time, frame_rate, x_offset, y_offset));
        }
 
        return images;
index 7187ca30b5c0c28ea2a9d9ff5ddcbd1fc43dde3a..38009403a45080e74e9e6240c0cafbe775de569e 100644 (file)
@@ -29,5 +29,11 @@ namespace dcpomatic {
 
 std::string marked_up (std::list<StringText> subtitles, int target_height, float fade_factor);
 std::list<PositionImage> render_text (
-       std::list<StringText>, std::list<boost::shared_ptr<dcpomatic::Font> > fonts, dcp::Size, dcpomatic::DCPTime, int
+       std::list<StringText> subtitles,
+       std::list<boost::shared_ptr<dcpomatic::Font> > fonts,
+       dcp::Size target,
+       dcpomatic::DCPTime time,
+       int frame_rate,
+       double x_offset,
+       double y_offset
        );
index 4f4958163c4f2773fae426ce02b9c73208fd366f..4063a688d96669ead8628b88f0a6d459c0c3fc4f 100644 (file)
@@ -38,4 +38,6 @@ public:
        int outline_width;
 };
 
+extern bool operator== (StringText const & a, StringText const & b);
+
 #endif
index b78586843aef7d4f7f1f35a101e112631c1acc70..19c8cc7ef9e1cd540d241fe6d7fdd50c282f3e5a 100644 (file)
@@ -39,6 +39,7 @@ sources = """
           audio_processor.cc
           audio_ring_buffers.cc
           audio_stream.cc
+          bitmap_text.cc
           butler.cc
           text_content.cc
           text_decoder.cc
@@ -135,6 +136,7 @@ sources = """
           position_image.cc
           ratio.cc
           raw_image_proxy.cc
+          rect.cc
           reel_writer.cc
           render_text.cc
           resampler.cc
@@ -152,6 +154,7 @@ sources = """
           spl.cc
           spl_entry.cc
           string_log_entry.cc
+          string_text.cc
           string_text_file.cc
           string_text_file_content.cc
           string_text_file_decoder.cc
index 893e1bf0fe4569dec8ede2d80bf75acc213b7beb..c4c1429dc59bdedd4c0112b28cc7205a532f17cb 100644 (file)
@@ -47,6 +47,7 @@
 #include "lib/config.h"
 #include "lib/compose.hpp"
 #include "lib/dcpomatic_log.h"
+#include "lib/text_content.h"
 extern "C" {
 #include <libavutil/pixfmt.h>
 }
@@ -519,7 +520,9 @@ FilmViewer::player_change (ChangeType type, int property, bool frequent)
                property == VideoContentProperty::FADE_OUT ||
                property == VideoContentProperty::COLOUR_CONVERSION ||
                property == PlayerProperty::VIDEO_CONTAINER_SIZE ||
-               property == PlayerProperty::FILM_CONTAINER
+               property == PlayerProperty::FILM_CONTAINER ||
+               property == TextContentProperty::X_OFFSET ||
+               property == TextContentProperty::Y_OFFSET
                ) {
                refreshed = quick_refresh ();
        }
index df854f9f3efec8bbb364e0ecbec2aedcb2fe7037..23b9032e9574e3abecb92bd7ba31fc0c684cb3ce 100644 (file)
@@ -99,11 +99,14 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
                        ColourConversion(),
                        VIDEO_RANGE_FULL,
                        weak_ptr<Content>(),
-                       optional<Frame>()
+                       optional<dcpomatic::DCPTime>(),
+                       24
                        )
                );
 
+       /* XXX_b
        pvf->set_text (PositionImage (sub_image, Position<int> (50, 60)));
+       */
 
        shared_ptr<DCPVideo> frame (
                new DCPVideo (
@@ -184,11 +187,14 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
                        ColourConversion(),
                        VIDEO_RANGE_FULL,
                        weak_ptr<Content>(),
-                       optional<Frame>()
+                       optional<dcpomatic::DCPTime>(),
+                       24
                        )
                );
 
+       /* XXX_b
        pvf->set_text (PositionImage (sub_image, Position<int> (50, 60)));
+       */
 
        shared_ptr<DCPVideo> frame (
                new DCPVideo (
@@ -256,7 +262,8 @@ BOOST_AUTO_TEST_CASE (client_server_test_j2k)
                        ColourConversion(),
                        VIDEO_RANGE_FULL,
                        weak_ptr<Content>(),
-                       optional<Frame>()
+                       optional<dcpomatic::DCPTime>(),
+                       24
                        )
                );
 
@@ -284,7 +291,8 @@ BOOST_AUTO_TEST_CASE (client_server_test_j2k)
                        PresetColourConversion::all().front().conversion,
                        VIDEO_RANGE_FULL,
                        weak_ptr<Content>(),
-                       optional<Frame>()
+                       optional<dcpomatic::DCPTime>(),
+                       24
                        )
                );