Optimise image scaling for the preview.
authorCarl Hetherington <cth@carlh.net>
Wed, 11 May 2016 23:18:18 +0000 (00:18 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 11 May 2016 23:18:18 +0000 (00:18 +0100)
ChangeLog
src/lib/dcp_video.cc
src/lib/dcp_video.h
src/lib/image.cc
src/lib/image.h
src/lib/player.cc
src/lib/player_video.cc
src/lib/player_video.h
src/wx/film_viewer.cc
src/wx/video_waveform_plot.cc
test/make_black_test.cc

index 3a08bd06980ee9013939c0cb9ee3f00236a93f79..85fd2880a552e9a1ba8095b77946e23dd2bd380b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2016-03-16  Carl Hetherington  <cth@carlh.net>
+
+       * Optimise image scaling for the preview.
+
 2016-05-11  Carl Hetherington  <cth@carlh.net>
 
        * Hopefully improve strange colour fringing on
index cd7d5229e8878cd00f639ad6d611527778faeec8..fbe863a8eaa2e574b5840916237218c6df48c2ea 100644 (file)
@@ -1,6 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
-    Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     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
@@ -99,7 +98,7 @@ DCPVideo::convert_to_xyz (shared_ptr<const PlayerVideo> frame, dcp::NoteHandler
 {
        shared_ptr<dcp::OpenJPEGImage> xyz;
 
-       shared_ptr<Image> image = frame->image (note);
+       shared_ptr<Image> image = frame->image (note, bind (&PlayerVideo::keep_xyz_or_rgb, _1), true, false);
        if (frame->colour_conversion()) {
                xyz = dcp::rgb_to_xyz (
                        image->data()[0],
index a61a757bd0900221c69e0721880a921d7cbb062d..3809d2792bfcd9a75abca0ec5a5b9bd9bb92fcba 100644 (file)
@@ -1,6 +1,5 @@
 /*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-    Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     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
index 9ddccb75f462b79bd0cf4940ae36389c15277432..92b850a62bce981e373cce86d199e25367fbfbad 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     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
@@ -110,10 +110,13 @@ Image::planes () const
        return d->nb_components;
 }
 
-/** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */
+/** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size'.
+ *  @param fast Try to be fast at the possible expense of quality; at present this means using
+ *  fast bilinear rather than bicubic scaling.
+ */
 shared_ptr<Image>
 Image::crop_scale_window (
-       Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned
+       Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast
        ) const
 {
        /* Empirical testing suggests that sws_scale() will crash if
@@ -156,7 +159,7 @@ Image::crop_scale_window (
        struct SwsContext* scale_context = sws_getContext (
                        cropped_size.width, cropped_size.height, pixel_format(),
                        inter_size.width, inter_size.height, out_format,
-                       SWS_BICUBIC, 0, 0, 0
+                       fast ? SWS_FAST_BILINEAR : SWS_BICUBIC, 0, 0, 0
                );
 
        if (!scale_context) {
@@ -213,8 +216,11 @@ Image::crop_scale_window (
        return out;
 }
 
+/** @param fast Try to be fast at the possible expense of quality; at present this means using
+ *  fast bilinear rather than bicubic scaling.
+ */
 shared_ptr<Image>
-Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned) const
+Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast) const
 {
        /* Empirical testing suggests that sws_scale() will crash if
           the input image is not aligned.
@@ -226,7 +232,7 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo
        struct SwsContext* scale_context = sws_getContext (
                size().width, size().height, pixel_format(),
                out_size.width, out_size.height, out_format,
-               SWS_BICUBIC, 0, 0, 0
+               fast ? SWS_FAST_BILINEAR : SWS_BICUBIC, 0, 0, 0
                );
 
        DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUV_TO_RGB_COUNT);
index 416ee3a8d8fd25b0f5cca0176a9e53af48af7085..b8d48275a5fed39dc9a9515a4951b9ec85ff85b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     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
@@ -57,8 +57,10 @@ public:
        int line_factor (int) const;
        dcp::Size sample_size (int) const;
 
-       boost::shared_ptr<Image> scale (dcp::Size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat, bool aligned) const;
-       boost::shared_ptr<Image> crop_scale_window (Crop c, dcp::Size, dcp::Size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat, bool aligned) const;
+       boost::shared_ptr<Image> scale (dcp::Size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat, bool aligned, bool fast) const;
+       boost::shared_ptr<Image> crop_scale_window (
+               Crop c, dcp::Size, dcp::Size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat, bool aligned, bool fast)
+               const;
 
        void make_black ();
        void make_transparent ();
index 30f36107f231cc05a724df4e380ab147ad69c875..d35c1b76bb711afd59d0e2df151445cb5b0bbf35 100644 (file)
@@ -318,7 +318,8 @@ Player::transform_image_subtitles (list<ImageSubtitle> subs) const
                                        scaled_size,
                                        dcp::YUV_TO_RGB_REC601,
                                        i->image->pixel_format (),
-                                       true
+                                       true,
+                                       _fast
                                        ),
                                Position<int> (
                                        lrint (_video_container_size.width * i->rectangle.x),
index ba4503d8e1ef84d9999cde135df9f0776d83688d..3129a171737c9f5252d25d400b05eb55e3f1b4a6 100644 (file)
@@ -34,6 +34,7 @@ using std::cout;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 using boost::optional;
+using boost::function;
 using dcp::Data;
 
 PlayerVideo::PlayerVideo (
@@ -94,8 +95,14 @@ PlayerVideo::set_subtitle (PositionImage image)
        _subtitle = image;
 }
 
+/** Create an image for this frame.
+ *  @param note Handler for any notes that are made during the process.
+ *  @param pixel_format Function which is called to decide what pixel format the output image should be;
+ *  it is passed the pixel format of the input image from the ImageProxy, and should return the desired
+ *  output pixel format.  Two functions always_rgb and keep_xyz_or_rgb are provided for use here.
+ */
 shared_ptr<Image>
-PlayerVideo::image (dcp::NoteHandler note) const
+PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
 {
        shared_ptr<Image> im = _in->image (optional<dcp::NoteHandler> (note));
 
@@ -122,10 +129,9 @@ PlayerVideo::image (dcp::NoteHandler note) const
                yuv_to_rgb = _colour_conversion.get().yuv_to_rgb();
        }
 
-       /* If the input is XYZ, keep it otherwise convert to RGB */
-       AVPixelFormat const p = _in->pixel_format() == AV_PIX_FMT_XYZ12LE ? AV_PIX_FMT_XYZ12LE : AV_PIX_FMT_RGB48LE;
-
-       shared_ptr<Image> out = im->crop_scale_window (total_crop, _inter_size, _out_size, yuv_to_rgb, p, true);
+       shared_ptr<Image> out = im->crop_scale_window (
+               total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (_in->pixel_format()), aligned, fast
+               );
 
        if (_subtitle) {
                out->alpha_blend (_subtitle->image, _subtitle->position);
@@ -230,3 +236,15 @@ PlayerVideo::same (shared_ptr<const PlayerVideo> other) const
 
        return _in->same (other->_in);
 }
+
+AVPixelFormat
+PlayerVideo::always_rgb (AVPixelFormat)
+{
+       return AV_PIX_FMT_RGB24;
+}
+
+AVPixelFormat
+PlayerVideo::keep_xyz_or_rgb (AVPixelFormat p)
+{
+       return p == AV_PIX_FMT_XYZ12LE ? AV_PIX_FMT_XYZ12LE : AV_PIX_FMT_RGB48LE;
+}
index 01b1c74f53e29e4878673759f2f73d3fb78031c1..8bd1c7be30d4b2b40ca61103afdd423154fd009e 100644 (file)
@@ -54,7 +54,10 @@ public:
 
        void set_subtitle (PositionImage);
 
-       boost::shared_ptr<Image> image (dcp::NoteHandler note) const;
+       boost::shared_ptr<Image> image (dcp::NoteHandler note, boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
+
+       static AVPixelFormat always_rgb (AVPixelFormat);
+       static AVPixelFormat keep_xyz_or_rgb (AVPixelFormat);
 
        void add_metadata (xmlpp::Node* node) const;
        void send_binary (boost::shared_ptr<Socket> socket) const;
index 9564c0fc944595a31520c588e271ef385b77e286..2ae9bc1dd541fdf106aaedf3aff51b2e89007078 100644 (file)
@@ -153,6 +153,7 @@ FilmViewer::set_film (shared_ptr<Film> film)
 
        try {
                _player.reset (new Player (_film, _film->playlist ()));
+               _player->set_fast ();
        } catch (bad_alloc) {
                error_dialog (this, _("There is not enough free memory to do that."));
                _film.reset ();
@@ -220,19 +221,12 @@ FilmViewer::get (DCPTime p, bool accurate)
                                pv = all_pv.front ();
                        }
 
-                       /* XXX: this could now give us a 48-bit image, which is a bit wasteful,
-                          or a XYZ image, which the code below will currently rely on FFmpeg
-                          to colourspace-convert.
-                       */
-                       _frame = pv->image (boost::bind (&Log::dcp_log, _film->log().get(), _1, _2));
-                       ImageChanged (pv);
+                       _frame = pv->image (
+                               bind (&Log::dcp_log, _film->log().get(), _1, _2), bind (&PlayerVideo::always_rgb, _1), false, true
+                               );
 
-                       dcp::YUVToRGB yuv_to_rgb = dcp::YUV_TO_RGB_REC601;
-                       if (pv->colour_conversion()) {
-                               yuv_to_rgb = pv->colour_conversion().get().yuv_to_rgb();
-                       }
+                       ImageChanged (pv);
 
-                       _frame = _frame->scale (_frame->size(), yuv_to_rgb, AV_PIX_FMT_RGB24, false);
                        _position = pv->time ();
                        _inter_position = pv->inter_position ();
                        _inter_size = pv->inter_size ();
index 0dedd9bb1f89555001c28dc6d2f0629770492511..357b2ca0cb6d9b83fbd6aa6f9c0b20678a5f366b 100644 (file)
@@ -166,7 +166,7 @@ VideoWaveformPlot::create_waveform ()
 
        _waveform = _waveform->scale (
                dcp::Size (GetSize().GetWidth() - 32, GetSize().GetHeight() - _vertical_margin * 2),
-               dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false
+               dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false, true
                );
 }
 
index 23f4a71ffd4fa342925ee425829402347174eb42..c402884b377cf67a2acf286beb433b6345f9743e 100644 (file)
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE (make_black_test)
        for (list<AVPixelFormat>::const_iterator i = pix_fmts.begin(); i != pix_fmts.end(); ++i) {
                boost::shared_ptr<Image> foo (new Image (*i, in_size, true));
                foo->make_black ();
-               boost::shared_ptr<Image> bar = foo->scale (out_size, dcp::YUV_TO_RGB_REC601, AV_PIX_FMT_RGB24, true);
+               boost::shared_ptr<Image> bar = foo->scale (out_size, dcp::YUV_TO_RGB_REC601, AV_PIX_FMT_RGB24, true, false);
 
                uint8_t* p = bar->data()[0];
                for (int y = 0; y < bar->size().height; ++y) {