2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
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.
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.
18 // instantiated template classes whose methods are defined elsewhere
19 # pragma warning(disable:4661)
22 #include "../OpenGL.hpp"
23 #include "../Color.hpp"
24 #include "../ImageWidgets.hpp"
26 #include "SubWidgetPrivateData.hpp"
27 #include "TopLevelWidgetPrivateData.hpp"
28 #include "WidgetPrivateData.hpp"
29 #include "WindowPrivateData.hpp"
32 #include "ImageBaseWidgets.cpp"
36 // -----------------------------------------------------------------------
38 #if defined(DGL_USE_GLES2)
39 static void notImplemented(const char* const name
)
41 // d_stderr2("GLES2 function not implemented: %s", name);
43 #elif defined(DGL_USE_GLES3)
44 static void notImplemented(const char* const name
)
46 d_stderr2("GLES3 function not implemented: %s", name
);
48 #elif defined(DGL_USE_OPENGL3)
49 static void notImplemented(const char* const name
)
51 d_stderr2("OpenGL3 function not implemented: %s", name
);
54 # define DGL_USE_COMPAT_OPENGL
57 // -----------------------------------------------------------------------
60 void Color::setFor(const GraphicsContext
&, const bool includeAlpha
)
62 #ifdef DGL_USE_COMPAT_OPENGL
64 glColor4f(red
, green
, blue
, alpha
);
66 glColor3f(red
, green
, blue
);
68 notImplemented("Color::setFor");
74 // -----------------------------------------------------------------------
77 #ifdef DGL_USE_COMPAT_OPENGL
79 static void drawLine(const Point
<T
>& posStart
, const Point
<T
>& posEnd
)
81 DISTRHO_SAFE_ASSERT_RETURN(posStart
!= posEnd
,);
86 glVertex2d(posStart
.getX(), posStart
.getY());
87 glVertex2d(posEnd
.getX(), posEnd
.getY());
95 void Line
<T
>::draw(const GraphicsContext
&, const T width
)
97 #ifdef DGL_USE_COMPAT_OPENGL
98 DISTRHO_SAFE_ASSERT_RETURN(width
!= 0,);
100 glLineWidth(static_cast<GLfloat
>(width
));
101 drawLine
<T
>(posStart
, posEnd
);
103 notImplemented("Line::draw");
111 #ifdef DGL_USE_COMPAT_OPENGL
112 drawLine
<T
>(posStart
, posEnd
);
114 notImplemented("Line::draw");
118 template class Line
<double>;
119 template class Line
<float>;
120 template class Line
<int>;
121 template class Line
<uint
>;
122 template class Line
<short>;
123 template class Line
<ushort
>;
125 // -----------------------------------------------------------------------
128 #ifdef DGL_USE_COMPAT_OPENGL
130 static void drawCircle(const Point
<T
>& pos
,
131 const uint numSegments
,
137 DISTRHO_SAFE_ASSERT_RETURN(numSegments
>= 3 && size
> 0.0f
,);
139 const T origx
= pos
.getX();
140 const T origy
= pos
.getY();
141 double t
, x
= size
, y
= 0.0;
143 glBegin(outline
? GL_LINE_LOOP
: GL_POLYGON
);
145 for (uint i
=0; i
<numSegments
; ++i
)
147 glVertex2d(x
+ origx
, y
+ origy
);
150 x
= cos
* x
- sin
* y
;
151 y
= sin
* t
+ cos
* y
;
159 void Circle
<T
>::draw(const GraphicsContext
&)
161 #ifdef DGL_USE_COMPAT_OPENGL
162 drawCircle
<T
>(fPos
, fNumSegments
, fSize
, fSin
, fCos
, false);
164 notImplemented("Circle::draw");
169 void Circle
<T
>::drawOutline(const GraphicsContext
&, const T lineWidth
)
171 DISTRHO_SAFE_ASSERT_RETURN(lineWidth
!= 0,);
173 glLineWidth(static_cast<GLfloat
>(lineWidth
));
174 #ifdef DGL_USE_COMPAT_OPENGL
175 drawCircle
<T
>(fPos
, fNumSegments
, fSize
, fSin
, fCos
, true);
177 notImplemented("Circle::drawOutline");
183 void Circle
<T
>::draw()
185 #ifdef DGL_USE_COMPAT_OPENGL
186 drawCircle
<T
>(fPos
, fNumSegments
, fSize
, fSin
, fCos
, false);
188 notImplemented("Circle::draw");
193 void Circle
<T
>::drawOutline()
195 #ifdef DGL_USE_COMPAT_OPENGL
196 drawCircle
<T
>(fPos
, fNumSegments
, fSize
, fSin
, fCos
, true);
198 notImplemented("Circle::drawOutline");
202 template class Circle
<double>;
203 template class Circle
<float>;
204 template class Circle
<int>;
205 template class Circle
<uint
>;
206 template class Circle
<short>;
207 template class Circle
<ushort
>;
209 // -----------------------------------------------------------------------
212 #ifdef DGL_USE_COMPAT_OPENGL
214 static void drawTriangle(const Point
<T
>& pos1
,
215 const Point
<T
>& pos2
,
216 const Point
<T
>& pos3
,
219 DISTRHO_SAFE_ASSERT_RETURN(pos1
!= pos2
&& pos1
!= pos3
,);
221 glBegin(outline
? GL_LINE_LOOP
: GL_TRIANGLES
);
224 glVertex2d(pos1
.getX(), pos1
.getY());
225 glVertex2d(pos2
.getX(), pos2
.getY());
226 glVertex2d(pos3
.getX(), pos3
.getY());
234 void Triangle
<T
>::draw(const GraphicsContext
&)
236 #ifdef DGL_USE_COMPAT_OPENGL
237 drawTriangle
<T
>(pos1
, pos2
, pos3
, false);
239 notImplemented("Triangle::draw");
244 void Triangle
<T
>::drawOutline(const GraphicsContext
&, const T lineWidth
)
246 DISTRHO_SAFE_ASSERT_RETURN(lineWidth
!= 0,);
248 glLineWidth(static_cast<GLfloat
>(lineWidth
));
249 #ifdef DGL_USE_COMPAT_OPENGL
250 drawTriangle
<T
>(pos1
, pos2
, pos3
, true);
252 notImplemented("Triangle::drawOutline");
258 void Triangle
<T
>::draw()
260 #ifdef DGL_USE_COMPAT_OPENGL
261 drawTriangle
<T
>(pos1
, pos2
, pos3
, false);
263 notImplemented("Triangle::draw");
268 void Triangle
<T
>::drawOutline()
270 #ifdef DGL_USE_COMPAT_OPENGL
271 drawTriangle
<T
>(pos1
, pos2
, pos3
, true);
273 notImplemented("Triangle::drawOutline");
277 template class Triangle
<double>;
278 template class Triangle
<float>;
279 template class Triangle
<int>;
280 template class Triangle
<uint
>;
281 template class Triangle
<short>;
282 template class Triangle
<ushort
>;
284 // -----------------------------------------------------------------------
287 #ifdef DGL_USE_COMPAT_OPENGL
289 static void drawRectangle(const Rectangle
<T
>& rect
, const bool outline
)
291 DISTRHO_SAFE_ASSERT_RETURN(rect
.isValid(),);
293 glBegin(outline
? GL_LINE_LOOP
: GL_QUADS
);
296 const T x
= rect
.getX();
297 const T y
= rect
.getY();
298 const T w
= rect
.getWidth();
299 const T h
= rect
.getHeight();
301 glTexCoord2f(0.0f
, 0.0f
);
304 glTexCoord2f(1.0f
, 0.0f
);
307 glTexCoord2f(1.0f
, 1.0f
);
308 glVertex2d(x
+w
, y
+h
);
310 glTexCoord2f(0.0f
, 1.0f
);
319 void Rectangle
<T
>::draw(const GraphicsContext
&)
321 #ifdef DGL_USE_COMPAT_OPENGL
322 drawRectangle
<T
>(*this, false);
324 notImplemented("Rectangle::draw");
329 void Rectangle
<T
>::drawOutline(const GraphicsContext
&, const T lineWidth
)
331 DISTRHO_SAFE_ASSERT_RETURN(lineWidth
!= 0,);
333 glLineWidth(static_cast<GLfloat
>(lineWidth
));
334 #ifdef DGL_USE_COMPAT_OPENGL
335 drawRectangle
<T
>(*this, true);
337 notImplemented("Rectangle::drawOutline");
343 void Rectangle
<T
>::draw()
345 #ifdef DGL_USE_COMPAT_OPENGL
346 drawRectangle
<T
>(*this, false);
348 notImplemented("Rectangle::draw");
353 void Rectangle
<T
>::drawOutline()
355 #ifdef DGL_USE_COMPAT_OPENGL
356 drawRectangle
<T
>(*this, true);
358 notImplemented("Rectangle::drawOutline");
362 template class Rectangle
<double>;
363 template class Rectangle
<float>;
364 template class Rectangle
<int>;
365 template class Rectangle
<uint
>;
366 template class Rectangle
<short>;
367 template class Rectangle
<ushort
>;
369 // -----------------------------------------------------------------------
372 static void setupOpenGLImage(const OpenGLImage
& image
, GLuint textureId
)
374 DISTRHO_SAFE_ASSERT_RETURN(image
.isValid(),);
376 glEnable(GL_TEXTURE_2D
);
377 glBindTexture(GL_TEXTURE_2D
, textureId
);
379 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
380 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
381 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_BORDER
);
382 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_BORDER
);
384 static const float trans
[] = { 0.0f
, 0.0f
, 0.0f
, 0.0f
};
385 glTexParameterfv(GL_TEXTURE_2D
, GL_TEXTURE_BORDER_COLOR
, trans
);
387 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
388 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
389 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
,
390 static_cast<GLsizei
>(image
.getWidth()),
391 static_cast<GLsizei
>(image
.getHeight()),
393 asOpenGLImageFormat(image
.getFormat()), GL_UNSIGNED_BYTE
, image
.getRawData());
395 glBindTexture(GL_TEXTURE_2D
, 0);
396 glDisable(GL_TEXTURE_2D
);
399 static void drawOpenGLImage(const OpenGLImage
& image
, const Point
<int>& pos
, const GLuint textureId
, bool& setupCalled
)
401 if (textureId
== 0 || image
.isInvalid())
406 setupOpenGLImage(image
, textureId
);
410 #ifdef DGL_USE_COMPAT_OPENGL
411 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
414 glEnable(GL_TEXTURE_2D
);
415 glBindTexture(GL_TEXTURE_2D
, textureId
);
417 #ifdef DGL_USE_COMPAT_OPENGL
421 const int x
= pos
.getX();
422 const int y
= pos
.getY();
423 const int w
= static_cast<int>(image
.getWidth());
424 const int h
= static_cast<int>(image
.getHeight());
426 glTexCoord2f(0.0f
, 0.0f
);
429 glTexCoord2f(1.0f
, 0.0f
);
432 glTexCoord2f(1.0f
, 1.0f
);
433 glVertex2d(x
+w
, y
+h
);
435 glTexCoord2f(0.0f
, 1.0f
);
442 glBindTexture(GL_TEXTURE_2D
, 0);
443 glDisable(GL_TEXTURE_2D
);
446 OpenGLImage::OpenGLImage()
451 glGenTextures(1, &textureId
);
452 DISTRHO_SAFE_ASSERT(textureId
!= 0);
455 OpenGLImage::OpenGLImage(const char* const rdata
, const uint w
, const uint h
, const ImageFormat fmt
)
456 : ImageBase(rdata
, w
, h
, fmt
),
460 glGenTextures(1, &textureId
);
461 DISTRHO_SAFE_ASSERT(textureId
!= 0);
464 OpenGLImage::OpenGLImage(const char* const rdata
, const Size
<uint
>& s
, const ImageFormat fmt
)
465 : ImageBase(rdata
, s
, fmt
),
469 glGenTextures(1, &textureId
);
470 DISTRHO_SAFE_ASSERT(textureId
!= 0);
473 OpenGLImage::OpenGLImage(const OpenGLImage
& image
)
478 glGenTextures(1, &textureId
);
479 DISTRHO_SAFE_ASSERT(textureId
!= 0);
482 OpenGLImage::~OpenGLImage()
485 glDeleteTextures(1, &textureId
);
488 void OpenGLImage::loadFromMemory(const char* const rdata
, const Size
<uint
>& s
, const ImageFormat fmt
) noexcept
491 ImageBase::loadFromMemory(rdata
, s
, fmt
);
494 void OpenGLImage::drawAt(const GraphicsContext
&, const Point
<int>& pos
)
496 drawOpenGLImage(*this, pos
, textureId
, setupCalled
);
499 OpenGLImage
& OpenGLImage::operator=(const OpenGLImage
& image
) noexcept
501 rawData
= image
.rawData
;
503 format
= image
.format
;
509 OpenGLImage::OpenGLImage(const char* const rdata
, const uint w
, const uint h
, const GLenum fmt
)
510 : ImageBase(rdata
, w
, h
, asDISTRHOImageFormat(fmt
)),
514 glGenTextures(1, &textureId
);
515 DISTRHO_SAFE_ASSERT(textureId
!= 0);
518 OpenGLImage::OpenGLImage(const char* const rdata
, const Size
<uint
>& s
, const GLenum fmt
)
519 : ImageBase(rdata
, s
, asDISTRHOImageFormat(fmt
)),
523 glGenTextures(1, &textureId
);
524 DISTRHO_SAFE_ASSERT(textureId
!= 0);
527 void OpenGLImage::draw()
529 drawOpenGLImage(*this, Point
<int>(0, 0), textureId
, setupCalled
);
532 void OpenGLImage::drawAt(const int x
, const int y
)
534 drawOpenGLImage(*this, Point
<int>(x
, y
), textureId
, setupCalled
);
537 void OpenGLImage::drawAt(const Point
<int>& pos
)
539 drawOpenGLImage(*this, pos
, textureId
, setupCalled
);
542 // -----------------------------------------------------------------------
543 // ImageBaseAboutWindow
547 void ImageBaseAboutWindow
<OpenGLImage
>::onDisplay()
549 const GraphicsContext
& context(getGraphicsContext());
554 template class ImageBaseAboutWindow
<OpenGLImage
>;
556 // -----------------------------------------------------------------------
559 template class ImageBaseButton
<OpenGLImage
>;
561 // -----------------------------------------------------------------------
565 void ImageBaseKnob
<OpenGLImage
>::PrivateData::init()
568 glGenTextures(1, &glTextureId
);
572 void ImageBaseKnob
<OpenGLImage
>::PrivateData::cleanup()
574 if (glTextureId
== 0)
577 glDeleteTextures(1, &glTextureId
);
582 void ImageBaseKnob
<OpenGLImage
>::onDisplay()
584 const GraphicsContext
& context(getGraphicsContext());
585 const float normValue
= getNormalizedValue();
587 glEnable(GL_TEXTURE_2D
);
588 glBindTexture(GL_TEXTURE_2D
, pData
->glTextureId
);
590 if (! pData
->isReady
)
592 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
593 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
594 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_BORDER
);
595 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_BORDER
);
597 static const float trans
[] = { 0.0f
, 0.0f
, 0.0f
, 0.0f
};
598 glTexParameterfv(GL_TEXTURE_2D
, GL_TEXTURE_BORDER_COLOR
, trans
);
600 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
601 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
603 uint imageDataOffset
= 0;
605 if (pData
->rotationAngle
== 0)
607 DISTRHO_SAFE_ASSERT_RETURN(pData
->imgLayerCount
> 0,);
608 DISTRHO_SAFE_ASSERT_RETURN(normValue
>= 0.0f
,);
610 const uint
& v1(pData
->isImgVertical
? pData
->imgLayerWidth
: pData
->imgLayerHeight
);
611 const uint
& v2(pData
->isImgVertical
? pData
->imgLayerHeight
: pData
->imgLayerWidth
);
613 // TODO kImageFormatGreyscale
614 const uint layerDataSize
= v1
* v2
* ((pData
->image
.getFormat() == kImageFormatBGRA
||
615 pData
->image
.getFormat() == kImageFormatRGBA
) ? 4 : 3);
616 /* */ imageDataOffset
= layerDataSize
* uint(normValue
* float(pData
->imgLayerCount
-1));
619 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
,
620 static_cast<GLsizei
>(getWidth()), static_cast<GLsizei
>(getHeight()), 0,
621 asOpenGLImageFormat(pData
->image
.getFormat()), GL_UNSIGNED_BYTE
, pData
->image
.getRawData() + imageDataOffset
);
623 pData
->isReady
= true;
626 const int w
= static_cast<int>(getWidth());
627 const int h
= static_cast<int>(getHeight());
629 if (pData
->rotationAngle
!= 0)
631 #ifdef DGL_USE_COMPAT_OPENGL
638 #ifdef DGL_USE_COMPAT_OPENGL
639 glTranslatef(static_cast<float>(w2
), static_cast<float>(h2
), 0.0f
);
640 glRotatef(normValue
*static_cast<float>(pData
->rotationAngle
), 0.0f
, 0.0f
, 1.0f
);
643 Rectangle
<int>(-w2
, -h2
, w
, h
).draw(context
);
645 #ifdef DGL_USE_COMPAT_OPENGL
651 Rectangle
<int>(0, 0, w
, h
).draw(context
);
654 glBindTexture(GL_TEXTURE_2D
, 0);
655 glDisable(GL_TEXTURE_2D
);
658 template class ImageBaseKnob
<OpenGLImage
>;
660 // -----------------------------------------------------------------------
663 template class ImageBaseSlider
<OpenGLImage
>;
665 // -----------------------------------------------------------------------
668 template class ImageBaseSwitch
<OpenGLImage
>;
670 // -----------------------------------------------------------------------
672 void SubWidget::PrivateData::display(const uint width
, const uint height
, const double autoScaleFactor
)
677 bool needsDisableScissor
= false;
679 if (needsViewportScaling
)
681 // limit viewport to widget bounds
682 const int x
= absolutePos
.getX();
683 const int w
= static_cast<int>(self
->getWidth());
684 const int h
= static_cast<int>(self
->getHeight());
686 if (viewportScaleFactor
!= 0.0 && viewportScaleFactor
!= 1.0)
689 -static_cast<int>(height
* viewportScaleFactor
- height
+ absolutePos
.getY() + 0.5),
690 static_cast<int>(width
* viewportScaleFactor
+ 0.5),
691 static_cast<int>(height
* viewportScaleFactor
+ 0.5));
695 const int y
= static_cast<int>(height
- self
->getHeight()) - absolutePos
.getY();
696 glViewport(x
, y
, w
, h
);
699 else if (needsFullViewportForDrawing
|| (absolutePos
.isZero() && self
->getSize() == Size
<uint
>(width
, height
)))
701 // full viewport size
703 -static_cast<int>(height
* autoScaleFactor
- height
+ 0.5),
704 static_cast<int>(width
* autoScaleFactor
+ 0.5),
705 static_cast<int>(height
* autoScaleFactor
+ 0.5));
710 glViewport(static_cast<int>(absolutePos
.getX() * autoScaleFactor
+ 0.5),
711 -static_cast<int>(std::round((height
* autoScaleFactor
- height
)
712 + (absolutePos
.getY() * autoScaleFactor
))),
713 static_cast<int>(std::round(width
* autoScaleFactor
)),
714 static_cast<int>(std::round(height
* autoScaleFactor
)));
716 // then cut the outer bounds
717 glScissor(static_cast<int>(absolutePos
.getX() * autoScaleFactor
+ 0.5),
718 static_cast<int>(height
- std::round((static_cast<int>(self
->getHeight()) + absolutePos
.getY())
720 static_cast<int>(std::round(self
->getWidth() * autoScaleFactor
)),
721 static_cast<int>(std::round(self
->getHeight() * autoScaleFactor
)));
723 glEnable(GL_SCISSOR_TEST
);
724 needsDisableScissor
= true;
730 if (needsDisableScissor
)
731 glDisable(GL_SCISSOR_TEST
);
733 selfw
->pData
->displaySubWidgets(width
, height
, autoScaleFactor
);
736 // -----------------------------------------------------------------------
738 void TopLevelWidget::PrivateData::display()
740 if (! selfw
->pData
->visible
)
743 const Size
<uint
> size(window
.getSize());
744 const uint width
= size
.getWidth();
745 const uint height
= size
.getHeight();
747 const double autoScaleFactor
= window
.pData
->autoScaleFactor
;
749 // full viewport size
750 if (window
.pData
->autoScaling
)
753 -static_cast<int>(height
* autoScaleFactor
- height
+ 0.5),
754 static_cast<int>(width
* autoScaleFactor
+ 0.5),
755 static_cast<int>(height
* autoScaleFactor
+ 0.5));
759 glViewport(0, 0, static_cast<int>(width
), static_cast<int>(height
));
762 // main widget drawing
765 // now draw subwidgets if there are any
766 selfw
->pData
->displaySubWidgets(width
, height
, autoScaleFactor
);
769 // -----------------------------------------------------------------------
771 void Window::PrivateData::renderToPicture(const char* const filename
,
772 const GraphicsContext
&,
776 FILE* const f
= fopen(filename
, "w");
777 DISTRHO_SAFE_ASSERT_RETURN(f
!= nullptr,);
779 GLubyte
* const pixels
= new GLubyte
[width
* height
* 3 * sizeof(GLubyte
)];
782 glReadPixels(0, 0, static_cast<GLsizei
>(width
), static_cast<GLsizei
>(height
), GL_RGB
, GL_UNSIGNED_BYTE
, pixels
);
784 fprintf(f
, "P3\n%d %d\n255\n", width
, height
);
785 for (uint y
= 0; y
< height
; y
++)
787 for (uint i
, x
= 0; x
< width
; x
++)
789 i
= 3 * ((height
- y
- 1) * width
+ x
);
790 fprintf(f
, "%3d %3d %3d ", pixels
[i
], pixels
[i
+1], pixels
[i
+2]);
799 // -----------------------------------------------------------------------
801 const GraphicsContext
& Window::PrivateData::getGraphicsContext() const noexcept
803 return (const GraphicsContext
&)graphicsContext
;
806 // -----------------------------------------------------------------------