Skip to content

Commit

Permalink
Stream output file to stdout (#1326)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meakk authored Mar 24, 2024
1 parent 340c56d commit eb2d844
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 21 deletions.
38 changes: 28 additions & 10 deletions application/F3DStarter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <algorithm>
#include <cassert>
#include <filesystem>
#include <iostream>
#include <set>

namespace fs = std::filesystem;
Expand Down Expand Up @@ -102,28 +103,28 @@ class F3DStarter::F3DInternals
return false;
}

static void SetVerboseLevel(const std::string& level)
static void SetVerboseLevel(const std::string& level, bool forceStdErr)
{
// A switch/case over verbose level
if (level == "quiet")
{
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::QUIET);
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::QUIET, forceStdErr);
}
else if (level == "error")
{
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::ERROR);
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::ERROR, forceStdErr);
}
else if (level == "warning")
{
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::WARN);
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::WARN, forceStdErr);
}
else if (level == "info")
{
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::INFO);
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::INFO, forceStdErr);
}
else if (level == "debug")
{
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG);
f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG, forceStdErr);
}
else
{
Expand Down Expand Up @@ -164,8 +165,16 @@ int F3DStarter::Start(int argc, char** argv)
this->Internals->Parser.GetOptions(
this->Internals->AppOptions, this->Internals->DynamicOptions, files);

const bool renderToStdout = this->Internals->AppOptions.Output == "-";

// Set verbosity level early from command line
F3DInternals::SetVerboseLevel(this->Internals->AppOptions.VerboseLevel);
F3DInternals::SetVerboseLevel(this->Internals->AppOptions.VerboseLevel, renderToStdout);

if (renderToStdout)
{
f3d::log::info("Output image will be saved to stdout, all log types including debug and info "
"levels are redirected to stderr");
}

f3d::log::debug("========== Initializing ==========");

Expand All @@ -184,7 +193,7 @@ int F3DStarter::Start(int argc, char** argv)
this->Internals->AppOptions, this->Internals->DynamicOptions, files);

// Set verbosity level again if it was defined in the configuration file global block
F3DInternals::SetVerboseLevel(this->Internals->AppOptions.VerboseLevel);
F3DInternals::SetVerboseLevel(this->Internals->AppOptions.VerboseLevel, renderToStdout);
}

#if __APPLE__
Expand Down Expand Up @@ -426,8 +435,17 @@ int F3DStarter::Start(int argc, char** argv)
}

f3d::image img = window.renderToImage(this->Internals->AppOptions.NoBackground);
img.save(this->Internals->AppOptions.Output);
f3d::log::debug("Output image saved to ", this->Internals->AppOptions.Output);
if (renderToStdout)
{
const auto buffer = img.saveBuffer();
std::copy(buffer.begin(), buffer.end(), std::ostreambuf_iterator(std::cout));
f3d::log::debug("Output image saved to stdout");
}
else
{
img.save(this->Internals->AppOptions.Output);
f3d::log::debug("Output image saved to ", this->Internals->AppOptions.Output);
}

if (this->Internals->FilesList.size() > 1)
{
Expand Down
2 changes: 2 additions & 0 deletions application/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ f3d_test(NAME TestUpDirectionNoSign DATA suzanne.ply ARGS --up=X DEFAULT_LIGHTS)
f3d_test(NAME TestTextureMatCap DATA suzanne.ply ARGS --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png DEFAULT_LIGHTS)
f3d_test(NAME TestTextureMatCapWithTCoords DATA WaterBottle.glb ARGS --geometry-only --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png DEFAULT_LIGHTS)
f3d_test(NAME TestConfigOrder DATA suzanne.ply ARGS CONFIG ${F3D_SOURCE_DIR}/testing/data/config-order.json DEFAULT_LIGHTS)
f3d_test(NAME TestOutputStream DATA suzanne.ply ARGS --verbose=quiet --output=- REGEXP "^.PNG" NO_BASELINE NO_OUTPUT)
f3d_test(NAME TestOutputStreamInfo DATA suzanne.ply ARGS --verbose=info --output=- REGEXP "redirected to stderr" NO_BASELINE NO_OUTPUT)

# Needs SSBO: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10675
if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20231108)
Expand Down
2 changes: 1 addition & 1 deletion doc/user/OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ F3D behavior can be fully controlled from the command line using the following o

Options|Default|Description
------|------|------
\-\-output=\<png file\>||Instead of showing a render view and render into it, *render directly into a png file*. When used with \-\-ref option, only outputs on failure.
\-\-output=\<png file\>||Instead of showing a render view and render into it, *render directly into a png file*. When used with \-\-ref option, only outputs on failure. If `-` is specified instead of a filename, the PNG file is streamed to the stdout.
\-\-no-background||Use with \-\-output to output a png file with a transparent background.
-h, \-\-help||Print *help* and exit. Ignore `--verbose`.
\-\-version||Show *version* information and exit. Ignore `--verbose`.
Expand Down
4 changes: 3 additions & 1 deletion library/public/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ class F3D_EXPORT log

/**
* Set the verbose level.
* By default, only warnings and errors are written to stderr, debug and info are written to
* stdout. If forceStdErr is true, all messages including debug and info are written to stderr.
*/
static void setVerboseLevel(VerboseLevel level);
static void setVerboseLevel(VerboseLevel level, bool forceStdErr = false);

/**
* Wait for user if applicable (eg: win32 output window).
Expand Down
14 changes: 12 additions & 2 deletions library/src/log.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,20 @@ void log::setUseColoring(bool use)
}

//----------------------------------------------------------------------------
void log::setVerboseLevel(log::VerboseLevel level)
void log::setVerboseLevel(log::VerboseLevel level, bool forceStdErr)
{
detail::init::initialize();
F3DLog::SetQuiet(level == log::VerboseLevel::QUIET);

if (level == log::VerboseLevel::QUIET)
{
F3DLog::SetStandardStream(F3DLog::StandardStream::None);
}
else
{
F3DLog::SetStandardStream(
forceStdErr ? F3DLog::StandardStream::AlwaysStdErr : F3DLog::StandardStream::Default);
}

switch (level)
{
case (log::VerboseLevel::DEBUG):
Expand Down
17 changes: 15 additions & 2 deletions vtkext/private/module/F3DLog.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,23 @@ void F3DLog::SetUseColoring(bool use)
}

//----------------------------------------------------------------------------
void F3DLog::SetQuiet(bool quiet)
void F3DLog::SetStandardStream(StandardStream mode)
{
vtkOutputWindow* win = vtkOutputWindow::GetInstance();
win->SetDisplayMode(quiet ? vtkOutputWindow::NEVER : vtkOutputWindow::ALWAYS);

switch (mode)
{
case StandardStream::None:
win->SetDisplayMode(vtkOutputWindow::NEVER);
break;
case StandardStream::AlwaysStdErr:
win->SetDisplayMode(vtkOutputWindow::ALWAYS_STDERR);
break;
case StandardStream::Default:
default:
win->SetDisplayMode(vtkOutputWindow::ALWAYS);
break;
}
}

//----------------------------------------------------------------------------
Expand Down
16 changes: 13 additions & 3 deletions vtkext/private/module/F3DLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ enum class Severity : unsigned char
Error
};

enum class StandardStream : unsigned char
{
Default = 0,
None,
AlwaysStdErr
};

/**
* Set this global variable to control the verbose level
* that actually display something in Print
Expand All @@ -41,10 +48,13 @@ void Print(Severity sev, const std::string& msg);
void SetUseColoring(bool use);

/**
* Set if any log should be shown or not.
* Override the verbosity level completely.
* Determine how standard stream should be used.
* If mode is None, then no message is written at all (including errors).
* If mode is AlwaysStdErr, then all messages are written to stderr.
* If mode is Default, then only warnings and errors are written to stderr. Debug and info messages
* are written to stdout.
*/
void SetQuiet(bool quiet);
void SetStandardStream(StandardStream mode);

/**
* If output window is a vtkF3DWin32OutputWindow,
Expand Down
4 changes: 2 additions & 2 deletions vtkext/private/module/Testing/TestF3DLog.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ int TestF3DLog(int argc, char* argv[])
F3DLog::Print(F3DLog::Severity::Error, "Test Error\n");

F3DLog::VerboseLevel = F3DLog::Severity::Info;
F3DLog::SetQuiet(true); // Next print calls should print nothing
F3DLog::SetStandardStream(F3DLog::StandardStream::None); // Next print calls should print nothing
F3DLog::Print(F3DLog::Severity::Debug, "Test Debug Quiet ");
F3DLog::Print(F3DLog::Severity::Info, "Test Info Quiet ");
F3DLog::Print(F3DLog::Severity::Warning, "Test Warning Quiet ");
F3DLog::Print(F3DLog::Severity::Error, "Test Error Quiet\n");
F3DLog::SetQuiet(false);
F3DLog::SetStandardStream(F3DLog::StandardStream::Default);

// Without the object factory created, this is expected to have no effect
F3DLog::SetUseColoring(true);
Expand Down

0 comments on commit eb2d844

Please sign in to comment.