Browse Source

Add FramebufferWidget::viewportMargin.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
d7096ae631
4 changed files with 31 additions and 12 deletions
  1. +6
    -4
      include/math.hpp
  2. +5
    -1
      include/widget/FramebufferWidget.hpp
  3. +1
    -1
      src/Window.cpp
  4. +19
    -6
      src/widget/FramebufferWidget.cpp

+ 6
- 4
include/math.hpp View File

@@ -435,14 +435,16 @@ struct Rect {

inline Vec Vec::clamp(Rect bound) const {
return Vec(
math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x),
math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y));
math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x),
math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)
);
}

inline Vec Vec::clampSafe(Rect bound) const {
return Vec(
math::clampSafe(x, bound.pos.x, bound.pos.x + bound.size.x),
math::clampSafe(y, bound.pos.y, bound.pos.y + bound.size.y));
math::clampSafe(x, bound.pos.x, bound.pos.x + bound.size.x),
math::clampSafe(y, bound.pos.y, bound.pos.y + bound.size.y)
);
}




+ 5
- 1
include/widget/FramebufferWidget.hpp View File

@@ -19,6 +19,10 @@ struct FramebufferWidget : Widget {
float oversample = 1.0;
/** Redraw when the world offset of the FramebufferWidget changes its fractional value. */
bool dirtyOnSubpixelChange = true;
/** If finite, the maximum size of the framebuffer is the viewport expanded by this margin.
The framebuffer is re-rendered when the viewport moves outside the margin.
*/
math::Vec viewportMargin = math::Vec(INFINITY, INFINITY);

FramebufferWidget();
~FramebufferWidget();
@@ -35,7 +39,7 @@ struct FramebufferWidget : Widget {
/** Re-renders the framebuffer, re-creating it if necessary.
Handles oversampling (if >1) by rendering to a temporary (larger) framebuffer and then downscaling it to the main persistent framebuffer.
*/
void render(math::Vec scale, math::Vec offsetF);
void render(math::Vec scale = math::Vec(1, 1), math::Vec offsetF = math::Vec(0, 0), math::Rect clipBox = math::Rect::inf());
/** Initializes the current GL context and draws children to it.
*/
virtual void drawFramebuffer();


+ 1
- 1
src/Window.cpp View File

@@ -566,7 +566,7 @@ void Window::screenshotModules(const std::string& screenshotsDir, float zoom) {
fbw->step();

// Draw to framebuffer
fbw->render(math::Vec(zoom, zoom), math::Vec(0, 0));
fbw->render(math::Vec(zoom, zoom));

// Read pixels
nvgluBindFramebuffer(fbw->getFramebuffer());


+ 19
- 6
src/widget/FramebufferWidget.cpp View File

@@ -23,6 +23,9 @@ struct FramebufferWidget::Internal {
math::Vec fbScale;
/** Framebuffer's subpixel offset relative to fbBox in world coordinates */
math::Vec fbOffsetF;
/** Local box where framebuffer content is valid.
*/
math::Rect fbClipBox = math::Rect::inf();
};


@@ -100,22 +103,26 @@ void FramebufferWidget::draw(const DrawArgs& args) {
math::Vec offsetI = offset.floor();
math::Vec offsetF = offset.minus(offsetI);

// If drawing to a new subpixel location, rerender in the next frame.
// Re-render if drawing to a new subpixel location.
// Anything less than 0.1 pixels isn't noticeable.
math::Vec offsetFDelta = offsetF.minus(internal->fbOffsetF);
if (dirtyOnSubpixelChange && APP->window->fbDirtyOnSubpixelChange() && offsetFDelta.square() >= std::pow(0.1f, 2)) {
// DEBUG("%p dirty subpixel (%f, %f) (%f, %f)", this, VEC_ARGS(offsetF), VEC_ARGS(internal->fbOffsetF));
setDirty();
}
if (!scale.equals(internal->fbScale)) {
// If rescaled, rerender in the next frame.
// Re-render if rescaled.
else if (!scale.equals(internal->fbScale)) {
// DEBUG("%p dirty scale", this);
setDirty();
}
// Re-render if viewport is outside framebuffer's clipbox when it was rendered.
else if (!internal->fbClipBox.contains(args.clipBox)) {
setDirty();
}

// It's more important to not lag the frame than to draw the framebuffer
if (dirty && APP->window->getFrameDurationRemaining() > 0.0) {
render(scale, offsetF);
render(scale, offsetF, args.clipBox);
}

if (!internal->fb)
@@ -153,7 +160,7 @@ void FramebufferWidget::draw(const DrawArgs& args) {
}


void FramebufferWidget::render(math::Vec scale, math::Vec offsetF) {
void FramebufferWidget::render(math::Vec scale, math::Vec offsetF, math::Rect clipBox) {
// In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here.
dirty = false;
NVGcontext* vg = APP->window->vg;
@@ -170,7 +177,13 @@ void FramebufferWidget::render(math::Vec scale, math::Vec offsetF) {
localBox = getVisibleChildrenBoundingBox();
}

// DEBUG("rendering FramebufferWidget localBox (%g %g %g %g) fbOffset (%g %g) fbScale (%g %g)", RECT_ARGS(localBox), VEC_ARGS(internal->fbOffsetF), VEC_ARGS(internal->fbScale));
// Intersect local box with viewport if viewportMargin is set
internal->fbClipBox = clipBox.grow(viewportMargin);
if (internal->fbClipBox.size.isFinite()) {
localBox = localBox.intersect(internal->fbClipBox);
}

// DEBUG("rendering FramebufferWidget localBox (%f, %f; %f, %f) fbOffset (%f, %f) fbScale (%f, %f)", RECT_ARGS(localBox), VEC_ARGS(internal->fbOffsetF), VEC_ARGS(internal->fbScale));
// Transform to world coordinates, then expand to nearest integer coordinates
math::Vec min = localBox.getTopLeft().mult(internal->fbScale).plus(internal->fbOffsetF).floor();
math::Vec max = localBox.getBottomRight().mult(internal->fbScale).plus(internal->fbOffsetF).ceil();


Loading…
Cancel
Save