Browse Source

Take screenshot when patch is saved (Currently disabled).

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
3bdf4bf06c
4 changed files with 85 additions and 21 deletions
  1. +6
    -2
      include/window.hpp
  2. +5
    -1
      src/patch.cpp
  3. +73
    -17
      src/window.cpp
  4. +1
    -1
      standalone/main.cpp

+ 6
- 2
include/window.hpp View File

@@ -76,8 +76,12 @@ struct Window {
Window(); Window();
~Window(); ~Window();
void run(); void run();
/** Takes a screenshot of each module */
void screenshot(float zoom);
/** Takes a screenshot of the screen and saves it to a PNG file. */
void screenshot(const std::string& screenshotPath);
/** Saves a PNG image of all modules to `screenshotsDir/<plugin slug>/<module slug>.png`.
Skips screenshot if the file already exists.
*/
void screenshotModules(const std::string& screenshotsDir, float zoom = 1.f);
void close(); void close();
void cursorLock(); void cursorLock();
void cursorUnlock(); void cursorUnlock();


+ 5
- 1
src/patch.cpp View File

@@ -61,10 +61,14 @@ static bool promptClear(std::string text) {


void PatchManager::save(std::string path) { void PatchManager::save(std::string path) {
INFO("Saving patch %s", path.c_str()); INFO("Saving patch %s", path.c_str());
// TEMP
// Save patch.json
saveAutosave(); saveAutosave();
// Clean up autosave directory (e.g. removed modules)
cleanAutosave(); cleanAutosave();


// Take screenshot (disabled because there is currently no way to quickly view them on any OS or website.)
// APP->window->screenshot(system::join(asset::autosavePath, "screenshot.png"));

uint64_t startTime = system::getNanoseconds(); uint64_t startTime = system::getNanoseconds();
// Set compression level to 1 so that a 500MB/s SSD is almost bottlenecked // Set compression level to 1 so that a 500MB/s SSD is almost bottlenecked
system::archiveFolder(path, asset::autosavePath, 1); system::archiveFolder(path, asset::autosavePath, 1);


+ 73
- 17
src/window.cpp View File

@@ -37,14 +37,17 @@ void Font::loadFile(const std::string& filename, NVGcontext* vg) {
} }
} }



Font::~Font() { Font::~Font() {
// There is no NanoVG deleteFont() function yet, so do nothing // There is no NanoVG deleteFont() function yet, so do nothing
} }



std::shared_ptr<Font> Font::load(const std::string& filename) { std::shared_ptr<Font> Font::load(const std::string& filename) {
return APP->window->loadFont(filename); return APP->window->loadFont(filename);
} }



void Image::loadFile(const std::string& filename, NVGcontext* vg) { void Image::loadFile(const std::string& filename, NVGcontext* vg) {
this->vg = vg; this->vg = vg;
handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
@@ -56,16 +59,19 @@ void Image::loadFile(const std::string& filename, NVGcontext* vg) {
} }
} }



Image::~Image() { Image::~Image() {
// TODO What if handle is invalid? // TODO What if handle is invalid?
if (handle >= 0) if (handle >= 0)
nvgDeleteImage(vg, handle); nvgDeleteImage(vg, handle);
} }



std::shared_ptr<Image> Image::load(const std::string& filename) { std::shared_ptr<Image> Image::load(const std::string& filename) {
return APP->window->loadImage(filename); return APP->window->loadImage(filename);
} }



void Svg::loadFile(const std::string& filename) { void Svg::loadFile(const std::string& filename) {
handle = nsvgParseFromFile(filename.c_str(), "px", app::SVG_DPI); handle = nsvgParseFromFile(filename.c_str(), "px", app::SVG_DPI);
if (handle) { if (handle) {
@@ -76,11 +82,13 @@ void Svg::loadFile(const std::string& filename) {
} }
} }



Svg::~Svg() { Svg::~Svg() {
if (handle) if (handle)
nsvgDelete(handle); nsvgDelete(handle);
} }



std::shared_ptr<Svg> Svg::load(const std::string& filename) { std::shared_ptr<Svg> Svg::load(const std::string& filename) {
return APP->window->loadSvg(filename); return APP->window->loadSvg(filename);
} }
@@ -109,6 +117,7 @@ static void windowSizeCallback(GLFWwindow* win, int width, int height) {
// Do nothing. Window size is reset each frame anyway. // Do nothing. Window size is reset each frame anyway.
} }



static void mouseButtonCallback(GLFWwindow* win, int button, int action, int mods) { static void mouseButtonCallback(GLFWwindow* win, int button, int action, int mods) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
#if defined ARCH_MAC #if defined ARCH_MAC
@@ -127,6 +136,7 @@ static void mouseButtonCallback(GLFWwindow* win, int button, int action, int mod
APP->event->handleButton(window->internal->lastMousePos, button, action, mods); APP->event->handleButton(window->internal->lastMousePos, button, action, mods);
} }



static void cursorPosCallback(GLFWwindow* win, double xpos, double ypos) { static void cursorPosCallback(GLFWwindow* win, double xpos, double ypos) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
math::Vec mousePos = math::Vec(xpos, ypos).div(window->pixelRatio / window->windowRatio).round(); math::Vec mousePos = math::Vec(xpos, ypos).div(window->pixelRatio / window->windowRatio).round();
@@ -165,12 +175,14 @@ static void cursorPosCallback(GLFWwindow* win, double xpos, double ypos) {
keyboard::mouseMove(scaledPos); keyboard::mouseMove(scaledPos);
} }



static void cursorEnterCallback(GLFWwindow* win, int entered) { static void cursorEnterCallback(GLFWwindow* win, int entered) {
if (!entered) { if (!entered) {
APP->event->handleLeave(); APP->event->handleLeave();
} }
} }



static void scrollCallback(GLFWwindow* win, double x, double y) { static void scrollCallback(GLFWwindow* win, double x, double y) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
math::Vec scrollDelta = math::Vec(x, y); math::Vec scrollDelta = math::Vec(x, y);
@@ -183,12 +195,14 @@ static void scrollCallback(GLFWwindow* win, double x, double y) {
APP->event->handleScroll(window->internal->lastMousePos, scrollDelta); APP->event->handleScroll(window->internal->lastMousePos, scrollDelta);
} }



static void charCallback(GLFWwindow* win, unsigned int codepoint) { static void charCallback(GLFWwindow* win, unsigned int codepoint) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
if (APP->event->handleText(window->internal->lastMousePos, codepoint)) if (APP->event->handleText(window->internal->lastMousePos, codepoint))
return; return;
} }



static void keyCallback(GLFWwindow* win, int key, int scancode, int action, int mods) { static void keyCallback(GLFWwindow* win, int key, int scancode, int action, int mods) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
if (APP->event->handleKey(window->internal->lastMousePos, key, scancode, action, mods)) if (APP->event->handleKey(window->internal->lastMousePos, key, scancode, action, mods))
@@ -203,6 +217,7 @@ static void keyCallback(GLFWwindow* win, int key, int scancode, int action, int
} }
} }



static void dropCallback(GLFWwindow* win, int count, const char** paths) { static void dropCallback(GLFWwindow* win, int count, const char** paths) {
Window* window = (Window*) glfwGetWindowUserPointer(win); Window* window = (Window*) glfwGetWindowUserPointer(win);
std::vector<std::string> pathsVec; std::vector<std::string> pathsVec;
@@ -212,10 +227,12 @@ static void dropCallback(GLFWwindow* win, int count, const char** paths) {
APP->event->handleDrop(window->internal->lastMousePos, pathsVec); APP->event->handleDrop(window->internal->lastMousePos, pathsVec);
} }



static void errorCallback(int error, const char* description) { static void errorCallback(int error, const char* description) {
WARN("GLFW error %d: %s", error, description); WARN("GLFW error %d: %s", error, description);
} }



Window::Window() { Window::Window() {
internal = new Internal; internal = new Internal;
int err; int err;
@@ -312,6 +329,7 @@ Window::Window() {
bndSetFont(uiFont->handle); bndSetFont(uiFont->handle);
} }



Window::~Window() { Window::~Window() {
if (glfwGetWindowAttrib(win, GLFW_MAXIMIZED)) { if (glfwGetWindowAttrib(win, GLFW_MAXIMIZED)) {
settings::windowSize = math::Vec(); settings::windowSize = math::Vec();
@@ -338,6 +356,7 @@ Window::~Window() {
delete internal; delete internal;
} }



void Window::run() { void Window::run() {
internal->frame = 0; internal->frame = 0;
while (!glfwWindowShouldClose(win)) { while (!glfwWindowShouldClose(win)) {
@@ -429,10 +448,41 @@ void Window::run() {
} }
} }


void Window::screenshot(float zoom) {

static void flipBitmap(uint8_t* pixels, int width, int height, int depth) {
for (int y = 0; y < height / 2; y++) {
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);
}
}


void Window::screenshot(const std::string& screenshotPath) {
// Get window framebuffer size
int width, height;
glfwGetFramebufferSize(APP->window->win, &width, &height);

// Allocate pixel color buffer
uint8_t* pixels = new uint8_t[height * width * 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, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

// Write pixels to PNG
flipBitmap(pixels, width, height, 4);
stbi_write_png(screenshotPath.c_str(), width, height, 4, pixels, width * 4);

delete[] pixels;
}


void Window::screenshotModules(const std::string& screenshotsDir, float zoom) {
// Iterate plugins and create directories // Iterate plugins and create directories
std::string screenshotsDir = asset::user("screenshots");
system::createDirectory(screenshotsDir);
system::createDirectories(screenshotsDir);
for (plugin::Plugin* p : plugin::plugins) { for (plugin::Plugin* p : plugin::plugins) {
std::string dir = system::join(screenshotsDir, p->slug); std::string dir = system::join(screenshotsDir, p->slug);
system::createDirectory(dir); system::createDirectory(dir);
@@ -461,33 +511,27 @@ void Window::screenshot(float zoom) {
// Read pixels // Read pixels
int width, height; int width, height;
nvgImageSize(vg, fbw->getImageHandle(), &width, &height); nvgImageSize(vg, fbw->getImageHandle(), &width, &height);
uint8_t* data = new uint8_t[height * width * 4];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);

// Flip image vertically
for (int y = 0; y < height / 2; y++) {
int flipY = height - y - 1;
uint8_t tmp[width * 4];
std::memcpy(tmp, &data[y * width * 4], width * 4);
std::memcpy(&data[y * width * 4], &data[flipY * width * 4], width * 4);
std::memcpy(&data[flipY * width * 4], tmp, width * 4);
}
uint8_t* pixels = new uint8_t[height * width * 4];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);


// Write pixels to PNG // Write pixels to PNG
stbi_write_png(filename.c_str(), width, height, 4, data, width * 4);
flipBitmap(pixels, width, height, 4);
stbi_write_png(filename.c_str(), width, height, 4, pixels, width * 4);


// Cleanup // Cleanup
delete[] data;
delete[] pixels;
nvgluBindFramebuffer(NULL); nvgluBindFramebuffer(NULL);
delete fbw; delete fbw;
} }
} }
} }



void Window::close() { void Window::close() {
glfwSetWindowShouldClose(win, GLFW_TRUE); glfwSetWindowShouldClose(win, GLFW_TRUE);
} }



void Window::cursorLock() { void Window::cursorLock() {
if (!settings::allowCursorLock) if (!settings::allowCursorLock)
return; return;
@@ -500,6 +544,7 @@ void Window::cursorLock() {
internal->ignoreNextMouseDelta = true; internal->ignoreNextMouseDelta = true;
} }



void Window::cursorUnlock() { void Window::cursorUnlock() {
if (!settings::allowCursorLock) if (!settings::allowCursorLock)
return; return;
@@ -508,10 +553,12 @@ void Window::cursorUnlock() {
internal->ignoreNextMouseDelta = true; internal->ignoreNextMouseDelta = true;
} }



bool Window::isCursorLocked() { bool Window::isCursorLocked() {
return glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL; return glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL;
} }



int Window::getMods() { int Window::getMods() {
int mods = 0; int mods = 0;
if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
@@ -525,6 +572,7 @@ int Window::getMods() {
return mods; return mods;
} }



void Window::setFullScreen(bool fullScreen) { void Window::setFullScreen(bool fullScreen) {
if (!fullScreen) { if (!fullScreen) {
glfwSetWindowMonitor(win, NULL, internal->lastWindowX, internal->lastWindowY, internal->lastWindowWidth, internal->lastWindowHeight, GLFW_DONT_CARE); glfwSetWindowMonitor(win, NULL, internal->lastWindowX, internal->lastWindowY, internal->lastWindowWidth, internal->lastWindowHeight, GLFW_DONT_CARE);
@@ -538,29 +586,35 @@ void Window::setFullScreen(bool fullScreen) {
} }
} }



bool Window::isFullScreen() { bool Window::isFullScreen() {
GLFWmonitor* monitor = glfwGetWindowMonitor(win); GLFWmonitor* monitor = glfwGetWindowMonitor(win);
return monitor != NULL; return monitor != NULL;
} }



double Window::getMonitorRefreshRate() { double Window::getMonitorRefreshRate() {
return internal->monitorRefreshRate; return internal->monitorRefreshRate;
} }



double Window::getLastFrameTime() { double Window::getLastFrameTime() {
return internal->lastFrameTime; return internal->lastFrameTime;
} }



double Window::getLastFrameDuration() { double Window::getLastFrameDuration() {
return internal->lastFrameDuration; return internal->lastFrameDuration;
} }



double Window::getFrameTimeOverdue() { double Window::getFrameTimeOverdue() {
double desiredFrameDuration = internal->frameSwapInterval / internal->monitorRefreshRate; double desiredFrameDuration = internal->frameSwapInterval / internal->monitorRefreshRate;
double frameDuration = glfwGetTime() - internal->lastFrameTime; double frameDuration = glfwGetTime() - internal->lastFrameTime;
return frameDuration - desiredFrameDuration; return frameDuration - desiredFrameDuration;
} }



std::shared_ptr<Font> Window::loadFont(const std::string& filename) { std::shared_ptr<Font> Window::loadFont(const std::string& filename) {
auto sp = fontCache[filename].lock(); auto sp = fontCache[filename].lock();
if (!sp) { if (!sp) {
@@ -570,6 +624,7 @@ std::shared_ptr<Font> Window::loadFont(const std::string& filename) {
return sp; return sp;
} }



std::shared_ptr<Image> Window::loadImage(const std::string& filename) { std::shared_ptr<Image> Window::loadImage(const std::string& filename) {
auto sp = imageCache[filename].lock(); auto sp = imageCache[filename].lock();
if (!sp) { if (!sp) {
@@ -579,6 +634,7 @@ std::shared_ptr<Image> Window::loadImage(const std::string& filename) {
return sp; return sp;
} }



std::shared_ptr<Svg> Window::loadSvg(const std::string& filename) { std::shared_ptr<Svg> Window::loadSvg(const std::string& filename) {
auto sp = svgCache[filename].lock(); auto sp = svgCache[filename].lock();
if (!sp) { if (!sp) {
@@ -606,10 +662,10 @@ void windowInit() {
} }
} }



void windowDestroy() { void windowDestroy() {
glfwTerminate(); glfwTerminate();
} }





} // namespace rack } // namespace rack

+ 1
- 1
standalone/main.cpp View File

@@ -218,7 +218,7 @@ int main(int argc, char* argv[]) {
} }
else if (screenshot) { else if (screenshot) {
INFO("Taking screenshots of all modules at %gx zoom", screenshotZoom); INFO("Taking screenshots of all modules at %gx zoom", screenshotZoom);
APP->window->screenshot(screenshotZoom);
APP->window->screenshotModules(asset::user("screenshots"), screenshotZoom);
} }
else { else {
INFO("Running window"); INFO("Running window");


Loading…
Cancel
Save