diff DPF-Prymula-audioplugins/dpf/dgl/src/OpenGL.cpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DPF-Prymula-audioplugins/dpf/dgl/src/OpenGL.cpp	Mon Oct 16 21:53:34 2023 +0200
@@ -0,0 +1,816 @@
+/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with
+ * or without fee is hereby granted, provided that the above copyright notice and this
+ * permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef _MSC_VER
+// instantiated template classes whose methods are defined elsewhere
+# pragma warning(disable:4661)
+#endif
+
+#include "../OpenGL.hpp"
+#include "../Color.hpp"
+#include "../ImageWidgets.hpp"
+
+#include "SubWidgetPrivateData.hpp"
+#include "TopLevelWidgetPrivateData.hpp"
+#include "WidgetPrivateData.hpp"
+#include "WindowPrivateData.hpp"
+
+// templated classes
+#include "ImageBaseWidgets.cpp"
+
+START_NAMESPACE_DGL
+
+// -----------------------------------------------------------------------
+
+#ifdef DGL_USE_OPENGL3
+static void notImplemented(const char* const name)
+{
+    d_stderr2("OpenGL3 function not implemented: %s", name);
+}
+#else
+# define DGL_USE_COMPAT_OPENGL
+#endif
+
+// -----------------------------------------------------------------------
+// Color
+
+void Color::setFor(const GraphicsContext&, const bool includeAlpha)
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    if (includeAlpha)
+        glColor4f(red, green, blue, alpha);
+    else
+        glColor3f(red, green, blue);
+#else
+    notImplemented("Color::setFor");
+    // unused
+    (void)includeAlpha;
+#endif
+}
+
+// -----------------------------------------------------------------------
+// Line
+
+#ifdef DGL_USE_COMPAT_OPENGL
+template<typename T>
+static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
+
+    glBegin(GL_LINES);
+
+    {
+        glVertex2d(posStart.getX(), posStart.getY());
+        glVertex2d(posEnd.getX(), posEnd.getY());
+    }
+
+    glEnd();
+}
+#endif
+
+template<typename T>
+void Line<T>::draw(const GraphicsContext&, const T width)
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    DISTRHO_SAFE_ASSERT_RETURN(width != 0,);
+
+    glLineWidth(static_cast<GLfloat>(width));
+    drawLine<T>(posStart, posEnd);
+#else
+    notImplemented("Line::draw");
+#endif
+}
+
+// deprecated calls
+template<typename T>
+void Line<T>::draw()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawLine<T>(posStart, posEnd);
+#else
+    notImplemented("Line::draw");
+#endif
+}
+
+template class Line<double>;
+template class Line<float>;
+template class Line<int>;
+template class Line<uint>;
+template class Line<short>;
+template class Line<ushort>;
+
+// -----------------------------------------------------------------------
+// Circle
+
+#ifdef DGL_USE_COMPAT_OPENGL
+template<typename T>
+static void drawCircle(const Point<T>& pos,
+                       const uint numSegments,
+                       const float size,
+                       const float sin,
+                       const float cos,
+                       const bool outline)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);
+
+    const T origx = pos.getX();
+    const T origy = pos.getY();
+    double t, x = size, y = 0.0;
+
+    glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);
+
+    for (uint i=0; i<numSegments; ++i)
+    {
+        glVertex2d(x + origx, y + origy);
+
+        t = x;
+        x = cos * x - sin * y;
+        y = sin * t + cos * y;
+    }
+
+    glEnd();
+}
+#endif
+
+template<typename T>
+void Circle<T>::draw(const GraphicsContext&)
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
+#else
+    notImplemented("Circle::draw");
+#endif
+}
+
+template<typename T>
+void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
+
+    glLineWidth(static_cast<GLfloat>(lineWidth));
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
+#else
+    notImplemented("Circle::drawOutline");
+#endif
+}
+
+// deprecated calls
+template<typename T>
+void Circle<T>::draw()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
+#else
+    notImplemented("Circle::draw");
+#endif
+}
+
+template<typename T>
+void Circle<T>::drawOutline()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
+#else
+    notImplemented("Circle::drawOutline");
+#endif
+}
+
+template class Circle<double>;
+template class Circle<float>;
+template class Circle<int>;
+template class Circle<uint>;
+template class Circle<short>;
+template class Circle<ushort>;
+
+// -----------------------------------------------------------------------
+// Triangle
+
+#ifdef DGL_USE_COMPAT_OPENGL
+template<typename T>
+static void drawTriangle(const Point<T>& pos1,
+                         const Point<T>& pos2,
+                         const Point<T>& pos3,
+                         const bool outline)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);
+
+    glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);
+
+    {
+        glVertex2d(pos1.getX(), pos1.getY());
+        glVertex2d(pos2.getX(), pos2.getY());
+        glVertex2d(pos3.getX(), pos3.getY());
+    }
+
+    glEnd();
+}
+#endif
+
+template<typename T>
+void Triangle<T>::draw(const GraphicsContext&)
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawTriangle<T>(pos1, pos2, pos3, false);
+#else
+    notImplemented("Triangle::draw");
+#endif
+}
+
+template<typename T>
+void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
+
+    glLineWidth(static_cast<GLfloat>(lineWidth));
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawTriangle<T>(pos1, pos2, pos3, true);
+#else
+    notImplemented("Triangle::drawOutline");
+#endif
+}
+
+// deprecated calls
+template<typename T>
+void Triangle<T>::draw()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawTriangle<T>(pos1, pos2, pos3, false);
+#else
+    notImplemented("Triangle::draw");
+#endif
+}
+
+template<typename T>
+void Triangle<T>::drawOutline()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawTriangle<T>(pos1, pos2, pos3, true);
+#else
+    notImplemented("Triangle::drawOutline");
+#endif
+}
+
+template class Triangle<double>;
+template class Triangle<float>;
+template class Triangle<int>;
+template class Triangle<uint>;
+template class Triangle<short>;
+template class Triangle<ushort>;
+
+// -----------------------------------------------------------------------
+// Rectangle
+
+#ifdef DGL_USE_COMPAT_OPENGL
+template<typename T>
+static void drawRectangle(const Rectangle<T>& rect, const bool outline)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);
+
+    glBegin(outline ? GL_LINE_LOOP : GL_QUADS);
+
+    {
+        const T x = rect.getX();
+        const T y = rect.getY();
+        const T w = rect.getWidth();
+        const T h = rect.getHeight();
+
+        glTexCoord2f(0.0f, 0.0f);
+        glVertex2d(x, y);
+
+        glTexCoord2f(1.0f, 0.0f);
+        glVertex2d(x+w, y);
+
+        glTexCoord2f(1.0f, 1.0f);
+        glVertex2d(x+w, y+h);
+
+        glTexCoord2f(0.0f, 1.0f);
+        glVertex2d(x, y+h);
+    }
+
+    glEnd();
+}
+#endif
+
+template<typename T>
+void Rectangle<T>::draw(const GraphicsContext&)
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawRectangle<T>(*this, false);
+#else
+    notImplemented("Rectangle::draw");
+#endif
+}
+
+template<typename T>
+void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
+
+    glLineWidth(static_cast<GLfloat>(lineWidth));
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawRectangle<T>(*this, true);
+#else
+    notImplemented("Rectangle::drawOutline");
+#endif
+}
+
+// deprecated calls
+template<typename T>
+void Rectangle<T>::draw()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawRectangle<T>(*this, false);
+#else
+    notImplemented("Rectangle::draw");
+#endif
+}
+
+template<typename T>
+void Rectangle<T>::drawOutline()
+{
+#ifdef DGL_USE_COMPAT_OPENGL
+    drawRectangle<T>(*this, true);
+#else
+    notImplemented("Rectangle::drawOutline");
+#endif
+}
+
+template class Rectangle<double>;
+template class Rectangle<float>;
+template class Rectangle<int>;
+template class Rectangle<uint>;
+template class Rectangle<short>;
+template class Rectangle<ushort>;
+
+// -----------------------------------------------------------------------
+// OpenGLImage
+
+static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);
+
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, textureId);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+
+    static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);
+
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                 static_cast<GLsizei>(image.getWidth()),
+                 static_cast<GLsizei>(image.getHeight()),
+                 0,
+                 asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+}
+
+static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
+{
+    if (textureId == 0 || image.isInvalid())
+        return;
+
+    if (! setupCalled)
+    {
+        setupOpenGLImage(image, textureId);
+        setupCalled = true;
+    }
+
+#ifdef DGL_USE_COMPAT_OPENGL
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+#endif
+
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, textureId);
+
+#ifdef DGL_USE_COMPAT_OPENGL
+    glBegin(GL_QUADS);
+
+    {
+        const int x = pos.getX();
+        const int y = pos.getY();
+        const int w = static_cast<int>(image.getWidth());
+        const int h = static_cast<int>(image.getHeight());
+
+        glTexCoord2f(0.0f, 0.0f);
+        glVertex2d(x, y);
+
+        glTexCoord2f(1.0f, 0.0f);
+        glVertex2d(x+w, y);
+
+        glTexCoord2f(1.0f, 1.0f);
+        glVertex2d(x+w, y+h);
+
+        glTexCoord2f(0.0f, 1.0f);
+        glVertex2d(x, y+h);
+    }
+
+    glEnd();
+#endif
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+}
+
+OpenGLImage::OpenGLImage()
+    : ImageBase(),
+      setupCalled(false),
+      textureInit(false),
+      textureId(0)
+{
+}
+
+OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
+    : ImageBase(rdata, w, h, fmt),
+      setupCalled(false),
+      textureInit(true),
+      textureId(0)
+{
+    glGenTextures(1, &textureId);
+    DISTRHO_SAFE_ASSERT(textureId != 0);
+}
+
+OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
+    : ImageBase(rdata, s, fmt),
+      setupCalled(false),
+      textureInit(true),
+      textureId(0)
+{
+    glGenTextures(1, &textureId);
+    DISTRHO_SAFE_ASSERT(textureId != 0);
+}
+
+OpenGLImage::OpenGLImage(const OpenGLImage& image)
+    : ImageBase(image),
+      setupCalled(false),
+      textureInit(true),
+      textureId(0)
+{
+    glGenTextures(1, &textureId);
+    DISTRHO_SAFE_ASSERT(textureId != 0);
+}
+
+OpenGLImage::~OpenGLImage()
+{
+    if (textureId != 0)
+        glDeleteTextures(1, &textureId);
+}
+
+void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
+{
+    if (!textureInit)
+    {
+        textureInit = true;
+        glGenTextures(1, &textureId);
+        DISTRHO_SAFE_ASSERT(textureId != 0);
+    }
+    setupCalled = false;
+    ImageBase::loadFromMemory(rdata, s, fmt);
+}
+
+void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
+{
+    drawOpenGLImage(*this, pos, textureId, setupCalled);
+}
+
+OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
+{
+    rawData = image.rawData;
+    size    = image.size;
+    format  = image.format;
+    setupCalled = false;
+
+    if (image.isValid() && !textureInit)
+    {
+        textureInit = true;
+        glGenTextures(1, &textureId);
+        DISTRHO_SAFE_ASSERT(textureId != 0);
+    }
+
+    return *this;
+}
+
+// deprecated calls
+OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
+    : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
+      setupCalled(false),
+      textureInit(true),
+      textureId(0)
+{
+    glGenTextures(1, &textureId);
+    DISTRHO_SAFE_ASSERT(textureId != 0);
+}
+
+OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt)
+    : ImageBase(rdata, s, asDISTRHOImageFormat(fmt)),
+      setupCalled(false),
+      textureInit(true),
+      textureId(0)
+{
+    glGenTextures(1, &textureId);
+    DISTRHO_SAFE_ASSERT(textureId != 0);
+}
+
+void OpenGLImage::draw()
+{
+    drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
+}
+
+void OpenGLImage::drawAt(const int x, const int y)
+{
+    drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
+}
+
+void OpenGLImage::drawAt(const Point<int>& pos)
+{
+    drawOpenGLImage(*this, pos, textureId, setupCalled);
+}
+
+// -----------------------------------------------------------------------
+// ImageBaseAboutWindow
+
+#if 0
+template <>
+void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
+{
+    const GraphicsContext& context(getGraphicsContext());
+    img.draw(context);
+}
+#endif
+
+template class ImageBaseAboutWindow<OpenGLImage>;
+
+// -----------------------------------------------------------------------
+// ImageBaseButton
+
+template class ImageBaseButton<OpenGLImage>;
+
+// -----------------------------------------------------------------------
+// ImageBaseKnob
+
+template <>
+void ImageBaseKnob<OpenGLImage>::PrivateData::init()
+{
+    glTextureId = 0;
+    glGenTextures(1, &glTextureId);
+}
+
+template <>
+void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
+{
+    if (glTextureId == 0)
+        return;
+
+    glDeleteTextures(1, &glTextureId);
+    glTextureId = 0;
+}
+
+template <>
+void ImageBaseKnob<OpenGLImage>::onDisplay()
+{
+    const GraphicsContext& context(getGraphicsContext());
+    const float normValue = getNormalizedValue();
+
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, pData->glTextureId);
+
+    if (! pData->isReady)
+    {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+
+        static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);
+
+        glPixelStorei(GL_PACK_ALIGNMENT, 1);
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+        uint imageDataOffset = 0;
+
+        if (pData->rotationAngle == 0)
+        {
+            DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
+            DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);
+
+            const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
+            const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);
+
+            // TODO kImageFormatGreyscale
+            const uint layerDataSize   = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
+                                                     pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
+            /*      */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
+        }
+
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                     static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
+                     asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);
+
+        pData->isReady = true;
+    }
+
+    const int w = static_cast<int>(getWidth());
+    const int h = static_cast<int>(getHeight());
+
+    if (pData->rotationAngle != 0)
+    {
+#ifdef DGL_USE_COMPAT_OPENGL
+        glPushMatrix();
+#endif
+
+        const int w2 = w/2;
+        const int h2 = h/2;
+
+#ifdef DGL_USE_COMPAT_OPENGL
+        glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
+        glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);
+#endif
+
+        Rectangle<int>(-w2, -h2, w, h).draw(context);
+
+#ifdef DGL_USE_COMPAT_OPENGL
+        glPopMatrix();
+#endif
+    }
+    else
+    {
+        Rectangle<int>(0, 0, w, h).draw(context);
+    }
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+}
+
+template class ImageBaseKnob<OpenGLImage>;
+
+// -----------------------------------------------------------------------
+// ImageBaseSlider
+
+template class ImageBaseSlider<OpenGLImage>;
+
+// -----------------------------------------------------------------------
+// ImageBaseSwitch
+
+template class ImageBaseSwitch<OpenGLImage>;
+
+// -----------------------------------------------------------------------
+
+void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
+{
+    if (skipDrawing)
+        return;
+
+    bool needsDisableScissor = false;
+
+    if (needsViewportScaling)
+    {
+        // limit viewport to widget bounds
+        const int x = absolutePos.getX();
+        const int w = static_cast<int>(self->getWidth());
+        const int h = static_cast<int>(self->getHeight());
+
+        if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0)
+        {
+            glViewport(x,
+                       -static_cast<int>(height * viewportScaleFactor - height + absolutePos.getY() + 0.5),
+                       static_cast<int>(width * viewportScaleFactor + 0.5),
+                       static_cast<int>(height * viewportScaleFactor + 0.5));
+        }
+        else
+        {
+            const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY();
+            glViewport(x, y, w, h);
+        }
+    }
+    else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
+    {
+        // full viewport size
+        glViewport(0,
+                   -static_cast<int>(height * autoScaleFactor - height + 0.5),
+                   static_cast<int>(width * autoScaleFactor + 0.5),
+                   static_cast<int>(height * autoScaleFactor + 0.5));
+    }
+    else
+    {
+        // set viewport pos
+        glViewport(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
+                   -static_cast<int>(std::round((height * autoScaleFactor - height)
+                                     + (absolutePos.getY() * autoScaleFactor))),
+                   static_cast<int>(std::round(width * autoScaleFactor)),
+                   static_cast<int>(std::round(height * autoScaleFactor)));
+
+        // then cut the outer bounds
+        glScissor(static_cast<int>(absolutePos.getX() * autoScaleFactor + 0.5),
+                  static_cast<int>(height - std::round((static_cast<int>(self->getHeight()) + absolutePos.getY())
+                                                       * autoScaleFactor)),
+                  static_cast<int>(std::round(self->getWidth() * autoScaleFactor)),
+                  static_cast<int>(std::round(self->getHeight() * autoScaleFactor)));
+
+        glEnable(GL_SCISSOR_TEST);
+        needsDisableScissor = true;
+    }
+
+    // display widget
+    self->onDisplay();
+
+    if (needsDisableScissor)
+        glDisable(GL_SCISSOR_TEST);
+
+    selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
+}
+
+// -----------------------------------------------------------------------
+
+void TopLevelWidget::PrivateData::display()
+{
+    if (! selfw->pData->visible)
+        return;
+
+    const Size<uint> size(window.getSize());
+    const uint width  = size.getWidth();
+    const uint height = size.getHeight();
+
+    const double autoScaleFactor = window.pData->autoScaleFactor;
+
+    // full viewport size
+    if (window.pData->autoScaling)
+    {
+        glViewport(0,
+                   -static_cast<int>(height * autoScaleFactor - height + 0.5),
+                   static_cast<int>(width * autoScaleFactor + 0.5),
+                   static_cast<int>(height * autoScaleFactor + 0.5));
+    }
+    else
+    {
+        glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
+    }
+
+    // main widget drawing
+    self->onDisplay();
+
+    // now draw subwidgets if there are any
+    selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
+}
+
+// -----------------------------------------------------------------------
+
+void Window::PrivateData::renderToPicture(const char* const filename,
+                                          const GraphicsContext&,
+                                          const uint width,
+                                          const uint height)
+{
+    FILE* const f = fopen(filename, "w");
+    DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
+
+    GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)];
+
+    glFlush();
+    glReadPixels(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGB, GL_UNSIGNED_BYTE, pixels);
+
+    fprintf(f, "P3\n%d %d\n255\n", width, height);
+    for (uint y = 0; y < height; y++)
+    {
+        for (uint i, x = 0; x < width; x++)
+        {
+            i = 3 * ((height - y - 1) * width + x);
+            fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]);
+        }
+        fprintf(f, "\n");
+    }
+
+    delete[] pixels;
+    fclose(f);
+}
+
+// -----------------------------------------------------------------------
+
+const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
+{
+    return (const GraphicsContext&)graphicsContext;
+}
+
+// -----------------------------------------------------------------------
+
+END_NAMESPACE_DGL