From 9bbd8891f9d043fd96a565c74016ee29143899cc Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 16 Apr 2026 22:05:24 +0200 Subject: Add --fill-crop to create CLI (#3008). This can be used to extract images from letterboxed/pillarboxed frames. --- src/lib/create_cli.cc | 32 ++++++++++++++++++++++++++++++++ src/lib/create_cli.h | 1 + test/create_cli_test.cc | 16 ++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc index 32834be23..3ef61fe11 100644 --- a/src/lib/create_cli.cc +++ b/src/lib/create_cli.cc @@ -85,6 +85,8 @@ help() " --left-eye next piece of content is for the left eye\n" " --right-eye next piece of content is for the right eye\n" " --auto-crop next piece of content should be auto-cropped\n" + " --fill-crop next piece of content should be cropped to fit the container\n" + " (e.g. to crop the letterboxing from a scope-in-flat image)\n" " --colourspace next piece of content is in the given colourspace: " + colour_conversions + "\n" " --colorspace same as --colourspace\n" " --channel next piece of content should be mapped to audio channel L, R, C, Lfe, Ls, Rs, BsL, BsR, HI, VI\n" @@ -184,6 +186,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) optional next_colour_conversion; auto next_frame_type = VideoFrameType::TWO_D; auto next_auto_crop = false; + auto next_fill_crop = false; optional channel; optional gain; optional fade_in; @@ -225,6 +228,9 @@ CreateCLI::CreateCLI(int argc, char* argv[]) } else if (a == "--auto-crop") { next_auto_crop = true; claimed = true; + } else if (a == "--fill-crop") { + next_fill_crop = true; + claimed = true; } else if (a == "--twok") { _twok = true; claimed = true; @@ -331,6 +337,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) c.path = a; c.frame_type = next_frame_type; c.auto_crop = next_auto_crop; + c.fill_crop = next_fill_crop; c.colour_conversion = next_colour_conversion; c.channel = channel; c.gain = gain; @@ -341,6 +348,7 @@ CreateCLI::CreateCLI(int argc, char* argv[]) content.push_back(c); next_frame_type = VideoFrameType::TWO_D; next_auto_crop = false; + next_fill_crop = false; next_colour_conversion = {}; channel = {}; gain = {}; @@ -555,6 +563,30 @@ CreateCLI::make_film(function error) const video->set_crop(crop); } + if (cli_content.fill_crop && video->size()) { + auto const source_ratio = video->size()->ratio(); + Crop crop; + if (source_ratio < film->container().ratio()) { + /* Part to extract is wider than the source */ + auto const height = video->size()->width / film->container().ratio(); + crop.top = crop.bottom = (video->size()->height - height) / 2; + } else { + /* Container is wider than the source */ + auto const width = video->size()->height * film->container().ratio(); + crop.left = crop.right = (video->size()->width - width) / 2; + } + + error(fmt::format( + "Cropped {} to {} left, {} right, {} top and {} bottom", + film_content->path(0).string(), + crop.left, + crop.right, + crop.top, + crop.bottom + )); + + video->set_crop(crop); + } if (cli_content.colour_conversion) { video->set_colour_conversion(PresetColourConversion::from_id(*cli_content.colour_conversion).conversion); } diff --git a/src/lib/create_cli.h b/src/lib/create_cli.h index 00abf85e5..cde8598cd 100644 --- a/src/lib/create_cli.h +++ b/src/lib/create_cli.h @@ -42,6 +42,7 @@ public: boost::filesystem::path path; VideoFrameType frame_type = VideoFrameType::TWO_D; bool auto_crop = false; + bool fill_crop = false; boost::optional colour_conversion; boost::optional channel; boost::optional gain; diff --git a/test/create_cli_test.cc b/test/create_cli_test.cc index c71031fbc..0f127c018 100644 --- a/test/create_cli_test.cc +++ b/test/create_cli_test.cc @@ -322,6 +322,22 @@ BOOST_AUTO_TEST_CASE (create_cli_test) BOOST_CHECK(film->content()[0]->video->fade_in() == 24); BOOST_CHECK(film->content()[0]->video->fade_out() == 0); BOOST_CHECK(collected_error.empty()); + + /* Extract a 1.85 frame from a 320x240 source */ + cc = run("dcpomatic2_create --container-ratio 185 --fill-crop test/data/red_24.mp4"); + BOOST_CHECK(!cc.error); + film = cc.make_film(error); + BOOST_REQUIRE_EQUAL(film->content().size(), 1U); + BOOST_REQUIRE(film->content()[0]->video); + BOOST_CHECK(film->content()[0]->video->requested_crop() == Crop(0, 0, 33, 33)); + + /* Extract a 1.85 frame from a 2048x858 source */ + cc = run("dcpomatic2_create --container-ratio 185 --fill-crop test/data/scope_dcp"); + BOOST_CHECK(!cc.error); + film = cc.make_film(error); + BOOST_REQUIRE_EQUAL(film->content().size(), 1U); + BOOST_REQUIRE(film->content()[0]->video); + BOOST_CHECK(film->content()[0]->video->requested_crop() == Crop(230, 230, 0, 0)); } -- cgit v1.2.3