Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream output file to stdout #1326

Merged
merged 4 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
mwestphal marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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
Loading