Change video content scaling so that it either:
authorCarl Hetherington <cth@carlh.net>
Sat, 9 May 2020 23:57:20 +0000 (01:57 +0200)
committerCarl Hetherington <cth@carlh.net>
Mon, 11 May 2020 20:02:49 +0000 (22:02 +0200)
1. scales the content up to fit the DCP container,
preserving aspect ratio, or
2. stretches the content to a custom aspect ratio, or
3. scales the content to some custom size.

35 files changed:
src/lib/create_cli.cc
src/lib/create_cli.h
src/lib/film.cc
src/lib/hints.cc
src/lib/player.cc
src/lib/player.h
src/lib/player_video.cc
src/lib/util.cc
src/lib/util.h
src/lib/video_content.cc
src/lib/video_content.h
src/lib/video_content_scale.cc [deleted file]
src/lib/video_content_scale.h [deleted file]
src/lib/wscript
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_create.cc
src/wx/custom_scale_dialog.cc [new file with mode: 0644]
src/wx/custom_scale_dialog.h [new file with mode: 0644]
src/wx/video_panel.cc
src/wx/video_panel.h
src/wx/wscript
test/4k_test.cc
test/create_cli_test.cc
test/data
test/empty_test.cc
test/ffmpeg_audio_test.cc
test/ffmpeg_dcp_test.cc
test/isdcf_name_test.cc
test/player_test.cc
test/recover_test.cc
test/repeat_frame_test.cc
test/scaling_test.cc
test/skip_frame_test.cc
test/threed_test.cc
test/video_content_scale_test.cc

index 878ee6f..b670282 100644 (file)
@@ -42,7 +42,6 @@ string CreateCLI::_help =
        "  -c, --dcp-content-type <type> FTR, SHR, TLR, TST, XSN, RTG, TSR, POL, PSA or ADV\n"
        "  -f, --dcp-frame-rate <rate>   set DCP video frame rate (otherwise guessed from content)\n"
        "      --container-ratio <ratio> 119, 133, 137, 138, 166, 178, 185 or 239\n"
-       "      --content-ratio <ratio>   119, 133, 137, 138, 166, 178, 185 or 239\n"
        "  -s, --still-length <n>        number of seconds that still content should last\n"
        "      --standard <standard>     SMPTE or interop (default SMPTE)\n"
        "      --no-use-isdcf-name       do not use an ISDCF name; use the specified name unmodified\n"
@@ -79,7 +78,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
        , threed (false)
        , dcp_content_type (0)
        , container_ratio (0)
-       , content_ratio (0)
        , still_length (10)
        , standard (dcp::SMPTE)
        , no_use_isdcf_name (false)
@@ -87,7 +85,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
        , fourk (false)
 {
        string dcp_content_type_string = "TST";
-       string content_ratio_string;
        string container_ratio_string;
        string standard_string = "SMPTE";
        int dcp_frame_rate_int = 0;
@@ -136,7 +133,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
                argument_option(i, argc, argv, "-c", "--dcp-content-type", &claimed, &error, &dcp_content_type_string);
                argument_option(i, argc, argv, "-f", "--dcp-frame-rate",   &claimed, &error, &dcp_frame_rate_int);
                argument_option(i, argc, argv, "",   "--container-ratio",  &claimed, &error, &container_ratio_string);
-               argument_option(i, argc, argv, "",   "--content-ratio",    &claimed, &error, &content_ratio_string);
                argument_option(i, argc, argv, "-s", "--still-length",     &claimed, &error, &still_length);
                argument_option(i, argc, argv, "",   "--standard",         &claimed, &error, &standard_string);
                argument_option(i, argc, argv, "",   "--config",           &claimed, &error, &config_dir_string);
@@ -185,25 +181,12 @@ CreateCLI::CreateCLI (int argc, char* argv[])
                return;
        }
 
-       if (content_ratio_string.empty()) {
-               error = String::compose("%1: missing required option --content-ratio", argv[0]);
-               return;
-       }
-
-       content_ratio = Ratio::from_id (content_ratio_string);
-       if (!content_ratio) {
-               error = String::compose("%1: unrecognised content ratio %2", content_ratio_string);
-               return;
-       }
-
-       if (container_ratio_string.empty()) {
-               container_ratio_string = content_ratio_string;
-       }
-
-       container_ratio = Ratio::from_id (container_ratio_string);
-       if (!container_ratio) {
-               error = String::compose("%1: unrecognised container ratio %2", argv[0], container_ratio_string);
-               return;
+       if (!container_ratio_string.empty()) {
+               container_ratio = Ratio::from_id (container_ratio_string);
+               if (!container_ratio) {
+                       error = String::compose("%1: unrecognised container ratio %2", argv[0], container_ratio_string);
+                       return;
+               }
        }
 
        if (standard_string != "SMPTE" && standard_string != "interop") {
index a9d9daa..01e8e66 100644 (file)
@@ -45,7 +45,6 @@ public:
        DCPContentType const * dcp_content_type;
        boost::optional<int> dcp_frame_rate;
        Ratio const * container_ratio;
-       Ratio const * content_ratio;
        int still_length;
        dcp::Standard standard;
        bool no_use_isdcf_name;
index b233e5e..b1dcb46 100644 (file)
@@ -132,8 +132,10 @@ string const Film::metadata_file = "metadata.xml";
  * 36 -> 37
  * TextContent can be in a Caption tag, and some of the tag names
  * have had Subtitle prefixes or suffixes removed.
+ * 37 -> 38
+ * VideoContent scale expressed just as "guess" or "custom"
  */
-int const Film::current_state_version = 37;
+int const Film::current_state_version = 38;
 
 /** Construct a Film object in a given directory.
  *
@@ -787,11 +789,7 @@ Film::isdcf_name (bool if_created_now) const
                BOOST_FOREACH (shared_ptr<Content> i, content ()) {
                        if (i->video) {
                                /* Here's the first piece of video content */
-                               if (i->video->scale().ratio ()) {
-                                       content_ratio = i->video->scale().ratio ();
-                               } else {
-                                       content_ratio = Ratio::from_ratio (i->video->size().ratio ());
-                               }
+                               content_ratio = Ratio::nearest_from_ratio(i->video->scaled_size(frame_size()).ratio());
                                break;
                        }
                }
index 581d639..0f18835 100644 (file)
@@ -123,7 +123,7 @@ Hints::thread ()
        int scope = 0;
        BOOST_FOREACH (shared_ptr<const Content> i, content) {
                if (i->video) {
-                       Ratio const * r = i->video->scale().ratio ();
+                       Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film->frame_size()).ratio());
                        if (r && r->id() == "239") {
                                ++scope;
                        } else if (r && r->id() != "239" && r->id() != "190") {
index e41cecf..0594657 100644 (file)
@@ -864,9 +864,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
                        video.image,
                        piece->content->video->crop (),
                        piece->content->video->fade (_film, video.frame),
-                       piece->content->video->scale().size (
-                               piece->content->video, _video_container_size, _film->frame_size ()
-                               ),
+                       scale_for_display(piece->content->video->scaled_size(_film->frame_size()), _video_container_size, _film->frame_size()),
                        _video_container_size,
                        video.eyes,
                        video.part,
index 51de789..bb2a0c6 100644 (file)
@@ -159,7 +159,9 @@ private:
        boost::atomic<int> _suspended;
        std::list<boost::shared_ptr<Piece> > _pieces;
 
-       /** Size of the image in the DCP (e.g. 1990x1080 for flat) */
+       /** Size of the image we are rendering to; this may be the DCP frame size, or
+        *  the size of preview in a window.
+        */
        dcp::Size _video_container_size;
        boost::shared_ptr<Image> _black_image;
 
index cb2a557..10e798e 100644 (file)
@@ -339,7 +339,7 @@ PlayerVideo::reset_metadata (shared_ptr<const Film> film, dcp::Size video_contai
 
        _crop = content->video->crop();
        _fade = content->video->fade(film, _video_frame.get());
-       _inter_size = content->video->scale().size(content->video, video_container_size, film_frame_size);
+       _inter_size = scale_for_display(content->video->scaled_size(film_frame_size), video_container_size, film_frame_size);
        _out_size = video_container_size;
        _colour_conversion = content->video->colour_conversion();
        _video_range = content->video->range();
index d04bbdf..c1ef424 100644 (file)
@@ -394,7 +394,6 @@ dcpomatic_setup ()
 
        Ratio::setup_ratios ();
        PresetColourConversion::setup_colour_conversion_presets ();
-       VideoContentScale::setup_scales ();
        DCPContentType::setup_dcp_content_types ();
        Filter::setup_filters ();
        CinemaSoundProcessor::setup_cinema_sound_processors ();
@@ -1184,3 +1183,21 @@ linear_to_db (double linear)
        return 20 * log10(linear);
 }
 
+
+dcp::Size
+scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container)
+{
+       /* Now scale it down if the display container is smaller than the film container */
+       if (display_container != film_container) {
+               float const scale = min (
+                       float (display_container.width) / film_container.width,
+                       float (display_container.height) / film_container.height
+                       );
+
+               s.width = lrintf (s.width * scale);
+               s.height = lrintf (s.height * scale);
+       }
+
+       return s;
+}
+
index f8e9409..f7b4704 100644 (file)
@@ -116,6 +116,7 @@ extern void copy_in_bits (boost::filesystem::path from, boost::filesystem::path
 extern boost::shared_ptr<dcp::CertificateChain> read_swaroop_chain (boost::filesystem::path path);
 extern void write_swaroop_chain (boost::shared_ptr<const dcp::CertificateChain> chain, boost::filesystem::path output);
 #endif
+extern dcp::Size scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container);
 
 template <class T>
 std::list<T>
index 9e8659b..b4ad30f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -48,6 +48,8 @@ int const VideoContentProperty::COLOUR_CONVERSION = 5;
 int const VideoContentProperty::FADE_IN           = 6;
 int const VideoContentProperty::FADE_OUT          = 7;
 int const VideoContentProperty::RANGE             = 8;
+int const VideoContentProperty::CUSTOM_RATIO      = 9;
+int const VideoContentProperty::CUSTOM_SIZE       = 10;
 
 using std::string;
 using std::setprecision;
@@ -70,7 +72,6 @@ VideoContent::VideoContent (Content* parent)
        , _use (true)
        , _length (0)
        , _frame_type (VIDEO_FRAME_TYPE_2D)
-       , _scale (VideoContentScale (Ratio::from_id ("178")))
        , _yuv (true)
        , _fade_in (0)
        , _fade_out (0)
@@ -139,10 +140,29 @@ VideoContent::VideoContent (Content* parent, cxml::ConstNodePtr node, int versio
        if (version <= 7) {
                optional<string> r = node->optional_string_child ("Ratio");
                if (r) {
-                       _scale = VideoContentScale (Ratio::from_id (r.get ()));
+                       _legacy_ratio = Ratio::from_id(r.get())->ratio();
                }
+       } else if (version <= 37) {
+               optional<string> ratio = node->node_child("Scale")->optional_string_child("Ratio");
+               if (ratio) {
+                       _legacy_ratio = Ratio::from_id(ratio.get())->ratio();
+               }
+               optional<bool> scale = node->node_child("Scale")->optional_bool_child("Scale");
+               if (scale) {
+                       if (*scale) {
+                               /* This is what we used to call "no stretch" */
+                               _legacy_ratio = _size.ratio();
+                       } else {
+                               /* This is what we used to call "no scale" */
+                               _custom_size = _size;
+                       }
+               }
+
        } else {
-               _scale = VideoContentScale (node->node_child ("Scale"));
+               _custom_ratio = node->optional_number_child<float>("CustomRatio");
+               if (node->optional_number_child<int>("CustomWidth")) {
+                       _custom_size = dcp::Size (node->number_child<int>("CustomWidth"), node->number_child<int>("CustomHeight"));
+               }
        }
 
        if (node->optional_node_child ("ColourConversion")) {
@@ -190,8 +210,12 @@ VideoContent::VideoContent (Content* parent, vector<shared_ptr<Content> > c)
                        throw JoinError (_("Content to be joined must have the same crop."));
                }
 
-               if (c[i]->video->scale() != ref->scale()) {
-                       throw JoinError (_("Content to be joined must have the same scale setting."));
+               if (c[i]->video->custom_ratio() != ref->custom_ratio()) {
+                       throw JoinError (_("Content to be joined must have the same custom ratio setting."));
+               }
+
+               if (c[i]->video->custom_size() != ref->custom_size()) {
+                       throw JoinError (_("Content to be joined must have the same custom size setting."));
                }
 
                if (c[i]->video->colour_conversion() != ref->colour_conversion()) {
@@ -213,7 +237,7 @@ VideoContent::VideoContent (Content* parent, vector<shared_ptr<Content> > c)
        _size = ref->size ();
        _frame_type = ref->frame_type ();
        _crop = ref->crop ();
-       _scale = ref->scale ();
+       _custom_ratio = ref->custom_ratio ();
        _colour_conversion = ref->colour_conversion ();
        _fade_in = ref->fade_in ();
        _fade_out = ref->fade_out ();
@@ -233,7 +257,13 @@ VideoContent::as_xml (xmlpp::Node* node) const
                node->add_child("SampleAspectRatio")->add_child_text (raw_convert<string> (_sample_aspect_ratio.get ()));
        }
        _crop.as_xml (node);
-       _scale.as_xml (node->add_child("Scale"));
+       if (_custom_ratio) {
+               node->add_child("CustomRatio")->add_child_text(raw_convert<string>(*_custom_ratio));
+       }
+       if (_custom_size) {
+               node->add_child("CustomWidth")->add_child_text(raw_convert<string>(_custom_size->width));
+               node->add_child("CustomHeight")->add_child_text(raw_convert<string>(_custom_size->height));
+       }
        if (_colour_conversion) {
                _colour_conversion.get().as_xml (node->add_child("ColourConversion"));
        }
@@ -265,15 +295,6 @@ VideoContent::take_from_examiner (shared_ptr<VideoExaminer> d)
                _sample_aspect_ratio = ar;
                _yuv = yuv;
                _range = range;
-
-               if (Config::instance()->default_scale_to ()) {
-                       _scale = VideoContentScale (Config::instance()->default_scale_to ());
-               } else {
-                       /* Guess correct scale from size and sample aspect ratio */
-                       _scale = VideoContentScale (
-                               Ratio::nearest_from_ratio (double (_size.width) * ar.get_value_or (1) / _size.height)
-                               );
-               }
        }
 
        LOG_GENERAL ("Video length obtained from header as %1 frames", _length);
@@ -289,13 +310,15 @@ VideoContent::identifier () const
 {
        char buffer[256];
        snprintf (
-               buffer, sizeof(buffer), "%d_%d_%d_%d_%d_%s_%" PRId64 "_%" PRId64 "_%d",
+               buffer, sizeof(buffer), "%d_%d_%d_%d_%d_%f_%d_%d%" PRId64 "_%" PRId64 "_%d",
                (_use ? 1 : 0),
                crop().left,
                crop().right,
                crop().top,
                crop().bottom,
-               scale().id().c_str(),
+               _custom_ratio.get_value_or(0),
+               _custom_size ? _custom_size->width : 0,
+               _custom_size ? _custom_size->height : 0,
                _fade_in,
                _fade_out,
                _range == VIDEO_RANGE_FULL ? 0 : 1
@@ -379,7 +402,7 @@ VideoContent::fade (shared_ptr<const Film> film, Frame f) const
 }
 
 string
-VideoContent::processing_description (shared_ptr<const Film> film) const
+VideoContent::processing_description (shared_ptr<const Film> film)
 {
        string d;
        char buffer[256];
@@ -416,7 +439,7 @@ VideoContent::processing_description (shared_ptr<const Film> film) const
        }
 
        dcp::Size const container_size = film->frame_size ();
-       dcp::Size const scaled = scale().size (shared_from_this(), container_size, container_size);
+       dcp::Size const scaled = scaled_size (container_size);
 
        if (scaled != size_after_crop ()) {
                d += String::compose (
@@ -489,11 +512,6 @@ VideoContent::set_bottom_crop (int c)
        maybe_set (_crop.bottom, c, VideoContentProperty::CROP);
 }
 
-void
-VideoContent::set_scale (VideoContentScale s)
-{
-       maybe_set (_scale, s, VideoContentProperty::SCALE);
-}
 
 void
 VideoContent::set_frame_type (VideoFrameType t)
@@ -551,7 +569,8 @@ VideoContent::take_settings_from (shared_ptr<const VideoContent> c)
        set_right_crop (c->_crop.right);
        set_top_crop (c->_crop.top);
        set_bottom_crop (c->_crop.bottom);
-       set_scale (c->_scale);
+       set_custom_ratio (c->_custom_ratio);
+       set_custom_size (c->_custom_size);
        set_fade_in (c->_fade_in);
        set_fade_out (c->_fade_out);
 }
@@ -569,3 +588,48 @@ VideoContent::modify_trim_start (ContentTime& trim) const
                trim = trim.round (_parent->video_frame_rate().get());
        }
 }
+
+
+/** @param film_container The size of the container for the DCP that we are working on */
+dcp::Size
+VideoContent::scaled_size (dcp::Size film_container)
+{
+       if (_custom_ratio) {
+               return fit_ratio_within(*_custom_ratio, film_container);
+       }
+
+       if (_custom_size) {
+               return *_custom_size;
+       }
+
+       dcp::Size size = size_after_crop ();
+       size.width *= _sample_aspect_ratio.get_value_or(1);
+
+       /* This is what we will return unless there is any legacy stuff to take into account */
+       dcp::Size auto_size = fit_ratio_within (size.ratio(), film_container);
+
+       if (_legacy_ratio) {
+               if (fit_ratio_within(*_legacy_ratio, film_container) != auto_size) {
+                       _custom_ratio = *_legacy_ratio;
+                       _legacy_ratio = optional<float>();
+                       return fit_ratio_within(*_custom_ratio, film_container);
+               }
+               _legacy_ratio = 0;
+       }
+
+       return auto_size;
+}
+
+
+void
+VideoContent::set_custom_ratio (optional<float> ratio)
+{
+       maybe_set (_custom_ratio, ratio, VideoContentProperty::CUSTOM_RATIO);
+}
+
+
+void
+VideoContent::set_custom_size (optional<dcp::Size> size)
+{
+       maybe_set (_custom_size, size, VideoContentProperty::CUSTOM_SIZE);
+}
index 9a7ad6d..b478666 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -22,7 +22,6 @@
 #define DCPOMATIC_VIDEO_CONTENT_H
 
 #include "colour_conversion.h"
-#include "video_content_scale.h"
 #include "dcpomatic_time.h"
 #include "user_property.h"
 #include "types.h"
@@ -48,6 +47,8 @@ public:
        static int const FADE_IN;
        static int const FADE_OUT;
        static int const RANGE;
+       static int const CUSTOM_RATIO;
+       static int const CUSTOM_SIZE;
 };
 
 class VideoContent : public ContentPart, public boost::enable_shared_from_this<VideoContent>
@@ -87,7 +88,9 @@ public:
        void set_top_crop (int);
        void set_bottom_crop (int);
 
-       void set_scale (VideoContentScale);
+       void set_custom_ratio (boost::optional<float> ratio);
+       void set_custom_size (boost::optional<dcp::Size> size);
+
        void unset_colour_conversion ();
        void set_colour_conversion (ColourConversion);
 
@@ -127,12 +130,19 @@ public:
                return _crop.bottom;
        }
 
-       /** @return Description of how to scale this content (if indeed it should be scaled) */
-       VideoContentScale scale () const {
+
+       boost::optional<float> custom_ratio () const {
                boost::mutex::scoped_lock lm (_mutex);
-               return _scale;
+               return _custom_ratio;
        }
 
+
+       boost::optional<dcp::Size> custom_size () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _custom_size;
+       }
+
+
        boost::optional<ColourConversion> colour_conversion () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _colour_conversion;
@@ -168,12 +178,14 @@ public:
                return _use;
        }
 
+       /* XXX: names for these? */
        dcp::Size size_after_3d_split () const;
        dcp::Size size_after_crop () const;
+       dcp::Size scaled_size (dcp::Size container_size);
 
        boost::optional<double> fade (boost::shared_ptr<const Film> film, Frame) const;
 
-       std::string processing_description (boost::shared_ptr<const Film> film) const;
+       std::string processing_description (boost::shared_ptr<const Film> film);
 
        void set_length (Frame);
 
@@ -191,6 +203,9 @@ private:
        friend struct best_dcp_frame_rate_test_single;
        friend struct best_dcp_frame_rate_test_double;
        friend struct audio_sampling_rate_test;
+       friend struct scaled_size_test1;
+       friend struct scaled_size_test2;
+       friend struct scaled_size_legacy_test;
 
        VideoContent (Content* parent, cxml::ConstNodePtr, int);
        void setup_default_colour_conversion ();
@@ -201,7 +216,15 @@ private:
        dcp::Size _size;
        VideoFrameType _frame_type;
        Crop _crop;
-       VideoContentScale _scale;
+       /** ratio to scale cropped image to (or none to guess); i.e. if set, scale to _custom_ratio:1 */
+       boost::optional<float> _custom_ratio;
+       /** size to scale cropped image to; only used if _custom_ratio is none */
+       boost::optional<dcp::Size> _custom_size;
+       /** ratio obtained from an older metadata file; will be used to set up
+        *  _custom_{ratio,size} (or not, if not required) on the first call to
+        *  scaled_size()
+        */
+       boost::optional<float> _legacy_ratio;
        /** Sample aspect ratio obtained from the content file's header, if there is one */
        boost::optional<double> _sample_aspect_ratio;
        bool _yuv;
diff --git a/src/lib/video_content_scale.cc b/src/lib/video_content_scale.cc
deleted file mode 100644 (file)
index 73dfd27..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic 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.
-
-    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "video_content_scale.h"
-#include "video_content.h"
-#include "ratio.h"
-#include "util.h"
-#include <libcxml/cxml.h>
-#include <libxml++/libxml++.h>
-#include <boost/optional.hpp>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::vector;
-using std::string;
-using std::min;
-using std::cout;
-using boost::shared_ptr;
-using boost::optional;
-
-vector<VideoContentScale> VideoContentScale::_scales;
-
-VideoContentScale::VideoContentScale (Ratio const * r)
-       : _ratio (r)
-       , _scale (true)
-{
-
-}
-
-VideoContentScale::VideoContentScale ()
-       : _ratio (0)
-       , _scale (false)
-{
-
-}
-
-VideoContentScale::VideoContentScale (bool scale)
-       : _ratio (0)
-       , _scale (scale)
-{
-
-}
-
-VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
-       : _ratio (0)
-       , _scale (true)
-{
-       optional<string> r = node->optional_string_child ("Ratio");
-       if (r) {
-               _ratio = Ratio::from_id (r.get ());
-       } else {
-               _scale = node->bool_child ("Scale");
-       }
-}
-
-void
-VideoContentScale::as_xml (xmlpp::Node* node) const
-{
-       if (_ratio) {
-               node->add_child("Ratio")->add_child_text (_ratio->id ());
-       } else {
-               node->add_child("Scale")->add_child_text (_scale ? "1" : "0");
-       }
-}
-
-string
-VideoContentScale::id () const
-{
-       if (_ratio) {
-               return _ratio->id ();
-       }
-
-       return (_scale ? "S1" : "S0");
-}
-
-string
-VideoContentScale::name () const
-{
-       if (_ratio) {
-               return _ratio->image_nickname ();
-       }
-
-       if (_scale) {
-               return _("No stretch");
-       }
-
-       return _("No scale");
-}
-
-/** @param display_container Size of the container that we are displaying this content in.
- *  @param film_container The size of the film's image.
- *  @return Size, in pixels that the VideoContent's image should be scaled to (taking into account its pixel aspect ratio)
- */
-dcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container) const
-{
-       /* Work out the size of the content if it were put inside film_container */
-
-       dcp::Size video_size_after_crop = c->size_after_crop();
-       video_size_after_crop.width *= c->sample_aspect_ratio().get_value_or(1);
-
-       dcp::Size size;
-
-       if (_ratio) {
-               /* Stretch to fit the requested ratio */
-               size = fit_ratio_within (_ratio->ratio (), film_container);
-       } else if (_scale || video_size_after_crop.width > film_container.width || video_size_after_crop.height > film_container.height) {
-               /* Scale, preserving aspect ratio; this is either if we have been asked to scale with no stretch
-                  or if the unscaled content is too big for film_container.
-               */
-               size = fit_ratio_within (video_size_after_crop.ratio(), film_container);
-       } else {
-               /* No stretch nor scale */
-               size = video_size_after_crop;
-       }
-
-       /* Now scale it down if the display container is smaller than the film container */
-       if (display_container != film_container) {
-               float const scale = min (
-                       float (display_container.width) / film_container.width,
-                       float (display_container.height) / film_container.height
-                       );
-
-               size.width = lrintf (size.width * scale);
-               size.height = lrintf (size.height * scale);
-       }
-
-       return size;
-}
-
-void
-VideoContentScale::setup_scales ()
-{
-       vector<Ratio const *> ratios = Ratio::all ();
-       for (vector<Ratio const *>::const_iterator i = ratios.begin(); i != ratios.end(); ++i) {
-               _scales.push_back (VideoContentScale (*i));
-       }
-
-       _scales.push_back (VideoContentScale (true));
-       _scales.push_back (VideoContentScale (false));
-}
-
-bool
-operator== (VideoContentScale const & a, VideoContentScale const & b)
-{
-       return (a.ratio() == b.ratio() && a.scale() == b.scale());
-}
-
-bool
-operator!= (VideoContentScale const & a, VideoContentScale const & b)
-{
-       return (a.ratio() != b.ratio() || a.scale() != b.scale());
-}
diff --git a/src/lib/video_content_scale.h b/src/lib/video_content_scale.h
deleted file mode 100644 (file)
index 845c71a..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic 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.
-
-    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DCPOMATIC_VIDEO_CONTENT_SCALE_H
-#define DCPOMATIC_VIDEO_CONTENT_SCALE_H
-
-#include <dcp/util.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-
-namespace cxml {
-       class Node;
-}
-
-namespace xmlpp {
-       class Node;
-}
-
-class Ratio;
-class VideoContent;
-
-class VideoContentScale
-{
-public:
-       VideoContentScale ();
-       explicit VideoContentScale (Ratio const *);
-       explicit VideoContentScale (bool);
-       explicit VideoContentScale (boost::shared_ptr<cxml::Node>);
-
-       dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size display_container, dcp::Size film_container) const;
-       std::string id () const;
-       std::string name () const;
-       void as_xml (xmlpp::Node *) const;
-
-       Ratio const * ratio () const {
-               return _ratio;
-       }
-
-       bool scale () const {
-               return _scale;
-       }
-
-       static void setup_scales ();
-       static std::vector<VideoContentScale> all () {
-               return _scales;
-       }
-
-private:
-       /** a ratio to stretch the content to, or 0 for no stretch */
-       Ratio const * _ratio;
-       /** true if we want to change the size of the content in any way */
-       bool _scale;
-
-       /* If _ratio is 0 and _scale is false there is no scale at all (i.e.
-          the content is used at its original size)
-       */
-
-       static std::vector<VideoContentScale> _scales;
-};
-
-bool operator== (VideoContentScale const & a, VideoContentScale const & b);
-bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
-
-#endif
index 0f2a5d1..fb222fc 100644 (file)
@@ -177,7 +177,6 @@ sources = """
           util.cc
           verify_dcp_job.cc
           video_content.cc
-          video_content_scale.cc
           video_decoder.cc
           video_filter_graph.cc
           video_mxf_content.cc
index 6a243e1..edf4808 100644 (file)
@@ -96,8 +96,10 @@ print_dump (shared_ptr<Film> film)
                             << "\tcrop left " << c->video->left_crop()
                             << " right " << c->video->right_crop()
                             << " top " << c->video->top_crop()
-                            << " bottom " << c->video->bottom_crop() << "\n"
-                            << "\tscale " << c->video->scale().name() << "\n";
+                            << " bottom " << c->video->bottom_crop() << "\n";
+                       if (c->video->custom_ratio()) {
+                               cout << "\tscale to custom ratio " << *c->video->custom_ratio() << ":1\n";
+                       }
                        if (c->video->colour_conversion()) {
                                if (c->video->colour_conversion().get().preset()) {
                                        cout << "\tcolour conversion "
index 68ae09c..bcfc2f6 100644 (file)
@@ -131,7 +131,6 @@ main (int argc, char* argv[])
 
                        BOOST_FOREACH (shared_ptr<Content> j, content) {
                                if (j->video) {
-                                       j->video->set_scale (VideoContentScale(cc.content_ratio));
                                        j->video->set_frame_type (i.frame_type);
                                }
                        }
diff --git a/src/wx/custom_scale_dialog.cc b/src/wx/custom_scale_dialog.cc
new file mode 100644 (file)
index 0000000..5725d23
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "custom_scale_dialog.h"
+#include "wx_util.h"
+#include "lib/util.h"
+#include <dcp/raw_convert.h>
+#include <wx/wx.h>
+#include <wx/propgrid/property.h>
+#include <wx/propgrid/props.h>
+
+
+using boost::optional;
+using namespace dcp;
+
+
+CustomScaleDialog::CustomScaleDialog (wxWindow* parent, dcp::Size initial, dcp::Size film_container, optional<float> custom_ratio, optional<dcp::Size> custom_size)
+       : TableDialog (parent, _("Custom scale"), 3, 1, true)
+       , _film_container (film_container)
+{
+       _ratio_to_fit = new wxRadioButton (this, wxID_ANY, _("Set ratio and fit to DCP container"));
+       add (_ratio_to_fit);
+       wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+       _ratio = new wxTextCtrl (this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, 0, wxNumericPropertyValidator(wxNumericPropertyValidator::Float));
+       s->Add (_ratio, 1, wxRIGHT, 4);
+       add_label_to_sizer (s, this, wxT(":1"), false);
+       add (s);
+       _size_from_ratio = new wxStaticText (this, wxID_ANY, wxT(""));
+       add (_size_from_ratio, 1, wxALIGN_CENTER_VERTICAL);
+
+       _size = new wxRadioButton (this, wxID_ANY, _("Set size"));
+       add (_size);
+       s = new wxBoxSizer (wxHORIZONTAL);
+       _width = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(64, -1), wxSP_ARROW_KEYS, 1, film_container.width);
+       s->Add (_width, 1, wxRIGHT, 4);
+       add_label_to_sizer (s, this, wxT("x"), false);
+       _height = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(64, -1), wxSP_ARROW_KEYS, 1, film_container.height);
+       s->Add (_height, 1, wxRIGHT, 4);
+       add (s);
+       _ratio_from_size = new wxStaticText (this, wxID_ANY, wxT(""));
+       add (_ratio_from_size, 1, wxALIGN_CENTER_VERTICAL);
+
+       if (custom_ratio) {
+               _ratio_to_fit->SetValue (true);
+               _size->SetValue (false);
+               _ratio->SetValue (wxString::Format("%.2f", *custom_ratio));
+               _width->SetValue (initial.width);
+               _height->SetValue (initial.height);
+       } else if (custom_size) {
+               _ratio_to_fit->SetValue (false);
+               _size->SetValue (true);
+               _ratio->SetValue (wxString::Format("%.2f", initial.ratio()));
+               _width->SetValue (custom_size->width);
+               _height->SetValue (custom_size->height);
+       } else {
+               _ratio_to_fit->SetValue (true);
+               _size->SetValue (false);
+               _ratio->SetValue (wxString::Format("%.2f", initial.ratio()));
+               _width->SetValue (initial.width);
+               _height->SetValue (initial.height);
+       }
+
+       setup_sensitivity ();
+       update_size_from_ratio ();
+       update_ratio_from_size ();
+
+       layout ();
+
+       _ratio_to_fit->Bind (wxEVT_RADIOBUTTON, boost::bind(&CustomScaleDialog::setup_sensitivity, this));
+       _ratio->Bind (wxEVT_TEXT, boost::bind(&CustomScaleDialog::update_size_from_ratio, this));
+       _size->Bind (wxEVT_RADIOBUTTON, boost::bind(&CustomScaleDialog::setup_sensitivity, this));
+       _width->Bind (wxEVT_TEXT, boost::bind(&CustomScaleDialog::update_ratio_from_size, this));
+       _height->Bind (wxEVT_TEXT, boost::bind(&CustomScaleDialog::update_ratio_from_size, this));
+}
+
+
+void
+CustomScaleDialog::update_size_from_ratio ()
+{
+       dcp::Size const s = fit_ratio_within (raw_convert<float>(wx_to_std(_ratio->GetValue())), _film_container);
+       _size_from_ratio->SetLabelMarkup (wxString::Format("<i>%dx%d</i>", s.width, s.height));
+}
+
+
+void
+CustomScaleDialog::update_ratio_from_size ()
+{
+       float const ratio = _height->GetValue() > 0 ? (float(_width->GetValue()) / _height->GetValue()) : 2;
+       _ratio_from_size->SetLabelMarkup (wxString::Format("<i>%.2f:1</i>", ratio));
+}
+
+
+void
+CustomScaleDialog::setup_sensitivity ()
+{
+       _ratio->Enable (_ratio_to_fit->GetValue());
+       _size_from_ratio->Enable (_ratio_to_fit->GetValue());
+       _width->Enable (_size->GetValue());
+       _height->Enable (_size->GetValue());
+       _ratio_from_size->Enable (_size->GetValue());
+}
+
+
+optional<float>
+CustomScaleDialog::custom_ratio () const
+{
+       if (!_ratio_to_fit->GetValue()) {
+               return optional<float>();
+       }
+
+       return raw_convert<float>(wx_to_std(_ratio->GetValue()));
+}
+
+
+optional<dcp::Size>
+CustomScaleDialog::custom_size () const
+{
+       if (!_size->GetValue()) {
+               return optional<dcp::Size>();
+       }
+
+       return dcp::Size (_width->GetValue(), _height->GetValue());
+}
+
diff --git a/src/wx/custom_scale_dialog.h b/src/wx/custom_scale_dialog.h
new file mode 100644 (file)
index 0000000..4c9ccf3
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "table_dialog.h"
+#include <dcp/types.h>
+#include <wx/wx.h>
+#include <wx/spinctrl.h>
+
+
+class CustomScaleDialog : public TableDialog
+{
+public:
+       CustomScaleDialog (wxWindow* parent, dcp::Size initial, dcp::Size film_container, boost::optional<float> custom_ratio, boost::optional<dcp::Size> custom_size);
+
+       boost::optional<float> custom_ratio () const;
+       boost::optional<dcp::Size> custom_size () const;
+
+private:
+       void update_size_from_ratio ();
+       void update_ratio_from_size ();
+       void setup_sensitivity ();
+
+       wxRadioButton* _ratio_to_fit;
+       wxTextCtrl* _ratio;
+       wxStaticText* _size_from_ratio;
+       wxRadioButton* _size;
+       wxSpinCtrl* _width;
+       wxSpinCtrl* _height;
+       wxStaticText* _ratio_from_size;
+
+       dcp::Size _film_container;
+};
+
index b01e9b8..8a91360 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -26,6 +26,7 @@
 #include "content_panel.h"
 #include "static_text.h"
 #include "check_box.h"
+#include "custom_scale_dialog.h"
 #include "dcpomatic_button.h"
 #include "lib/filter.h"
 #include "lib/ffmpeg_content.h"
@@ -55,27 +56,6 @@ using boost::bind;
 using boost::optional;
 using namespace dcpomatic;
 
-static VideoContentScale
-index_to_scale (int n)
-{
-       vector<VideoContentScale> scales = VideoContentScale::all ();
-       DCPOMATIC_ASSERT (n >= 0);
-       DCPOMATIC_ASSERT (n < int (scales.size ()));
-       return scales[n];
-}
-
-static int
-scale_to_index (VideoContentScale scale)
-{
-       vector<VideoContentScale> scales = VideoContentScale::all ();
-       for (size_t i = 0; i < scales.size(); ++i) {
-               if (scales[i] == scale) {
-                       return i;
-               }
-       }
-
-       DCPOMATIC_ASSERT (false);
-}
 
 VideoPanel::VideoPanel (ContentPanel* p)
        : ContentSubPanel (p, _("Video"))
@@ -150,22 +130,15 @@ VideoPanel::VideoPanel (ContentPanel* p)
        _fade_out_label = create_label (this, _("Fade out"), true);
        _fade_out = new Timecode<ContentTime> (this);
 
-       _scale_to_label = create_label (this, _("Scale to"), true);
-       _scale = new ContentChoice<VideoContent, VideoContentScale> (
-               this,
-               new wxChoice (this, wxID_ANY),
-               VideoContentProperty::SCALE,
-               &Content::video,
-               boost::mem_fn (&VideoContent::scale),
-               boost::mem_fn (&VideoContent::set_scale),
-               &index_to_scale,
-               &scale_to_index
-               );
-
        wxClientDC dc (this);
        wxSize size = dc.GetTextExtent (wxT ("A quite long name"));
        size.SetHeight (-1);
 
+       _scale_label = create_label (this, _("Scale"), true);
+       _scale_fit = new wxRadioButton (this, wxID_ANY, _("to fit DCP"));
+       _scale_custom = new wxRadioButton (this, wxID_ANY, _("custom"), wxDefaultPosition, size);
+       _scale_custom_edit = new Button (this, _("Edit..."));
+
        _filters_label = create_label (this, _("Filters"), true);
        _filters = new StaticText (this, _("None"), wxDefaultPosition, size);
        _filters_button = new Button (this, _("Edit..."));
@@ -194,11 +167,6 @@ VideoPanel::VideoPanel (ContentPanel* p)
        _right_crop->wrapped()->SetRange (0, 4096);
        _bottom_crop->wrapped()->SetRange (0, 4096);
 
-       _scale->wrapped()->Clear ();
-       BOOST_FOREACH (VideoContentScale const & i, VideoContentScale::all ()) {
-               _scale->wrapped()->Append (std_to_wx (i.name ()));
-       }
-
        _frame_type->wrapped()->Append (_("2D"));
        _frame_type->wrapped()->Append (_("3D"));
        _frame_type->wrapped()->Append (_("3D left/right"));
@@ -215,6 +183,9 @@ VideoPanel::VideoPanel (ContentPanel* p)
        _use->Bind                           (wxEVT_CHECKBOX, boost::bind (&VideoPanel::use_clicked, this));
        _reference->Bind                     (wxEVT_CHECKBOX, boost::bind (&VideoPanel::reference_clicked, this));
        _filters_button->Bind                (wxEVT_BUTTON,   boost::bind (&VideoPanel::edit_filters_clicked, this));
+       _scale_fit->Bind                     (wxEVT_RADIOBUTTON, boost::bind (&VideoPanel::scale_fit_clicked, this));
+       _scale_custom->Bind                  (wxEVT_RADIOBUTTON, boost::bind (&VideoPanel::scale_custom_clicked, this));
+       _scale_custom_edit->Bind             (wxEVT_BUTTON,   boost::bind (&VideoPanel::scale_custom_edit_clicked, this));
        _colour_conversion->Bind             (wxEVT_CHOICE,   boost::bind (&VideoPanel::colour_conversion_changed, this));
        _range->Bind                         (wxEVT_CHOICE,   boost::bind (&VideoPanel::range_changed, this));
        _edit_colour_conversion_button->Bind (wxEVT_BUTTON,   boost::bind (&VideoPanel::edit_colour_conversion_clicked, this));
@@ -260,11 +231,13 @@ VideoPanel::add_to_grid ()
        add_label_to_sizer (crop, _bottom_crop_label, true, wxGBPosition (cr, 2));
        _bottom_crop->add (crop, wxGBPosition (cr, 3));
        add_label_to_sizer (_grid, _crop_label, true, wxGBPosition(r, 0));
-       _grid->Add (crop, wxGBPosition(r, 1), wxGBSpan(2, 3));
-       r += 2;
+       _grid->Add (crop, wxGBPosition(r, 1));
+       ++r;
 
-       _scale_to_label->Show (full);
-       _scale->show (full);
+       _scale_label->Show (full);
+       _scale_fit->Show (full);
+       _scale_custom->Show (full);
+       _scale_custom_edit->Show (full);
        _filters_label->Show (full);
        _filters->Show (full);
        _filters_button->Show (full);
@@ -283,8 +256,16 @@ VideoPanel::add_to_grid ()
        ++r;
 
        if (full) {
-               add_label_to_sizer (_grid, _scale_to_label, true, wxGBPosition (r, 0));
-               _scale->add (_grid, wxGBPosition (r, 1), wxGBSpan (1, 2));
+               add_label_to_sizer (_grid, _scale_label, true, wxGBPosition (r, 0));
+               {
+                       wxSizer* v = new wxBoxSizer (wxVERTICAL);
+                       v->Add (_scale_fit, 0, wxBOTTOM, 4);
+                       wxSizer* h = new wxBoxSizer (wxHORIZONTAL);
+                       h->Add (_scale_custom, 1, wxRIGHT, 6);
+                       h->Add (_scale_custom_edit, 0);
+                       v->Add (h, 0);
+                       _grid->Add (v, wxGBPosition(r, 1));
+               }
                ++r;
 
                add_label_to_sizer (_grid, _filters_label, true, wxGBPosition (r, 0));
@@ -485,6 +466,20 @@ VideoPanel::film_content_changed (int property)
                        checked_set (_range, 0);
                }
 
+               setup_sensitivity ();
+       } else if (property == VideoContentProperty::CUSTOM_RATIO || property == VideoContentProperty::CUSTOM_SIZE) {
+               set<Frame> check;
+               BOOST_FOREACH (shared_ptr<const Content> i, vc) {
+                       check.insert (i->video->custom_ratio() || i->video->custom_size());
+               }
+
+               if (check.size() == 1) {
+                       checked_set (_scale_fit, !vc.front()->video->custom_ratio() && !vc.front()->video->custom_size());
+                       checked_set (_scale_custom, vc.front()->video->custom_ratio() || vc.front()->video->custom_size());
+               } else {
+                       checked_set (_scale_fit, true);
+                       checked_set (_scale_custom, false);
+               }
                setup_sensitivity ();
        }
 }
@@ -576,7 +571,6 @@ VideoPanel::content_selection_changed ()
        _right_crop->set_content (video_sel);
        _top_crop->set_content (video_sel);
        _bottom_crop->set_content (video_sel);
-       _scale->set_content (video_sel);
 
        film_content_changed (ContentProperty::VIDEO_FRAME_RATE);
        film_content_changed (VideoContentProperty::CROP);
@@ -585,6 +579,8 @@ VideoPanel::content_selection_changed ()
        film_content_changed (VideoContentProperty::FADE_OUT);
        film_content_changed (VideoContentProperty::RANGE);
        film_content_changed (VideoContentProperty::USE);
+       film_content_changed (VideoContentProperty::CUSTOM_RATIO);
+       film_content_changed (VideoContentProperty::CUSTOM_SIZE);
        film_content_changed (FFmpegContentProperty::FILTERS);
        film_content_changed (DCPContentProperty::REFERENCE_VIDEO);
 
@@ -623,7 +619,9 @@ VideoPanel::setup_sensitivity ()
                _bottom_crop->wrapped()->Enable (false);
                _fade_in->Enable (false);
                _fade_out->Enable (false);
-               _scale->wrapped()->Enable (false);
+               _scale_fit->Enable (false);
+               _scale_custom->Enable (false);
+               _scale_custom_edit->Enable (false);
                _description->Enable (false);
                _filters->Enable (false);
                _filters_button->Enable (false);
@@ -641,7 +639,9 @@ VideoPanel::setup_sensitivity ()
                _bottom_crop->wrapped()->Enable (true);
                _fade_in->Enable (!video_sel.empty ());
                _fade_out->Enable (!video_sel.empty ());
-               _scale->wrapped()->Enable (true);
+               _scale_fit->Enable (true);
+               _scale_custom->Enable (true);
+               _scale_custom_edit->Enable (_scale_custom->GetValue());
                _description->Enable (true);
                _filters->Enable (true);
                _filters_button->Enable (single && !ffmpeg_sel.empty ());
@@ -703,3 +703,39 @@ VideoPanel::reference_clicked ()
 
        d->set_reference_video (_reference->GetValue ());
 }
+
+
+void
+VideoPanel::scale_fit_clicked ()
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+               i->video->set_custom_ratio (optional<float>());
+       }
+}
+
+
+void
+VideoPanel::scale_custom_clicked ()
+{
+       if (!scale_custom_edit_clicked()) {
+               _scale_fit->SetValue (true);
+       }
+}
+
+
+bool
+VideoPanel::scale_custom_edit_clicked ()
+{
+       shared_ptr<const VideoContent> vc = _parent->selected_video().front()->video;
+       CustomScaleDialog* d = new CustomScaleDialog (this, vc->size(), _parent->film()->frame_size(), vc->custom_ratio(), vc->custom_size());
+       int const r = d->ShowModal ();
+       if (r == wxID_OK) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+                       i->video->set_custom_ratio (d->custom_ratio());
+                       i->video->set_custom_size (d->custom_size());
+               }
+       }
+       d->Destroy ();
+       return r == wxID_OK;
+}
+
index 04063ef..22c5644 100644 (file)
@@ -25,7 +25,6 @@
 #include "content_sub_panel.h"
 #include "content_widget.h"
 #include "timecode.h"
-#include "lib/video_content_scale.h"
 #include "lib/film.h"
 
 class wxChoice;
@@ -55,6 +54,9 @@ private:
        void fade_in_changed ();
        void fade_out_changed ();
        void add_to_grid ();
+       void scale_fit_clicked ();
+       void scale_custom_clicked ();
+       bool scale_custom_edit_clicked ();
 
        void setup_description ();
        void setup_sensitivity ();
@@ -77,8 +79,10 @@ private:
        Timecode<dcpomatic::ContentTime>* _fade_in;
        wxStaticText* _fade_out_label;
        Timecode<dcpomatic::ContentTime>* _fade_out;
-       wxStaticText* _scale_to_label;
-       ContentChoice<VideoContent, VideoContentScale>* _scale;
+       wxStaticText* _scale_label;
+       wxRadioButton* _scale_fit;
+       wxRadioButton* _scale_custom;
+       wxButton* _scale_custom_edit;
        wxStaticText* _description;
        wxStaticText* _filters_label;
        wxStaticText* _filters;
index 22d9f0d..a2fbe0c 100644 (file)
@@ -49,6 +49,7 @@ sources = """
           controls.cc
           closed_captions_dialog.cc
           credentials_download_certificate_panel.cc
+          custom_scale_dialog.cc
           dcp_panel.cc
           dcpomatic_button.cc
           disk_warning_dialog.cc
@@ -165,7 +166,7 @@ sources = """
 
 def configure(conf):
 
-    wx_libs = 'core,richtext,adv,html,xml'
+    wx_libs = 'core,richtext,adv,html,xml,propgrid'
 
     try:
         wx_config = '/usr/lib64/wx/config/gtk2-unicode-3.0'
index 71d60f5..d8d3d66 100644 (file)
@@ -50,8 +50,6 @@ BOOST_AUTO_TEST_CASE (fourk_test)
        film->examine_and_add_content (c);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
-
        film->make_dcp ();
        BOOST_REQUIRE (!wait_for_jobs());
 
index b92cb28..5425969 100644 (file)
@@ -70,60 +70,53 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
        cc = run ("dcpomatic2_create -h");
        BOOST_REQUIRE (cc.error);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --name frobozz --template bar");
+       cc = run ("dcpomatic2_create x --name frobozz --template bar");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL (cc.name, "frobozz");
        BOOST_REQUIRE (cc.template_name);
        BOOST_CHECK_EQUAL (*cc.template_name, "bar");
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --dcp-content-type FTR");
+       cc = run ("dcpomatic2_create x --dcp-content-type FTR");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL (cc.dcp_content_type, DCPContentType::from_isdcf_name("FTR"));
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --dcp-frame-rate 30");
+       cc = run ("dcpomatic2_create x --dcp-frame-rate 30");
        BOOST_CHECK (!cc.error);
        BOOST_REQUIRE (cc.dcp_frame_rate);
        BOOST_CHECK_EQUAL (*cc.dcp_frame_rate, 30);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --container-ratio 185");
+       cc = run ("dcpomatic2_create x --container-ratio 185");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL (cc.container_ratio, Ratio::from_id("185"));
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --container-ratio XXX");
+       cc = run ("dcpomatic2_create x --container-ratio XXX");
        BOOST_CHECK (cc.error);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --content-ratio 239");
-       BOOST_CHECK (!cc.error);
-       BOOST_CHECK_EQUAL (cc.content_ratio, Ratio::from_id("239"));
-
-       cc = run ("dcpomatic2_create x --content-ratio 240");
-       BOOST_CHECK (cc.error);
-
-       cc = run ("dcpomatic2_create x --content-ratio 185 --still-length 42");
+       cc = run ("dcpomatic2_create x --still-length 42");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL (cc.still_length, 42);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --standard SMPTE");
+       cc = run ("dcpomatic2_create x --standard SMPTE");
        BOOST_CHECK (!cc.error);
        BOOST_CHECK_EQUAL (cc.standard, dcp::SMPTE);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --standard SMPTEX");
+       cc = run ("dcpomatic2_create x --standard SMPTEX");
        BOOST_CHECK (cc.error);
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --config foo/bar");
+       cc = run ("dcpomatic2_create x --config foo/bar");
        BOOST_CHECK (!cc.error);
        BOOST_REQUIRE (cc.config_dir);
        BOOST_CHECK_EQUAL (*cc.config_dir, "foo/bar");
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --output fred/jim");
+       cc = run ("dcpomatic2_create x --output fred/jim");
        BOOST_CHECK (!cc.error);
        BOOST_REQUIRE (cc.output_dir);
        BOOST_CHECK_EQUAL (*cc.output_dir, "fred/jim");
 
-       cc = run ("dcpomatic2_create x --content-ratio 185 --outputX fred/jim");
+       cc = run ("dcpomatic2_create x --outputX fred/jim");
        BOOST_CHECK (cc.error);
 
-       cc = run ("dcpomatic2_create --content-ratio 185 --config foo/bar --still-length 42 --output flaps fred jim sheila");
+       cc = run ("dcpomatic2_create --config foo/bar --still-length 42 --output flaps fred jim sheila");
        BOOST_CHECK (!cc.error);
        BOOST_REQUIRE (cc.config_dir);
        BOOST_CHECK_EQUAL (*cc.config_dir, "foo/bar");
@@ -138,7 +131,7 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
        BOOST_CHECK_EQUAL (cc.content[2].path, "sheila");
        BOOST_CHECK_EQUAL (cc.content[2].frame_type, VIDEO_FRAME_TYPE_2D);
 
-       cc = run ("dcpomatic2_create --content-ratio 185 --left-eye left.mp4 --right-eye right.mp4");
+       cc = run ("dcpomatic2_create --left-eye left.mp4 --right-eye right.mp4");
        BOOST_REQUIRE_EQUAL (cc.content.size(), 2);
        BOOST_CHECK_EQUAL (cc.content[0].path, "left.mp4");
        BOOST_CHECK_EQUAL (cc.content[0].frame_type, VIDEO_FRAME_TYPE_3D_LEFT);
@@ -146,13 +139,13 @@ BOOST_AUTO_TEST_CASE (create_cli_test)
        BOOST_CHECK_EQUAL (cc.content[1].frame_type, VIDEO_FRAME_TYPE_3D_RIGHT);
        BOOST_CHECK_EQUAL (cc.fourk, false);
 
-       cc = run ("dcpomatic2_create --fourk --content-ratio 185 foo.mp4");
+       cc = run ("dcpomatic2_create --fourk foo.mp4");
        BOOST_REQUIRE_EQUAL (cc.content.size(), 1);
        BOOST_CHECK_EQUAL (cc.content[0].path, "foo.mp4");
        BOOST_CHECK_EQUAL (cc.fourk, true);
        BOOST_CHECK (!cc.error);
 
-       cc = run ("dcpomatic2_create --j2k-bandwidth 120 --content-ratio 185 foo.mp4");
+       cc = run ("dcpomatic2_create --j2k-bandwidth 120 foo.mp4");
        BOOST_REQUIRE_EQUAL (cc.content.size(), 1);
        BOOST_CHECK_EQUAL (cc.content[0].path, "foo.mp4");
        BOOST_REQUIRE (cc.j2k_bandwidth);
index 6b69488..9ec245b 160000 (submodule)
--- a/test/data
+++ b/test/data
@@ -1 +1 @@
-Subproject commit 6b694889479064979b52c1839a1919dc5fde6731
+Subproject commit 9ec245b7f59b65ad6dd6d0f717686931ff66d748
index 029e839..a2557d4 100644 (file)
@@ -55,10 +55,8 @@ BOOST_AUTO_TEST_CASE (empty_test1)
        /* 0 1 2 3 4 5 6 7
         *     A A A     B
         */
-       contentA->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentA->video->set_length (3);
        contentA->set_position (film, DCPTime::from_frames (2, vfr));
-       contentB->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentB->video->set_length (1);
        contentB->set_position (film, DCPTime::from_frames (7, vfr));
 
@@ -89,10 +87,8 @@ BOOST_AUTO_TEST_CASE (empty_test2)
        /* 0 1 2 3 4 5 6 7
         * A A A         B
         */
-       contentA->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentA->video->set_length (3);
        contentA->set_position (film, DCPTime(0));
-       contentB->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentB->video->set_length (1);
        contentB->set_position (film, DCPTime::from_frames(7, vfr));
 
@@ -129,10 +125,8 @@ BOOST_AUTO_TEST_CASE (empty_test3)
        /* 0 1 2 3 4 5 6 7
         * A A A         B
         */
-       contentA->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentA->video->set_length (3);
        contentA->set_position (film, DCPTime(0));
-       contentB->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentB->video->set_length (1);
        contentB->set_position (film, DCPTime::from_frames(7, vfr));
 
index 6ad6c1f..e9b0c70 100644 (file)
@@ -53,8 +53,6 @@ BOOST_AUTO_TEST_CASE (ffmpeg_audio_test)
 
        BOOST_REQUIRE (!wait_for_jobs());
 
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
-
        film->set_container (Ratio::from_id ("185"));
        film->set_audio_channels (6);
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
index 78847e4..ad09e5e 100644 (file)
@@ -46,8 +46,6 @@ BOOST_AUTO_TEST_CASE (ffmpeg_dcp_test)
 
        BOOST_REQUIRE (!wait_for_jobs());
 
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
-
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
        film->make_dcp ();
index 3315833..487f80e 100644 (file)
@@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE (isdcf_name_test)
        shared_ptr<ImageContent> content (new ImageContent ("test/data/simple_testcard_640x480.png"));
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
-       content->video->set_scale (VideoContentScale (Ratio::from_id ("133")));
+       content->video->set_custom_ratio (1.33);
        film->set_container (Ratio::from_id ("185"));
        BOOST_CHECK_EQUAL (film->isdcf_name(false), "MyNiceFilmWith_TLR-2_F_DE-fr_US-R_MOS_4K_DI_20140704_PP_SMPTE_OV");
 
@@ -108,12 +108,12 @@ BOOST_AUTO_TEST_CASE (isdcf_name_test)
 
        /* And it should always be numeric */
 
-       content->video->set_scale (VideoContentScale (Ratio::from_id ("239")));
+       content->video->set_custom_ratio (2.39);
        BOOST_CHECK_EQUAL (film->isdcf_name(false), "MyNiceFilmWith_XSN-2_F-239_DE-fr_US-R_MOS_4K_DI_20140704_PP_SMPTE_OV");
 
-       content->video->set_scale (VideoContentScale (Ratio::from_id ("190")));
+       content->video->set_custom_ratio (1.9);
        BOOST_CHECK_EQUAL (film->isdcf_name(false), "MyNiceFilmWith_XSN-2_F-190_DE-fr_US-R_MOS_4K_DI_20140704_PP_SMPTE_OV");
-       content->video->set_scale (VideoContentScale (Ratio::from_id ("133")));
+       content->video->set_custom_ratio (1.33);
 
        /* Test 3D */
 
index 6877d7e..2e979eb 100644 (file)
@@ -103,12 +103,12 @@ BOOST_AUTO_TEST_CASE (player_black_fill_test)
        film->examine_and_add_content (contentB);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       contentA->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        contentA->video->set_length (3);
        contentA->set_position (film, DCPTime::from_frames(2, film->video_frame_rate()));
-       contentB->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
+       contentA->video->set_custom_ratio (1.85);
        contentB->video->set_length (1);
        contentB->set_position (film, DCPTime::from_frames(7, film->video_frame_rate()));
+       contentB->video->set_custom_ratio (1.85);
 
        film->make_dcp ();
 
index e8194fd..9c1ed7e 100644 (file)
@@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE (recover_test_2d)
        film->make_dcp ();
        BOOST_REQUIRE (!wait_for_jobs());
 
-       boost::filesystem::path const video = "build/test/recover_test_2d/video/185_2K_d4343facdd66ca71f62a964fbade89f3_24_100000000_P_S_0_1200000.mxf";
+       boost::filesystem::path const video = "build/test/recover_test_2d/video/185_2K_02543352c540f4b083bff3f1e309d4a9_24_100000000_P_S_0_1200000.mxf";
        boost::filesystem::copy_file (
                video,
                "build/test/recover_test_2d/original.mxf"
@@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE (recover_test_3d, * boost::unit_test::depends_on("recover_t
        film->make_dcp ();
        BOOST_REQUIRE (!wait_for_jobs());
 
-       boost::filesystem::path const video = "build/test/recover_test_3d/video/185_2K_342fe9115d2b446914b31f7602e48cc6_24_100000000_P_S_3D_0_96000.mxf";
+       boost::filesystem::path const video = "build/test/recover_test_3d/video/185_2K_70e6661af92ae94458784c16a21a9748_24_100000000_P_S_3D_0_96000.mxf";
 
        boost::filesystem::copy_file (
                video,
@@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE (recover_test_2d_encrypted, * boost::unit_test::depends_on(
        BOOST_REQUIRE (!wait_for_jobs());
 
        boost::filesystem::path const video =
-               "build/test/recover_test_2d_encrypted/video/185_2K_d4343facdd66ca71f62a964fbade89f3_24_100000000_Eeafcb91c9f5472edf01f3a2404c57258_S_0_1200000.mxf";
+               "build/test/recover_test_2d_encrypted/video/185_2K_02543352c540f4b083bff3f1e309d4a9_24_100000000_Eeafcb91c9f5472edf01f3a2404c57258_S_0_1200000.mxf";
 
        boost::filesystem::copy_file (
                video,
index 1075f96..358e6fe 100644 (file)
@@ -45,10 +45,8 @@ BOOST_AUTO_TEST_CASE (repeat_frame_test)
        film->set_interop (false);
        shared_ptr<FFmpegContent> c (new FFmpegContent("test/data/red_24.mp4"));
        film->examine_and_add_content (c);
-
        BOOST_REQUIRE (!wait_for_jobs());
-
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
+       c->video->set_custom_ratio (1.85);
 
        film->set_video_frame_rate (48);
        film->make_dcp ();
index 93c9947..dd3b611 100644 (file)
@@ -34,9 +34,9 @@
 using std::string;
 using boost::shared_ptr;
 
-static void scaling_test_for (shared_ptr<Film> film, shared_ptr<Content> content, string image, string container)
+static void scaling_test_for (shared_ptr<Film> film, shared_ptr<Content> content, float ratio, std::string image, string container)
 {
-       content->video->set_scale (VideoContentScale (Ratio::from_id (image)));
+       content->video->set_custom_ratio (ratio);
        film->set_container (Ratio::from_id (container));
        film->set_interop (false);
        film->make_dcp ();
@@ -71,16 +71,16 @@ BOOST_AUTO_TEST_CASE (scaling_test)
        imc->video->set_length (1);
 
        /* F-133: 133 image in a flat container */
-       scaling_test_for (film, imc, "133", "185");
+       scaling_test_for (film, imc, 4.0 / 3, "133", "185");
        /* F: flat image in a flat container */
-       scaling_test_for (film, imc, "185", "185");
+       scaling_test_for (film, imc, 1.85, "185", "185");
        /* F-S: scope image in a flat container */
-       scaling_test_for (film, imc, "239", "185");
+       scaling_test_for (film, imc, 2.38695, "239", "185");
 
        /* S-133: 133 image in a scope container */
-       scaling_test_for (film, imc, "133", "239");
+       scaling_test_for (film, imc, 4.0 / 3, "133", "239");
        /* S-F: flat image in a scope container */
-       scaling_test_for (film, imc, "185", "239");
+       scaling_test_for (film, imc, 1.85, "185", "239");
        /* S: scope image in a scope container */
-       scaling_test_for (film, imc, "239", "239");
+       scaling_test_for (film, imc, 2.38695, "239", "239");
 }
index e30143b..aea389c 100644 (file)
@@ -48,7 +48,6 @@ BOOST_AUTO_TEST_CASE (skip_frame_test)
 
        BOOST_REQUIRE (!wait_for_jobs());
 
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
        film->write_metadata ();
 
        film->set_video_frame_rate (24);
index 473cc00..266b7bf 100644 (file)
@@ -49,7 +49,6 @@ BOOST_AUTO_TEST_CASE (threed_test1)
        BOOST_REQUIRE (!wait_for_jobs());
 
        c->video->set_frame_type (VIDEO_FRAME_TYPE_3D_LEFT_RIGHT);
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
 
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
@@ -72,7 +71,6 @@ BOOST_AUTO_TEST_CASE (threed_test2)
        BOOST_REQUIRE (!wait_for_jobs());
 
        c->video->set_frame_type (VIDEO_FRAME_TYPE_3D_ALTERNATE);
-       c->video->set_scale (VideoContentScale (Ratio::from_id ("185")));
 
        film->set_container (Ratio::from_id ("185"));
        film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
index 67bfda3..b3e3ff7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
-/** @file  test/video_content_scale_test.cc
- *  @brief Test VideoContentScale
- *  @ingroup selfcontained
- */
 
-#include "lib/ffmpeg_content.h"
 #include "lib/ratio.h"
 #include "lib/video_content.h"
-#include <dcp/raw_convert.h>
 #include <boost/test/unit_test.hpp>
 
-using std::list;
-using std::string;
-using std::cerr;
-using boost::shared_ptr;
-using boost::optional;
-using dcp::raw_convert;
 
-static
-void
-test (dcp::Size content_size, dcp::Size display_size, dcp::Size film_size, Crop crop, Ratio const * ratio, bool scale, dcp::Size correct)
-{
-       shared_ptr<Film> film;
-       string s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-               "<Content>"
-               "<Type>FFmpeg</Type>"
-               "<Path>/home/c.hetherington/DCP/prophet_long_clip.mkv</Path>"
-               "<Digest>f3f23663da5bef6d2cbaa0db066f3351314142710</Digest>"
-               "<Position>0</Position>"
-               "<TrimStart>0</TrimStart>"
-               "<TrimEnd>0</TrimEnd>"
-               "<VideoLength>2879</VideoLength>"
-               "<VideoWidth>" + raw_convert<string>(content_size.width) + "</VideoWidth>"
-               "<VideoHeight>" + raw_convert<string>(content_size.height) + "</VideoHeight>"
-               "<VideoFrameRate>23.97602462768555</VideoFrameRate>"
-               "<OriginalVideoFrameRate>23.97602462768555</OriginalVideoFrameRate>"
-               "<VideoFrameType>0</VideoFrameType>"
-               "<SampleAspectRatio>1</SampleAspectRatio>"
-               "<BitsPerPixel>12</BitsPerPixel>"
-               "<LeftCrop>" + raw_convert<string>(crop.left) + "</LeftCrop>"
-               "<RightCrop>" + raw_convert<string>(crop.right) + "</RightCrop>"
-               "<TopCrop>" + raw_convert<string>(crop.top) + "</TopCrop>"
-               "<BottomCrop>" + raw_convert<string>(crop.bottom) + "</BottomCrop>"
-               "<Scale>";
-
-       if (ratio) {
-               s += "<Ratio>" + ratio->id() + "</Ratio>";
-       } else {
-               s += "<Scale>" + string(scale ? "1" : "0") + "</Scale>";
-       }
+static dcp::Size const FOUR_TO_THREE(1436, 1080);
+static dcp::Size const FLAT(1998, 1080);
+static dcp::Size const SCOPE(2048, 858);
 
-       s += "</Scale>"
-               "<ColourConversion>"
-               "<InputGamma>2.4</InputGamma>"
-               "<InputGammaLinearised>1</InputGammaLinearised>"
-               "<Matrix i=\"0\" j=\"0\">0.4124564</Matrix>"
-               "<Matrix i=\"0\" j=\"1\">0.3575761</Matrix>"
-               "<Matrix i=\"0\" j=\"2\">0.1804375</Matrix>"
-               "<Matrix i=\"1\" j=\"0\">0.2126729</Matrix>"
-               "<Matrix i=\"1\" j=\"1\">0.7151522</Matrix>"
-               "<Matrix i=\"1\" j=\"2\">0.072175</Matrix>"
-               "<Matrix i=\"2\" j=\"0\">0.0193339</Matrix>"
-               "<Matrix i=\"2\" j=\"1\">0.119192</Matrix>"
-               "<Matrix i=\"2\" j=\"2\">0.9503041</Matrix>"
-               "<OutputGamma>2.6</OutputGamma>"
-               "</ColourConversion>"
-               "<AudioGain>0</AudioGain>"
-               "<AudioDelay>0</AudioDelay>"
-               "<SubtitleXOffset>0</SubtitleXOffset>"
-               "<SubtitleYOffset>0</SubtitleYOffset>"
-               "<SubtitleXScale>0</SubtitleXScale>"
-               "<SubtitleYScale>0</SubtitleYScale>"
-               "</Content>";
-
-       shared_ptr<cxml::Document> doc (new cxml::Document ());
-       doc->read_string (s);
-
-       list<string> notes;
-       shared_ptr<FFmpegContent> vc (new FFmpegContent (doc, 10, notes));
-
-       optional<VideoContentScale> sc;
-       if (ratio) {
-               sc = VideoContentScale (ratio);
-       } else {
-               sc = VideoContentScale (scale);
-       }
 
-       dcp::Size answer = sc.get().size (vc->video, display_size, film_size);
-       if (answer != correct) {
-               cerr << "Testing " << vc->video->size().width << "x" << vc->video->size().height << "\n";
-               cerr << "Testing " << display_size.width << "x" << display_size.height << "\n";
-               cerr << answer.width << "x" << answer.height << " instead of " << correct.width << "x" << correct.height << "\n";
-       }
-       BOOST_CHECK (answer == correct);
+/* Test VideoContent::scaled_size() without any legacy stuff */
+BOOST_AUTO_TEST_CASE (scaled_size_test1)
+{
+       VideoContent vc (0);
+
+       /* Images at full size and in DCP-approved sizes that will not be scaled */
+       // Flat/scope content into flat/scope container
+       vc._size = FLAT;
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FLAT);
+       vc._size = SCOPE;
+       BOOST_CHECK_EQUAL (vc.scaled_size(SCOPE), SCOPE);
+       // 1.33:1 into flat container
+       vc._size = FOUR_TO_THREE;
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(FOUR_TO_THREE));
+       // Scope into flat container
+       vc._size = SCOPE;
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 837));
+
+       /* Smaller images but in the same ratios */
+       vc._size = dcp::Size(185, 100);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FLAT);
+       vc._size = dcp::Size(955, 400);
+       BOOST_CHECK_EQUAL (vc.scaled_size(SCOPE), SCOPE);
+       // 1.33:1 into flat container
+       vc._size = dcp::Size(133, 100);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(FOUR_TO_THREE));
+       // Scope into flat container
+       vc._size = dcp::Size(239, 100);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 836));
+
+       /* Images at full size that are not DCP-approved but will still remain unscaled */
+       vc._size = dcp::Size(600, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(600, 1080));
+       vc._size = dcp::Size(1700, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1700, 1080));
+
+       /* Image at full size that is too big for the container and will be shrunk */
+       vc._size = dcp::Size(3000, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 719));
 }
 
-/* Test scale and stretch to specified ratio */
-BOOST_AUTO_TEST_CASE (video_content_scale_test_to_ratio)
+
+/* Same as scaled_size_test1 but with a non-unity sample aspect ratio */
+BOOST_AUTO_TEST_CASE (scaled_size_test2)
 {
-       /* To DCP */
-
-       // Flat in flat container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (1998, 1080),
-               dcp::Size (1998, 1080),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("185"),
-               true,
-               dcp::Size (1998, 1080)
-               );
-
-       // Scope in flat container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (1998, 1080),
-               dcp::Size (1998, 1080),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("239"),
-               true,
-               dcp::Size (1998, 837)
-               );
-
-       // Flat in scope container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (2048, 858),
-               dcp::Size (2048, 858),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("185"),
-               true,
-               dcp::Size (1587, 858)
-               );
-
-
-       /* To player */
-
-       // Flat in flat container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (185, 100),
-               dcp::Size (1998, 1080),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("185"),
-               true,
-               dcp::Size (185, 100)
-               );
-
-       // Scope in flat container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (185, 100),
-               dcp::Size (1998, 1080),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("239"),
-               true,
-               dcp::Size (185, 78)
-               );
-
-       // Flat in scope container
-       test (
-               dcp::Size (400, 200),
-               dcp::Size (239, 100),
-               dcp::Size (2048, 858),
-               Crop (0, 0, 0, 0),
-               Ratio::from_id ("185"),
-               true,
-               dcp::Size (185, 100)
-               );
+       VideoContent vc (0);
+
+       vc._sample_aspect_ratio = 2;
+
+       /* Images at full size and in DCP-approved sizes that will not be scaled */
+       // Flat/scope content into flat/scope container
+       vc._size = dcp::Size (1998 / 2, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FLAT);
+       vc._size = dcp::Size (2048 / 2, 858);
+       BOOST_CHECK_EQUAL (vc.scaled_size(SCOPE), SCOPE);
+       // 1.33:1 into flat container
+       vc._size = dcp::Size (1436 / 2, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(FOUR_TO_THREE));
+       // Scope into flat container
+       vc._size = dcp::Size (2048 / 2, 858);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 837));
+
+       /* Smaller images but in the same ratios */
+       vc._size = dcp::Size(185, 200);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FLAT);
+       vc._size = dcp::Size(955, 800);
+       BOOST_CHECK_EQUAL (vc.scaled_size(SCOPE), SCOPE);
+       // 4:3 into flat container
+       vc._size = dcp::Size(133, 200);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(FOUR_TO_THREE));
+       // Scope into flat container
+       vc._size = dcp::Size(239, 200);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 836));
+
+       /* Images at full size that are not DCP-approved but will still remain unscaled */
+       vc._size = dcp::Size(600 / 2, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(600, 1080));
+       vc._size = dcp::Size(1700 / 2, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1700, 1080));
+
+       /* Image at full size that is too big for the container and will be shrunk */
+       vc._size = dcp::Size(3000 / 2, 1080);
+       BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(1998, 719));
 }
 
-/* Test no scale */
-BOOST_AUTO_TEST_CASE (video_content_scale_no_scale)
+
+/* Test VideoContent::scaled_size() with some legacy stuff */
+BOOST_AUTO_TEST_CASE (scaled_size_legacy_test)
 {
-       /* No scale where the content is bigger than even the film container */
-       test (
-               dcp::Size (1920, 1080),
-               dcp::Size (887, 371),
-               dcp::Size (2048, 858),
-               Crop (),
-               0,
-               false,
-               dcp::Size (659, 371)
-               );
+       {
+               /* 640x480 content that the user had asked to be stretched to 1.85:1 */
+               VideoContent vc (0);
+               vc._size = dcp::Size(640, 480);
+               vc._legacy_ratio = Ratio::from_id("185")->ratio();
+               BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FLAT);
+       }
+
+       {
+               /* 640x480 content that the user had asked to be scaled to fit the container, without stretch */
+               VideoContent vc (0);
+               vc._size = dcp::Size(640, 480);
+               vc._legacy_ratio = 1.33;
+               BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), FOUR_TO_THREE);
+       }
+
+       {
+               /* 640x480 content that the user had asked to be kept the same size */
+               VideoContent vc (0);
+               vc._size = dcp::Size(640, 480);
+               vc._custom_size = dcp::Size(640, 480);
+               BOOST_CHECK_EQUAL (vc.scaled_size(FLAT), dcp::Size(640, 480));
+       }
 }
+