Browse Source

Clean up README. Move SVGPanel to source file. Fix alignment bugs in FramebufferWidget.

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
223341e2f1
17 changed files with 227 additions and 279 deletions
  1. +3
    -92
      README.md
  2. +3
    -28
      include/app/SVGPanel.hpp
  3. +5
    -5
      include/app/common.hpp
  4. +7
    -0
      include/math.hpp
  5. +11
    -7
      include/widgets/FramebufferWidget.hpp
  6. +5
    -1
      include/window.hpp
  7. +6
    -6
      src/app/ModuleWidget.cpp
  8. +39
    -0
      src/app/SVGPanel.cpp
  9. +9
    -9
      src/asset.cpp
  10. +10
    -10
      src/bridge.cpp
  11. +18
    -0
      src/dep.cpp
  12. +8
    -8
      src/plugin.cpp
  13. +5
    -5
      src/system.cpp
  14. +6
    -1
      src/ui/ScrollWidget.cpp
  15. +70
    -64
      src/widgets/FramebufferWidget.cpp
  16. +5
    -8
      src/widgets/Widget.cpp
  17. +17
    -35
      src/window.cpp

+ 3
- 92
README.md View File

@@ -1,94 +1,5 @@
# VCV Rack
# Rack

*Rack* is the main application for the VCV open-source virtual modular synthesizer.
*Rack* is the engine for the VCV open-source virtual modular synthesizer.

This README includes instructions for building Rack from source. For information about the software, go to https://vcvrack.com/.

## The [Issue Tracker](https://github.com/VCVRack/Rack/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) is the official developer's forum

Bug reports, feature requests, questions, and discussions are welcome on the GitHub Issue Tracker for all repos under the VCVRack organization.
However, please search before posting to avoid duplicates, and limit to one issue per post.

Please vote on feature requests by using the Thumbs Up/Down reaction on the first post.

I rarely accept code contributions to Rack itself, so please notify me in advance if you wish to send a pull request.

## Setting up your development environment

Before building Rack, you must install build dependencies provided by your system's package manager.
Rack's own dependencies (GLEW, glfw, etc) do not need to be installed on your system, since specific versions are compiled locally during the build process.
However, you need proper tools to build Rack and these dependencies.

### Mac

Install [Xcode](https://developer.apple.com/xcode/).
Using [Homebrew](https://brew.sh/), install the build dependencies.
```
brew install git wget cmake autoconf automake libtool
```

### Windows

Install [MSYS2](http://www.msys2.org/) and launch the MinGW 64-bit shell (not the default MSYS shell).
```
pacman -S git wget make tar unzip zip mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake autoconf automake mingw-w64-x86_64-libtool
```

### Linux

On Ubuntu 16.04:
```
sudo apt install git curl cmake libx11-dev libglu1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev zlib1g-dev libasound2-dev libgtk2.0-dev libjack-jackd2-dev
```

On Arch Linux:
```
pacman -S git wget gcc make cmake tar unzip zip curl
```

## Building

*If the build fails for you, please report the issue with a detailed error message to help the portability of Rack.*

Clone this repository with `git clone https://github.com/VCVRack/Rack.git` and `cd Rack`.
Make sure there are no spaces in your absolute path, as this breaks many build systems.

Clone submodules.

git submodule update --init --recursive

Build dependencies locally.
You may add `-j$(nproc)` to your make commands to parallelize builds across all CPU cores.

make dep

Build Rack.

make

Run Rack.

make run

## Building plugins

Be sure to check out and build the version of Rack you wish to build your plugins against.

You must clone the plugin in Rack's `plugins/` directory, e.g.

cd plugins
git clone https://github.com/VCVRack/Fundamental.git

Clone submodules.

cd Fundamental
git submodule update --init --recursive

Build plugin.

make dep
make

## Licenses

See [LICENSE.md](LICENSE.md) for a description of all licenses for VCV Rack.
For information about the software, go to the [VCV website](https://vcvrack.com/) or the [VCV Rack manual](https://vcvrack.com/manual/).

+ 3
- 28
include/app/SVGPanel.hpp View File

@@ -10,38 +10,13 @@ namespace rack {


struct PanelBorder : TransparentWidget {
void draw(NVGcontext *vg) override {
NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
nvgBeginPath(vg);
nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
nvgStrokeColor(vg, borderColor);
nvgStrokeWidth(vg, 1.0);
nvgStroke(vg);
}
void draw(NVGcontext *vg) override;
};


struct SVGPanel : FramebufferWidget {
void step() override {
if (math::isNear(app()->window->pixelRatio, 1.0)) {
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
oversample = 2.0;
}
FramebufferWidget::step();
}

void setBackground(std::shared_ptr<SVG> svg) {
SVGWidget *sw = new SVGWidget;
sw->setSVG(svg);
addChild(sw);

// Set size
box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);

PanelBorder *pb = new PanelBorder;
pb->box.size = box.size;
addChild(pb);
}
void step() override;
void setBackground(std::shared_ptr<SVG> svg);
};




+ 5
- 5
include/app/common.hpp View File

@@ -11,26 +11,26 @@ extern const std::string APP_NAME;
extern const std::string APP_VERSION;
extern const std::string API_HOST;

static const float SVG_DPI = 75.0;
static const float APP_SVG_DPI = 75.0;
static const float MM_PER_IN = 25.4;


/** Converts inch measurements to pixels */
inline float in2px(float in) {
return in * SVG_DPI;
return in * APP_SVG_DPI;
}

inline math::Vec in2px(math::Vec in) {
return in.mult(SVG_DPI);
return in.mult(APP_SVG_DPI);
}

/** Converts millimeter measurements to pixels */
inline float mm2px(float mm) {
return mm * (SVG_DPI / MM_PER_IN);
return mm * (APP_SVG_DPI / MM_PER_IN);
}

inline math::Vec mm2px(math::Vec mm) {
return mm.mult(SVG_DPI / MM_PER_IN);
return mm.mult(APP_SVG_DPI / MM_PER_IN);
}




+ 7
- 0
include/math.hpp View File

@@ -346,5 +346,12 @@ inline Vec Vec::clampSafe(Rect bound) const {
}


/** Useful for debugging Vecs and Rects, e.g.
printf("%f %f %f %f", RECT_ARGS(r));
*/
#define VEC_ARGS(v) (v).x, (v).y
#define RECT_ARGS(r) (r).pos.x, (r).pos.y, (r).size.x, (r).size.y


} // namespace math
} // namespace rack

+ 11
- 7
include/widgets/FramebufferWidget.hpp View File

@@ -12,19 +12,23 @@ Events are not passed to the underlying scene.
struct FramebufferWidget : Widget {
/** Set this to true to re-render the children to the framebuffer the next time it is drawn */
bool dirty = true;
/** A margin in pixels around the children in the framebuffer
This prevents cutting the rendered SVG off on the box edges.
*/
float oversample;
/** The root object in the framebuffer scene
The FramebufferWidget owns the pointer
NVGLUframebuffer *fb = NULL;
/** Pixel dimensions of the allocated framebuffer */
math::Vec fbSize;
/** Bounding box in world coordinates of where the framebuffer should be painted
Always has integer coordinates so that blitting framebuffers is pixel-perfect.
*/
struct Internal;
Internal *internal;
math::Rect fbBox;
/** Local scale relative to the world scale */
math::Vec fbScale;
/** Subpixel offset of fbBox in world coordinates */
math::Vec fbOffset;

FramebufferWidget();
~FramebufferWidget();
void draw(NVGcontext *vg) override;
virtual void drawFramebuffer(NVGcontext *vg);
int getImageHandle();

void onZoom(const event::Zoom &e) override {


+ 5
- 1
include/window.hpp View File

@@ -7,6 +7,9 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <nanovg.h>
#define NANOVG_GL2
#include <nanovg_gl.h>
#include <nanovg_gl_utils.h>
#include <nanosvg.h>


@@ -60,7 +63,8 @@ struct SVG {
struct Window {
GLFWwindow *win = NULL;
NVGcontext *vg = NULL;
NVGcontext *framebufferVg = NULL;
/** Secondary nanovg context for drawing to framebuffers */
NVGcontext *fbVg = NULL;
/** The scaling ratio */
float pixelRatio = 1.f;
/* The ratio between the framebuffer size and the window size reported by the OS.


+ 6
- 6
src/app/ModuleWidget.cpp View File

@@ -181,12 +181,12 @@ void ModuleWidget::onHover(const event::Hover &e) {
OpaqueWidget::onHover(e);

// Instead of checking key-down events, delete the module even if key-repeat hasn't fired yet and the cursor is hovering over the widget.
if (glfwGetKey(app()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(app()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
if ((app()->window->getMods() & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) {
removeAction();
e.consume(NULL);
return;
}
if ((glfwGetKey(app()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS
|| glfwGetKey(app()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS)
&& (app()->window->getMods() & WINDOW_MOD_MASK) == 0) {
removeAction();
e.consume(NULL);
return;
}
}



+ 39
- 0
src/app/SVGPanel.cpp View File

@@ -0,0 +1,39 @@
#include "app/SVGPanel.hpp"


namespace rack {


void PanelBorder::draw(NVGcontext *vg) {
NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
nvgBeginPath(vg);
nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
nvgStrokeColor(vg, borderColor);
nvgStrokeWidth(vg, 1.0);
nvgStroke(vg);
}


void SVGPanel::step() {
if (math::isNear(app()->window->pixelRatio, 1.0)) {
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
oversample = 2.0;
}
FramebufferWidget::step();
}

void SVGPanel::setBackground(std::shared_ptr<SVG> svg) {
SVGWidget *sw = new SVGWidget;
sw->setSVG(svg);
addChild(sw);

// Set size
box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);

PanelBorder *pb = new PanelBorder;
pb->box.size = box.size;
addChild(pb);
}


} // namespace rack

+ 9
- 9
src/asset.cpp View File

@@ -2,18 +2,18 @@
#include "system.hpp"
#include "plugin/Plugin.hpp"

#if ARCH_MAC
#if defined ARCH_MAC
#include <CoreFoundation/CoreFoundation.h>
#include <pwd.h>
#endif

#if ARCH_WIN
#if defined ARCH_WIN
#include <Windows.h>
#include <Shlobj.h>
#include <Shlwapi.h>
#endif

#if ARCH_LIN
#if defined ARCH_LIN
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
@@ -31,7 +31,7 @@ void init(bool devMode) {
systemDir = ".";
}
else {
#if ARCH_MAC
#if defined ARCH_MAC
CFBundleRef bundle = CFBundleGetMainBundle();
assert(bundle);
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(bundle);
@@ -41,14 +41,14 @@ void init(bool devMode) {
CFRelease(resourcesUrl);
systemDir = resourcesBuf;
#endif
#if ARCH_WIN
#if defined ARCH_WIN
char moduleBuf[MAX_PATH];
DWORD length = GetModuleFileName(NULL, moduleBuf, sizeof(moduleBuf));
assert(length > 0);
PathRemoveFileSpec(moduleBuf);
systemDir = moduleBuf;
#endif
#if ARCH_LIN
#if defined ARCH_LIN
// TODO For now, users should launch Rack from their terminal in the system directory
systemDir = ".";
#endif
@@ -61,7 +61,7 @@ void init(bool devMode) {
userDir = ".";
}
else {
#if ARCH_WIN
#if defined ARCH_WIN
// Get "My Documents" folder
char documentsBuf[MAX_PATH];
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBuf);
@@ -69,14 +69,14 @@ void init(bool devMode) {
userDir = documentsBuf;
userDir += "/Rack";
#endif
#if ARCH_MAC
#if defined ARCH_MAC
// Get home directory
struct passwd *pw = getpwuid(getuid());
assert(pw);
userDir = pw->pw_dir;
userDir += "/Documents/Rack";
#endif
#if ARCH_LIN
#if defined ARCH_LIN
// Get home directory
const char *homeBuf = getenv("HOME");
if (!homeBuf) {


+ 10
- 10
src/bridge.cpp View File

@@ -4,7 +4,7 @@
#include "dsp/ringbuffer.hpp"

#include <unistd.h>
#if ARCH_WIN
#if defined ARCH_WIN
#include <winsock2.h>
#else
#include <sys/socket.h>
@@ -87,7 +87,7 @@ struct BridgeClientConnection {
if (length <= 0)
return false;

#if ARCH_LIN
#if defined ARCH_LIN
int flags = MSG_NOSIGNAL;
#else
int flags = 0;
@@ -114,7 +114,7 @@ struct BridgeClientConnection {
if (length <= 0)
return false;

#if ARCH_LIN
#if defined ARCH_LIN
int flags = MSG_NOSIGNAL;
#else
int flags = 0;
@@ -282,7 +282,7 @@ struct BridgeClientConnection {

static void clientRun(int client) {
DEFER({
#if ARCH_WIN
#if defined ARCH_WIN
if (shutdown(client, SD_SEND)) {
WARN("Bridge client shutdown() failed");
}
@@ -296,7 +296,7 @@ static void clientRun(int client) {
#endif
});

#if ARCH_MAC
#if defined ARCH_MAC
// Avoid SIGPIPE
int flag = 1;
if (setsockopt(client, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(int))) {
@@ -306,7 +306,7 @@ static void clientRun(int client) {
#endif

// Disable non-blocking
#if ARCH_WIN
#if defined ARCH_WIN
unsigned long blockingMode = 0;
if (ioctlsocket(client, FIONBIO, &blockingMode)) {
WARN("Bridge client ioctlsocket() failed");
@@ -327,7 +327,7 @@ static void clientRun(int client) {

static void serverConnect() {
// Initialize sockets
#if ARCH_WIN
#if defined ARCH_WIN
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
WARN("Bridge server WSAStartup() failed");
@@ -343,7 +343,7 @@ static void serverConnect() {
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(BRIDGE_PORT);
#if ARCH_WIN
#if defined ARCH_WIN
addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST);
#else
inet_pton(AF_INET, BRIDGE_HOST, &addr.sin_addr);
@@ -363,7 +363,7 @@ static void serverConnect() {
INFO("Bridge server closed");
});

#if ARCH_MAC || ARCH_LIN
#if defined ARCH_MAC || defined ARCH_LIN
int reuseAddrFlag = 1;
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuseAddrFlag, sizeof(reuseAddrFlag));
#endif
@@ -382,7 +382,7 @@ static void serverConnect() {
INFO("Bridge server started");

// Enable non-blocking
#if ARCH_WIN
#if defined ARCH_WIN
unsigned long blockingMode = 1;
if (ioctlsocket(server, FIONBIO, &blockingMode)) {
WARN("Bridge server ioctlsocket() failed");


+ 18
- 0
src/dep.cpp View File

@@ -0,0 +1,18 @@
// This source file compiles those annoying implementation-in-header libraries

#define GLEW_STATIC
#include <GL/glew.h>
#include <nanovg.h>
#define NANOVG_GL2_IMPLEMENTATION
// #define NANOVG_GL3_IMPLEMENTATION
// #define NANOVG_GLES2_IMPLEMENTATION
// #define NANOVG_GLES3_IMPLEMENTATION
#include <nanovg_gl.h>
// Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported)
#define NANOVG_FBO_VALID
#include <nanovg_gl_utils.h>
#define BLENDISH_IMPLEMENTATION
#include <blendish.h>
#define NANOSVG_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#include <nanosvg.h>

+ 8
- 8
src/plugin.cpp View File

@@ -19,7 +19,7 @@
#include <zip.h>
#include <jansson.h>

#if ARCH_WIN
#if defined ARCH_WIN
#include <windows.h>
#include <direct.h>
#define mkdir(_dir, _perms) _mkdir(_dir)
@@ -62,9 +62,9 @@ static bool loadPlugin(std::string path) {

// Load plugin library
std::string libraryFilename;
#if ARCH_LIN
#if defined ARCH_LIN
libraryFilename = path + "/" + "plugin.so";
#elif ARCH_WIN
#elif defined ARCH_WIN
libraryFilename = path + "/" + "plugin.dll";
#elif ARCH_MAC
libraryFilename = path + "/" + "plugin.dylib";
@@ -77,7 +77,7 @@ static bool loadPlugin(std::string path) {
}

// Load dynamic/shared library
#if ARCH_WIN
#if defined ARCH_WIN
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
SetErrorMode(0);
@@ -97,7 +97,7 @@ static bool loadPlugin(std::string path) {
// Call plugin's init() function
typedef void (*InitCallback)(Plugin *);
InitCallback initCallback;
#if ARCH_WIN
#if defined ARCH_WIN
initCallback = (InitCallback) GetProcAddress(handle, "init");
#else
initCallback = (InitCallback) dlsym(handle, "init");
@@ -170,11 +170,11 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
name = slug;
}

#if ARCH_WIN
#if defined ARCH_WIN
std::string arch = "win";
#elif ARCH_MAC
std::string arch = "mac";
#elif ARCH_LIN
#elif defined ARCH_LIN
std::string arch = "lin";
#endif

@@ -359,7 +359,7 @@ void init(bool devMode) {
void destroy() {
for (Plugin *plugin : plugins) {
// Free library handle
#if ARCH_WIN
#if defined ARCH_WIN
if (plugin->handle)
FreeLibrary((HINSTANCE) plugin->handle);
#else


+ 5
- 5
src/system.cpp View File

@@ -2,7 +2,7 @@
#include <dirent.h>
#include <sys/stat.h>

#if ARCH_WIN
#if defined ARCH_WIN
#include <windows.h>
#include <shellapi.h>
#endif
@@ -70,7 +70,7 @@ void copyFile(const std::string &srcPath, const std::string &destPath) {
}

void createDirectory(const std::string &path) {
#if ARCH_WIN
#if defined ARCH_WIN
CreateDirectory(path.c_str(), NULL);
#else
mkdir(path.c_str(), 0755);
@@ -78,15 +78,15 @@ void createDirectory(const std::string &path) {
}

void openBrowser(const std::string &url) {
#if ARCH_LIN
#if defined ARCH_LIN
std::string command = "xdg-open " + url;
(void) std::system(command.c_str());
#endif
#if ARCH_MAC
#if defined ARCH_MAC
std::string command = "open " + url;
std::system(command.c_str());
#endif
#if ARCH_WIN
#if defined ARCH_WIN
ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);
#endif
}


+ 6
- 1
src/ui/ScrollWidget.cpp View File

@@ -73,7 +73,12 @@ void ScrollWidget::onHover(const event::Hover &e) {
}

void ScrollWidget::onHoverScroll(const event::HoverScroll &e) {
offset = offset.minus(e.scrollDelta);
math::Vec scrollDelta = e.scrollDelta;
// Flip coordinates if shift is held
if ((app()->window->getMods() & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT)
scrollDelta = scrollDelta.flip();

offset = offset.minus(scrollDelta);
e.consume(this);
}



+ 70
- 64
src/widgets/FramebufferWidget.cpp View File

@@ -1,34 +1,17 @@
#include "widgets/FramebufferWidget.hpp"
#include "app.hpp"
#include <nanovg_gl.h>
#include <nanovg_gl_utils.h>


namespace rack {


struct FramebufferWidget::Internal {
NVGLUframebuffer *fb = NULL;
math::Rect box;

~Internal() {
setFramebuffer(NULL);
}
void setFramebuffer(NVGLUframebuffer *fb) {
if (this->fb)
nvgluDeleteFramebuffer(this->fb);
this->fb = fb;
}
};


FramebufferWidget::FramebufferWidget() {
oversample = 1.0;
internal = new Internal;
}

FramebufferWidget::~FramebufferWidget() {
delete internal;
if (fb)
nvgluDeleteFramebuffer(fb);
}

void FramebufferWidget::draw(NVGcontext *vg) {
@@ -40,83 +23,106 @@ void FramebufferWidget::draw(NVGcontext *vg) {
float xform[6];
nvgCurrentTransform(vg, xform);
// Skew and rotate is not supported
assert(std::abs(xform[1]) < 1e-6);
assert(std::abs(xform[2]) < 1e-6);
math::Vec s = math::Vec(xform[0], xform[3]);
math::Vec b = math::Vec(xform[4], xform[5]);
math::Vec bi = b.floor();
math::Vec bf = b.minus(bi);
assert(math::isNear(xform[1], 0.f));
assert(math::isNear(xform[2], 0.f));
// Extract scale and offset from world transform
math::Vec scale = math::Vec(xform[0], xform[3]);
math::Vec offset = math::Vec(xform[4], xform[5]);
math::Vec offsetI = offset.floor();

// Render to framebuffer
if (dirty) {
dirty = false;

internal->box = getChildrenBoundingBox();
internal->box.pos = internal->box.pos.mult(s).floor();
internal->box.size = internal->box.size.mult(s).ceil().plus(math::Vec(1, 1));

math::Vec fbSize = internal->box.size.mult(app()->window->pixelRatio * oversample);

if (!fbSize.isFinite())
return;
if (fbSize.isZero())
return;
fbScale = scale;
// World coordinates, in range [0, 1)
fbOffset = offset.minus(offsetI);

math::Rect localBox;
if (children.empty()) {
localBox = box.zeroPos();
}
else {
localBox = getChildrenBoundingBox();
}

// DEBUG("%g %g %g %g, %g %g, %g %g", RECT_ARGS(localBox), VEC_ARGS(fbOffset), VEC_ARGS(scale));
// Transform to world coordinates, then expand to nearest integer coordinates
math::Vec min = localBox.getTopLeft().mult(scale).plus(fbOffset).floor();
math::Vec max = localBox.getBottomRight().mult(scale).plus(fbOffset).ceil();
fbBox = math::Rect::fromMinMax(min, max);
// DEBUG("%g %g %g %g", RECT_ARGS(fbBox));

math::Vec newFbSize = fbBox.size.mult(app()->window->pixelRatio * oversample);

if (!fb || !newFbSize.isEqual(fbSize)) {
fbSize = newFbSize;
// Delete old framebuffer
if (fb)
nvgluDeleteFramebuffer(fb);
// Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context.
if (fbSize.isFinite() && !fbSize.isZero())
fb = nvgluCreateFramebuffer(vg, fbSize.x, fbSize.y, 0);
}

// INFO("rendering framebuffer %f %f", fbSize.x, fbSize.y);
// Delete old one first to free up GPU memory
internal->setFramebuffer(NULL);
// Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context.
NVGLUframebuffer *fb = nvgluCreateFramebuffer(app()->window->vg, fbSize.x, fbSize.y, 0);
if (!fb)
return;
internal->setFramebuffer(fb);

nvgluBindFramebuffer(fb);
glViewport(0.0, 0.0, fbSize.x, fbSize.y);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

NVGcontext *framebufferVg = app()->window->framebufferVg;
nvgBeginFrame(framebufferVg, fbSize.x, fbSize.y, app()->window->pixelRatio * oversample);

nvgScale(framebufferVg, app()->window->pixelRatio * oversample, app()->window->pixelRatio * oversample);
// Use local scaling
nvgTranslate(framebufferVg, bf.x, bf.y);
nvgTranslate(framebufferVg, -internal->box.pos.x, -internal->box.pos.y);
nvgScale(framebufferVg, s.x, s.y);
Widget::draw(framebufferVg);

nvgEndFrame(framebufferVg);
drawFramebuffer(app()->window->fbVg);
nvgluBindFramebuffer(NULL);
}

if (!internal->fb) {
if (!fb)
return;
}

// Draw framebuffer image, using world coordinates
nvgSave(vg);
nvgResetTransform(vg);
nvgTranslate(vg, bi.x, bi.y);

nvgBeginPath(vg);
nvgRect(vg, internal->box.pos.x, internal->box.pos.y, internal->box.size.x, internal->box.size.y);
NVGpaint paint = nvgImagePattern(vg, internal->box.pos.x, internal->box.pos.y, internal->box.size.x, internal->box.size.y, 0.0, internal->fb->image, 1.0);
nvgRect(vg,
offsetI.x + fbBox.pos.x,
offsetI.y + fbBox.pos.y,
fbBox.size.x, fbBox.size.y);
NVGpaint paint = nvgImagePattern(vg,
offsetI.x + fbBox.pos.x,
offsetI.y + fbBox.pos.y,
fbBox.size.x, fbBox.size.y,
0.0, fb->image, 1.0);
nvgFillPaint(vg, paint);
nvgFill(vg);

// For debugging the bounding box of the framebuffer
// nvgStrokeWidth(vg, 2.0);
// nvgStrokeColor(vg, nvgRGBA(255, 0, 0, 128));
// nvgStrokeColor(vg, nvgRGBAf(1, 1, 0, 0.5));
// nvgStroke(vg);

nvgRestore(vg);
}

void FramebufferWidget::drawFramebuffer(NVGcontext *vg) {
float pixelRatio = fbSize.x / fbBox.size.x;
nvgBeginFrame(vg, fbBox.size.x, fbBox.size.y, pixelRatio);

// Use local scaling
nvgTranslate(vg, -fbBox.pos.x, -fbBox.pos.y);
nvgTranslate(vg, fbOffset.x, fbOffset.y);
nvgScale(vg, fbScale.x, fbScale.y);

Widget::draw(vg);

glViewport(0.0, 0.0, fbSize.x, fbSize.y);
glClearColor(0.0, 0.0, 0.0, 0.0);
// glClearColor(0.0, 1.0, 1.0, 0.5);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
nvgEndFrame(vg);
}

int FramebufferWidget::getImageHandle() {
if (!internal->fb)
if (!fb)
return -1;
return internal->fb->image;
return fb->image;
}




+ 5
- 8
src/widgets/Widget.cpp View File

@@ -14,16 +14,13 @@ Widget::~Widget() {
}

math::Rect Widget::getChildrenBoundingBox() {
math::Rect bound;
math::Vec min = math::Vec(INFINITY, INFINITY);
math::Vec max = math::Vec(-INFINITY, -INFINITY);
for (Widget *child : children) {
if (child == children.front()) {
bound = child->box;
}
else {
bound = bound.expand(child->box);
}
min = min.min(child->box.getTopLeft());
max = max.max(child->box.getBottomRight());
}
return bound;
return math::Rect::fromMinMax(min, max);
}

math::Vec Widget::getRelativeOffset(math::Vec v, Widget *relative) {


+ 17
- 35
src/window.cpp View File

@@ -17,20 +17,6 @@

#include <osdialog.h>

#define NANOVG_GL2_IMPLEMENTATION 1
// #define NANOVG_GL3_IMPLEMENTATION 1
// #define NANOVG_GLES2_IMPLEMENTATION 1
// #define NANOVG_GLES3_IMPLEMENTATION 1
#include <nanovg_gl.h>
// Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported)
#define NANOVG_FBO_VALID 1
#include <nanovg_gl_utils.h>
#define BLENDISH_IMPLEMENTATION
#include <blendish.h>
#define NANOSVG_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#include <nanosvg.h>


namespace rack {

@@ -84,7 +70,7 @@ std::shared_ptr<Image> Image::load(const std::string &filename) {
}

SVG::SVG(const std::string &filename) {
handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI);
handle = nsvgParseFromFile(filename.c_str(), "px", APP_SVG_DPI);
if (handle) {
INFO("Loaded SVG %s", filename.c_str());
}
@@ -171,10 +157,6 @@ static void scrollCallback(GLFWwindow *win, double x, double y) {
math::Vec scrollDelta = math::Vec(x, y);
scrollDelta = scrollDelta.mult(50.0);

// Flip coordinates if shift is held
if ((window->getMods() & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT)
scrollDelta = scrollDelta.flip();

app()->event->handleScroll(window->mousePos, scrollDelta);
}

@@ -215,10 +197,10 @@ Window::Window() {
internal = new Internal;
int err;

#if NANOVG_GL2
#if defined NANOVG_GL2
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#elif NANOVG_GL3
#elif defined NANOVG_GL3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
@@ -275,13 +257,13 @@ Window::Window() {
assert(vg);

#if defined NANOVG_GL2
framebufferVg = nvgCreateGL2(nvgFlags);
fbVg = nvgCreateGL2(nvgFlags);
#elif defined NANOVG_GL3
framebufferVg = nvgCreateGL3(nvgFlags);
fbVg = nvgCreateGL3(nvgFlags);
#elif defined NANOVG_GLES2
framebufferVg = nvgCreateGLES2(nvgFlags);
fbVg = nvgCreateGLES2(nvgFlags);
#endif
assert(framebufferVg);
assert(fbVg);
}

Window::~Window() {
@@ -294,11 +276,11 @@ Window::~Window() {
#endif

#if defined NANOVG_GL2
nvgDeleteGL2(framebufferVg);
nvgDeleteGL2(fbVg);
#elif defined NANOVG_GL3
nvgDeleteGL3(framebufferVg);
nvgDeleteGL3(fbVg);
#elif defined NANOVG_GLES2
nvgDeleteGLES2(framebufferVg);
nvgDeleteGLES2(fbVg);
#endif

glfwDestroyWindow(win);
@@ -311,10 +293,11 @@ void Window::run() {
frame = 0;
while(!glfwWindowShouldClose(win)) {
double startTime = glfwGetTime();
frame++;

// Poll events
glfwPollEvents();
// In case glfwPollEvents() set another OpenGL context
glfwMakeContextCurrent(win);
// Call cursorPosCallback every frame, not just when the mouse moves
{
double xpos, ypos;
@@ -364,12 +347,6 @@ void Window::run() {
// Render
bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED);
if (visible) {
// In case glfwPollEvents() worked with another OpenGL context
glfwMakeContextCurrent(win);
glViewport(0, 0, fbWidth, fbHeight);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// Update and render
nvgBeginFrame(vg, winWidth, winHeight, pixelRatio);

@@ -378,7 +355,11 @@ void Window::run() {

app()->event->rootWidget->draw(vg);

glViewport(0, 0, fbWidth, fbHeight);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
nvgEndFrame(vg);

glfwSwapBuffers(win);
}

@@ -391,6 +372,7 @@ void Window::run() {
}
endTime = glfwGetTime();
// INFO("%lf fps", 1.0 / (endTime - startTime));
frame++;
}
}



Loading…
Cancel
Save