diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 744f7f2d..9effa695 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -382,6 +382,13 @@ public: */ void repaint(const Rectangle& rect) noexcept; + /** + Render this window's content into a picture file, specified by @a filename. + Window must be visible and on screen. + Written picture format is PPM. + */ + void renderToPicture(const char* filename); + /** Run this window as a modal, blocking input events from the parent. Only valid for windows that have been created with another window as parent (as passed in the constructor). diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp index ebefaa56..6cd7f201 100644 --- a/dgl/src/Cairo.cpp +++ b/dgl/src/Cairo.cpp @@ -808,6 +808,13 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) +{ + notImplemented("Window::PrivateData::renderToPicture"); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { GraphicsContext& context((GraphicsContext&)graphicsContext); diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp index 098c33e6..739669cd 100644 --- a/dgl/src/OpenGL.cpp +++ b/dgl/src/OpenGL.cpp @@ -667,6 +667,34 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char* const filename, + const GraphicsContext&, + const uint width, + const uint height) +{ + FILE* const f = fopen(filename, "w"); + DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,); + + GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)]; + + glFlush(); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + fprintf(f, "P3\n%d %d\n255\n", width, height); + for (uint y = 0; y < height; y++) { + for (uint i, x = 0; x < width; x++) { + i = 3 * ((height - y - 1) * width + x); + fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]); + } + fprintf(f, "\n"); + } + + delete[] pixels; + fclose(f); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; diff --git a/dgl/src/Vulkan.cpp b/dgl/src/Vulkan.cpp index d18b3c32..2fc97589 100644 --- a/dgl/src/Vulkan.cpp +++ b/dgl/src/Vulkan.cpp @@ -231,6 +231,13 @@ void TopLevelWidget::PrivateData::display() // ----------------------------------------------------------------------- +void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) +{ + notImplemented("Window::PrivateData::renderToPicture"); +} + +// ----------------------------------------------------------------------- + const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index b418fdad..7d27613c 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -336,6 +336,11 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } +void Window::renderToPicture(const char* const filename) +{ + pData->filenameToRenderInto = strdup(filename); +} + void Window::runAsModal(bool blockWait) { pData->runAsModal(blockWait); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index deaa5595..e869fa67 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -95,6 +95,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -120,6 +121,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -149,6 +151,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -180,6 +183,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, minHeight(0), keepAspectRatio(false), ignoreIdleCallbacks(false), + filenameToRenderInto(nullptr), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -195,6 +199,7 @@ Window::PrivateData::~PrivateData() { appData->idleCallbacks.remove(this); appData->windows.remove(self); + std::free(filenameToRenderInto); if (view == nullptr) return; @@ -744,6 +749,14 @@ void Window::PrivateData::onPuglExpose() widget->pData->display(); } #endif + + if (char* const filename = filenameToRenderInto) + { + const PuglRect rect = puglGetFrame(view); + filenameToRenderInto = nullptr; + renderToPicture(filename, getGraphicsContext(), static_cast(rect.width), static_cast(rect.height)); + std::free(filename); + } } void Window::PrivateData::onPuglClose() diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 6687afc5..5f9f8982 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -77,6 +77,9 @@ struct Window::PrivateData : IdleCallback { /** Whether to ignore idle callback requests, useful for temporary windows. */ bool ignoreIdleCallbacks; + /** Render to a picture file when non-null, automatically free+unset after saving. */ + char* filenameToRenderInto; + #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ const char* win32SelectedFile; @@ -162,6 +165,8 @@ struct Window::PrivateData : IdleCallback { # endif #endif + static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); + // modal handling void startModal(); void stopModal();