You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.1KB

  1. #include "widgets/FramebufferWidget.hpp"
  2. #include "nanovg_gl.h"
  3. #include "nanovg_gl_utils.h"
  4. namespace rack {
  5. struct FramebufferWidget::Internal {
  6. NVGLUframebuffer *fb = NULL;
  7. math::Rect box;
  8. ~Internal() {
  9. setFramebuffer(NULL);
  10. }
  11. void setFramebuffer(NVGLUframebuffer *fb) {
  12. if (this->fb)
  13. nvgluDeleteFramebuffer(this->fb);
  14. this->fb = fb;
  15. }
  16. };
  17. FramebufferWidget::FramebufferWidget() {
  18. oversample = 1.0;
  19. internal = new Internal();
  20. }
  21. FramebufferWidget::~FramebufferWidget() {
  22. delete internal;
  23. }
  24. void FramebufferWidget::draw(NVGcontext *vg) {
  25. // Bypass framebuffer rendering entirely
  26. // Widget::draw(vg);
  27. // return;
  28. // Get world transform
  29. float xform[6];
  30. nvgCurrentTransform(vg, xform);
  31. // Skew and rotate is not supported
  32. assert(std::abs(xform[1]) < 1e-6);
  33. assert(std::abs(xform[2]) < 1e-6);
  34. math::Vec s = math::Vec(xform[0], xform[3]);
  35. math::Vec b = math::Vec(xform[4], xform[5]);
  36. math::Vec bi = b.floor();
  37. math::Vec bf = b.minus(bi);
  38. // Render to framebuffer
  39. if (dirty) {
  40. dirty = false;
  41. internal->box = getChildrenBoundingBox();
  42. internal->box.pos = internal->box.pos.mult(s).floor();
  43. internal->box.size = internal->box.size.mult(s).ceil().plus(math::Vec(1, 1));
  44. math::Vec fbSize = internal->box.size.mult(gPixelRatio * oversample);
  45. if (!fbSize.isFinite())
  46. return;
  47. if (fbSize.isZero())
  48. return;
  49. // INFO("rendering framebuffer %f %f", fbSize.x, fbSize.y);
  50. // Delete old one first to free up GPU memory
  51. internal->setFramebuffer(NULL);
  52. // Create a framebuffer from the main nanovg context. We will draw to this in the secondary nanovg context.
  53. NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, fbSize.x, fbSize.y, 0);
  54. if (!fb)
  55. return;
  56. internal->setFramebuffer(fb);
  57. nvgluBindFramebuffer(fb);
  58. glViewport(0.0, 0.0, fbSize.x, fbSize.y);
  59. glClearColor(0.0, 0.0, 0.0, 0.0);
  60. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  61. nvgBeginFrame(gFramebufferVg, fbSize.x, fbSize.y, gPixelRatio * oversample);
  62. nvgScale(gFramebufferVg, gPixelRatio * oversample, gPixelRatio * oversample);
  63. // Use local scaling
  64. nvgTranslate(gFramebufferVg, bf.x, bf.y);
  65. nvgTranslate(gFramebufferVg, -internal->box.pos.x, -internal->box.pos.y);
  66. nvgScale(gFramebufferVg, s.x, s.y);
  67. Widget::draw(gFramebufferVg);
  68. nvgEndFrame(gFramebufferVg);
  69. nvgluBindFramebuffer(NULL);
  70. }
  71. if (!internal->fb) {
  72. return;
  73. }
  74. // Draw framebuffer image, using world coordinates
  75. nvgSave(vg);
  76. nvgResetTransform(vg);
  77. nvgTranslate(vg, bi.x, bi.y);
  78. nvgBeginPath(vg);
  79. nvgRect(vg, internal->box.pos.x, internal->box.pos.y, internal->box.size.x, internal->box.size.y);
  80. 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);
  81. nvgFillPaint(vg, paint);
  82. nvgFill(vg);
  83. // For debugging the bounding box of the framebuffer
  84. // nvgStrokeWidth(vg, 2.0);
  85. // nvgStrokeColor(vg, nvgRGBA(255, 0, 0, 128));
  86. // nvgStroke(vg);
  87. nvgRestore(vg);
  88. }
  89. int FramebufferWidget::getImageHandle() {
  90. if (!internal->fb)
  91. return -1;
  92. return internal->fb->image;
  93. }
  94. } // namespace rack