Re-work scaling following excellent insights by Carsten Kurz, described
authorCarl Hetherington <cth@carlh.net>
Tue, 12 May 2020 18:34:42 +0000 (20:34 +0200)
committerCarl Hetherington <cth@carlh.net>
Tue, 12 May 2020 18:34:42 +0000 (20:34 +0200)
on #1669.  Now the scaling of video content is guessed based on its
aspect ratio (taking into account pixel aspect ratio) and there is a
"custom" option for overriding incorrectly-marked-up content or content
which actually needs its shape to be changed.

Essentially we are defaulting to what used to be called "no stretch".

This commit also adds buttons to change left/right and top/bottom
crop together, and auto-guessing of DCP container size when any change
results in there being just one piece of video content in a project.

These changes include breaking changes to metadata.xml; old projects
should load (with new settings that preserve the project's apperance)
but once saved they will not load into 2.14.x.  DCP-o-matic saves a
backup file (metadata.37.xml) when 2.15.x loads an old project.

53 files changed:
graphics/link.png [new file with mode: 0644]
graphics/src/link.svg [new file with mode: 0644]
graphics/update
graphics/web/favicon-128x128.png
graphics/web/favicon-16x16.png
graphics/web/favicon-32x32.png
graphics/web/favicon-64x64.png
src/lib/config.cc
src/lib/config.h
src/lib/create_cli.cc
src/lib/create_cli.h
src/lib/dcp.cc
src/lib/film.cc
src/lib/film.h
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.cc
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_create.cc
src/wx/content_widget.h
src/wx/custom_scale_dialog.cc [new file with mode: 0644]
src/wx/custom_scale_dialog.h [new file with mode: 0644]
src/wx/full_config_dialog.cc
src/wx/timeline_dialog.cc
src/wx/timeline_dialog.h
src/wx/video_panel.cc
src/wx/video_panel.h
src/wx/wscript
src/wx/wx_util.cc
src/wx/wx_util.h
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

diff --git a/graphics/link.png b/graphics/link.png
new file mode 100644 (file)
index 0000000..69af402
Binary files /dev/null and b/graphics/link.png differ
diff --git a/graphics/src/link.svg b/graphics/src/link.svg
new file mode 100644 (file)
index 0000000..bf58ad7
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="40.736046mm"
+   height="72.459007mm"
+   viewBox="0 0 40.736045 72.459008"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="link.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.979899"
+     inkscape:cx="30.0516"
+     inkscape:cy="107.36255"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1680"
+     inkscape:window-height="995"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     fit-margin-top="1"
+     fit-margin-left="1"
+     fit-margin-right="1"
+     fit-margin-bottom="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-43.172802,-2.2134689)">
+    <g
+       id="g11"
+       transform="matrix(-3.0676153e-5,-1.0020549,1.0020464,-0.00413633,25.020746,102.13853)"
+       style="stroke-width:0.99795336">
+      <path
+         sodipodi:nodetypes="ccsccccccscscccccsssccc"
+         inkscape:connector-curvature="0"
+         id="rect819"
+         d="m 65.199219,27.601562 0.3125,0.01172 c 0,0 -1.921609,-0.08686 -3.953125,0.625 -2.031517,0.711861 -4.556382,2.227912 -6.824219,5.013672 l 6.205078,5.050781 c 1.370276,-1.683216 2.525799,-2.25443 3.265625,-2.513672 0.739826,-0.259241 0.679688,-0.199218 0.679688,-0.199218 l 0.15625,0.01172 h 19.550781 c 2.172037,0.06924 3.36417,0.821834 4.335937,2.03125 0.973224,1.211229 1.583731,3.040311 1.589844,4.931641 0.0061,1.89133 -0.59167,3.751118 -1.582031,5.015625 -0.986061,1.259016 -2.231578,2.067616 -4.441406,2.205078 h -15.51516 l -3.779762,8 h 19.626953 l 0.11914,-0.0078 c 4.347056,-0.25782 8.013718,-2.360449 10.289063,-5.26564 2.275345,-2.905192 3.294538,-6.465865 3.283203,-9.972657 C 98.506244,39.032271 97.46527,35.48507 95.164062,32.621094 92.862855,29.757118 89.15903,27.737178 84.832031,27.603516 l -0.0625,-0.002 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.98362684;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+      <path
+         sodipodi:nodetypes="ccscscccccccccccscscccc"
+         inkscape:connector-curvature="0"
+         id="rect819-7"
+         d="m 41.986328,19.101562 -0.117187,0.0059 c -4.347056,0.257804 -8.013718,2.360434 -10.289063,5.265625 -2.275345,2.905191 -3.296491,6.467818 -3.285156,9.974609 0.01133,3.506791 1.054262,7.05204 3.355469,9.916016 2.301207,2.863976 6.005032,4.885869 10.332031,5.019531 l 0.0625,0.002 h 19.570312 l -0.3125,-0.01172 c 0,0 1.921609,0.08491 3.953125,-0.626954 2.031517,-0.711861 4.556382,-2.227911 6.824219,-5.013672 L 65.875,38.582031 c -1.370276,1.683217 -2.525798,2.256384 -3.265625,2.515625 -0.739827,0.259242 -0.681641,0.199219 -0.681641,0.199219 l -0.15625,-0.01172 H 42.220703 c -2.172033,-0.06924 -3.362218,-0.821835 -4.333984,-2.03125 -0.973223,-1.211229 -1.585684,-3.04031 -1.591797,-4.93164 -0.0061,-1.891331 0.59167,-3.751118 1.582031,-5.015625 0.984624,-1.257182 2.229804,-2.06585 4.433594,-2.205079 h 14.957961 l 4.346726,-8 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.98362684;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    </g>
+  </g>
+</svg>
index 9b75493dbd94a0e5480d92540241f06a2d2ad6f9..f0a7fd597b418123b4eea8a7b061807484c4fa9c 100755 (executable)
@@ -84,6 +84,9 @@ else
     $INKSCAPE tick.png src/tick.svg -w 16 -h 16
     $INKSCAPE no_tick.png src/no_tick.svg -w 16 -h 16
 
+    # Link icon
+    $INKSCAPE link.png src/link.svg -w 9 -h 16
+
     # favicon
     mkdir -p web
     convert src/web.png -resize 256x256 -transparent white web/favicon-256x256.png
index 6214556419d2b3b204e3cb6afd2b6d10c5387eb2..9330c0cdc6476efca5d0e176f0d03b1a1407a739 100644 (file)
Binary files a/graphics/web/favicon-128x128.png and b/graphics/web/favicon-128x128.png differ
index 4c31c0258b920b7011bf47ae6e91059812748ffc..82639fddf987b9e87d7507ac5edb649c71ead67a 100644 (file)
Binary files a/graphics/web/favicon-16x16.png and b/graphics/web/favicon-16x16.png differ
index 847bc813a7a7e6c3206fd2bdc6e50cbcbdee54fe..e9772da248ae57c4f609973824954f2f36474a5d 100644 (file)
Binary files a/graphics/web/favicon-32x32.png and b/graphics/web/favicon-32x32.png differ
index e836814d1bb9b2fda9c4684643886de028795d96..0ff824f40465981e9159fa7a0a447ecf772dbedc 100644 (file)
Binary files a/graphics/web/favicon-64x64.png and b/graphics/web/favicon-64x64.png differ
index ebb6ece931f0bed3899e734f17c0baefdb2f4dad..18955a8969283f618eaebd05ed8aaddd15f01435 100644 (file)
@@ -99,7 +99,6 @@ Config::set_defaults ()
        _language = optional<string> ();
        _default_still_length = 10;
        _default_container = Ratio::from_id ("185");
-       _default_scale_to = 0;
        _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
        _default_dcp_audio_channels = 6;
        _default_j2k_bandwidth = 150000000;
@@ -309,11 +308,6 @@ try
                _default_container = Ratio::from_id ("185");
        }
 
-       c = f.optional_string_child ("DefaultScaleTo");
-       if (c) {
-               _default_scale_to = Ratio::from_id (c.get ());
-       }
-
        _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
        _default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
 
@@ -719,12 +713,6 @@ Config::write_config () const
                */
                root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
        }
-       if (_default_scale_to) {
-               /* [XML:opt] DefaultScaleTo ID of default ratio to scale content to when creating new films
-                  (see <code>DefaultContainer</code> for IDs).
-               */
-               root->add_child("DefaultScaleTo")->add_child_text (_default_scale_to->id ());
-       }
        if (_default_dcp_content_type) {
                /* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
                   <code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
index f54ca3814043abf5c8fa51c619c975cf87591113..23f7ce0b3c885f1ac3e83cd9aea4227f4f1d8b49 100644 (file)
@@ -186,10 +186,6 @@ public:
                return _default_container;
        }
 
-       Ratio const * default_scale_to () const {
-               return _default_scale_to;
-       }
-
        DCPContentType const * default_dcp_content_type () const {
                return _default_dcp_content_type;
        }
@@ -686,10 +682,6 @@ public:
                maybe_set (_default_container, c);
        }
 
-       void set_default_scale_to (Ratio const * c) {
-               maybe_set (_default_scale_to, c);
-       }
-
        void set_default_dcp_content_type (DCPContentType const * t) {
                maybe_set (_default_dcp_content_type, t);
        }
@@ -1233,7 +1225,6 @@ private:
        /** Default length of still image content (seconds) */
        int _default_still_length;
        Ratio const * _default_container;
-       Ratio const * _default_scale_to;
        DCPContentType const * _default_dcp_content_type;
        int _default_dcp_audio_channels;
        std::string _dcp_issuer;
index 878ee6fddd7fb7ce8ff6719366028164a577b802..b670282b4717a0bfcbc3988b391a55cd1f5d1bcb 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 a9d9daa22e2e0bbdc3d6a6e92d68f4f41240b9f8..01e8e66335f32ec8519f0b9549742a690147f80c 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 c62b339fa2207260c4c9ac81509aa264825d6169..477b61e9f4849dc0a1c620a7f3f35eb7f423d994 100644 (file)
@@ -75,9 +75,9 @@ DCP::cpls () const
                list<dcp::VerificationNote> notes;
                dcp->read (&notes, true);
                if (!_tolerant) {
-                       /** We accept and ignore EmptyAssetPathError but everything else is bad */
+                       /** We accept and ignore EMPTY_ASSET_PATH and EXTERNAL_ASSET but everything else is bad */
                        BOOST_FOREACH (dcp::VerificationNote j, notes) {
-                               if (j.code() == dcp::VerificationNote::EMPTY_ASSET_PATH) {
+                               if (j.code() == dcp::VerificationNote::EMPTY_ASSET_PATH || j.code() == dcp::VerificationNote::EXTERNAL_ASSET) {
                                        LOG_WARNING("Empty path in ASSETMAP of %1", i.string());
                                } else {
                                        boost::throw_exception(dcp::ReadError(dcp::note_to_string(j)));
index b233e5ee690a5ccf984aac7f730dd04ad8c13f94..2f631bd89c02173abba3fc0910289d92fc401208 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.
  *
@@ -162,6 +164,8 @@ Film::Film (optional<boost::filesystem::path> dir)
        , _upload_after_make_dcp (Config::instance()->default_upload_after_make_dcp())
        , _reencode_j2k (false)
        , _user_explicit_video_frame_rate (false)
+       , _user_explicit_container (false)
+       , _user_explicit_resolution (false)
        , _state_version (current_state_version)
        , _dirty (false)
        , _tolerant (false)
@@ -464,6 +468,8 @@ Film::metadata (bool with_content_paths) const
                i.as_xml (root->add_child("Rating"));
        }
        root->add_child("ContentVersion")->add_child_text(_content_version);
+       root->add_child("UserExplicitContainer")->add_child_text(_user_explicit_container ? "1" : "0");
+       root->add_child("UserExplicitResolution")->add_child_text(_user_explicit_resolution ? "1" : "0");
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
@@ -613,6 +619,10 @@ Film::read_metadata (optional<boost::filesystem::path> path)
 
        _content_version = f.optional_string_child("ContentVersion").get_value_or("");
 
+       /* Disable guessing for files made in previous DCP-o-matic versions */
+       _user_explicit_container = f.optional_bool_child("UserExplicitContainer").get_value_or(true);
+       _user_explicit_resolution = f.optional_bool_child("UserExplicitResolution").get_value_or(true);
+
        list<string> notes;
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
@@ -787,11 +797,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;
                        }
                }
@@ -971,20 +977,39 @@ Film::set_dcp_content_type (DCPContentType const * t)
        _dcp_content_type = t;
 }
 
+
+/** @param explicit_user true if this is being set because of
+ *  a direct user request, false if it is being done because
+ *  DCP-o-matic is guessing the best container to use.
+ */
 void
-Film::set_container (Ratio const * c)
+Film::set_container (Ratio const * c, bool explicit_user)
 {
        ChangeSignaller<Film> ch (this, CONTAINER);
        _container = c;
+
+       if (explicit_user) {
+               _user_explicit_container = true;
+       }
 }
 
+
+/** @param explicit_user true if this is being set because of
+ *  a direct user request, false if it is being done because
+ *  DCP-o-matic is guessing the best resolution to use.
+ */
 void
-Film::set_resolution (Resolution r)
+Film::set_resolution (Resolution r, bool explicit_user)
 {
        ChangeSignaller<Film> ch (this, RESOLUTION);
        _resolution = r;
+
+       if (explicit_user) {
+               _user_explicit_resolution = true;
+       }
 }
 
+
 void
 Film::set_j2k_bandwidth (int b)
 {
@@ -1269,12 +1294,54 @@ Film::add_content (shared_ptr<Content> c)
        }
 
        _playlist->add (shared_from_this(), c);
+
+       maybe_set_container_and_resolution ();
+}
+
+
+void
+Film::maybe_set_container_and_resolution ()
+{
+       /* Get the only piece of video content, if there is only one */
+       shared_ptr<VideoContent> video;
+       BOOST_FOREACH (shared_ptr<const Content> i, _playlist->content()) {
+               if (i->video) {
+                       if (!video) {
+                               video = i->video;
+                       } else {
+                               video.reset ();
+                       }
+               }
+       }
+
+       if (video) {
+               /* This is the only piece of video content in this Film.  Use it to make a guess for
+                * DCP container size and resolution, unless the user has already explicitly set these
+                * things.
+                */
+               if (!_user_explicit_container) {
+                       if (video->size().ratio() > 2.3) {
+                               set_container (Ratio::from_id("239"), false);
+                       } else {
+                               set_container (Ratio::from_id("185"), false);
+                       }
+               }
+
+               if (!_user_explicit_resolution) {
+                       if (video->size_after_crop().width > 2048 || video->size_after_crop().height > 1080) {
+                               set_resolution (RESOLUTION_4K, false);
+                       } else {
+                               set_resolution (RESOLUTION_2K, false);
+                       }
+               }
+       }
 }
 
 void
 Film::remove_content (shared_ptr<Content> c)
 {
        _playlist->remove (c);
+       maybe_set_container_and_resolution ();
 }
 
 void
index 40d366f8f68c8a331ee7840c9bf37ee944515f89..b03b0258e7f074316b9c3560911a14f55dec1120 100644 (file)
@@ -351,8 +351,8 @@ public:
        void move_content_earlier (boost::shared_ptr<Content>);
        void move_content_later (boost::shared_ptr<Content>);
        void set_dcp_content_type (DCPContentType const *);
-       void set_container (Ratio const *);
-       void set_resolution (Resolution);
+       void set_container (Ratio const *, bool user_explicit = true);
+       void set_resolution (Resolution, bool user_explicit = true);
        void set_signed (bool);
        void set_encrypted (bool);
        void set_key (dcp::Key key);
@@ -409,6 +409,7 @@ private:
        void maybe_add_content (boost::weak_ptr<Job>, boost::weak_ptr<Content>, bool disable_audio_analysis);
        void audio_analysis_finished ();
        void check_settings_consistency ();
+       void maybe_set_container_and_resolution ();
 
        static std::string const metadata_file;
 
@@ -462,6 +463,8 @@ private:
        bool _reencode_j2k;
        /** true if the user has ever explicitly set the video frame rate of this film */
        bool _user_explicit_video_frame_rate;
+       bool _user_explicit_container;
+       bool _user_explicit_resolution;
        std::map<dcp::Marker, dcpomatic::DCPTime> _markers;
        std::vector<dcp::Rating> _ratings;
        std::string _content_version;
index 581d63972a1cac97b0c257243b0482a1fded12f5..0f18835ef47887a8171eba059ae2b5e8032a1877 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 e41cecf13f353b417ceeca4b63649e084cea39bc..059465724def5c930af32ce1b3612712fbbe261c 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 51de78982da44ba352caf796f16277a38c095aa8..bb2a0c613e3760c5cce9fdb6642d7abb535e94c2 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 cb2a55724726cb46803ad29f2178a620759ada6d..10e798ed510644a86e03d11249d737139268d3b0 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 d04bbdf24cc8b0948b6c45c18591d39f117c1150..c1ef4245f16b18713dd008bbb9d212ab858b1674 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 f8e9409f18d8f999c8b0d44e06fd615ec1f060a9..f7b4704ace52caa583c2d09fb85926ba4a050aa7 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 6853363678ff76173d6c1943038c3cb5da572359..b4ad30f70d1c77ad523b57ad4edc65ff00a93faa 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
@@ -354,29 +377,6 @@ VideoContent::size_after_crop () const
        return crop().apply (size_after_3d_split ());
 }
 
-void
-VideoContent::scale_and_crop_to_fit_width (shared_ptr<const Film> film)
-{
-       set_scale (VideoContentScale(film->container()));
-
-       int const crop = max (0, int (size().height - double (film->frame_size().height) * size().width / film->frame_size().width));
-       set_left_crop (0);
-       set_right_crop (0);
-       set_top_crop (crop / 2);
-       set_bottom_crop (crop / 2);
-}
-
-void
-VideoContent::scale_and_crop_to_fit_height (shared_ptr<const Film> film)
-{
-       set_scale (VideoContentScale(film->container()));
-
-       int const crop = max (0, int (size().width - double (film->frame_size().width) * size().height / film->frame_size().height));
-       set_left_crop (crop / 2);
-       set_right_crop (crop / 2);
-       set_top_crop (0);
-       set_bottom_crop (0);
-}
 
 /** @param f Frame index within the whole (untrimmed) content.
  *  @return Fade factor (between 0 and 1) or unset if there is no fade.
@@ -402,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];
@@ -439,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 (
@@ -512,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)
@@ -574,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);
 }
@@ -592,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 37223c45788eef070d5ab5780a761e40f763f663..b478666bb0ae28462ccac08e371bc27060094791 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 _custom_ratio;
+       }
+
+
+       boost::optional<dcp::Size> custom_size () const {
                boost::mutex::scoped_lock lm (_mutex);
-               return _scale;
+               return _custom_size;
        }
 
+
        boost::optional<ColourConversion> colour_conversion () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _colour_conversion;
@@ -168,15 +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;
 
-       void scale_and_crop_to_fit_width (boost::shared_ptr<const Film> film);
-       void scale_and_crop_to_fit_height (boost::shared_ptr<const Film> film);
-
-       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);
 
@@ -194,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 ();
@@ -204,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 0f2a5d1975d8feccef0548f218b2daf812e76d34..fb222fcf4c73f4189a042629e97ebfed4f993c00 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 f69ab5a6a5d7eefb73a05b414968790de62a1bcf..13569d11d7acd648961358ae1f36cf18fd268ea4 100644 (file)
@@ -222,8 +222,6 @@ enum {
        ID_file_close = 100,
        ID_edit_copy,
        ID_edit_paste,
-       ID_content_scale_to_fit_width,
-       ID_content_scale_to_fit_height,
        ID_jobs_make_dcp,
        ID_jobs_make_dcp_batch,
        ID_jobs_make_kdms,
@@ -316,8 +314,6 @@ public:
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_copy, this),               ID_edit_copy);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_paste, this),              ID_edit_paste);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_preferences, this),        wxID_PREFERENCES);
-               Bind (wxEVT_MENU, boost::bind (&DOMFrame::content_scale_to_fit_width, this), ID_content_scale_to_fit_width);
-               Bind (wxEVT_MENU, boost::bind (&DOMFrame::content_scale_to_fit_height, this), ID_content_scale_to_fit_height);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dcp, this),           ID_jobs_make_dcp);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_kdms, this),          ID_jobs_make_kdms);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dkdms, this),         ID_jobs_make_dkdms);
@@ -983,22 +979,6 @@ private:
                d->Destroy ();
        }
 
-       void content_scale_to_fit_width ()
-       {
-               ContentList vc = _film_editor->content_panel()->selected_video ();
-               for (ContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
-                       (*i)->video->scale_and_crop_to_fit_width (_film);
-               }
-       }
-
-       void content_scale_to_fit_height ()
-       {
-               ContentList vc = _film_editor->content_panel()->selected_video ();
-               for (ContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
-                       (*i)->video->scale_and_crop_to_fit_height (_film);
-               }
-       }
-
        void jobs_send_dcp_to_tms ()
        {
                _film->send_dcp_to_tms ();
@@ -1323,10 +1303,6 @@ private:
                add_item (edit, _("&Preferences...\tCtrl-P"), wxID_PREFERENCES, ALWAYS);
 #endif
 
-               wxMenu* content = new wxMenu;
-               add_item (content, _("Scale to fit &width"), ID_content_scale_to_fit_width, NEEDS_FILM | NEEDS_SELECTED_VIDEO_CONTENT);
-               add_item (content, _("Scale to fit &height"), ID_content_scale_to_fit_height, NEEDS_FILM | NEEDS_SELECTED_VIDEO_CONTENT);
-
                wxMenu* jobs_menu = new wxMenu;
                add_item (jobs_menu, _("&Make DCP\tCtrl-M"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
                add_item (jobs_menu, _("Make DCP in &batch converter\tCtrl-B"), ID_jobs_make_dcp_batch, NEEDS_FILM | NOT_DURING_DCP_CREATION);
@@ -1365,7 +1341,6 @@ private:
 
                m->Append (_file_menu, _("&File"));
                m->Append (edit, _("&Edit"));
-               m->Append (content, _("&Content"));
                m->Append (jobs_menu, _("&Jobs"));
                m->Append (view, _("&View"));
                m->Append (tools, _("&Tools"));
index 6a243e126e6a9140cf693a70c5067683758cca0a..edf4808a0d9e7a158de7e08187bb71a839aea464 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 68ae09c31fdf5bfa446903ad4db6a0b2e63b0b34..857359117c3c64309b2c4a35f0c1fcba452bfa5f 100644 (file)
@@ -96,14 +96,18 @@ main (int argc, char* argv[])
                }
                film->set_name (cc.name);
 
-               film->set_container (cc.container_ratio);
+               if (cc.container_ratio) {
+                       film->set_container (cc.container_ratio);
+               }
                film->set_dcp_content_type (cc.dcp_content_type);
                film->set_interop (cc.standard == dcp::INTEROP);
                film->set_use_isdcf_name (!cc.no_use_isdcf_name);
                film->set_signed (!cc.no_sign);
                film->set_encrypted (cc.encrypt);
                film->set_three_d (cc.threed);
-               film->set_resolution (cc.fourk ? RESOLUTION_4K : RESOLUTION_2K);
+               if (cc.fourk) {
+                       film->set_resolution (RESOLUTION_4K);
+               }
                if (cc.j2k_bandwidth) {
                        film->set_j2k_bandwidth (*cc.j2k_bandwidth);
                }
@@ -131,7 +135,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);
                                }
                        }
index c4ae4d591aaa5d97a472953d4e06626e0236019e..de7ebedc5022b321e0d92679c5a22a41b1d46e4f 100644 (file)
@@ -52,6 +52,7 @@ public:
         *  @param part Part of Content that the property is in (e.g. &Content::video)
         *  @param model_getter Function on the Content to get the value.
         *  @param model_setter Function on the Content to set the value.
+        *  @param view_changed Function called when the view has changed; useful for linking controls.
         *  @param view_to_model Function to convert a view value to a model value.
         *  @param model_to_view Function to convert a model value to a view value.
         */
@@ -62,6 +63,7 @@ public:
                boost::function<boost::shared_ptr<S> (Content*)> part,
                boost::function<U (S*)> model_getter,
                boost::function<void (S*, U)> model_setter,
+               boost::function<void ()> view_changed,
                boost::function<U (V)> view_to_model,
                boost::function<V (U)> model_to_view
                )
@@ -72,6 +74,7 @@ public:
                , _part (part)
                , _model_getter (model_getter)
                , _model_setter (model_setter)
+               , _view_changed (view_changed)
                , _view_to_model (view_to_model)
                , _model_to_view (model_to_view)
                , _ignore_model_changes (false)
@@ -146,6 +149,9 @@ public:
                for (size_t i = 0; i < _content.size(); ++i) {
                        boost::bind (_model_setter, _part (_content[i].get()).get(), _view_to_model (wx_get (_wrapped))) ();
                }
+               if (_view_changed) {
+                       _view_changed ();
+               }
                _ignore_model_changes = false;
        }
 
@@ -207,6 +213,7 @@ private:
        boost::function<boost::shared_ptr<S> (Content *)> _part;
        boost::function<U (S*)> _model_getter;
        boost::function<void (S*, U)> _model_setter;
+       boost::function<void ()> _view_changed;
        boost::function<U (V)> _view_to_model;
        boost::function<V (U)> _model_to_view;
        std::list<boost::signals2::connection> _connections;
@@ -229,7 +236,8 @@ public:
                int property,
                boost::function<boost::shared_ptr<S> (Content *)> part,
                boost::function<int (S*)> getter,
-               boost::function<void (S*, int)> setter
+               boost::function<void (S*, int)> setter,
+               boost::function<void ()> view_changed = boost::function<void ()>()
                )
                : ContentWidget<S, wxSpinCtrl, int, int> (
                        parent,
@@ -237,6 +245,7 @@ public:
                        property,
                        part,
                        getter, setter,
+                       view_changed,
                        &caster<int, int>,
                        &caster<int, int>
                        )
@@ -255,7 +264,8 @@ public:
                int property,
                boost::function<boost::shared_ptr<S> (Content *)> part,
                boost::function<double (S*)> getter,
-               boost::function<void (S*, double)> setter
+               boost::function<void (S*, double)> setter,
+               boost::function<void ()> view_changed = boost::function<void ()>()
                )
                : ContentWidget<S, wxSpinCtrlDouble, double, double> (
                        parent,
@@ -263,6 +273,7 @@ public:
                        property,
                        part,
                        getter, setter,
+                       view_changed,
                        &caster<double, double>,
                        &caster<double, double>
                        )
@@ -283,7 +294,8 @@ public:
                boost::function<U (S*)> getter,
                boost::function<void (S*, U)> setter,
                boost::function<U (int)> view_to_model,
-               boost::function<int (U)> model_to_view
+               boost::function<int (U)> model_to_view,
+               boost::function<void ()> view_changed = boost::function<void()>()
                )
                : ContentWidget<S, wxChoice, U, int> (
                        parent,
@@ -292,6 +304,7 @@ public:
                        part,
                        getter,
                        setter,
+                       view_changed,
                        view_to_model,
                        model_to_view
                        )
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 370ec35a1a90ea0b13cfd5ceb1780595658fe814..fff0d2035289f91e774e0d2d10e36311027d13ce 100644 (file)
@@ -340,10 +340,6 @@ private:
                _container = new wxChoice (_panel, wxID_ANY);
                table->Add (_container);
 
-               add_label_to_sizer (table, _panel, _("Default scale-to"), true);
-               _scale_to = new wxChoice (_panel, wxID_ANY);
-               table->Add (_scale_to);
-
                add_label_to_sizer (table, _panel, _("Default content type"), true);
                _dcp_content_type = new wxChoice (_panel, wxID_ANY);
                table->Add (_dcp_content_type);
@@ -400,14 +396,6 @@ private:
 
                _container->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::container_changed, this));
 
-               _scale_to->Append (_("Guess from content"));
-
-               BOOST_FOREACH (Ratio const * i, Ratio::all()) {
-                       _scale_to->Append (std_to_wx(i->image_nickname()));
-               }
-
-               _scale_to->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::scale_to_changed, this));
-
                BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
                        _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
                }
@@ -441,17 +429,6 @@ private:
                        }
                }
 
-               vector<Ratio const *> ratios = Ratio::all ();
-               for (size_t i = 0; i < ratios.size(); ++i) {
-                       if (ratios[i] == config->default_scale_to ()) {
-                               _scale_to->SetSelection (i + 1);
-                       }
-               }
-
-               if (!config->default_scale_to()) {
-                       _scale_to->SetSelection (0);
-               }
-
                vector<DCPContentType const *> const ct = DCPContentType::all ();
                for (size_t i = 0; i < ct.size(); ++i) {
                        if (ct[i] == config->default_dcp_content_type ()) {
@@ -519,17 +496,6 @@ private:
                Config::instance()->set_default_container (ratio[_container->GetSelection()]);
        }
 
-       void scale_to_changed ()
-       {
-               int const s = _scale_to->GetSelection ();
-               if (s == 0) {
-                       Config::instance()->set_default_scale_to (0);
-               } else {
-                       vector<Ratio const *> ratio = Ratio::all ();
-                       Config::instance()->set_default_scale_to (ratio[s - 1]);
-               }
-       }
-
        void dcp_content_type_changed ()
        {
                vector<DCPContentType const *> ct = DCPContentType::all ();
@@ -558,7 +524,6 @@ private:
        wxDirPickerCtrl* _kdm_directory;
 #endif
        wxChoice* _container;
-       wxChoice* _scale_to;
        wxChoice* _dcp_content_type;
        wxChoice* _dcp_audio_channels;
        wxChoice* _standard;
index 3a720cfecff8ec958d336c139c920bb64bcc6144..0d0d4f9f98facbe2fcfc33db4c0debb33cbdc419 100644 (file)
@@ -93,27 +93,6 @@ TimelineDialog::TimelineDialog (ContentPanel* cp, shared_ptr<Film> film, weak_pt
        _film_changed_connection = film->Change.connect (bind (&TimelineDialog::film_change, this, _1, _2));
 }
 
-wxString
-TimelineDialog::bitmap_path (string name)
-{
-       boost::filesystem::path base;
-
-#ifdef DCPOMATIC_DEBUG
-       /* Hack to allow OS X to find icons when running from the source tree */
-       char* path = getenv ("DCPOMATIC_GRAPHICS");
-       if (path) {
-               base = path;
-       } else {
-               base = shared_path();
-       }
-#else
-       base = shared_path();
-#endif
-
-       boost::filesystem::path p = base / String::compose("%1.png", name);
-       return std_to_wx (p.string());
-}
-
 void
 TimelineDialog::film_change (ChangeType type, Film::Property p)
 {
index 62649a5f2a06f7ff1dee71900d09bcc7622857b4..77dfede50c452d69107daf080918cc95b514f0b8 100644 (file)
@@ -35,7 +35,6 @@ public:
 private:
        void film_change (ChangeType type, Film::Property);
        void tool_clicked (wxCommandEvent& id);
-       wxString bitmap_path (std::string name);
 
        boost::weak_ptr<Film> _film;
        Timeline _timeline;
index 992ddf115b80455e063fe5674952b3f3bcaa73e4..24099e1ccd97ed2b524ce31a9bc20490542e4e25 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"
@@ -37,6 +38,7 @@
 #include "lib/dcp_content.h"
 #include "lib/video_content.h"
 #include <wx/spinctrl.h>
+#include <wx/tglbtn.h>
 #include <boost/foreach.hpp>
 #include <boost/unordered_set.hpp>
 #include <boost/functional/hash.hpp>
@@ -55,27 +57,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"))
@@ -102,44 +83,59 @@ VideoPanel::VideoPanel (ContentPanel* p)
                &caster<VideoFrameType, int>
                );
 
-       _left_crop_label = create_label (this, _("Left crop"), true);
+       _crop_label = create_label (this, _("Crop"), true);
+
+       int const crop_width = 56;
+       int const link_height = 28;
+
+       _left_crop_label = create_label (this, _("Left"), true);
        _left_crop = new ContentSpinCtrl<VideoContent> (
                this,
-               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
+               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(crop_width, -1)),
                VideoContentProperty::CROP,
                &Content::video,
                boost::mem_fn (&VideoContent::left_crop),
-               boost::mem_fn (&VideoContent::set_left_crop)
+               boost::mem_fn (&VideoContent::set_left_crop),
+               boost::bind (&VideoPanel::left_crop_changed, this)
                );
 
-       _right_crop_label = create_label (this, _("Right crop"), true);
+       _left_right_link = new wxToggleButton (this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(22, link_height));
+       _left_right_link->SetBitmap (wxBitmap(bitmap_path("link"), wxBITMAP_TYPE_PNG));
+
+       _right_crop_label = create_label (this, _("Right"), true);
        _right_crop = new ContentSpinCtrl<VideoContent> (
                this,
-               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
+               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(crop_width, -1)),
                VideoContentProperty::CROP,
                &Content::video,
                boost::mem_fn (&VideoContent::right_crop),
-               boost::mem_fn (&VideoContent::set_right_crop)
+               boost::mem_fn (&VideoContent::set_right_crop),
+               boost::bind (&VideoPanel::right_crop_changed, this)
                );
 
-       _top_crop_label = create_label (this, _("Top crop"), true);
+       _top_crop_label = create_label (this, _("Top"), true);
        _top_crop = new ContentSpinCtrl<VideoContent> (
                this,
-               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
+               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(crop_width, -1)),
                VideoContentProperty::CROP,
                &Content::video,
                boost::mem_fn (&VideoContent::top_crop),
-               boost::mem_fn (&VideoContent::set_top_crop)
+               boost::mem_fn (&VideoContent::set_top_crop),
+               boost::bind (&VideoPanel::top_crop_changed, this)
                );
 
-       _bottom_crop_label = create_label (this, _("Bottom crop"), true);
+       _top_bottom_link = new wxToggleButton (this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(22, link_height));
+       _top_bottom_link->SetBitmap (wxBitmap(bitmap_path("link"), wxBITMAP_TYPE_PNG));
+
+       _bottom_crop_label = create_label (this, _("Bottom"), true);
        _bottom_crop = new ContentSpinCtrl<VideoContent> (
                this,
-               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
+               new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(crop_width, -1)),
                VideoContentProperty::CROP,
                &Content::video,
                boost::mem_fn (&VideoContent::bottom_crop),
-               boost::mem_fn (&VideoContent::set_bottom_crop)
+               boost::mem_fn (&VideoContent::set_bottom_crop),
+               boost::bind (&VideoPanel::bottom_crop_changed, this)
                );
 
        _fade_in_label = create_label (this, _("Fade in"), true);
@@ -148,22 +144,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..."));
@@ -192,11 +181,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"));
@@ -213,9 +197,14 @@ 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));
+       _left_right_link->Bind               (wxEVT_TOGGLEBUTTON, boost::bind(&VideoPanel::left_right_link_clicked, this));
+       _top_bottom_link->Bind               (wxEVT_TOGGLEBUTTON, boost::bind(&VideoPanel::top_bottom_link_clicked, this));
 
        add_to_grid ();
 }
@@ -250,18 +239,23 @@ VideoPanel::add_to_grid ()
        wxGridBagSizer* crop = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
        add_label_to_sizer (crop, _left_crop_label, true, wxGBPosition (cr, 0));
        _left_crop->add (crop, wxGBPosition (cr, 1));
-       add_label_to_sizer (crop, _right_crop_label, true, wxGBPosition (cr, 2));
-       _right_crop->add (crop, wxGBPosition (cr, 3));
+       crop->Add (_left_right_link, wxGBPosition(cr, 2));
+       add_label_to_sizer (crop, _right_crop_label, true, wxGBPosition (cr, 3));
+       _right_crop->add (crop, wxGBPosition (cr, 4));
        ++cr;
        add_label_to_sizer (crop, _top_crop_label, true, wxGBPosition (cr, 0));
        _top_crop->add (crop, wxGBPosition (cr, 1));
-       add_label_to_sizer (crop, _bottom_crop_label, true, wxGBPosition (cr, 2));
-       _bottom_crop->add (crop, wxGBPosition (cr, 3));
-       _grid->Add (crop, wxGBPosition (r, 0), wxGBSpan (2, 4));
-       r += 2;
+       crop->Add (_top_bottom_link, wxGBPosition(cr, 2));
+       add_label_to_sizer (crop, _bottom_crop_label, true, wxGBPosition (cr, 3));
+       _bottom_crop->add (crop, wxGBPosition (cr, 4));
+       add_label_to_sizer (_grid, _crop_label, true, wxGBPosition(r, 0));
+       _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);
@@ -280,8 +274,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));
@@ -482,6 +484,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 ();
        }
 }
@@ -573,7 +589,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);
@@ -582,6 +597,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);
 
@@ -620,7 +637,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);
@@ -638,7 +657,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 ());
@@ -700,3 +721,99 @@ 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;
+}
+
+
+void
+VideoPanel::left_right_link_clicked ()
+{
+       right_crop_changed ();
+}
+
+
+void
+VideoPanel::top_bottom_link_clicked ()
+{
+       bottom_crop_changed ();
+}
+
+
+void
+VideoPanel::left_crop_changed ()
+{
+       if (_left_right_link->GetValue()) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+                       i->video->set_right_crop (i->video->left_crop());
+               }
+       }
+}
+
+
+void
+VideoPanel::right_crop_changed ()
+{
+       if (_left_right_link->GetValue()) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+                       i->video->set_left_crop (i->video->right_crop());
+               }
+       }
+}
+
+
+void
+VideoPanel::top_crop_changed ()
+{
+       if (_top_bottom_link->GetValue()) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+                       i->video->set_bottom_crop (i->video->top_crop());
+               }
+       }
+}
+
+
+void
+VideoPanel::bottom_crop_changed ()
+{
+       if (_top_bottom_link->GetValue()) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected_video()) {
+                       i->video->set_top_crop (i->video->bottom_crop());
+               }
+       }
+}
+
+
+
index ad0ba402a763e4a23a9216dc1fc9a80acce45b5a..31aeed2e1d36ea166d1df8e635391ca8b692bf09 100644 (file)
 #include "content_sub_panel.h"
 #include "content_widget.h"
 #include "timecode.h"
-#include "lib/video_content_scale.h"
 #include "lib/film.h"
 
 class wxChoice;
 class wxStaticText;
 class wxSpinCtrl;
 class wxButton;
+class wxToggleButton;
 
 /** @class VideoPanel
  *  @brief The video tab of the film editor.
@@ -55,6 +55,15 @@ 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 left_right_link_clicked ();
+       void top_bottom_link_clicked ();
+       void left_crop_changed ();
+       void right_crop_changed ();
+       void top_crop_changed ();
+       void bottom_crop_changed ();
 
        void setup_description ();
        void setup_sensitivity ();
@@ -64,20 +73,25 @@ private:
        wxCheckBox* _use;
        wxStaticText* _type_label;
        ContentChoice<VideoContent, VideoFrameType>* _frame_type;
+       wxStaticText* _crop_label;
        wxStaticText* _left_crop_label;
        ContentSpinCtrl<VideoContent>* _left_crop;
+       wxToggleButton* _left_right_link;
        wxStaticText* _right_crop_label;
        ContentSpinCtrl<VideoContent>* _right_crop;
        wxStaticText* _top_crop_label;
        ContentSpinCtrl<VideoContent>* _top_crop;
+       wxToggleButton* _top_bottom_link;
        wxStaticText* _bottom_crop_label;
        ContentSpinCtrl<VideoContent>* _bottom_crop;
        wxStaticText* _fade_in_label;
        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 22d9f0db6a18938d045c6446feaa6f121a5630c8..a2fbe0c4ea5752392f9123b3d3d899d5559289d3 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 6eef0d14795020f0cab6481e76e98ba9687e6567..f8756549b44bca308ca591cace3c3859a9fea02e 100644 (file)
@@ -567,3 +567,26 @@ get_offsets (vector<Offset>& offsets)
 
        return utc;
 }
+
+
+wxString
+bitmap_path (string name)
+{
+       boost::filesystem::path base;
+
+#ifdef DCPOMATIC_DEBUG
+       /* Hack to allow OS X to find icons when running from the source tree */
+       char* path = getenv ("DCPOMATIC_GRAPHICS");
+       if (path) {
+               base = path;
+       } else {
+               base = shared_path();
+       }
+#else
+       base = shared_path();
+#endif
+
+       boost::filesystem::path p = base / String::compose("%1.png", name);
+       return std_to_wx (p.string());
+}
+
index 8e0befba92d606a4da09cb5302c3b691ea19c69d..bff58a3239940b735093f8ad66bd72305d3792d6 100644 (file)
@@ -87,7 +87,7 @@ extern wxSplashScreen* maybe_show_splash ();
 extern double calculate_mark_interval (double start);
 extern bool display_progress (wxString title, wxString task);
 extern bool report_errors_from_last_job (wxWindow* parent);
-
+extern wxString bitmap_path (std::string name);
 
 struct Offset
 {
index 71d60f5732e10106a721b02ebf016b1cc9d8612f..d8d3d66ec0e763c4a391564e9dbf0b88506895ad 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 b92cb284b9e8532ba9efbfb16005e67580ad3b71..5425969b6d8f1c4acd745772b4a4b9dfec1f465f 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 6b694889479064979b52c1839a1919dc5fde6731..3b21196b894bfbc096a5e90ee11dcf5f50bd4bf9 160000 (submodule)
--- a/test/data
+++ b/test/data
@@ -1 +1 @@
-Subproject commit 6b694889479064979b52c1839a1919dc5fde6731
+Subproject commit 3b21196b894bfbc096a5e90ee11dcf5f50bd4bf9
index 029e83966b85be624a189ef009e523838fc33269..a2557d4d846dbc7fbbe88b950b6d7388f9938c5c 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 6ad6c1fdb8d2ae38aaf27188f752c0a143c139d7..e9b0c70394b2ecf62d10f24284032b50f2f40fea 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 78847e40b9450464dc8bea3924051e79774fa4f3..ad09e5e1ae38029061eaba04fba7139f105df315 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 3315833b066aacef969af863a013163cfe97a161..487f80ea21e04aa85546c5c1dc23a42336387f26 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 6877d7e21f5ef37a37c850691845b1ae8e1dc2dc..2e979eb44f5ea0b067972d5b47237ff7810428fe 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 e8194fd81735f6b9a418cf482e21ee4fce343ac6..9c1ed7e66e0d7f1172882a26e3843f05c4ad4d78 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 1075f96fcdeeed08475f912226da3947dd3c13c0..358e6fe8f468c5f9f98a1a6dfc9bfd1ded932ddf 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 93c994741f50e4926993ed8e4b2c7fe84396ea35..dd3b6118ecf6eea8a76acd870d84c9788185a0bd 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 e30143b1819d18aa2c96c673855b812a8eedeaaa..aea389ce301f735c3849e6bd212b8a3e34d973ba 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 473cc00d310df716601daf77b3255d92fef5818c..266b7bf58d7811c9d60529540f86884b716aa833 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 67bfda3e5afae3f6e2f6c81a091b949d53ac6f8e..b3e3ff76c4a28947ad4ba3f36b6800c8a35aa741 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));
+       }
 }
+