DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

OpenGL.cpp (21385B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #ifdef _MSC_VER
     18 // instantiated template classes whose methods are defined elsewhere
     19 # pragma warning(disable:4661)
     20 #endif
     21 
     22 #include "../OpenGL.hpp"
     23 #include "../Color.hpp"
     24 #include "../ImageWidgets.hpp"
     25 
     26 #include "SubWidgetPrivateData.hpp"
     27 #include "TopLevelWidgetPrivateData.hpp"
     28 #include "WidgetPrivateData.hpp"
     29 #include "WindowPrivateData.hpp"
     30 
     31 // templated classes
     32 #include "ImageBaseWidgets.cpp"
     33 
     34 START_NAMESPACE_DGL
     35 
     36 // -----------------------------------------------------------------------
     37 
     38 #ifdef DGL_USE_OPENGL3
     39 static void notImplemented(const char* const name)
     40 {
     41     d_stderr2("OpenGL3 function not implemented: %s", name);
     42 }
     43 #else
     44 # define DGL_USE_COMPAT_OPENGL
     45 #endif
     46 
     47 // -----------------------------------------------------------------------
     48 // Color
     49 
     50 void Color::setFor(const GraphicsContext&, const bool includeAlpha)
     51 {
     52 #ifdef DGL_USE_COMPAT_OPENGL
     53     if (includeAlpha)
     54         glColor4f(red, green, blue, alpha);
     55     else
     56         glColor3f(red, green, blue);
     57 #else
     58     notImplemented("Color::setFor");
     59     // unused
     60     (void)includeAlpha;
     61 #endif
     62 }
     63 
     64 // -----------------------------------------------------------------------
     65 // Line
     66 
     67 #ifdef DGL_USE_COMPAT_OPENGL
     68 template<typename T>
     69 static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
     70 {
     71     DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
     72 
     73     glBegin(GL_LINES);
     74 
     75     {
     76         glVertex2d(posStart.getX(), posStart.getY());
     77         glVertex2d(posEnd.getX(), posEnd.getY());
     78     }
     79 
     80     glEnd();
     81 }
     82 #endif
     83 
     84 template<typename T>
     85 void Line<T>::draw(const GraphicsContext&, const T width)
     86 {
     87 #ifdef DGL_USE_COMPAT_OPENGL
     88     DISTRHO_SAFE_ASSERT_RETURN(width != 0,);
     89 
     90     glLineWidth(static_cast<GLfloat>(width));
     91     drawLine<T>(posStart, posEnd);
     92 #else
     93     notImplemented("Line::draw");
     94 #endif
     95 }
     96 
     97 // deprecated calls
     98 template<typename T>
     99 void Line<T>::draw()
    100 {
    101 #ifdef DGL_USE_COMPAT_OPENGL
    102     drawLine<T>(posStart, posEnd);
    103 #else
    104     notImplemented("Line::draw");
    105 #endif
    106 }
    107 
    108 template class Line<double>;
    109 template class Line<float>;
    110 template class Line<int>;
    111 template class Line<uint>;
    112 template class Line<short>;
    113 template class Line<ushort>;
    114 
    115 // -----------------------------------------------------------------------
    116 // Circle
    117 
    118 #ifdef DGL_USE_COMPAT_OPENGL
    119 template<typename T>
    120 static void drawCircle(const Point<T>& pos,
    121                        const uint numSegments,
    122                        const float size,
    123                        const float sin,
    124                        const float cos,
    125                        const bool outline)
    126 {
    127     DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);
    128 
    129     const T origx = pos.getX();
    130     const T origy = pos.getY();
    131     double t, x = size, y = 0.0;
    132 
    133     glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);
    134 
    135     for (uint i=0; i<numSegments; ++i)
    136     {
    137         glVertex2d(x + origx, y + origy);
    138 
    139         t = x;
    140         x = cos * x - sin * y;
    141         y = sin * t + cos * y;
    142     }
    143 
    144     glEnd();
    145 }
    146 #endif
    147 
    148 template<typename T>
    149 void Circle<T>::draw(const GraphicsContext&)
    150 {
    151 #ifdef DGL_USE_COMPAT_OPENGL
    152     drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
    153 #else
    154     notImplemented("Circle::draw");
    155 #endif
    156 }
    157 
    158 template<typename T>
    159 void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
    160 {
    161     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    162 
    163     glLineWidth(static_cast<GLfloat>(lineWidth));
    164 #ifdef DGL_USE_COMPAT_OPENGL
    165     drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
    166 #else
    167     notImplemented("Circle::drawOutline");
    168 #endif
    169 }
    170 
    171 // deprecated calls
    172 template<typename T>
    173 void Circle<T>::draw()
    174 {
    175 #ifdef DGL_USE_COMPAT_OPENGL
    176     drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
    177 #else
    178     notImplemented("Circle::draw");
    179 #endif
    180 }
    181 
    182 template<typename T>
    183 void Circle<T>::drawOutline()
    184 {
    185 #ifdef DGL_USE_COMPAT_OPENGL
    186     drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
    187 #else
    188     notImplemented("Circle::drawOutline");
    189 #endif
    190 }
    191 
    192 template class Circle<double>;
    193 template class Circle<float>;
    194 template class Circle<int>;
    195 template class Circle<uint>;
    196 template class Circle<short>;
    197 template class Circle<ushort>;
    198 
    199 // -----------------------------------------------------------------------
    200 // Triangle
    201 
    202 #ifdef DGL_USE_COMPAT_OPENGL
    203 template<typename T>
    204 static void drawTriangle(const Point<T>& pos1,
    205                          const Point<T>& pos2,
    206                          const Point<T>& pos3,
    207                          const bool outline)
    208 {
    209     DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);
    210 
    211     glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);
    212 
    213     {
    214         glVertex2d(pos1.getX(), pos1.getY());
    215         glVertex2d(pos2.getX(), pos2.getY());
    216         glVertex2d(pos3.getX(), pos3.getY());
    217     }
    218 
    219     glEnd();
    220 }
    221 #endif
    222 
    223 template<typename T>
    224 void Triangle<T>::draw(const GraphicsContext&)
    225 {
    226 #ifdef DGL_USE_COMPAT_OPENGL
    227     drawTriangle<T>(pos1, pos2, pos3, false);
    228 #else
    229     notImplemented("Triangle::draw");
    230 #endif
    231 }
    232 
    233 template<typename T>
    234 void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
    235 {
    236     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    237 
    238     glLineWidth(static_cast<GLfloat>(lineWidth));
    239 #ifdef DGL_USE_COMPAT_OPENGL
    240     drawTriangle<T>(pos1, pos2, pos3, true);
    241 #else
    242     notImplemented("Triangle::drawOutline");
    243 #endif
    244 }
    245 
    246 // deprecated calls
    247 template<typename T>
    248 void Triangle<T>::draw()
    249 {
    250 #ifdef DGL_USE_COMPAT_OPENGL
    251     drawTriangle<T>(pos1, pos2, pos3, false);
    252 #else
    253     notImplemented("Triangle::draw");
    254 #endif
    255 }
    256 
    257 template<typename T>
    258 void Triangle<T>::drawOutline()
    259 {
    260 #ifdef DGL_USE_COMPAT_OPENGL
    261     drawTriangle<T>(pos1, pos2, pos3, true);
    262 #else
    263     notImplemented("Triangle::drawOutline");
    264 #endif
    265 }
    266 
    267 template class Triangle<double>;
    268 template class Triangle<float>;
    269 template class Triangle<int>;
    270 template class Triangle<uint>;
    271 template class Triangle<short>;
    272 template class Triangle<ushort>;
    273 
    274 // -----------------------------------------------------------------------
    275 // Rectangle
    276 
    277 #ifdef DGL_USE_COMPAT_OPENGL
    278 template<typename T>
    279 static void drawRectangle(const Rectangle<T>& rect, const bool outline)
    280 {
    281     DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);
    282 
    283     glBegin(outline ? GL_LINE_LOOP : GL_QUADS);
    284 
    285     {
    286         const T x = rect.getX();
    287         const T y = rect.getY();
    288         const T w = rect.getWidth();
    289         const T h = rect.getHeight();
    290 
    291         glTexCoord2f(0.0f, 0.0f);
    292         glVertex2d(x, y);
    293 
    294         glTexCoord2f(1.0f, 0.0f);
    295         glVertex2d(x+w, y);
    296 
    297         glTexCoord2f(1.0f, 1.0f);
    298         glVertex2d(x+w, y+h);
    299 
    300         glTexCoord2f(0.0f, 1.0f);
    301         glVertex2d(x, y+h);
    302     }
    303 
    304     glEnd();
    305 }
    306 #endif
    307 
    308 template<typename T>
    309 void Rectangle<T>::draw(const GraphicsContext&)
    310 {
    311 #ifdef DGL_USE_COMPAT_OPENGL
    312     drawRectangle<T>(*this, false);
    313 #else
    314     notImplemented("Rectangle::draw");
    315 #endif
    316 }
    317 
    318 template<typename T>
    319 void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
    320 {
    321     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    322 
    323     glLineWidth(static_cast<GLfloat>(lineWidth));
    324 #ifdef DGL_USE_COMPAT_OPENGL
    325     drawRectangle<T>(*this, true);
    326 #else
    327     notImplemented("Rectangle::drawOutline");
    328 #endif
    329 }
    330 
    331 // deprecated calls
    332 template<typename T>
    333 void Rectangle<T>::draw()
    334 {
    335 #ifdef DGL_USE_COMPAT_OPENGL
    336     drawRectangle<T>(*this, false);
    337 #else
    338     notImplemented("Rectangle::draw");
    339 #endif
    340 }
    341 
    342 template<typename T>
    343 void Rectangle<T>::drawOutline()
    344 {
    345 #ifdef DGL_USE_COMPAT_OPENGL
    346     drawRectangle<T>(*this, true);
    347 #else
    348     notImplemented("Rectangle::drawOutline");
    349 #endif
    350 }
    351 
    352 template class Rectangle<double>;
    353 template class Rectangle<float>;
    354 template class Rectangle<int>;
    355 template class Rectangle<uint>;
    356 template class Rectangle<short>;
    357 template class Rectangle<ushort>;
    358 
    359 // -----------------------------------------------------------------------
    360 // OpenGLImage
    361 
    362 static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
    363 {
    364     DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);
    365 
    366     glEnable(GL_TEXTURE_2D);
    367     glBindTexture(GL_TEXTURE_2D, textureId);
    368 
    369     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    370     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    371     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    372     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    373 
    374     static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    375     glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);
    376 
    377     glPixelStorei(GL_PACK_ALIGNMENT, 1);
    378     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    379     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
    380                  static_cast<GLsizei>(image.getWidth()),
    381                  static_cast<GLsizei>(image.getHeight()),
    382                  0,
    383                  asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());
    384 
    385     glBindTexture(GL_TEXTURE_2D, 0);
    386     glDisable(GL_TEXTURE_2D);
    387 }
    388 
    389 static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
    390 {
    391     if (textureId == 0 || image.isInvalid())
    392         return;
    393 
    394     if (! setupCalled)
    395     {
    396         setupOpenGLImage(image, textureId);
    397         setupCalled = true;
    398     }
    399 
    400 #ifdef DGL_USE_COMPAT_OPENGL
    401     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    402 #endif
    403 
    404     glEnable(GL_TEXTURE_2D);
    405     glBindTexture(GL_TEXTURE_2D, textureId);
    406 
    407 #ifdef DGL_USE_COMPAT_OPENGL
    408     glBegin(GL_QUADS);
    409 
    410     {
    411         const int x = pos.getX();
    412         const int y = pos.getY();
    413         const int w = static_cast<int>(image.getWidth());
    414         const int h = static_cast<int>(image.getHeight());
    415 
    416         glTexCoord2f(0.0f, 0.0f);
    417         glVertex2d(x, y);
    418 
    419         glTexCoord2f(1.0f, 0.0f);
    420         glVertex2d(x+w, y);
    421 
    422         glTexCoord2f(1.0f, 1.0f);
    423         glVertex2d(x+w, y+h);
    424 
    425         glTexCoord2f(0.0f, 1.0f);
    426         glVertex2d(x, y+h);
    427     }
    428 
    429     glEnd();
    430 #endif
    431 
    432     glBindTexture(GL_TEXTURE_2D, 0);
    433     glDisable(GL_TEXTURE_2D);
    434 }
    435 
    436 OpenGLImage::OpenGLImage()
    437     : ImageBase(),
    438       setupCalled(false),
    439       textureInit(false),
    440       textureId(0)
    441 {
    442 }
    443 
    444 OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
    445     : ImageBase(rdata, w, h, fmt),
    446       setupCalled(false),
    447       textureInit(true),
    448       textureId(0)
    449 {
    450     glGenTextures(1, &textureId);
    451     DISTRHO_SAFE_ASSERT(textureId != 0);
    452 }
    453 
    454 OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
    455     : ImageBase(rdata, s, fmt),
    456       setupCalled(false),
    457       textureInit(true),
    458       textureId(0)
    459 {
    460     glGenTextures(1, &textureId);
    461     DISTRHO_SAFE_ASSERT(textureId != 0);
    462 }
    463 
    464 OpenGLImage::OpenGLImage(const OpenGLImage& image)
    465     : ImageBase(image),
    466       setupCalled(false),
    467       textureInit(true),
    468       textureId(0)
    469 {
    470     glGenTextures(1, &textureId);
    471     DISTRHO_SAFE_ASSERT(textureId != 0);
    472 }
    473 
    474 OpenGLImage::~OpenGLImage()
    475 {
    476     if (textureId != 0)
    477         glDeleteTextures(1, &textureId);
    478 }
    479 
    480 void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
    481 {
    482     if (!textureInit)
    483     {
    484         textureInit = true;
    485         glGenTextures(1, &textureId);
    486         DISTRHO_SAFE_ASSERT(textureId != 0);
    487     }
    488     setupCalled = false;
    489     ImageBase::loadFromMemory(rdata, s, fmt);
    490 }
    491 
    492 void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
    493 {
    494     drawOpenGLImage(*this, pos, textureId, setupCalled);
    495 }
    496 
    497 OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
    498 {
    499     rawData = image.rawData;
    500     size    = image.size;
    501     format  = image.format;
    502     setupCalled = false;
    503 
    504     if (image.isValid() && !textureInit)
    505     {
    506         textureInit = true;
    507         glGenTextures(1, &textureId);
    508         DISTRHO_SAFE_ASSERT(textureId != 0);
    509     }
    510 
    511     return *this;
    512 }
    513 
    514 // deprecated calls
    515 OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
    516     : ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
    517       setupCalled(false),
    518       textureInit(true),
    519       textureId(0)
    520 {
    521     glGenTextures(1, &textureId);
    522     DISTRHO_SAFE_ASSERT(textureId != 0);
    523 }
    524 
    525 OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLenum fmt)
    526     : ImageBase(rdata, s, asDISTRHOImageFormat(fmt)),
    527       setupCalled(false),
    528       textureInit(true),
    529       textureId(0)
    530 {
    531     glGenTextures(1, &textureId);
    532     DISTRHO_SAFE_ASSERT(textureId != 0);
    533 }
    534 
    535 void OpenGLImage::draw()
    536 {
    537     drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
    538 }
    539 
    540 void OpenGLImage::drawAt(const int x, const int y)
    541 {
    542     drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
    543 }
    544 
    545 void OpenGLImage::drawAt(const Point<int>& pos)
    546 {
    547     drawOpenGLImage(*this, pos, textureId, setupCalled);
    548 }
    549 
    550 // -----------------------------------------------------------------------
    551 // ImageBaseAboutWindow
    552 
    553 #if 0
    554 template <>
    555 void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
    556 {
    557     const GraphicsContext& context(getGraphicsContext());
    558     img.draw(context);
    559 }
    560 #endif
    561 
    562 template class ImageBaseAboutWindow<OpenGLImage>;
    563 
    564 // -----------------------------------------------------------------------
    565 // ImageBaseButton
    566 
    567 template class ImageBaseButton<OpenGLImage>;
    568 
    569 // -----------------------------------------------------------------------
    570 // ImageBaseKnob
    571 
    572 template <>
    573 void ImageBaseKnob<OpenGLImage>::PrivateData::init()
    574 {
    575     glTextureId = 0;
    576     glGenTextures(1, &glTextureId);
    577 }
    578 
    579 template <>
    580 void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
    581 {
    582     if (glTextureId == 0)
    583         return;
    584 
    585     glDeleteTextures(1, &glTextureId);
    586     glTextureId = 0;
    587 }
    588 
    589 template <>
    590 void ImageBaseKnob<OpenGLImage>::onDisplay()
    591 {
    592     const GraphicsContext& context(getGraphicsContext());
    593     const float normValue = getNormalizedValue();
    594 
    595     glEnable(GL_TEXTURE_2D);
    596     glBindTexture(GL_TEXTURE_2D, pData->glTextureId);
    597 
    598     if (! pData->isReady)
    599     {
    600         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    601         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    602         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    603         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    604 
    605         static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    606         glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);
    607 
    608         glPixelStorei(GL_PACK_ALIGNMENT, 1);
    609         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    610 
    611         uint imageDataOffset = 0;
    612 
    613         if (pData->rotationAngle == 0)
    614         {
    615             DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
    616             DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);
    617 
    618             const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
    619             const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);
    620 
    621             // TODO kImageFormatGreyscale
    622             const uint layerDataSize   = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
    623                                                      pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
    624             /*      */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
    625         }
    626 
    627         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
    628                      static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
    629                      asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);
    630 
    631         pData->isReady = true;
    632     }
    633 
    634     const int w = static_cast<int>(getWidth());
    635     const int h = static_cast<int>(getHeight());
    636 
    637     if (pData->rotationAngle != 0)
    638     {
    639 #ifdef DGL_USE_COMPAT_OPENGL
    640         glPushMatrix();
    641 #endif
    642 
    643         const int w2 = w/2;
    644         const int h2 = h/2;
    645 
    646 #ifdef DGL_USE_COMPAT_OPENGL
    647         glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
    648         glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);
    649 #endif
    650 
    651         Rectangle<int>(-w2, -h2, w, h).draw(context);
    652 
    653 #ifdef DGL_USE_COMPAT_OPENGL
    654         glPopMatrix();
    655 #endif
    656     }
    657     else
    658     {
    659         Rectangle<int>(0, 0, w, h).draw(context);
    660     }
    661 
    662     glBindTexture(GL_TEXTURE_2D, 0);
    663     glDisable(GL_TEXTURE_2D);
    664 }
    665 
    666 template class ImageBaseKnob<OpenGLImage>;
    667 
    668 // -----------------------------------------------------------------------
    669 // ImageBaseSlider
    670 
    671 template class ImageBaseSlider<OpenGLImage>;
    672 
    673 // -----------------------------------------------------------------------
    674 // ImageBaseSwitch
    675 
    676 template class ImageBaseSwitch<OpenGLImage>;
    677 
    678 // -----------------------------------------------------------------------
    679 
    680 void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
    681 {
    682     if (skipDrawing)
    683         return;
    684 
    685     bool needsDisableScissor = false;
    686 
    687     if (needsViewportScaling)
    688     {
    689         // limit viewport to widget bounds
    690         const int x = absolutePos.getX();
    691         const int w = static_cast<int>(self->getWidth());
    692         const int h = static_cast<int>(self->getHeight());
    693 
    694         if (d_isNotZero(viewportScaleFactor) && d_isNotEqual(viewportScaleFactor, 1.0))
    695         {
    696             glViewport(x,
    697                        -d_roundToIntPositive(height * viewportScaleFactor - height + absolutePos.getY()),
    698                        d_roundToIntPositive(width * viewportScaleFactor),
    699                        d_roundToIntPositive(height * viewportScaleFactor));
    700         }
    701         else
    702         {
    703             const int y = static_cast<int>(height - self->getHeight()) - absolutePos.getY();
    704             glViewport(x, y, w, h);
    705         }
    706     }
    707     else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
    708     {
    709         // full viewport size
    710         glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
    711     }
    712     else
    713     {
    714         // set viewport pos
    715         glViewport(d_roundToIntPositive(absolutePos.getX() * autoScaleFactor),
    716                    -d_roundToIntPositive(absolutePos.getY() * autoScaleFactor),
    717                    static_cast<int>(width),
    718                    static_cast<int>(height));
    719 
    720         // then cut the outer bounds
    721         glScissor(d_roundToIntPositive(absolutePos.getX() * autoScaleFactor),
    722                   d_roundToIntPositive(height - (static_cast<int>(self->getHeight()) + absolutePos.getY()) * autoScaleFactor),
    723                   d_roundToIntPositive(self->getWidth() * autoScaleFactor),
    724                   d_roundToIntPositive(self->getHeight() * autoScaleFactor));
    725 
    726         glEnable(GL_SCISSOR_TEST);
    727         needsDisableScissor = true;
    728     }
    729 
    730     // display widget
    731     self->onDisplay();
    732 
    733     if (needsDisableScissor)
    734         glDisable(GL_SCISSOR_TEST);
    735 
    736     selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
    737 }
    738 
    739 // -----------------------------------------------------------------------
    740 
    741 void TopLevelWidget::PrivateData::display()
    742 {
    743     if (! selfw->pData->visible)
    744         return;
    745 
    746     const Size<uint> size(window.getSize());
    747     const uint width  = size.getWidth();
    748     const uint height = size.getHeight();
    749 
    750     // full viewport size
    751     glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
    752 
    753     // main widget drawing
    754     self->onDisplay();
    755 
    756     // now draw subwidgets if there are any
    757     selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor);
    758 }
    759 
    760 // -----------------------------------------------------------------------
    761 
    762 void Window::PrivateData::renderToPicture(const char* const filename,
    763                                           const GraphicsContext&,
    764                                           const uint width,
    765                                           const uint height)
    766 {
    767     FILE* const f = fopen(filename, "w");
    768     DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
    769 
    770     GLubyte* const pixels = new GLubyte[width * height * 3 * sizeof(GLubyte)];
    771 
    772     glFlush();
    773     glReadPixels(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGB, GL_UNSIGNED_BYTE, pixels);
    774 
    775     fprintf(f, "P3\n%d %d\n255\n", width, height);
    776     for (uint y = 0; y < height; y++)
    777     {
    778         for (uint i, x = 0; x < width; x++)
    779         {
    780             i = 3 * ((height - y - 1) * width + x);
    781             fprintf(f, "%3d %3d %3d ", pixels[i], pixels[i+1], pixels[i+2]);
    782         }
    783         fprintf(f, "\n");
    784     }
    785 
    786     delete[] pixels;
    787     fclose(f);
    788 }
    789 
    790 // -----------------------------------------------------------------------
    791 
    792 const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
    793 {
    794     return (const GraphicsContext&)graphicsContext;
    795 }
    796 
    797 // -----------------------------------------------------------------------
    798 
    799 END_NAMESPACE_DGL