3232# include < GLFW/glfw3native.h>
3333#endif
3434
35+ #include < charconv>
3536#include < iostream>
3637#include < thread>
3738
@@ -58,6 +59,18 @@ void redrawWindow() {
5859 }
5960}
6061
62+ // Stricter version of from_chars that only returns true if the entire input was consumed and no error occurred.
63+ template <typename T> bool fromChars (const char * begin, const char * end, T&& value) {
64+ const auto result = std::from_chars (begin, end, value);
65+ return result.ec == std::errc{} && result.ptr == end;
66+ }
67+
68+ template <typename T> bool fromChars (string_view s, T&& value) { return fromChars (s.data (), s.data () + s.size (), std::forward<T>(value)); }
69+
70+ template <typename T> bool fromChars (const string& s, T&& value) {
71+ return fromChars (s.data (), s.data () + s.size (), std::forward<T>(value));
72+ }
73+
6174static void handleIpcPacket (const IpcPacket& packet, const std::shared_ptr<BackgroundImagesLoader>& imagesLoader) {
6275 switch (packet.type ()) {
6376 case IpcPacket::OpenImage:
@@ -309,6 +322,14 @@ static int mainFunc(span<const string> arguments) {
309322 {' p' , " play" },
310323 };
311324
325+ ValueFlag<string> sizeFlag{
326+ parser,
327+ " SIZE" ,
328+ " Initial size of the tev window as <width>x<height>. "
329+ " Default is 1024x800." ,
330+ {" size" },
331+ };
332+
312333 ValueFlag<string> tonemapFlag{
313334 parser,
314335 " TONEMAP" ,
@@ -581,7 +602,26 @@ static int mainFunc(span<const string> arguments) {
581602 return -3 ;
582603 }
583604
584- const nanogui::Vector2i size = {1024 , 800 };
605+ nanogui::Vector2i size = {1024 , 800 };
606+ if (sizeFlag) {
607+ const string sizeString = get (sizeFlag);
608+ const auto parts = split (sizeString, " x" );
609+ if (parts.size () != 2 ) {
610+ tlog::error () << fmt::format (" Invalid size specification '{}'. Must be of the form <width>x<height>." , sizeString);
611+ return -4 ;
612+ }
613+
614+ if (!fromChars (parts[0 ], size.x ()) || !fromChars (parts[1 ], size.y ())) {
615+ tlog::error () << fmt::format (" Invalid size specification '{}'. Must be of the form <width>x<height>." , sizeString);
616+ return -4 ;
617+ }
618+
619+ if (size.x () <= 0 || size.y () <= 0 ) {
620+ tlog::error () << fmt::format (" Invalid size specification '{}'. Width and height must be positive." , sizeString);
621+ return -4 ;
622+ }
623+ }
624+
585625 if (!maximize) {
586626 // Wait until the first image is loaded before creating the window such that it can size itself appropriately. We can not pass the
587627 // Window a size right away, because we don't have information about the user's monitor size or DPI scaling yet, hence `size` stays
@@ -650,17 +690,18 @@ static int mainFunc(span<const string> arguments) {
650690 }
651691
652692 if (whiteLevelFlag) {
653- if (get (whiteLevelFlag) == " image" ) {
693+ const string wlValue = get (whiteLevelFlag);
694+ if (toLower (wlValue) == " image" ) {
654695 sImageViewer ->setDisplayWhiteLevelSetting (ImageViewer::EDisplayWhiteLevelSetting::ImageMetadata);
655696 } else {
656- try {
657- const float whiteLevel = stof (get (whiteLevelFlag));
658- sImageViewer ->setDisplayWhiteLevelSetting (ImageViewer::EDisplayWhiteLevelSetting::Custom);
659- sImageViewer ->setDisplayWhiteLevel (whiteLevel);
660- } catch (const invalid_argument&) {
697+ float whiteLevel = 0 .0f ;
698+ if (!fromChars (wlValue, whiteLevel)) {
661699 tlog::error () << fmt::format (" Invalid white level value '{}'. Must be a float or 'image'." , get (whiteLevelFlag));
662700 return -4 ;
663701 }
702+
703+ sImageViewer ->setDisplayWhiteLevelSetting (ImageViewer::EDisplayWhiteLevelSetting::Custom);
704+ sImageViewer ->setDisplayWhiteLevel (whiteLevel);
664705 }
665706 }
666707
0 commit comments