@@ -15,19 +15,24 @@ struct FramebufferWidget : Widget { | |||||
bool dirty = true; | bool dirty = true; | ||||
float oversample; | float oversample; | ||||
NVGLUframebuffer *fb = NULL; | NVGLUframebuffer *fb = NULL; | ||||
/** Scale relative to the world */ | |||||
math::Vec scale; | |||||
/** Offset in world coordinates */ | |||||
math::Vec offset; | |||||
/** Pixel dimensions of the allocated framebuffer */ | /** Pixel dimensions of the allocated framebuffer */ | ||||
math::Vec fbSize; | math::Vec fbSize; | ||||
/** Bounding box in world coordinates of where the framebuffer should be painted | |||||
/** Bounding box in world coordinates of where the framebuffer should be painted. | |||||
Always has integer coordinates so that blitting framebuffers is pixel-perfect. | Always has integer coordinates so that blitting framebuffers is pixel-perfect. | ||||
*/ | */ | ||||
math::Rect fbBox; | math::Rect fbBox; | ||||
/** Local scale relative to the world scale */ | |||||
/** Framebuffer's scale relative to the world */ | |||||
math::Vec fbScale; | math::Vec fbScale; | ||||
/** Subpixel offset of fbBox in world coordinates */ | |||||
/** Framebuffer's subpixel offset relative to fbBox in world coordinates */ | |||||
math::Vec fbOffset; | math::Vec fbOffset; | ||||
FramebufferWidget(); | FramebufferWidget(); | ||||
~FramebufferWidget(); | ~FramebufferWidget(); | ||||
void step() override; | |||||
void draw(const DrawArgs &args) override; | void draw(const DrawArgs &args) override; | ||||
virtual void drawFramebuffer(); | virtual void drawFramebuffer(); | ||||
int getImageHandle(); | int getImageHandle(); | ||||
@@ -84,6 +84,7 @@ struct Widget { | |||||
struct DrawArgs { | struct DrawArgs { | ||||
NVGcontext *vg; | NVGcontext *vg; | ||||
math::Rect clipBox; | math::Rect clipBox; | ||||
NVGLUframebuffer *fb = NULL; | |||||
}; | }; | ||||
/** Draws the widget to the NanoVG context */ | /** Draws the widget to the NanoVG context */ | ||||
@@ -74,8 +74,6 @@ DEPRECATED typedef Svg SVG; | |||||
struct Window { | struct Window { | ||||
GLFWwindow *win = NULL; | GLFWwindow *win = NULL; | ||||
NVGcontext *vg = NULL; | NVGcontext *vg = NULL; | ||||
/** Secondary nanovg context for drawing to framebuffers */ | |||||
NVGcontext *fbVg = NULL; | |||||
/** The scaling ratio */ | /** The scaling ratio */ | ||||
float pixelRatio = 1.f; | float pixelRatio = 1.f; | ||||
/* The ratio between the framebuffer size and the window size reported by the OS. | /* The ratio between the framebuffer size and the window size reported by the OS. | ||||
@@ -17,7 +17,7 @@ void LedDisplay::draw(const DrawArgs &args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
@@ -64,11 +64,11 @@ struct BrowserOverlay : widget::OpaqueWidget { | |||||
box = parent->box.zeroPos(); | box = parent->box.zeroPos(); | ||||
// Only step if visible, since there are potentially thousands of descendants that don't need to be stepped. | // Only step if visible, since there are potentially thousands of descendants that don't need to be stepped. | ||||
if (visible) | if (visible) | ||||
widget::OpaqueWidget::step(); | |||||
OpaqueWidget::step(); | |||||
} | } | ||||
void onButton(const event::Button &e) override { | void onButton(const event::Button &e) override { | ||||
widget::OpaqueWidget::onButton(e); | |||||
OpaqueWidget::onButton(e); | |||||
if (e.getConsumed() != this) | if (e.getConsumed() != this) | ||||
return; | return; | ||||
@@ -189,6 +189,7 @@ struct ModelBox : widget::OpaqueWidget { | |||||
if (previewWidget && ++visibleFrames >= 60) { | if (previewWidget && ++visibleFrames >= 60) { | ||||
deletePreview(); | deletePreview(); | ||||
} | } | ||||
OpaqueWidget::step(); | |||||
} | } | ||||
void draw(const DrawArgs &args) override { | void draw(const DrawArgs &args) override { | ||||
@@ -210,7 +211,7 @@ struct ModelBox : widget::OpaqueWidget { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
widget::OpaqueWidget::draw(args); | |||||
OpaqueWidget::draw(args); | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
// Translucent overlay when selected | // Translucent overlay when selected | ||||
@@ -239,7 +240,7 @@ struct BrowserSearchField : ui::TextField { | |||||
void step() override { | void step() override { | ||||
// Steal focus when step is called | // Steal focus when step is called | ||||
APP->event->setSelected(this); | APP->event->setSelected(this); | ||||
ui::TextField::step(); | |||||
TextField::step(); | |||||
} | } | ||||
void onSelectKey(const event::SelectKey &e) override { | void onSelectKey(const event::SelectKey &e) override { | ||||
if (e.action == GLFW_PRESS) { | if (e.action == GLFW_PRESS) { | ||||
@@ -316,7 +317,7 @@ struct BrowserSidebar : widget::Widget { | |||||
tagScroll->box.size.y = (box.size.y - searchField->box.size.y) / 2; | tagScroll->box.size.y = (box.size.y - searchField->box.size.y) / 2; | ||||
tagScroll->box.size.x = box.size.x; | tagScroll->box.size.x = box.size.x; | ||||
tagList->box.size.x = tagScroll->box.size.x; | tagList->box.size.x = tagScroll->box.size.x; | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -363,12 +364,12 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
modelMargin->box.size.x = modelScroll->box.size.x; | modelMargin->box.size.x = modelScroll->box.size.x; | ||||
modelMargin->box.size.y = modelContainer->getChildrenBoundingBox().size.y + 2 * modelMargin->margin.y; | modelMargin->box.size.y = modelContainer->getChildrenBoundingBox().size.y + 2 * modelMargin->margin.y; | ||||
widget::OpaqueWidget::step(); | |||||
OpaqueWidget::step(); | |||||
} | } | ||||
void draw(const DrawArgs &args) override { | void draw(const DrawArgs &args) override { | ||||
bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, 0); | bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, 0); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
void setSearch(const std::string &search) { | void setSearch(const std::string &search) { | ||||
@@ -388,7 +389,7 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
void ModelBox::onButton(const event::Button &e) { | void ModelBox::onButton(const event::Button &e) { | ||||
widget::OpaqueWidget::onButton(e); | |||||
OpaqueWidget::onButton(e); | |||||
if (e.getConsumed() != this) | if (e.getConsumed() != this) | ||||
return; | return; | ||||
@@ -148,7 +148,7 @@ void ModuleWidget::draw(const DrawArgs &args) { | |||||
nvgGlobalAlpha(args.vg, 0.25); | nvgGlobalAlpha(args.vg, 0.25); | ||||
} | } | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
// Power meter | // Power meter | ||||
if (module && settings.cpuMeter) { | if (module && settings.cpuMeter) { | ||||
@@ -21,7 +21,7 @@ struct ParamField : ui::TextField { | |||||
void step() override { | void step() override { | ||||
// Keep selected | // Keep selected | ||||
APP->event->setSelected(this); | APP->event->setSelected(this); | ||||
ui::TextField::step(); | |||||
TextField::step(); | |||||
} | } | ||||
void setParamWidget(ParamWidget *paramWidget) { | void setParamWidget(ParamWidget *paramWidget) { | ||||
@@ -54,7 +54,7 @@ struct ParamField : ui::TextField { | |||||
} | } | ||||
if (!e.getConsumed()) | if (!e.getConsumed()) | ||||
ui::TextField::onSelectKey(e); | |||||
TextField::onSelectKey(e); | |||||
} | } | ||||
}; | }; | ||||
@@ -73,7 +73,7 @@ struct ParamTooltip : ui::Tooltip { | |||||
} | } | ||||
// Position at bottom-right of parameter | // Position at bottom-right of parameter | ||||
box.pos = paramWidget->getAbsoluteOffset(paramWidget->box.size).round(); | box.pos = paramWidget->getAbsoluteOffset(paramWidget->box.size).round(); | ||||
ui::Tooltip::step(); | |||||
Tooltip::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -82,7 +82,7 @@ struct ParamLabel : ui::MenuLabel { | |||||
ParamWidget *paramWidget; | ParamWidget *paramWidget; | ||||
void step() override { | void step() override { | ||||
text = paramWidget->paramQuantity->getString(); | text = paramWidget->paramQuantity->getString(); | ||||
ui::MenuLabel::step(); | |||||
MenuLabel::step(); | |||||
} | } | ||||
}; | }; | ||||
@@ -138,11 +138,11 @@ void ParamWidget::step() { | |||||
} | } | ||||
} | } | ||||
widget::OpaqueWidget::step(); | |||||
OpaqueWidget::step(); | |||||
} | } | ||||
void ParamWidget::draw(const DrawArgs &args) { | void ParamWidget::draw(const DrawArgs &args) { | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
// if (paramQuantity) { | // if (paramQuantity) { | ||||
// nvgBeginPath(args.vg); | // nvgBeginPath(args.vg); | ||||
@@ -185,7 +185,7 @@ void ParamWidget::onButton(const event::Button &e) { | |||||
} | } | ||||
if (!e.getConsumed()) | if (!e.getConsumed()) | ||||
widget::OpaqueWidget::onButton(e); | |||||
OpaqueWidget::onButton(e); | |||||
} | } | ||||
void ParamWidget::onDoubleClick(const event::DoubleClick &e) { | void ParamWidget::onDoubleClick(const event::DoubleClick &e) { | ||||
@@ -45,6 +45,8 @@ void PortWidget::step() { | |||||
values[i] = module->inputs[portId].plugLights[i].getBrightness(); | values[i] = module->inputs[portId].plugLights[i].getBrightness(); | ||||
} | } | ||||
plugLight->setBrightnesses(values); | plugLight->setBrightnesses(values); | ||||
OpaqueWidget::step(); | |||||
} | } | ||||
void PortWidget::draw(const DrawArgs &args) { | void PortWidget::draw(const DrawArgs &args) { | ||||
@@ -54,7 +56,7 @@ void PortWidget::draw(const DrawArgs &args) { | |||||
if (type == OUTPUT ? cw->outputPort : cw->inputPort) | if (type == OUTPUT ? cw->outputPort : cw->inputPort) | ||||
nvgGlobalAlpha(args.vg, 0.5); | nvgGlobalAlpha(args.vg, 0.5); | ||||
} | } | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
void PortWidget::onButton(const event::Button &e) { | void PortWidget::onButton(const event::Button &e) { | ||||
@@ -24,12 +24,12 @@ void RackScrollWidget::step() { | |||||
if (pos.y >= viewport.pos.y + viewport.size.y - margin) | if (pos.y >= viewport.pos.y + viewport.size.y - margin) | ||||
offset.y += speed; | offset.y += speed; | ||||
} | } | ||||
ui::ScrollWidget::step(); | |||||
ScrollWidget::step(); | |||||
} | } | ||||
void RackScrollWidget::draw(const DrawArgs &args) { | void RackScrollWidget::draw(const DrawArgs &args) { | ||||
ui::ScrollWidget::draw(args); | |||||
ScrollWidget::draw(args); | |||||
} | } | ||||
@@ -55,14 +55,14 @@ struct ModuleContainer : widget::Widget { | |||||
nvgRestore(args.vg); | nvgRestore(args.vg); | ||||
} | } | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
}; | }; | ||||
struct CableContainer : widget::TransparentWidget { | struct CableContainer : widget::TransparentWidget { | ||||
void draw(const DrawArgs &args) override { | void draw(const DrawArgs &args) override { | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
// Draw cable plugs | // Draw cable plugs | ||||
for (widget::Widget *w : children) { | for (widget::Widget *w : children) { | ||||
@@ -114,11 +114,11 @@ void RackWidget::step() { | |||||
rail->box.size = rails->box.size; | rail->box.size = rails->box.size; | ||||
} | } | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
void RackWidget::draw(const DrawArgs &args) { | void RackWidget::draw(const DrawArgs &args) { | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
void RackWidget::onHover(const event::Hover &e) { | void RackWidget::onHover(const event::Hover &e) { | ||||
@@ -51,7 +51,7 @@ void Scene::step() { | |||||
.plus(math::Vec(500, 500)) | .plus(math::Vec(500, 500)) | ||||
.div(zoomWidget->zoom); | .div(zoomWidget->zoom); | ||||
widget::OpaqueWidget::step(); | |||||
OpaqueWidget::step(); | |||||
zoomWidget->box.size = rackWidget->box.size.mult(zoomWidget->zoom); | zoomWidget->box.size = rackWidget->box.size.mult(zoomWidget->zoom); | ||||
@@ -88,7 +88,7 @@ void Scene::step() { | |||||
} | } | ||||
void Scene::draw(const DrawArgs &args) { | void Scene::draw(const DrawArgs &args) { | ||||
widget::OpaqueWidget::draw(args); | |||||
OpaqueWidget::draw(args); | |||||
} | } | ||||
void Scene::onHoverKey(const event::HoverKey &e) { | void Scene::onHoverKey(const event::HoverKey &e) { | ||||
@@ -149,7 +149,7 @@ void Scene::onHoverKey(const event::HoverKey &e) { | |||||
} | } | ||||
if (!e.getConsumed()) | if (!e.getConsumed()) | ||||
widget::OpaqueWidget::onHoverKey(e); | |||||
OpaqueWidget::onHoverKey(e); | |||||
} | } | ||||
void Scene::onPathDrop(const event::PathDrop &e) { | void Scene::onPathDrop(const event::PathDrop &e) { | ||||
@@ -162,7 +162,7 @@ void Scene::onPathDrop(const event::PathDrop &e) { | |||||
} | } | ||||
if (!e.getConsumed()) | if (!e.getConsumed()) | ||||
widget::OpaqueWidget::onPathDrop(e); | |||||
OpaqueWidget::onPathDrop(e); | |||||
} | } | ||||
void Scene::runCheckVersion() { | void Scene::runCheckVersion() { | ||||
@@ -20,7 +20,7 @@ void SvgPanel::step() { | |||||
// Small details draw poorly at low DPI, so oversample when drawing to the framebuffer | // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer | ||||
oversample = 2.0; | oversample = 2.0; | ||||
} | } | ||||
widget::FramebufferWidget::step(); | |||||
FramebufferWidget::step(); | |||||
} | } | ||||
void SvgPanel::setBackground(std::shared_ptr<Svg> svg) { | void SvgPanel::setBackground(std::shared_ptr<Svg> svg) { | ||||
@@ -25,7 +25,7 @@ namespace app { | |||||
struct MenuButton : ui::Button { | struct MenuButton : ui::Button { | ||||
void step() override { | void step() override { | ||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0; | box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 1.0; | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
void draw(const DrawArgs &args) override { | void draw(const DrawArgs &args) override { | ||||
bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); | bndMenuItem(args.vg, 0.0, 0.0, box.size.x, box.size.y, state, -1, text.c_str()); | ||||
@@ -680,7 +680,7 @@ void Toolbar::draw(const DrawArgs &args) { | |||||
bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL); | bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_ALL); | ||||
bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y); | bndBevel(args.vg, 0.0, 0.0, box.size.x, box.size.y); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
@@ -6,7 +6,7 @@ namespace ui { | |||||
void List::step() { | void List::step() { | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
// Set positions of children | // Set positions of children | ||||
box.size.y = 0.0; | box.size.y = 0.0; | ||||
@@ -7,7 +7,7 @@ namespace ui { | |||||
void MarginLayout::step() { | void MarginLayout::step() { | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
math::Rect childBox = box.zeroPos().grow(margin.neg()); | math::Rect childBox = box.zeroPos().grow(margin.neg()); | ||||
for (Widget *child : children) { | for (Widget *child : children) { | ||||
@@ -27,7 +27,7 @@ void Menu::setChildMenu(Menu *menu) { | |||||
} | } | ||||
void Menu::step() { | void Menu::step() { | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
// Set positions of children | // Set positions of children | ||||
box.size = math::Vec(0, 0); | box.size = math::Vec(0, 0); | ||||
@@ -51,7 +51,7 @@ void Menu::step() { | |||||
void Menu::draw(const DrawArgs &args) { | void Menu::draw(const DrawArgs &args) { | ||||
bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE); | bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, BND_CORNER_NONE); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
void Menu::onHoverScroll(const event::HoverScroll &e) { | void Menu::onHoverScroll(const event::HoverScroll &e) { | ||||
@@ -34,7 +34,7 @@ void MenuItem::step() { | |||||
// HACK use APP->window->vg from the window. | // HACK use APP->window->vg from the window. | ||||
// All this does is inspect the font, so it shouldn't modify APP->window->vg and should work when called from a widget::FramebufferWidget for example. | // All this does is inspect the font, so it shouldn't modify APP->window->vg and should work when called from a widget::FramebufferWidget for example. | ||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + bndLabelWidth(APP->window->vg, -1, rightText.c_str()) + rightPadding; | box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + bndLabelWidth(APP->window->vg, -1, rightText.c_str()) + rightPadding; | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
void MenuItem::onEnter(const event::Enter &e) { | void MenuItem::onEnter(const event::Enter &e) { | ||||
@@ -15,7 +15,7 @@ void MenuLabel::step() { | |||||
const float rightPadding = 10.0; | const float rightPadding = 10.0; | ||||
// HACK use APP->window->vg from the window. | // HACK use APP->window->vg from the window. | ||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + rightPadding; | box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + rightPadding; | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
@@ -14,7 +14,7 @@ void MenuOverlay::step() { | |||||
child->box = child->box.nudge(box.zeroPos()); | child->box = child->box.nudge(box.zeroPos()); | ||||
} | } | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
void MenuOverlay::onButton(const event::Button &e) { | void MenuOverlay::onButton(const event::Button &e) { | ||||
@@ -29,12 +29,12 @@ void ScrollWidget::scrollTo(math::Rect r) { | |||||
void ScrollWidget::draw(const DrawArgs &args) { | void ScrollWidget::draw(const DrawArgs &args) { | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
void ScrollWidget::step() { | void ScrollWidget::step() { | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
// Clamp scroll offset | // Clamp scroll offset | ||||
math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight(); | math::Vec containerCorner = container->getChildrenBoundingBox().getBottomRight(); | ||||
@@ -11,7 +11,7 @@ namespace ui { | |||||
void SequentialLayout::step() { | void SequentialLayout::step() { | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
// Sort widgets into rows (or columns if vertical) | // Sort widgets into rows (or columns if vertical) | ||||
std::vector<std::vector<widget::Widget*>> rows; | std::vector<std::vector<widget::Widget*>> rows; | ||||
@@ -11,13 +11,13 @@ void Tooltip::step() { | |||||
// Wrap size to contents | // Wrap size to contents | ||||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 10.0; | box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + 10.0; | ||||
box.size.y = bndLabelHeight(APP->window->vg, -1, text.c_str(), INFINITY); | box.size.y = bndLabelHeight(APP->window->vg, -1, text.c_str(), INFINITY); | ||||
widget::Widget::step(); | |||||
Widget::step(); | |||||
} | } | ||||
void Tooltip::draw(const DrawArgs &args) { | void Tooltip::draw(const DrawArgs &args) { | ||||
bndTooltipBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y); | bndTooltipBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y); | ||||
bndMenuLabel(args.vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str()); | bndMenuLabel(args.vg, 0.0, 0.0, box.size.x, box.size.y, -1, text.c_str()); | ||||
widget::Widget::draw(args); | |||||
Widget::draw(args); | |||||
} | } | ||||
@@ -15,31 +15,18 @@ FramebufferWidget::~FramebufferWidget() { | |||||
nvgluDeleteFramebuffer(fb); | nvgluDeleteFramebuffer(fb); | ||||
} | } | ||||
void FramebufferWidget::draw(const DrawArgs &args) { | |||||
// Bypass framebuffer rendering if we're already drawing in a framebuffer | |||||
// In other words, disallow nested framebuffers. They look bad. | |||||
if (args.vg == APP->window->fbVg) { | |||||
Widget::draw(args); | |||||
return; | |||||
} | |||||
// Get world transform | |||||
float xform[6]; | |||||
nvgCurrentTransform(args.vg, xform); | |||||
// Skew and rotate is not supported | |||||
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(); | |||||
void FramebufferWidget::step() { | |||||
Widget::step(); | |||||
// Render to framebuffer | |||||
if (dirty) { | |||||
// Render to framebuffer if dirty. | |||||
// Also check that scale has been set by `draw()` yet. | |||||
if (dirty && !scale.isZero()) { | |||||
// In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here. | |||||
dirty = false; | dirty = false; | ||||
fbScale = scale; | fbScale = scale; | ||||
// World coordinates, in range [0, 1) | |||||
// Get subpixel offset in range [0, 1) | |||||
math::Vec offsetI = offset.floor(); | |||||
fbOffset = offset.minus(offsetI); | fbOffset = offset.minus(offsetI); | ||||
math::Rect localBox; | math::Rect localBox; | ||||
@@ -50,15 +37,16 @@ void FramebufferWidget::draw(const DrawArgs &args) { | |||||
localBox = getChildrenBoundingBox(); | localBox = getChildrenBoundingBox(); | ||||
} | } | ||||
// DEBUG("%g %g %g %g, %g %g, %g %g", RECT_ARGS(localBox), VEC_ARGS(fbOffset), VEC_ARGS(scale)); | |||||
// DEBUG("%g %g %g %g, %g %g, %g %g", RECT_ARGS(localBox), VEC_ARGS(fbOffset), VEC_ARGS(fbScale)); | |||||
// Transform to world coordinates, then expand to nearest integer coordinates | // 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(); | |||||
math::Vec min = localBox.getTopLeft().mult(fbScale).plus(fbOffset).floor(); | |||||
math::Vec max = localBox.getBottomRight().mult(fbScale).plus(fbOffset).ceil(); | |||||
fbBox = math::Rect::fromMinMax(min, max); | fbBox = math::Rect::fromMinMax(min, max); | ||||
// DEBUG("%g %g %g %g", RECT_ARGS(fbBox)); | // DEBUG("%g %g %g %g", RECT_ARGS(fbBox)); | ||||
math::Vec newFbSize = fbBox.size.mult(APP->window->pixelRatio * oversample); | math::Vec newFbSize = fbBox.size.mult(APP->window->pixelRatio * oversample); | ||||
// Create framebuffer if a new size is needed | |||||
if (!fb || !newFbSize.isEqual(fbSize)) { | if (!fb || !newFbSize.isEqual(fbSize)) { | ||||
fbSize = newFbSize; | fbSize = newFbSize; | ||||
// Delete old framebuffer | // Delete old framebuffer | ||||
@@ -66,7 +54,7 @@ void FramebufferWidget::draw(const DrawArgs &args) { | |||||
nvgluDeleteFramebuffer(fb); | nvgluDeleteFramebuffer(fb); | ||||
// Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. | // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context. | ||||
if (fbSize.isFinite() && !fbSize.isZero()) | if (fbSize.isFinite() && !fbSize.isZero()) | ||||
fb = nvgluCreateFramebuffer(args.vg, fbSize.x, fbSize.y, 0); | |||||
fb = nvgluCreateFramebuffer(APP->window->vg, fbSize.x, fbSize.y, 0); | |||||
} | } | ||||
if (!fb) | if (!fb) | ||||
@@ -76,6 +64,25 @@ void FramebufferWidget::draw(const DrawArgs &args) { | |||||
drawFramebuffer(); | drawFramebuffer(); | ||||
nvgluBindFramebuffer(NULL); | nvgluBindFramebuffer(NULL); | ||||
} | } | ||||
} | |||||
void FramebufferWidget::draw(const DrawArgs &args) { | |||||
// Draw directly if already drawing in a framebuffer | |||||
if (args.fb) { | |||||
Widget::draw(args); | |||||
return; | |||||
} | |||||
// Get world transform | |||||
float xform[6]; | |||||
nvgCurrentTransform(args.vg, xform); | |||||
// Skew and rotate is not supported | |||||
assert(math::isNear(xform[1], 0.f)); | |||||
assert(math::isNear(xform[2], 0.f)); | |||||
// Extract scale and offset from world transform | |||||
scale = math::Vec(xform[0], xform[3]); | |||||
offset = math::Vec(xform[4], xform[5]); | |||||
math::Vec offsetI = offset.floor(); | |||||
if (!fb) | if (!fb) | ||||
return; | return; | ||||
@@ -106,7 +113,7 @@ void FramebufferWidget::draw(const DrawArgs &args) { | |||||
} | } | ||||
void FramebufferWidget::drawFramebuffer() { | void FramebufferWidget::drawFramebuffer() { | ||||
NVGcontext *vg = APP->window->fbVg; | |||||
NVGcontext *vg = APP->window->vg; | |||||
float pixelRatio = fbSize.x / fbBox.size.x; | float pixelRatio = fbSize.x / fbBox.size.x; | ||||
nvgBeginFrame(vg, fbBox.size.x, fbBox.size.y, pixelRatio); | nvgBeginFrame(vg, fbBox.size.x, fbBox.size.y, pixelRatio); | ||||
@@ -119,6 +126,7 @@ void FramebufferWidget::drawFramebuffer() { | |||||
DrawArgs args; | DrawArgs args; | ||||
args.vg = vg; | args.vg = vg; | ||||
args.clipBox = box.zeroPos(); | args.clipBox = box.zeroPos(); | ||||
args.fb = fb; | |||||
Widget::draw(args); | Widget::draw(args); | ||||
glViewport(0.0, 0.0, fbSize.x, fbSize.y); | glViewport(0.0, 0.0, fbSize.x, fbSize.y); | ||||
@@ -275,15 +275,6 @@ Window::Window() { | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize NanoVG. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize NanoVG. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
#if defined NANOVG_GL2 | |||||
fbVg = nvgCreateGL2(nvgFlags); | |||||
#elif defined NANOVG_GL3 | |||||
fbVg = nvgCreateGL3(nvgFlags); | |||||
#elif defined NANOVG_GLES2 | |||||
fbVg = nvgCreateGLES2(nvgFlags); | |||||
#endif | |||||
assert(fbVg); | |||||
} | } | ||||
Window::~Window() { | Window::~Window() { | ||||
@@ -308,14 +299,6 @@ Window::~Window() { | |||||
nvgDeleteGLES2(vg); | nvgDeleteGLES2(vg); | ||||
#endif | #endif | ||||
#if defined NANOVG_GL2 | |||||
nvgDeleteGL2(fbVg); | |||||
#elif defined NANOVG_GL3 | |||||
nvgDeleteGL3(fbVg); | |||||
#elif defined NANOVG_GLES2 | |||||
nvgDeleteGLES2(fbVg); | |||||
#endif | |||||
glfwDestroyWindow(win); | glfwDestroyWindow(win); | ||||
delete internal; | delete internal; | ||||
} | } | ||||