1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
#include "screencapture.hpp"
#include <components/debug/debuglog.hpp>
#include <components/files/conversion.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <osg/Image>
#include <osg/ref_ptr>
#include <osgDB/ReaderWriter>
#include <osgDB/Registry>
#include <atomic>
#include <cassert>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
namespace
{
class ScreenCaptureWorkItem : public SceneUtil::WorkItem
{
public:
ScreenCaptureWorkItem(const osg::ref_ptr<osgViewer::ScreenCaptureHandler::CaptureOperation>& impl,
const osg::Image& image, unsigned int contextId)
: mImpl(impl)
, mImage(new osg::Image(image))
, mContextId(contextId)
{
assert(mImpl != nullptr);
}
void doWork() override
{
if (mAborted)
return;
try
{
(*mImpl)(*mImage, mContextId);
}
catch (const std::exception& e)
{
Log(Debug::Error) << "ScreenCaptureWorkItem exception: " << e.what();
}
}
void abort() override { mAborted = true; }
private:
const osg::ref_ptr<osgViewer::ScreenCaptureHandler::CaptureOperation> mImpl;
const osg::ref_ptr<const osg::Image> mImage;
const unsigned int mContextId;
std::atomic_bool mAborted{ false };
};
}
namespace SceneUtil
{
std::filesystem::path writeScreenshotToFile(
const std::filesystem::path& screenshotPath, const std::string& screenshotFormat, const osg::Image& image)
{
// Count screenshots.
int shotCount = 0;
// Find the first unused filename with a do-while
std::ostringstream stream;
std::string lastFileName;
std::filesystem::path lastFilePath;
do
{
// Reset the stream
stream.str("");
stream.clear();
stream << "screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << screenshotFormat;
lastFileName = stream.str();
lastFilePath = screenshotPath / lastFileName;
} while (std::filesystem::exists(lastFilePath));
std::ofstream outStream;
outStream.open(lastFilePath, std::ios::binary);
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(screenshotFormat);
if (!readerwriter)
{
Log(Debug::Error) << "Error: Can't write screenshot, no '" << screenshotFormat << "' readerwriter found";
return std::string();
}
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);
if (!result.success())
{
Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status();
return std::string();
}
return lastFileName;
}
WriteScreenshotToFileOperation::WriteScreenshotToFileOperation(const std::filesystem::path& screenshotPath,
const std::string& screenshotFormat, std::function<void(std::string)> callback)
: mScreenshotPath(screenshotPath)
, mScreenshotFormat(screenshotFormat)
, mCallback(std::move(callback))
{
}
void WriteScreenshotToFileOperation::operator()(const osg::Image& image, const unsigned int /*context_id*/)
{
std::filesystem::path fileName;
try
{
fileName = writeScreenshotToFile(mScreenshotPath, mScreenshotFormat, image);
}
catch (const std::exception& e)
{
Log(Debug::Error) << "Failed to write screenshot to file with path=\"" << mScreenshotPath << "\", format=\""
<< mScreenshotFormat << "\": " << e.what();
}
if (fileName.empty())
mCallback(std::string());
else
{
mCallback(Files::pathToUnicodeString(fileName));
Log(Debug::Info) << mScreenshotPath / fileName << " has been saved";
}
}
AsyncScreenCaptureOperation::AsyncScreenCaptureOperation(
osg::ref_ptr<WorkQueue> queue, osg::ref_ptr<CaptureOperation> impl)
: mQueue(std::move(queue))
, mImpl(std::move(impl))
{
assert(mQueue != nullptr);
assert(mImpl != nullptr);
}
AsyncScreenCaptureOperation::~AsyncScreenCaptureOperation()
{
stop();
}
void AsyncScreenCaptureOperation::stop()
{
for (const osg::ref_ptr<SceneUtil::WorkItem>& item : *mWorkItems.lockConst())
item->abort();
for (const osg::ref_ptr<SceneUtil::WorkItem>& item : *mWorkItems.lockConst())
item->waitTillDone();
}
void AsyncScreenCaptureOperation::operator()(const osg::Image& image, const unsigned int context_id)
{
osg::ref_ptr<SceneUtil::WorkItem> item(new ScreenCaptureWorkItem(mImpl, image, context_id));
mQueue->addWorkItem(item);
const auto isDone = [](const osg::ref_ptr<SceneUtil::WorkItem>& v) { return v->isDone(); };
const auto workItems = mWorkItems.lock();
workItems->erase(std::remove_if(workItems->begin(), workItems->end(), isDone), workItems->end());
workItems->emplace_back(std::move(item));
}
}
|