diff options
53 files changed, 903 insertions, 765 deletions
diff --git a/graphics/link.png b/graphics/link.png Binary files differnew file mode 100644 index 000000000..69af402f6 --- /dev/null +++ b/graphics/link.png diff --git a/graphics/src/link.svg b/graphics/src/link.svg new file mode 100644 index 000000000..bf58ad7de --- /dev/null +++ b/graphics/src/link.svg @@ -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> diff --git a/graphics/update b/graphics/update index 9b75493db..f0a7fd597 100755 --- a/graphics/update +++ b/graphics/update @@ -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 diff --git a/graphics/web/favicon-128x128.png b/graphics/web/favicon-128x128.png Binary files differindex 621455641..9330c0cdc 100644 --- a/graphics/web/favicon-128x128.png +++ b/graphics/web/favicon-128x128.png diff --git a/graphics/web/favicon-16x16.png b/graphics/web/favicon-16x16.png Binary files differindex 4c31c0258..82639fddf 100644 --- a/graphics/web/favicon-16x16.png +++ b/graphics/web/favicon-16x16.png diff --git a/graphics/web/favicon-32x32.png b/graphics/web/favicon-32x32.png Binary files differindex 847bc813a..e9772da24 100644 --- a/graphics/web/favicon-32x32.png +++ b/graphics/web/favicon-32x32.png diff --git a/graphics/web/favicon-64x64.png b/graphics/web/favicon-64x64.png Binary files differindex e836814d1..0ff824f40 100644 --- a/graphics/web/favicon-64x64.png +++ b/graphics/web/favicon-64x64.png diff --git a/src/lib/config.cc b/src/lib/config.cc index ebb6ece93..18955a896 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -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>, diff --git a/src/lib/config.h b/src/lib/config.h index f54ca3814..23f7ce0b3 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -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; diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc index 878ee6fdd..b670282b4 100644 --- a/src/lib/create_cli.cc +++ b/src/lib/create_cli.cc @@ -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") { diff --git a/src/lib/create_cli.h b/src/lib/create_cli.h index a9d9daa22..01e8e6633 100644 --- a/src/lib/create_cli.h +++ b/src/lib/create_cli.h @@ -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; diff --git a/src/lib/dcp.cc b/src/lib/dcp.cc index c62b339fa..477b61e9f 100644 --- a/src/lib/dcp.cc +++ b/src/lib/dcp.cc @@ -75,9 +75,9 @@ DCP::cpls () const list<dcp::VerificationNote> notes; dcp->read (¬es, 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))); diff --git a/src/lib/film.cc b/src/lib/film.cc index b233e5ee6..2f631bd89 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -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 diff --git a/src/lib/film.h b/src/lib/film.h index 40d366f8f..b03b0258e 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -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; diff --git a/src/lib/hints.cc b/src/lib/hints.cc index 581d63972..0f18835ef 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -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") { diff --git a/src/lib/player.cc b/src/lib/player.cc index e41cecf13..059465724 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -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, diff --git a/src/lib/player.h b/src/lib/player.h index 51de78982..bb2a0c613 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -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; diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc index cb2a55724..10e798ed5 100644 --- a/src/lib/player_video.cc +++ b/src/lib/player_video.cc @@ -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(); diff --git a/src/lib/util.cc b/src/lib/util.cc index d04bbdf24..c1ef4245f 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -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; +} + diff --git a/src/lib/util.h b/src/lib/util.h index f8e9409f1..f7b4704ac 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -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> diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 685336367..b4ad30f70 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -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); +} diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 37223c457..b478666bb 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -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 index 73dfd27fe..000000000 --- a/src/lib/video_content_scale.cc +++ /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 index 845c71a52..000000000 --- a/src/lib/video_content_scale.h +++ /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 diff --git a/src/lib/wscript b/src/lib/wscript index 0f2a5d197..fb222fcf4 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -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 diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index f69ab5a6a..13569d11d 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -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")); diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc index 6a243e126..edf4808a0 100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@ -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 " diff --git a/src/tools/dcpomatic_create.cc b/src/tools/dcpomatic_create.cc index 68ae09c31..857359117 100644 --- a/src/tools/dcpomatic_create.cc +++ b/src/tools/dcpomatic_create.cc @@ -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); } } diff --git a/src/wx/content_widget.h b/src/wx/content_widget.h index c4ae4d591..de7ebedc5 100644 --- a/src/wx/content_widget.h +++ b/src/wx/content_widget.h @@ -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 index 000000000..5725d2378 --- /dev/null +++ b/src/wx/custom_scale_dialog.cc @@ -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 index 000000000..4c9ccf388 --- /dev/null +++ b/src/wx/custom_scale_dialog.h @@ -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; +}; + diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc index 370ec35a1..fff0d2035 100644 --- a/src/wx/full_config_dialog.cc +++ b/src/wx/full_config_dialog.cc @@ -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; diff --git a/src/wx/timeline_dialog.cc b/src/wx/timeline_dialog.cc index 3a720cfec..0d0d4f9f9 100644 --- a/src/wx/timeline_dialog.cc +++ b/src/wx/timeline_dialog.cc @@ -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) { diff --git a/src/wx/timeline_dialog.h b/src/wx/timeline_dialog.h index 62649a5f2..77dfede50 100644 --- a/src/wx/timeline_dialog.h +++ b/src/wx/timeline_dialog.h @@ -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; diff --git a/src/wx/video_panel.cc b/src/wx/video_panel.cc index 992ddf115..24099e1cc 100644 --- a/src/wx/video_panel.cc +++ b/src/wx/video_panel.cc @@ -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)); @@ -483,6 +485,20 @@ VideoPanel::film_content_changed (int property) } 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()); + } + } +} + + + diff --git a/src/wx/video_panel.h b/src/wx/video_panel.h index ad0ba402a..31aeed2e1 100644 --- a/src/wx/video_panel.h +++ b/src/wx/video_panel.h @@ -25,13 +25,13 @@ #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; diff --git a/src/wx/wscript b/src/wx/wscript index 22d9f0db6..a2fbe0c4e 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -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' diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index 6eef0d147..f8756549b 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -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()); +} + diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index 8e0befba9..bff58a323 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -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 { diff --git a/test/4k_test.cc b/test/4k_test.cc index 71d60f573..d8d3d66ec 100644 --- a/test/4k_test.cc +++ b/test/4k_test.cc @@ -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()); diff --git a/test/create_cli_test.cc b/test/create_cli_test.cc index b92cb284b..5425969b6 100644 --- a/test/create_cli_test.cc +++ b/test/create_cli_test.cc @@ -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); diff --git a/test/data b/test/data -Subproject 6b694889479064979b52c1839a1919dc5fde673 +Subproject 3b21196b894bfbc096a5e90ee11dcf5f50bd4bf diff --git a/test/empty_test.cc b/test/empty_test.cc index 029e83966..a2557d4d8 100644 --- a/test/empty_test.cc +++ b/test/empty_test.cc @@ -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)); diff --git a/test/ffmpeg_audio_test.cc b/test/ffmpeg_audio_test.cc index 6ad6c1fdb..e9b0c7039 100644 --- a/test/ffmpeg_audio_test.cc +++ b/test/ffmpeg_audio_test.cc @@ -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")); diff --git a/test/ffmpeg_dcp_test.cc b/test/ffmpeg_dcp_test.cc index 78847e40b..ad09e5e1a 100644 --- a/test/ffmpeg_dcp_test.cc +++ b/test/ffmpeg_dcp_test.cc @@ -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 (); diff --git a/test/isdcf_name_test.cc b/test/isdcf_name_test.cc index 3315833b0..487f80ea2 100644 --- a/test/isdcf_name_test.cc +++ b/test/isdcf_name_test.cc @@ -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 */ diff --git a/test/player_test.cc b/test/player_test.cc index 6877d7e21..2e979eb44 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -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 (); diff --git a/test/recover_test.cc b/test/recover_test.cc index e8194fd81..9c1ed7e66 100644 --- a/test/recover_test.cc +++ b/test/recover_test.cc @@ -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, diff --git a/test/repeat_frame_test.cc b/test/repeat_frame_test.cc index 1075f96fc..358e6fe8f 100644 --- a/test/repeat_frame_test.cc +++ b/test/repeat_frame_test.cc @@ -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 (); diff --git a/test/scaling_test.cc b/test/scaling_test.cc index 93c994741..dd3b6118e 100644 --- a/test/scaling_test.cc +++ b/test/scaling_test.cc @@ -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"); } diff --git a/test/skip_frame_test.cc b/test/skip_frame_test.cc index e30143b18..aea389ce3 100644 --- a/test/skip_frame_test.cc +++ b/test/skip_frame_test.cc @@ -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); diff --git a/test/threed_test.cc b/test/threed_test.cc index 473cc00d3..266b7bf58 100644 --- a/test/threed_test.cc +++ b/test/threed_test.cc @@ -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")); diff --git a/test/video_content_scale_test.cc b/test/video_content_scale_test.cc index 67bfda3e5..b3e3ff76c 100644 --- a/test/video_content_scale_test.cc +++ b/test/video_content_scale_test.cc @@ -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. @@ -18,188 +18,128 @@ */ -/** @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)); + } } + |
