mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-02-05 03:15:35 +00:00
Let developers provide an optional filename for dumping main texture contents.
This commit is contained in:
@ -32,13 +32,23 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Writes a .bmp framedump after rendering the next main texture, before shaders are applied.
|
||||
* @brief Writes a .bmp main texture dump after rendering the next main texture, before shaders are applied.
|
||||
*
|
||||
* The image is written to the current working directory and is named "frame_texture_contents-[date].bmp".
|
||||
* If no file name is given, the image is written to the current working directory
|
||||
* and will be named named "frame_texture_contents-YYYY-mm-dd-HH:MM:SS-frame.bmp".
|
||||
*
|
||||
* Note this is the main texture contents, not the final rendering result. If the active preset
|
||||
* uses a composite shader, the dumped image will not have it applied. The main texture is what is
|
||||
* passed over to the next frame, the composite shader is only applied to the display framebuffer
|
||||
* after updating the main texture.
|
||||
*
|
||||
* To capture the actual output, dump the contents of the main framebuffer after calling
|
||||
* @a projectm_render_frame() on the application side.
|
||||
*
|
||||
* @param instance The projectM instance handle.
|
||||
* @param output_file The filename to write the dump to or NULL.
|
||||
*/
|
||||
PROJECTM_EXPORT void projectm_write_debug_image_on_next_frame(projectm_handle instance);
|
||||
PROJECTM_EXPORT void projectm_write_debug_image_on_next_frame(projectm_handle instance, const char* output_file);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@ -115,8 +115,9 @@ void ProjectM::ResetTextures()
|
||||
m_renderer->ResetTextures();
|
||||
}
|
||||
|
||||
void ProjectM::DumpDebugImageOnNextFrame()
|
||||
void ProjectM::DumpDebugImageOnNextFrame(const std::string& outputFile)
|
||||
{
|
||||
m_renderer->frameDumpOutputFile = outputFile;
|
||||
m_renderer->writeNextFrameToFile = true;
|
||||
}
|
||||
|
||||
|
||||
@ -208,7 +208,7 @@ public:
|
||||
*
|
||||
* The main texture is dumped after render pass 1, e.g. before shaders are applied.
|
||||
*/
|
||||
void DumpDebugImageOnNextFrame();
|
||||
void DumpDebugImageOnNextFrame(const std::string& outputFile);
|
||||
|
||||
private:
|
||||
void EvaluateSecondPreset();
|
||||
|
||||
@ -388,9 +388,15 @@ auto projectm_pcm_add_uint8(projectm_handle instance, const uint8_t* samples, un
|
||||
PcmAdd(instance, samples, count, channels);
|
||||
}
|
||||
|
||||
auto projectm_write_debug_image_on_next_frame(projectm_handle instance) -> void
|
||||
auto projectm_write_debug_image_on_next_frame(projectm_handle instance, const char* output_file) -> void
|
||||
{
|
||||
auto* projectMInstance = handle_to_instance(instance);
|
||||
|
||||
projectMInstance->DumpDebugImageOnNextFrame();
|
||||
std::string outputFile;
|
||||
if (output_file)
|
||||
{
|
||||
outputFile = output_file;
|
||||
}
|
||||
|
||||
projectMInstance->DumpDebugImageOnNextFrame(outputFile);
|
||||
}
|
||||
@ -204,11 +204,13 @@ void Renderer::RenderTouch(const Pipeline& pipeline, const PipelineContext& pipe
|
||||
void Renderer::FinishPass1()
|
||||
{
|
||||
m_textureManager->updateMainTexture();
|
||||
if(writeNextFrameToFile) {
|
||||
debugWriteMainTextureToFile();
|
||||
writeNextFrameToFile = false;
|
||||
}
|
||||
|
||||
if (writeNextFrameToFile)
|
||||
{
|
||||
debugWriteMainTextureToFile();
|
||||
writeNextFrameToFile = false;
|
||||
frameDumpOutputFile = "";
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::Pass2(const Pipeline& pipeline, const PipelineContext& pipelineContext)
|
||||
@ -530,36 +532,41 @@ void Renderer::touchDestroyAll()
|
||||
m_waveformList.clear();
|
||||
}
|
||||
|
||||
void Renderer::debugWriteMainTextureToFile() const {
|
||||
GLuint fbo;
|
||||
auto mainTexture = m_textureManager->getMainTexture();
|
||||
void Renderer::debugWriteMainTextureToFile() const
|
||||
{
|
||||
std::string outputFile = frameDumpOutputFile;
|
||||
if (outputFile.empty())
|
||||
{
|
||||
static constexpr char prefix[] = "frame_texture_contents-";
|
||||
static constexpr auto prefixLen = sizeof(prefix) - 1;
|
||||
constexpr auto fileNameMaxLength = 150;
|
||||
constexpr auto fileExtensionLength = 4;
|
||||
std::vector<char> fileNameBuffer;
|
||||
fileNameBuffer.resize(fileNameMaxLength);
|
||||
std::memcpy(fileNameBuffer.data(), prefix, prefixLen);
|
||||
auto t = std::time(nullptr);
|
||||
auto tm = *std::localtime(&t);
|
||||
const auto bytesWritten = std::strftime(fileNameBuffer.data() + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm);
|
||||
const auto offset = prefixLen + bytesWritten;
|
||||
const auto spaceLeft = fileNameMaxLength - offset;
|
||||
std::snprintf(fileNameBuffer.data() + offset, spaceLeft - fileExtensionLength, "-%d.bmp", totalframes);
|
||||
outputFile = std::string(fileNameBuffer.data());
|
||||
}
|
||||
|
||||
const auto safeWriteFile = [](const auto frameNumber, auto data, const auto width, const auto height) {
|
||||
static const std::string prefix{"frame_texture_contents-"};
|
||||
const auto prefixLen = prefix.size();
|
||||
constexpr auto fileNameMaxLength = 150;
|
||||
constexpr auto fileExtensionLength = 4;
|
||||
char fileNameBuffer[fileNameMaxLength];
|
||||
std::memcpy(fileNameBuffer, prefix.data(), prefixLen);
|
||||
auto t = std::time(nullptr);
|
||||
auto tm = *std::localtime(&t);
|
||||
const auto bytesWritten = std::strftime(fileNameBuffer + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm);
|
||||
const auto offset = prefixLen + bytesWritten;
|
||||
const auto spaceLeft = fileNameMaxLength - offset;
|
||||
std::snprintf(fileNameBuffer + offset, spaceLeft - fileExtensionLength, "%d.bmp", frameNumber);
|
||||
stbi_write_bmp( fileNameBuffer, width, height, 4, data);
|
||||
};
|
||||
GLuint fbo;
|
||||
auto mainTexture = m_textureManager->getMainTexture();
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0);
|
||||
auto dataSize = mainTexture->width * mainTexture->height * 3;
|
||||
GLubyte* pixels = new GLubyte[dataSize];
|
||||
glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0);
|
||||
auto dataSize = mainTexture->width * mainTexture->height * 4;
|
||||
GLubyte* pixels = new GLubyte[dataSize];
|
||||
glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
safeWriteFile(totalframes, pixels, mainTexture->width, mainTexture->height);
|
||||
delete[] pixels;
|
||||
stbi_write_bmp(outputFile.c_str(), mainTexture->width, mainTexture->height, 3, pixels);
|
||||
|
||||
delete[] pixels;
|
||||
}
|
||||
|
||||
void Renderer::UpdateContext(PipelineContext& context)
|
||||
|
||||
@ -104,6 +104,7 @@ public:
|
||||
bool correction{ true };
|
||||
|
||||
bool writeNextFrameToFile{ false };
|
||||
std::string frameDumpOutputFile;
|
||||
|
||||
milliseconds lastTimeFPS{ nowMilliseconds() };
|
||||
milliseconds currentTimeFPS{ nowMilliseconds() };
|
||||
|
||||
Reference in New Issue
Block a user