Skip to content

Commit eca0561

Browse files
author
alcomposer
committed
Expand framebuffer by 32pix to allow objects on the edge of the canvas to be blurred as they are moved outside of the canvas
1 parent 89beeca commit eca0561

7 files changed

Lines changed: 66 additions & 41 deletions

File tree

Source/Canvas.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ void Canvas::performRender(NVGcontext* nvg, Rectangle<int> invalidRegion) {
607607
auto const halfSize = infiniteCanvasSize / 2;
608608
auto const zoom = getValue<float>(zoomScale);
609609
bool isLocked = getValue<bool>(locked);
610+
auto cnvMargin = NVGSurface::cnvMargin;
610611
nvgSave(nvg);
611612

612613
if (auto parentCnv = findParentComponentOfClass<Canvas>(); parentCnv && parentCnv->viewport && isQuickCanvas) {
@@ -615,19 +616,19 @@ void Canvas::performRender(NVGcontext* nvg, Rectangle<int> invalidRegion) {
615616
auto offset = quickCanvasOffset * zoom;
616617
nvgTranslate(nvg, -viewPos.x - offset.x, -viewPos.y - offset.y);
617618
nvgScale(nvg, zoom, zoom);
618-
invalidRegion = invalidRegion.translated(viewPos.x + offset.x, viewPos.y + offset.y);
619+
invalidRegion = invalidRegion.translated(viewPos.x + offset.x - cnvMargin, viewPos.y + offset.y - cnvMargin);
619620
invalidRegion /= zoom;
620621
}
621622
} else if (viewport) {
622623
nvgTranslate(nvg, -viewport->getViewPositionX(), -viewport->getViewPositionY());
623624
nvgScale(nvg, zoom, zoom);
624-
invalidRegion = invalidRegion.translated(viewport->getViewPositionX(), viewport->getViewPositionY());
625+
invalidRegion = invalidRegion.translated(viewport->getViewPositionX() - cnvMargin, viewport->getViewPositionY() - cnvMargin);
625626
invalidRegion /= zoom;
626627
}
627628
if (isQuickCanvas || viewport) {
628629
if (!isQuickCanvas) {
629630
nvgFillColor(nvg, canvasBackgroundCol);
630-
nvgFillRect(nvg, invalidRegion.getX(), invalidRegion.getY(), invalidRegion.getWidth(), invalidRegion.getHeight());
631+
nvgFillRect(nvg, 0, 0, infiniteCanvasSize, infiniteCanvasSize); // fill the whole thing (which will get cropped to visible anyway)
631632
}
632633
if (!isLocked) {
633634
nvgBeginPath(nvg);
@@ -1483,7 +1484,7 @@ void Canvas::mouseDrag(MouseEvent const& e)
14831484
return;
14841485
}
14851486

1486-
auto usedViewport = isQuickCanvas ? findParentComponentOfClass<Canvas>()->viewport.get() : viewport.get();
1487+
auto usedViewport = getActiveViewport();
14871488

14881489
auto viewportEvent = e.getEventRelativeTo(usedViewport);
14891490
if (usedViewport && !ObjectBase::isBeingEdited() && autoscroll(viewportEvent)) {
@@ -1497,9 +1498,14 @@ void Canvas::mouseDrag(MouseEvent const& e)
14971498
}
14981499
}
14991500

1501+
Viewport* Canvas::getActiveViewport()
1502+
{
1503+
return isQuickCanvas ? findParentComponentOfClass<Canvas>()->viewport.get() : viewport.get();
1504+
}
1505+
15001506
bool Canvas::autoscroll(MouseEvent const& e)
15011507
{
1502-
auto usedViewport = isQuickCanvas ? findParentComponentOfClass<Canvas>()->viewport.get() : viewport.get();
1508+
auto usedViewport = getActiveViewport();
15031509

15041510
if (!usedViewport)
15051511
return false;
@@ -1944,7 +1950,7 @@ void Canvas::duplicateSelection()
19441950
// Adjust the viewport position to ensure the duplicated objects are visible
19451951

19461952
// Use the base canvas viewport (quick canvas uses base canvas viewport)
1947-
auto usedViewport = isQuickCanvas ? findParentComponentOfClass<Canvas>()->viewport.get() : viewport.get();
1953+
auto usedViewport = getActiveViewport();
19481954

19491955
auto viewportPos = usedViewport->getViewPosition();
19501956

Source/Canvas.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class Canvas : public Component
9292
void middleMouseChanged(bool isHeld) override;
9393
void altKeyChanged(bool isHeld) override;
9494

95+
Viewport* getActiveViewport();
96+
9597
void settingsChanged(String const& name, var const& value) override;
9698

9799
void focusGained(FocusChangeType cause) override;

Source/Components/WelcomePanel.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -992,11 +992,6 @@ class WelcomePanel : public Component
992992
Graphics g(*nvgContext);
993993
g.reduceClipRegion(editor->nvgSurface.getInvalidArea());
994994
paintEntireComponent(g, false);
995-
996-
auto gradient = nvgLinearGradient(nvg, 0, viewport.getY(), 0, viewport.getY() + 20, convertColour(findColour(PlugDataColour::panelBackgroundColourId)), nvgRGBA(255, 255, 255, 0));
997-
998-
nvgFillPaint(nvg, gradient);
999-
nvgFillRect(nvg, viewport.getX() + 8, viewport.getY(), viewport.getWidth() - 16, 20);
1000995
}
1001996

1002997
void lookAndFeelChanged() override

Source/NVGSurface.cpp

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ void NVGSurface::detachContext()
202202
void NVGSurface::updateBufferSize()
203203
{
204204
float pixelScale = getRenderScale();
205-
int scaledWidth = getWidth() * pixelScale;
206-
int scaledHeight = getHeight() * pixelScale;
205+
int scaledWidth = getWidth() * pixelScale + doubleCnvMargin;
206+
int scaledHeight = getHeight() * pixelScale + doubleCnvMargin;
207207

208208
if (fbWidth != scaledWidth || fbHeight != scaledHeight || !invalidFBO) {
209209
if (invalidFBO)
@@ -216,11 +216,12 @@ void NVGSurface::updateBufferSize()
216216

217217
if (quickCanvasBlurFBO)
218218
nvgDeleteFramebuffer(quickCanvasBlurFBO);
219+
// The blur frame buffer is Float16 RGB (no alpha) with two colour attachments for multi-pass optimized blurring
219220
quickCanvasBlurFBO = nvgCreateFramebuffer(nvg, scaledWidth, scaledHeight, NVG_IMAGE_PREMULTIPLIED | NVG_IMAGE_FLOAT | NVG_DOUBLE_COLOUR_ATTACH);
220221

221222
fbWidth = scaledWidth;
222223
fbHeight = scaledHeight;
223-
invalidArea = getLocalBounds();
224+
invalidArea = getLocalBounds().expanded(32);
224225
}
225226
}
226227

@@ -308,12 +309,12 @@ void NVGSurface::resized()
308309

309310
void NVGSurface::invalidateAll()
310311
{
311-
invalidArea = invalidArea.getUnion(getLocalBounds());
312+
invalidArea = invalidArea.getUnion(getLocalBounds().expanded(cnvMargin).translated(cnvMargin, cnvMargin));
312313
}
313314

314315
void NVGSurface::invalidateArea(Rectangle<int> area)
315316
{
316-
invalidArea = invalidArea.getUnion(area);
317+
invalidArea = invalidArea.getUnion(area.translated(cnvMargin, cnvMargin));
317318
}
318319

319320
void NVGSurface::render()
@@ -368,15 +369,15 @@ void NVGSurface::render()
368369

369370
updateBufferSize();
370371

371-
invalidArea = invalidArea.getIntersection(getLocalBounds());
372+
invalidArea = invalidArea.getIntersection(getLocalBounds().translated(cnvMargin, cnvMargin).expanded(cnvMargin));
372373

373374
if (auto* cnv = editor->getPluginModeCanvas()) {
374-
cnv->updateFramebuffers(nvg, cnv->getLocalBounds());
375+
cnv->updateFramebuffers(nvg, cnv->getLocalBounds().expanded(cnvMargin));
375376
} else {
376377
for (auto* cnv : editor->getTabComponent().getVisibleCanvases()) {
377-
cnv->updateFramebuffers(nvg, cnv->getLocalBounds());
378+
cnv->updateFramebuffers(nvg, cnv->getLocalBounds().expanded(cnvMargin));
378379
if (cnv->quickCanvas) {
379-
cnv->quickCanvas->updateFramebuffers(nvg, cnv->getLocalBounds());
380+
cnv->quickCanvas->updateFramebuffers(nvg, cnv->getLocalBounds().expanded(cnvMargin));
380381
}
381382
}
382383
}
@@ -386,48 +387,60 @@ void NVGSurface::render()
386387
if (!invalidArea.isEmpty()) {
387388
// Draw only the invalidated region on top of framebuffer
388389
nvgBindFramebuffer(invalidFBO);
389-
nvgViewport(0, 0, viewWidth, viewHeight);
390+
nvgViewport(0, 0, viewWidth + doubleCnvMargin, viewHeight + doubleCnvMargin);
390391
#if NANOVG_GL_IMPLEMENTATION
391392
glClear(GL_STENCIL_BUFFER_BIT);
392393
#endif
393-
nvgBeginFrame(nvg, getWidth() * desktopScale, getHeight() * desktopScale, devicePixelScale);
394+
nvgBeginFrame(nvg, getWidth() * desktopScale + doubleCnvMargin, getHeight() * desktopScale + doubleCnvMargin, devicePixelScale);
395+
nvgTranslate(nvg, cnvMargin, cnvMargin);
394396
nvgScale(nvg, desktopScale, desktopScale);
395397
doQuickCanvasPass = editor->renderArea(nvg, invalidArea);
398+
399+
//#define DEBUG_QUICKCANVAS_PAINT
400+
#ifdef DEBUG_QUICKCANVAS_PAINT
401+
nvgTranslate(nvg, -cnvMargin, -cnvMargin);
402+
Random random;
403+
nvgBeginPath(nvg);
404+
nvgFillColor(nvg, nvgRGBA(random.nextInt(256), random.nextInt(256), random.nextInt(256), 100));
405+
nvgFillRect(nvg, invalidArea.getX() * pixelScale, invalidArea.getY() * pixelScale, invalidArea.getWidth() * pixelScale, invalidArea.getHeight() * pixelScale);
406+
#endif
396407
nvgGlobalScissor(nvg, invalidArea.getX() * pixelScale, invalidArea.getY() * pixelScale, invalidArea.getWidth() * pixelScale, invalidArea.getHeight() * pixelScale);
408+
397409
nvgEndFrame(nvg);
398410

399411
#if ENABLE_FPS_COUNT
400412
frameTimer->render(nvg, getWidth(), getHeight(), pixelScale);
401413
#endif
402414

403-
if (doQuickCanvasPass && !invalidArea.isEmpty()) {
415+
if (doQuickCanvasPass) {
404416
for (auto cnv : editor->getTabComponent().getSplitCanvasesQuickCanvases()) {
417+
auto sX = cnv->getActiveViewport()->getX();
418+
auto sY = cnv->getActiveViewport()->getY() - 30; // height of tabbar
419+
auto sW = cnv->getActiveViewport()->getWidth();
420+
auto sH = cnv->getActiveViewport()->getHeight();
421+
// Blur the current canvas invalidFBO
422+
nvgBindFramebuffer(quickCanvasBlurFBO);
423+
nvgBlitFramebuffer(nvg, invalidFBO, 0, 0, fbWidth, fbHeight, 0, 0, fbWidth, fbHeight);
424+
425+
//glScissor(sX, sY, sW, sH);
426+
glDisable(GL_SCISSOR_TEST);
427+
nvgBlurFramebuffer(nvg, quickCanvasBlurFBO, 0, 0, fbWidth, fbHeight, fbWidth, fbHeight, cnv->quickCanvasAlpha * getValue<float>(cnv->zoomScale));
428+
glEnable(GL_SCISSOR_TEST);
405429

406430
nvgBindFramebuffer(quickCanvasFBO);
407431

408432
// Clear only the invalid region
409433
glClearColor(0, 0, 0, 0);
410434
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
411435

412-
nvgBeginFrame(nvg, getWidth() * desktopScale, getHeight() * desktopScale, devicePixelScale);
436+
nvgBeginFrame(nvg, getWidth() * desktopScale + doubleCnvMargin, getHeight() * desktopScale + doubleCnvMargin, devicePixelScale);
437+
nvgTranslate(nvg, cnvMargin, cnvMargin);
413438
nvgScale(nvg, desktopScale, desktopScale);
414439
editor->renderArea(nvg, invalidArea, true);
415440
nvgGlobalScissor(nvg, invalidArea.getX() * pixelScale, invalidArea.getY() * pixelScale, invalidArea.getWidth() * pixelScale, invalidArea.getHeight() * pixelScale);
416441

417-
//#define DEBUG_QUICKCANVAS_PAINT
418-
#ifdef DEBUG_QUICKCANVAS_PAINT
419-
Random random;
420-
nvgBeginPath(nvg);
421-
nvgFillColor(nvg, nvgRGBA(random.nextInt(256), random.nextInt(256), random.nextInt(256), 100));
422-
nvgFillRect(nvg, invalidArea.getX() * pixelScale, invalidArea.getY() * pixelScale, invalidArea.getWidth() * pixelScale, invalidArea.getHeight() * pixelScale);
423-
#endif
424442
nvgEndFrame(nvg);
425443

426-
// Blur the current canvas invalidFBO
427-
nvgBindFramebuffer(quickCanvasBlurFBO);
428-
nvgBlitFramebuffer(nvg, invalidFBO, 0, 0, viewWidth, viewHeight, 0, 0, viewWidth, viewHeight);
429-
nvgBlurFramebuffer(nvg, quickCanvasBlurFBO, fbWidth, fbHeight, cnv->quickCanvasAlpha * getValue<float>(cnv->zoomScale));
430-
431444
nvgBindFramebuffer(quickCanvasBlurFBO);
432445
nvgViewport(0, 0, fbWidth, fbHeight);
433446
nvgBeginFrame(nvg, fbWidth, fbHeight, 1);
@@ -451,10 +464,13 @@ void NVGSurface::render()
451464

452465
if (needsBufferSwap) {
453466
nvgBindFramebuffer(nullptr);
467+
glDisable(GL_SCISSOR_TEST);
454468
if (doQuickCanvasPass)
455-
nvgBlitFramebuffer(nvg, quickCanvasBlurFBO, 0, 0, viewWidth, viewHeight, 0, 0, viewWidth, viewHeight);
469+
nvgBlitFramebuffer(nvg, quickCanvasBlurFBO, cnvMargin, -cnvMargin, viewWidth, viewHeight + doubleCnvMargin, 0, 0, viewWidth, viewHeight + doubleCnvMargin);
456470
else
457-
nvgBlitFramebuffer(nvg, invalidFBO, 0, 0, viewWidth, viewHeight, 0, 0, viewWidth, viewHeight);
471+
nvgBlitFramebuffer(nvg, invalidFBO, cnvMargin, -cnvMargin, viewWidth, viewHeight + doubleCnvMargin, 0, 0, viewWidth, viewHeight + doubleCnvMargin);
472+
473+
glEnable(GL_SCISSOR_TEST);
458474

459475
#ifdef NANOVG_GL_IMPLEMENTATION
460476
glContext->swapBuffers();

Source/NVGSurface.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class NVGSurface :
5151

5252
void lookAndFeelChanged() override;
5353

54-
Rectangle<int> getInvalidArea() { return invalidArea; }
54+
Rectangle<int> getInvalidArea() { return invalidArea.translated(-cnvMargin, -cnvMargin); }
5555

5656
float getRenderScale() const;
5757

@@ -108,6 +108,9 @@ class NVGSurface :
108108

109109
void renderFrameToImage(Image& image, Rectangle<int> area);
110110

111+
inline static int cnvMargin = 32;
112+
inline static int doubleCnvMargin = cnvMargin * 2;
113+
111114
private:
112115
float calculateRenderScale() const;
113116

Source/TabComponent.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,12 @@ bool TabComponent::renderArea(NVGcontext* nvg, Rectangle<int> area, bool renderQ
609609
{
610610
bool isQuickCanvasShowing = false;
611611

612+
auto cnvMargin = NVGSurface::cnvMargin;
613+
auto cnvDoubleMargin = NVGSurface::doubleCnvMargin;
614+
612615
if (splits[0]) {
613616
NVGScopedState scopedState(nvg);
614-
nvgScissor(nvg, 0, 0, splits[1] ? (splitSize - 3) : getWidth(), getHeight());
617+
nvgScissor(nvg, -cnvMargin, -cnvMargin, splits[1] ? (splitSize - 3) : getWidth() + cnvDoubleMargin, getHeight() + cnvDoubleMargin);
615618
if (renderQuickCanvas && splits[0]->quickCanvas)
616619
splits[0]->quickCanvas->performRender(nvg, area);
617620
else

0 commit comments

Comments
 (0)