@@ -382,6 +382,13 @@ public: | |||||
*/ | */ | ||||
void repaint(const Rectangle<uint>& rect) noexcept; | void repaint(const Rectangle<uint>& 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. | 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). | Only valid for windows that have been created with another window as parent (as passed in the constructor). | ||||
@@ -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 | const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | ||||
{ | { | ||||
GraphicsContext& context((GraphicsContext&)graphicsContext); | GraphicsContext& context((GraphicsContext&)graphicsContext); | ||||
@@ -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 | const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | ||||
{ | { | ||||
return (const GraphicsContext&)graphicsContext; | return (const GraphicsContext&)graphicsContext; | ||||
@@ -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 | const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | ||||
{ | { | ||||
return (const GraphicsContext&)graphicsContext; | return (const GraphicsContext&)graphicsContext; | ||||
@@ -336,6 +336,11 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept | |||||
puglPostRedisplayRect(pData->view, prect); | puglPostRedisplayRect(pData->view, prect); | ||||
} | } | ||||
void Window::renderToPicture(const char* const filename) | |||||
{ | |||||
pData->filenameToRenderInto = strdup(filename); | |||||
} | |||||
void Window::runAsModal(bool blockWait) | void Window::runAsModal(bool blockWait) | ||||
{ | { | ||||
pData->runAsModal(blockWait); | pData->runAsModal(blockWait); | ||||
@@ -95,6 +95,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) | |||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
filenameToRenderInto(nullptr), | |||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
win32SelectedFile(nullptr), | win32SelectedFile(nullptr), | ||||
#endif | #endif | ||||
@@ -120,6 +121,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
filenameToRenderInto(nullptr), | |||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
win32SelectedFile(nullptr), | win32SelectedFile(nullptr), | ||||
#endif | #endif | ||||
@@ -149,6 +151,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
filenameToRenderInto(nullptr), | |||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
win32SelectedFile(nullptr), | win32SelectedFile(nullptr), | ||||
#endif | #endif | ||||
@@ -180,6 +183,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||||
minHeight(0), | minHeight(0), | ||||
keepAspectRatio(false), | keepAspectRatio(false), | ||||
ignoreIdleCallbacks(false), | ignoreIdleCallbacks(false), | ||||
filenameToRenderInto(nullptr), | |||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
win32SelectedFile(nullptr), | win32SelectedFile(nullptr), | ||||
#endif | #endif | ||||
@@ -195,6 +199,7 @@ Window::PrivateData::~PrivateData() | |||||
{ | { | ||||
appData->idleCallbacks.remove(this); | appData->idleCallbacks.remove(this); | ||||
appData->windows.remove(self); | appData->windows.remove(self); | ||||
std::free(filenameToRenderInto); | |||||
if (view == nullptr) | if (view == nullptr) | ||||
return; | return; | ||||
@@ -744,6 +749,14 @@ void Window::PrivateData::onPuglExpose() | |||||
widget->pData->display(); | widget->pData->display(); | ||||
} | } | ||||
#endif | #endif | ||||
if (char* const filename = filenameToRenderInto) | |||||
{ | |||||
const PuglRect rect = puglGetFrame(view); | |||||
filenameToRenderInto = nullptr; | |||||
renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); | |||||
std::free(filename); | |||||
} | |||||
} | } | ||||
void Window::PrivateData::onPuglClose() | void Window::PrivateData::onPuglClose() | ||||
@@ -77,6 +77,9 @@ struct Window::PrivateData : IdleCallback { | |||||
/** Whether to ignore idle callback requests, useful for temporary windows. */ | /** Whether to ignore idle callback requests, useful for temporary windows. */ | ||||
bool ignoreIdleCallbacks; | bool ignoreIdleCallbacks; | ||||
/** Render to a picture file when non-null, automatically free+unset after saving. */ | |||||
char* filenameToRenderInto; | |||||
#ifdef DISTRHO_OS_WINDOWS | #ifdef DISTRHO_OS_WINDOWS | ||||
/** Selected file for openFileBrowser on windows, stored for fake async operation. */ | /** Selected file for openFileBrowser on windows, stored for fake async operation. */ | ||||
const char* win32SelectedFile; | const char* win32SelectedFile; | ||||
@@ -162,6 +165,8 @@ struct Window::PrivateData : IdleCallback { | |||||
# endif | # endif | ||||
#endif | #endif | ||||
static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height); | |||||
// modal handling | // modal handling | ||||
void startModal(); | void startModal(); | ||||
void stopModal(); | void stopModal(); | ||||