/* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho * * 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 "Common.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" #include "WidgetPrivateData.hpp" #include "WindowPrivateData.hpp" // templated classes #include "ImageBaseWidgets.cpp" START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Color void Color::setFor(const GraphicsContext&, const bool includeAlpha) { if (includeAlpha) glColor4f(red, green, blue, alpha); else glColor3f(red, green, blue); } // ----------------------------------------------------------------------- // Line template static void drawLine(const Point& posStart, const Point& posEnd) { DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); glBegin(GL_LINES); { glVertex2d(posStart.getX(), posStart.getY()); glVertex2d(posEnd.getX(), posEnd.getY()); } glEnd(); } template void Line::draw(const GraphicsContext&, const T width) { DISTRHO_SAFE_ASSERT_RETURN(width != 0,); glLineWidth(static_cast(width)); drawLine(posStart, posEnd); } // deprecated calls template void Line::draw() { drawLine(posStart, posEnd); } template class Line; template class Line; template class Line; template class Line; template class Line; template class Line; // ----------------------------------------------------------------------- // Circle template static void drawCircle(const Point& 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 void Circle::draw(const GraphicsContext&) { drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); } template void Circle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); } // deprecated calls template void Circle::draw() { drawCircle(fPos, fNumSegments, fSize, fSin, fCos, false); } template void Circle::drawOutline() { drawCircle(fPos, fNumSegments, fSize, fSin, fCos, true); } template class Circle; template class Circle; template class Circle; template class Circle; template class Circle; template class Circle; // ----------------------------------------------------------------------- // Triangle template static void drawTriangle(const Point& pos1, const Point& pos2, const Point& 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(); } template void Triangle::draw(const GraphicsContext&) { drawTriangle(pos1, pos2, pos3, false); } template void Triangle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); drawTriangle(pos1, pos2, pos3, true); } // deprecated calls template void Triangle::draw() { drawTriangle(pos1, pos2, pos3, false); } template void Triangle::drawOutline() { drawTriangle(pos1, pos2, pos3, true); } template class Triangle; template class Triangle; template class Triangle; template class Triangle; template class Triangle; template class Triangle; // ----------------------------------------------------------------------- // Rectangle template static void drawRectangle(const Rectangle& 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(); } template void Rectangle::draw(const GraphicsContext&) { drawRectangle(*this, false); } template void Rectangle::drawOutline(const GraphicsContext&, const T lineWidth) { DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); glLineWidth(static_cast(lineWidth)); drawRectangle(*this, true); } // deprecated calls template void Rectangle::draw() { drawRectangle(*this, false); } template void Rectangle::drawOutline() { drawRectangle(*this, true); } template class Rectangle; template class Rectangle; template class Rectangle; template class Rectangle; template class Rectangle; template class Rectangle; // ----------------------------------------------------------------------- // 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(image.getWidth()), static_cast(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& pos, const GLuint textureId, bool& setupCalled) { if (textureId == 0 || image.isInvalid()) return; if (! setupCalled) { setupOpenGLImage(image, textureId); setupCalled = true; } glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureId); glBegin(GL_QUADS); { const int x = pos.getX(); const int y = pos.getY(); const int w = static_cast(image.getWidth()); const int h = static_cast(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(); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } OpenGLImage::OpenGLImage() : ImageBase(), textureId(0), setupCalled(false) { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) : ImageBase(rdata, w, h, fmt), textureId(0), setupCalled(false) { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const ImageFormat fmt) : ImageBase(rdata, s, fmt), textureId(0), setupCalled(false) { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::OpenGLImage(const OpenGLImage& image) : ImageBase(image), textureId(0), setupCalled(false) { 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& s, const ImageFormat fmt) noexcept { setupCalled = false; ImageBase::loadFromMemory(rdata, s, fmt); } void OpenGLImage::drawAt(const GraphicsContext&, const Point& pos) { drawOpenGLImage(*this, pos, textureId, setupCalled); } OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept { rawData = image.rawData; size = image.size; format = image.format; setupCalled = false; 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)), textureId(0), setupCalled(false) { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } OpenGLImage::OpenGLImage(const char* const rdata, const Size& s, const GLenum fmt) : ImageBase(rdata, s, asDISTRHOImageFormat(fmt)), textureId(0), setupCalled(false) { glGenTextures(1, &textureId); DISTRHO_SAFE_ASSERT(textureId != 0); } void OpenGLImage::draw() { drawOpenGLImage(*this, Point(0, 0), textureId, setupCalled); } void OpenGLImage::drawAt(const int x, const int y) { drawOpenGLImage(*this, Point(x, y), textureId, setupCalled); } void OpenGLImage::drawAt(const Point& pos) { drawOpenGLImage(*this, pos, textureId, setupCalled); } // ----------------------------------------------------------------------- // ImageBaseAboutWindow #if 0 template <> void ImageBaseAboutWindow::onDisplay() { const GraphicsContext& context(getGraphicsContext()); img.draw(context); } #endif template class ImageBaseAboutWindow; // ----------------------------------------------------------------------- // ImageBaseButton template class ImageBaseButton; // ----------------------------------------------------------------------- // ImageBaseKnob template <> void ImageBaseKnob::PrivateData::init() { glTextureId = 0; glGenTextures(1, &glTextureId); } template <> void ImageBaseKnob::PrivateData::cleanup() { if (glTextureId == 0) return; glDeleteTextures(1, &glTextureId); glTextureId = 0; } template <> void ImageBaseKnob::onDisplay() { const GraphicsContext& context(getGraphicsContext()); const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) / (pData->maximum - pData->minimum); 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(getWidth()), static_cast(getHeight()), 0, asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset); pData->isReady = true; } const int w = static_cast(getWidth()); const int h = static_cast(getHeight()); if (pData->rotationAngle != 0) { glPushMatrix(); const int w2 = w/2; const int h2 = h/2; glTranslatef(static_cast(w2), static_cast(h2), 0.0f); glRotatef(normValue*static_cast(pData->rotationAngle), 0.0f, 0.0f, 1.0f); Rectangle(-w2, -h2, w, h).draw(context); glPopMatrix(); } else { Rectangle(0, 0, w, h).draw(context); } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } template class ImageBaseKnob; // ----------------------------------------------------------------------- // ImageBaseSlider template class ImageBaseSlider; // ----------------------------------------------------------------------- // ImageBaseSwitch template class ImageBaseSwitch; // ----------------------------------------------------------------------- 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(self->getWidth()); const int h = static_cast(self->getHeight()); if (viewportScaleFactor != 0.0 && viewportScaleFactor != 1.0) { glViewport(x, -static_cast(height * viewportScaleFactor - height + absolutePos.getY() + 0.5), static_cast(width * viewportScaleFactor + 0.5), static_cast(height * viewportScaleFactor + 0.5)); } else { const int y = static_cast(height - self->getHeight()) - absolutePos.getY(); glViewport(x, y, w, h); } } else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size(width, height))) { // full viewport size glViewport(0, 0, static_cast(width), static_cast(height)); } else { // set viewport pos glViewport(static_cast(absolutePos.getX() * autoScaleFactor + 0.5), -static_cast(std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor))), static_cast(std::round(width * autoScaleFactor)), static_cast(std::round(height * autoScaleFactor))); // then cut the outer bounds glScissor(static_cast(absolutePos.getX() * autoScaleFactor + 0.5), static_cast(height - std::round((static_cast(self->getHeight()) + absolutePos.getY()) * autoScaleFactor)), static_cast(std::round(self->getWidth() * autoScaleFactor)), static_cast(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 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(height * autoScaleFactor - height), static_cast(width * autoScaleFactor), static_cast(height * autoScaleFactor)); } else { glViewport(0, 0, static_cast(width), static_cast(height)); } // main widget drawing self->onDisplay(); // now draw subwidgets if there are any selfw->pData->displaySubWidgets(width, height, autoScaleFactor); } // ----------------------------------------------------------------------- const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept { return (const GraphicsContext&)graphicsContext; } // ----------------------------------------------------------------------- END_NAMESPACE_DGL