Initial experimental work for screenshots
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
39540b7dc8
commit
fff4d6f61d
5 changed files with 132 additions and 10 deletions
|
@ -37,6 +37,10 @@ namespace ui {
|
||||||
struct Menu;
|
struct Menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace window {
|
||||||
|
void generateScreenshot();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rack
|
} // namespace rack
|
||||||
|
|
||||||
namespace patchUtils {
|
namespace patchUtils {
|
||||||
|
|
|
@ -49,10 +49,10 @@
|
||||||
|
|
||||||
#ifndef HEADLESS
|
#ifndef HEADLESS
|
||||||
# include "WindowParameters.hpp"
|
# include "WindowParameters.hpp"
|
||||||
static const constexpr uint kCardinalStateCount = 3; // patch, text, windowSize
|
static const constexpr uint kCardinalStateCount = 4; // patch, screenshot, comment, windowSize
|
||||||
#else
|
#else
|
||||||
# define kWindowParameterCount 0
|
# define kWindowParameterCount 0
|
||||||
static const constexpr uint kCardinalStateCount = 2; // patch, text
|
static const constexpr uint kCardinalStateCount = 3; // patch, screenshot, comment
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace rack {
|
namespace rack {
|
||||||
|
@ -698,10 +698,13 @@ protected:
|
||||||
stateKey = "patch";
|
stateKey = "patch";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
stateKey = "text";
|
stateKey = "screenshot";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
stateKey = "comment";
|
||||||
break;
|
break;
|
||||||
#ifndef HEADLESS
|
#ifndef HEADLESS
|
||||||
case 2:
|
case 3:
|
||||||
stateKey = "windowSize";
|
stateKey = "windowSize";
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -712,7 +715,8 @@ protected:
|
||||||
{
|
{
|
||||||
switch (index)
|
switch (index)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1: // screenshot
|
||||||
|
case 2: // comment
|
||||||
return kStateIsHostVisible;
|
return kStateIsHostVisible;
|
||||||
default:
|
default:
|
||||||
return 0x0;
|
return 0x0;
|
||||||
|
|
|
@ -479,6 +479,8 @@ struct ViewButton : MenuButton {
|
||||||
|
|
||||||
menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
|
menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules));
|
||||||
|
|
||||||
|
menu->addChild(new ui::MenuSeparator);
|
||||||
|
|
||||||
static const std::vector<std::string> rateLimitLabels = {
|
static const std::vector<std::string> rateLimitLabels = {
|
||||||
"None",
|
"None",
|
||||||
"2x",
|
"2x",
|
||||||
|
|
|
@ -185,11 +185,20 @@ math::Vec Scene::getMousePos() {
|
||||||
|
|
||||||
|
|
||||||
void Scene::step() {
|
void Scene::step() {
|
||||||
|
if (APP->window->isFullScreen()) {
|
||||||
|
// Expand RackScrollWidget to cover entire screen if fullscreen
|
||||||
|
rackScroll->box.pos.y = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Always show MenuBar if not fullscreen
|
||||||
|
menuBar->show();
|
||||||
|
rackScroll->box.pos.y = menuBar->box.size.y;
|
||||||
|
}
|
||||||
|
|
||||||
internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
|
internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
|
||||||
|
|
||||||
// Resize owned descendants
|
// Resize owned descendants
|
||||||
menuBar->box.size.x = box.size.x;
|
menuBar->box.size.x = box.size.x;
|
||||||
rackScroll->box.pos.y = menuBar->box.size.y;
|
|
||||||
rackScroll->box.size = box.size.minus(rackScroll->box.pos);
|
rackScroll->box.size = box.size.minus(rackScroll->box.pos);
|
||||||
|
|
||||||
// Scroll RackScrollWidget with arrow keys
|
// Scroll RackScrollWidget with arrow keys
|
||||||
|
@ -334,6 +343,10 @@ void Scene::onHoverKey(const HoverKeyEvent& e) {
|
||||||
patchUtils::deployToRemote();
|
patchUtils::deployToRemote();
|
||||||
e.consume(this);
|
e.consume(this);
|
||||||
}
|
}
|
||||||
|
if (e.key == GLFW_KEY_F9 && (e.mods & RACK_MOD_MASK) == 0) {
|
||||||
|
window::generateScreenshot();
|
||||||
|
e.consume(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Module selections
|
// Module selections
|
||||||
if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
|
if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
|
||||||
|
|
|
@ -36,15 +36,25 @@
|
||||||
#include <context.hpp>
|
#include <context.hpp>
|
||||||
#include <patch.hpp>
|
#include <patch.hpp>
|
||||||
#include <settings.hpp>
|
#include <settings.hpp>
|
||||||
#include <plugin.hpp> // used in Window::screenshot
|
#include <system.hpp>
|
||||||
#include <system.hpp> // used in Window::screenshot
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
# undef DEBUG
|
# undef DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// comment out if wanting to generate a local screenshot.png
|
||||||
|
#define STBI_WRITE_NO_STDIO
|
||||||
|
|
||||||
|
// uncomment to generate screenshots without the rack rail background (ie, transparent)
|
||||||
|
// #define CARDINAL_TRANSPARENT_SCREENSHOTS
|
||||||
|
|
||||||
|
// used in Window::screenshot
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "../src/Rack/dep/glfw/deps/stb_image_write.h"
|
||||||
|
|
||||||
#include "DistrhoUI.hpp"
|
#include "DistrhoUI.hpp"
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
|
#include "extra/String.hpp"
|
||||||
#include "../CardinalCommon.hpp"
|
#include "../CardinalCommon.hpp"
|
||||||
#include "../WindowParameters.hpp"
|
#include "../WindowParameters.hpp"
|
||||||
|
|
||||||
|
@ -119,6 +129,15 @@ std::shared_ptr<Image> Image::load(const std::string& filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum ScreenshotStep {
|
||||||
|
kScreenshotStepNone,
|
||||||
|
kScreenshotStepStarted,
|
||||||
|
kScreenshotStepFirstPass,
|
||||||
|
kScreenshotStepSecondPass,
|
||||||
|
kScreenshotStepSaving
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Window::Internal {
|
struct Window::Internal {
|
||||||
std::string lastWindowTitle;
|
std::string lastWindowTitle;
|
||||||
|
|
||||||
|
@ -139,6 +158,7 @@ struct Window::Internal {
|
||||||
|
|
||||||
int frame = 0;
|
int frame = 0;
|
||||||
int frameSwapInterval = 1;
|
int frameSwapInterval = 1;
|
||||||
|
int generateScreenshotStep = kScreenshotStepNone;
|
||||||
double monitorRefreshRate = 60.0;
|
double monitorRefreshRate = 60.0;
|
||||||
double frameTime = 0.0;
|
double frameTime = 0.0;
|
||||||
double lastFrameDuration = 0.0;
|
double lastFrameDuration = 0.0;
|
||||||
|
@ -342,6 +362,26 @@ void Window::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Window__flipBitmap(uint8_t* pixels, int width, int height, int depth) {
|
||||||
|
for (int y = 0; y < height / 2; y++) {
|
||||||
|
const int flipY = height - y - 1;
|
||||||
|
uint8_t tmp[width * depth];
|
||||||
|
std::memcpy(tmp, &pixels[y * width * depth], width * depth);
|
||||||
|
std::memcpy(&pixels[y * width * depth], &pixels[flipY * width * depth], width * depth);
|
||||||
|
std::memcpy(&pixels[flipY * width * depth], tmp, width * depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Window__writeImagePNG(void* context, void* data, int size) {
|
||||||
|
USE_NAMESPACE_DISTRHO
|
||||||
|
UI* const ui = static_cast<UI*>(context);
|
||||||
|
String encodedPNG("data:image/png;base64,");
|
||||||
|
encodedPNG += String::asBase64(data, size);
|
||||||
|
ui->setState("screenshot", encodedPNG.buffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Window::step() {
|
void Window::step() {
|
||||||
DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,);
|
DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,);
|
||||||
|
|
||||||
|
@ -378,6 +418,16 @@ void Window::step() {
|
||||||
APP->event->handleDirty();
|
APP->event->handleDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide menu and background if generating screenshot
|
||||||
|
if (internal->generateScreenshotStep == kScreenshotStepStarted) {
|
||||||
|
#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
|
||||||
|
APP->scene->menuBar->hide();
|
||||||
|
APP->scene->rack->children.front()->hide();
|
||||||
|
#else
|
||||||
|
internal->generateScreenshotStep = kScreenshotStepSecondPass;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Get framebuffer/window ratio
|
// Get framebuffer/window ratio
|
||||||
int winWidth = internal->ui->getWidth();
|
int winWidth = internal->ui->getWidth();
|
||||||
int winHeight = internal->ui->getHeight();
|
int winHeight = internal->ui->getHeight();
|
||||||
|
@ -388,7 +438,7 @@ void Window::step() {
|
||||||
if (APP->scene) {
|
if (APP->scene) {
|
||||||
// DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth);
|
// DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth);
|
||||||
// Resize scene
|
// Resize scene
|
||||||
APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio);
|
APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(newPixelRatio);
|
||||||
|
|
||||||
// Step scene
|
// Step scene
|
||||||
APP->scene->step();
|
APP->scene->step();
|
||||||
|
@ -396,7 +446,7 @@ void Window::step() {
|
||||||
// Render scene
|
// Render scene
|
||||||
{
|
{
|
||||||
// Update and render
|
// Update and render
|
||||||
nvgScale(vg, pixelRatio, pixelRatio);
|
nvgScale(vg, newPixelRatio, newPixelRatio);
|
||||||
|
|
||||||
// Draw scene
|
// Draw scene
|
||||||
widget::Widget::DrawArgs args;
|
widget::Widget::DrawArgs args;
|
||||||
|
@ -405,12 +455,52 @@ void Window::step() {
|
||||||
APP->scene->draw(args);
|
APP->scene->draw(args);
|
||||||
|
|
||||||
glViewport(0, 0, fbWidth, fbHeight);
|
glViewport(0, 0, fbWidth, fbHeight);
|
||||||
|
#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
#else
|
||||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
#endif
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++internal->frame;
|
++internal->frame;
|
||||||
|
|
||||||
|
if (internal->generateScreenshotStep != kScreenshotStepNone) {
|
||||||
|
++internal->generateScreenshotStep;
|
||||||
|
|
||||||
|
int y = 0;
|
||||||
|
#ifndef CARDINAL_TRANSPARENT_SCREENSHOTS
|
||||||
|
y = APP->scene->menuBar->box.size.y * newPixelRatio;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Allocate pixel color buffer
|
||||||
|
uint8_t* const pixels = new uint8_t[winHeight * winWidth * 4];
|
||||||
|
|
||||||
|
// glReadPixels defaults to GL_BACK, but the back-buffer is unstable, so use the front buffer (what the user sees)
|
||||||
|
glReadBuffer(GL_FRONT);
|
||||||
|
glReadPixels(0, 0, winWidth, winHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||||
|
|
||||||
|
if (internal->generateScreenshotStep == kScreenshotStepSaving)
|
||||||
|
{
|
||||||
|
// Write pixels to PNG
|
||||||
|
const int stride = winWidth * 4;
|
||||||
|
const uint8_t* const pixelsWithOffset = pixels + (stride * y);
|
||||||
|
Window__flipBitmap(pixels, winWidth, winHeight, 4);
|
||||||
|
#ifdef STBI_WRITE_NO_STDIO
|
||||||
|
stbi_write_png_to_func(Window__writeImagePNG, internal->ui,
|
||||||
|
winWidth, winHeight - y, 4, pixelsWithOffset, stride);
|
||||||
|
#else
|
||||||
|
stbi_write_png("screenshot.png", winWidth, winHeight - y, 4, pixelsWithOffset, stride);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal->generateScreenshotStep = kScreenshotStepNone;
|
||||||
|
APP->scene->menuBar->show();
|
||||||
|
APP->scene->rack->children.front()->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] pixels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -456,7 +546,11 @@ void Window::setFullScreen(bool) {
|
||||||
|
|
||||||
|
|
||||||
bool Window::isFullScreen() {
|
bool Window::isFullScreen() {
|
||||||
|
#ifdef CARDINAL_TRANSPARENT_SCREENSHOTS
|
||||||
|
return internal->generateScreenshotStep != kScreenshotStepNone;
|
||||||
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -533,6 +627,11 @@ int& Window::fbCount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void generateScreenshot() {
|
||||||
|
APP->window->internal->generateScreenshotStep = kScreenshotStepStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue