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.

130 lines
3.2KB

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