diff --git a/modules/juce_opengl/juce_opengl.cpp b/modules/juce_opengl/juce_opengl.cpp index eaca156171..2924319f5f 100644 --- a/modules/juce_opengl/juce_opengl.cpp +++ b/modules/juce_opengl/juce_opengl.cpp @@ -120,6 +120,14 @@ #include #endif +#if JUCE_WINDOWS + #define JUCE_DECLARE_GL_EXTENSION_FUNCTION(name, returnType, params) \ + typedef returnType (__stdcall *type_ ## name) params; static type_ ## name name; +#else + #define JUCE_DECLARE_GL_EXTENSION_FUNCTION(name, returnType, params) \ + typedef returnType (*type_ ## name) params; static type_ ## name name; +#endif + //============================================================================== // START_AUTOINCLUDE opengl/*.cpp #include "opengl/juce_OpenGLComponent.cpp" diff --git a/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm b/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm index 29681bffd5..9eae7bf6d7 100644 --- a/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm +++ b/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm @@ -114,6 +114,7 @@ public: OpenGLPixelFormat getPixelFormat() const { return pixelFormat; } void* getRawContext() const noexcept { return glLayer; } + unsigned int getFrameBufferID() const { return (unsigned int) frameBufferHandle; } void updateWindowPosition (const Rectangle& bounds) { diff --git a/modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp b/modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp index 8934762259..43d8f8a5e3 100644 --- a/modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp +++ b/modules/juce_opengl/native/juce_linux_OpenGLComponent.cpp @@ -146,6 +146,11 @@ public: return renderContext; } + unsigned int getFrameBufferID() const + { + return 0; + } + void updateWindowPosition (const Rectangle& bounds) { ScopedXLock xlock; diff --git a/modules/juce_opengl/native/juce_mac_OpenGLComponent.mm b/modules/juce_opengl/native/juce_mac_OpenGLComponent.mm index cc4e374627..b965ee156e 100644 --- a/modules/juce_opengl/native/juce_mac_OpenGLComponent.mm +++ b/modules/juce_opengl/native/juce_mac_OpenGLComponent.mm @@ -207,6 +207,7 @@ public: OpenGLPixelFormat getPixelFormat() const { return pixelFormat; } void* getRawContext() const noexcept { return renderContext; } + unsigned int getFrameBufferID() const { return 0; } void updateWindowPosition (const Rectangle&) {} diff --git a/modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp b/modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp index ba14dc1df4..d0caa48e4c 100644 --- a/modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp +++ b/modules/juce_opengl/native/juce_win32_OpenGLComponent.cpp @@ -26,7 +26,6 @@ #define WGL_EXT_FUNCTION_INIT(extType, extFunc) \ ((extFunc = (extType) wglGetProcAddress (#extFunc)) != 0) -typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); @@ -59,16 +58,6 @@ enum WGL_TYPE_RGBA_ARB = 0x202B }; -static void getWglExtensions (HDC dc, StringArray& result) noexcept -{ - PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0; - - if (WGL_EXT_FUNCTION_INIT (PFNWGLGETEXTENSIONSSTRINGARBPROC, wglGetExtensionsStringARB)) - result.addTokens (String (wglGetExtensionsStringARB (dc)), false); - else - jassertfalse; // If this fails, it may be because you didn't activate the openGL context -} - extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component* component, void* parent); //============================================================================== @@ -146,12 +135,8 @@ public: OpenGLPixelFormat getPixelFormat() const { OpenGLPixelFormat pf; - makeActive(); - StringArray availableExtensions; - getWglExtensions (dc, availableExtensions); - - fillInPixelFormatDetails (GetPixelFormat (dc), pf, availableExtensions); + fillInPixelFormatDetails (GetPixelFormat (dc), pf); return pf; } @@ -160,6 +145,11 @@ public: return renderContext; } + unsigned int getFrameBufferID() const + { + return 0; + } + bool setPixelFormat (const OpenGLPixelFormat& pixelFormat) { makeActive(); @@ -188,10 +178,7 @@ public: PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = 0; - StringArray availableExtensions; - getWglExtensions (dc, availableExtensions); - - if (availableExtensions.contains ("WGL_ARB_pixel_format") + if (OpenGLHelpers::isExtensionSupported ("WGL_ARB_pixel_format") && WGL_EXT_FUNCTION_INIT (PFNWGLCHOOSEPIXELFORMATARBPROC, wglChoosePixelFormatARB)) { int attributes[64]; @@ -236,7 +223,7 @@ public: attributes[n++] = WGL_ACCUM_ALPHA_BITS_ARB; attributes[n++] = pixelFormat.accumulationBufferAlphaBits; - if (availableExtensions.contains ("WGL_ARB_multisample") + if (OpenGLHelpers::isExtensionSupported ("WGL_ARB_multisample") && pixelFormat.fullSceneAntiAliasingNumSamples > 0) { attributes[n++] = WGL_SAMPLE_BUFFERS_ARB; @@ -303,12 +290,9 @@ public: { makeActive(); - StringArray availableExtensions; - getWglExtensions (dc, availableExtensions); - PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = 0; - return availableExtensions.contains ("WGL_EXT_swap_control") + return OpenGLHelpers::isExtensionSupported ("WGL_EXT_swap_control") && WGL_EXT_FUNCTION_INIT (PFNWGLSWAPINTERVALEXTPROC, wglSwapIntervalEXT) && wglSwapIntervalEXT (numFramesPerSwap) != FALSE; } @@ -317,12 +301,9 @@ public: { makeActive(); - StringArray availableExtensions; - getWglExtensions (dc, availableExtensions); - PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = 0; - if (availableExtensions.contains ("WGL_EXT_swap_control") + if (OpenGLHelpers::isExtensionSupported ("WGL_EXT_swap_control") && WGL_EXT_FUNCTION_INIT (PFNWGLGETSWAPINTERVALEXTPROC, wglGetSwapIntervalEXT)) return wglGetSwapIntervalEXT(); @@ -333,13 +314,10 @@ public: { jassert (isActive()); - StringArray availableExtensions; - getWglExtensions (dc, availableExtensions); - PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = 0; int numTypes = 0; - if (availableExtensions.contains("WGL_ARB_pixel_format") + if (OpenGLHelpers::isExtensionSupported ("WGL_ARB_pixel_format") && WGL_EXT_FUNCTION_INIT (PFNWGLGETPIXELFORMATATTRIBIVARBPROC, wglGetPixelFormatAttribivARB)) { int attributes = WGL_NUMBER_PIXEL_FORMATS_ARB; @@ -356,7 +334,7 @@ public: for (int i = 0; i < numTypes; ++i) { - if (fillInPixelFormatDetails (i + 1, pf, availableExtensions)) + if (fillInPixelFormatDetails (i + 1, pf)) { bool alreadyListed = false; for (int j = results.size(); --j >= 0;) @@ -391,13 +369,11 @@ private: dc = GetDC ((HWND) nativeWindow->getNativeHandle()); } - bool fillInPixelFormatDetails (const int pixelFormatIndex, - OpenGLPixelFormat& result, - const StringArray& availableExtensions) const noexcept + bool fillInPixelFormatDetails (const int pixelFormatIndex, OpenGLPixelFormat& result) const noexcept { PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = 0; - if (availableExtensions.contains ("WGL_ARB_pixel_format") + if (OpenGLHelpers::isExtensionSupported ("WGL_ARB_pixel_format") && WGL_EXT_FUNCTION_INIT (PFNWGLGETPIXELFORMATATTRIBIVARBPROC, wglGetPixelFormatAttribivARB)) { int attributes[32]; @@ -419,7 +395,7 @@ private: attributes[numAttributes++] = WGL_ACCUM_BLUE_BITS_ARB; attributes[numAttributes++] = WGL_ACCUM_ALPHA_BITS_ARB; - if (availableExtensions.contains ("WGL_ARB_multisample")) + if (OpenGLHelpers::isExtensionSupported ("WGL_ARB_multisample")) attributes[numAttributes++] = WGL_SAMPLES_ARB; int values[32] = { 0 }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp index 33559ca28f..94b9fd5941 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp @@ -433,5 +433,9 @@ void OpenGLComponent::internalRepaint (int x, int y, int w, int h) context->repaint(); } +unsigned int OpenGLComponent::getFrameBufferID() const +{ + return context != nullptr ? context->getFrameBufferID() : 0; +} END_JUCE_NAMESPACE diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.h b/modules/juce_opengl/opengl/juce_OpenGLComponent.h index d3239a3c0b..e08ea0f813 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.h +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.h @@ -205,6 +205,11 @@ public: */ void deleteContext(); + /** If this component is backed by a frame buffer, this returns its ID number, or + 0 if the component has no accessible framebuffer. + */ + unsigned int getFrameBufferID() const; + //============================================================================== /** Returns the native handle of an embedded heavyweight window, if there is one. diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.h b/modules/juce_opengl/opengl/juce_OpenGLContext.h index 4e738ee85c..1eeee5e5fb 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.h @@ -100,6 +100,11 @@ public: */ virtual void deleteContext() = 0; + /** If this context is backed by a frame buffer, this returns its ID number, or + 0 if the context has no accessible framebuffer. + */ + virtual unsigned int getFrameBufferID() const = 0; + //============================================================================== /** Returns the context that's currently in active use by the calling thread. diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index e64a4fb409..b35676c76d 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -60,16 +60,7 @@ enum USE_FUNCTION (glGetFramebufferAttachmentParameterivEXT, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\ USE_FUNCTION (glGenerateMipmapEXT, void, (GLenum target))\ -#if JUCE_WINDOWS - #define APICALLTYPE __stdcall -#else - #define APICALLTYPE -#endif - -#define DECLARE_FUNCTION(name, returnType, params) \ - typedef returnType (APICALLTYPE * type_ ## name) params; static type_ ## name name; -FRAMEBUFFER_FUNCTION_LIST (DECLARE_FUNCTION) -#undef DECLARE_FUNCTION +FRAMEBUFFER_FUNCTION_LIST (JUCE_DECLARE_GL_EXTENSION_FUNCTION) static bool framebufferFunctionsInitialised = false; @@ -79,16 +70,9 @@ static void initialiseFrameBufferFunctions() { framebufferFunctionsInitialised = true; - #if JUCE_LINUX - #define JUCE_LOOKUP_FUNCTION(name) glXGetProcAddress ((const GLubyte*) name) - #else - #define JUCE_LOOKUP_FUNCTION(name) wglGetProcAddress (name) - #endif - - #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) JUCE_LOOKUP_FUNCTION (#name); + #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); FRAMEBUFFER_FUNCTION_LIST (FIND_FUNCTION) #undef FIND_FUNCTION - #undef JUCE_LOOKUP_FUNCTION } } @@ -134,8 +118,7 @@ class OpenGLFrameBuffer::Pimpl { public: Pimpl (const int width_, const int height_, - const bool wantsDepthBuffer, const bool wantsStencilBuffer, - const GLenum textureType = GL_TEXTURE_2D) + const bool wantsDepthBuffer, const bool wantsStencilBuffer) : width (width_), height (height_), textureID (0), @@ -159,14 +142,19 @@ public: OpenGLHelpers::resetErrorState(); glGenFramebuffersEXT (1, &frameBufferHandle); - glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, frameBufferHandle); glGenTextures (1, &textureID); - glBindTexture (textureType, textureID); - glTexImage2D (textureType, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindTexture (GL_TEXTURE_2D, textureID); - glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, textureType, textureID, 0); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureID, 0); if (wantsDepthBuffer || wantsStencilBuffer) { @@ -196,11 +184,6 @@ public: ok = checkStatus(); - glTexParameterf (textureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf (textureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf (textureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf (textureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); } @@ -254,14 +237,14 @@ public: : width (w), height (h), data (w * h) { - buffer.readPixels (data, Rectangle (0, 0, w, h)); + buffer.readPixels (data, Rectangle (w, h)); } bool restore (OpenGLFrameBuffer& buffer) { if (buffer.initialise (width, height)) { - buffer.writePixels (data, 4, Rectangle (0, 0, width, height)); + buffer.writePixels (data, Rectangle (width, height)); return true; } @@ -270,7 +253,7 @@ public: private: const int width, height; - HeapBlock data; + HeapBlock data; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState); }; @@ -292,19 +275,13 @@ bool OpenGLFrameBuffer::initialise (int width, int height) bool OpenGLFrameBuffer::initialise (const Image& image) { - if (initialise (image.getWidth(), image.getHeight())) - { - { - Image::BitmapData bitmap (image, Image::BitmapData::readOnly); + if (! image.isARGB()) + return initialise (image.convertedToFormat (Image::ARGB)); - if (bitmap.lineStride == image.getWidth() * bitmap.pixelStride) - return writePixels (bitmap.data, bitmap.pixelStride, image.getBounds()); - } + Image::BitmapData bitmap (image, Image::BitmapData::readOnly); - return initialise (Image (image.getSharedImage()->clone())); - } - - return false; + return initialise (bitmap.width, bitmap.height) + && writePixels ((const PixelARGB*) bitmap.data, image.getBounds()); } bool OpenGLFrameBuffer::initialise (const OpenGLFrameBuffer& other) @@ -320,6 +297,7 @@ bool OpenGLFrameBuffer::initialise (const OpenGLFrameBuffer& other) if (initialise (p->width, p->height)) { pimpl->bind(); + OpenGLHelpers::prepareFor2D (p->width, p->height); glDisable (GL_BLEND); glColor4f (1.0f, 1.0f, 1.0f, 1.0f); other.drawAt (0, 0); @@ -372,10 +350,14 @@ bool OpenGLFrameBuffer::makeCurrentRenderingTarget() return pimpl != nullptr && pimpl->bind(); } +void OpenGLFrameBuffer::setCurrentFrameBufferTarget (GLuint frameBufferID) +{ + glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, frameBufferID); +} + void OpenGLFrameBuffer::releaseAsRenderingTarget() { - if (pimpl != nullptr) - pimpl->unbind(); + setCurrentFrameBufferTarget (0); } void OpenGLFrameBuffer::clear (const Colour& colour) @@ -396,69 +378,53 @@ void OpenGLFrameBuffer::makeCurrentAndClear() } } -bool OpenGLFrameBuffer::readPixels (void* target, const Rectangle& area) +bool OpenGLFrameBuffer::readPixels (PixelARGB* target, const Rectangle& area) { if (! makeCurrentRenderingTarget()) return false; glPixelStorei (GL_PACK_ALIGNMENT, 4); glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, target); - glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); return true; } -bool OpenGLFrameBuffer::writePixels (const void* data, int pixelStride, const Rectangle& area) +bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle& area) { if (! makeCurrentRenderingTarget()) return false; - jassert (pixelStride == 3 || pixelStride == 4); // can only handle RGB or ARGB - const int format = pixelStride == 3 ? GL_RGB : GL_BGRA_EXT; const int invertedY = pimpl->height - area.getBottom(); OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); glDisable (GL_DEPTH_TEST); glDisable (GL_BLEND); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); #if JUCE_OPENGL_ES - // GLES has no glDrawPixels function, so we have to create a texture and draw it.. - GLuint temporaryTexture = 0; - glGenTextures (1, &temporaryTexture); - jassert (temporaryTexture != 0); // can't create a texture! - - if (temporaryTexture != 0) { + // GLES has no glDrawPixels function, so we have to create a texture and draw it.. glEnable (GL_TEXTURE_2D); - glBindTexture (GL_TEXTURE_2D, temporaryTexture); - glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, area.getWidth(), area.getHeight(), 0, - format, GL_UNSIGNED_BYTE, data); + OpenGLTexture temp; + temp.load (data, area.getWidth(), area.getHeight()); + temp.bind(); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - const int cropRect[4] = { 0, 0, area.getWidth(), area.getHeight() }; + const GLint cropRect[4] = { 0, 0, area.getWidth(), area.getHeight() }; glTexParameteriv (GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect); glDrawTexiOES (area.getX(), invertedY, 1, area.getWidth(), area.getHeight()); - glBindTexture (GL_TEXTURE_2D, 0); - glDeleteTextures (1, &temporaryTexture); } - #else glRasterPos2i (area.getX(), invertedY); glBindTexture (GL_TEXTURE_2D, 0); - glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glDrawPixels (area.getWidth(), area.getHeight(), format, GL_UNSIGNED_BYTE, data); + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + glDrawPixels (area.getWidth(), area.getHeight(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, data); #endif glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); - return true; } diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h index f9f047d866..ca96357fa8 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h @@ -98,6 +98,11 @@ public: /** Deselects this buffer as the current OpenGL rendering target. */ void releaseAsRenderingTarget(); + /** Selects a framebuffer as the active target, or deselects the current + target buffer if you pass 0. + */ + static void setCurrentFrameBufferTarget (GLuint frameBufferID); + /** Clears the framebuffer with the specified colour. */ void clear (const Colour& colour); @@ -125,14 +130,13 @@ public: The lineStride is measured as a number of pixels, not bytes - pass a stride of 0 to indicate a packed array. */ - bool readPixels (void* targetData, const Rectangle& sourceArea); + bool readPixels (PixelARGB* targetData, const Rectangle& sourceArea); /** Writes an area of pixels into the framebuffer from a specified pixel array. The lineStride is measured as a number of pixels, not bytes - pass a stride of 0 to indicate a packed array. */ - bool writePixels (const void* srcData, int srcPixelStride, - const Rectangle& targetArea); + bool writePixels (const PixelARGB* srcData, const Rectangle& targetArea); private: class Pimpl; diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index 62bf2ea230..2b81e8d9e4 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -31,6 +31,84 @@ void OpenGLHelpers::resetErrorState() while (glGetError() != GL_NO_ERROR) {} } +void* OpenGLHelpers::getExtensionFunction (const char* functionName) +{ + #if JUCE_WINDOWS + return (void*) wglGetProcAddress (functionName); + + #elif JUCE_MAC + static void* handle = dlopen (nullptr, RTLD_LAZY); + return dlsym (handle, functionName); + + #elif JUCE_LINUX + return (void*) glXGetProcAddress ((const GLubyte*) functionName); + #endif +} + +#if ! JUCE_OPENGL_ES +namespace +{ + bool isExtensionSupportedV3 (const char* extensionName) + { + #ifndef GL_NUM_EXTENSIONS + enum { GL_NUM_EXTENSIONS = 0x821d }; + #endif + + JUCE_DECLARE_GL_EXTENSION_FUNCTION (glGetStringi, const GLubyte*, (GLenum, GLuint)) + + if (glGetStringi == nullptr) + glGetStringi = (type_glGetStringi) OpenGLHelpers::getExtensionFunction ("glGetStringi"); + + if (glGetStringi != nullptr) + { + GLint numExtensions = 0; + glGetIntegerv (GL_NUM_EXTENSIONS, &numExtensions); + + for (int i = 0; i < numExtensions; ++i) + if (strcmp (extensionName, (const char*) glGetStringi (GL_EXTENSIONS, i)) == 0) + return true; + } + + return false; + } +} +#endif + +bool OpenGLHelpers::isExtensionSupported (const char* const extensionName) +{ + jassert (extensionName != nullptr); // you must supply a genuine string for this. + jassert (isContextActive()); // An OpenGL context will need to be active before calling this. + + #if ! JUCE_OPENGL_ES + const GLubyte* version = glGetString (GL_VERSION); + + if (version != nullptr && version[0] >= '3') + { + return isExtensionSupportedV3 (extensionName); + } + else + #endif + { + const char* extensions = (const char*) glGetString (GL_EXTENSIONS); + jassert (extensions != nullptr); // Perhaps you didn't activate an OpenGL context before calling this? + + for (;;) + { + const char* found = strstr (extensions, extensionName); + + if (found == nullptr) + break; + + extensions = found + strlen (extensionName); + + if (extensions[0] == ' ' || extensions[0] == 0) + return true; + } + } + + return false; +} + void OpenGLHelpers::clear (const Colour& colour) { glClearColor (colour.getFloatRed(), colour.getFloatGreen(), @@ -295,15 +373,14 @@ void OpenGLHelpers::fillRectWithTiledTexture (int textureWidth, int textureHeigh glEnable (GL_TEXTURE_2D); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glEnableClientState (GL_VERTEX_ARRAY); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_COLOR_ARRAY); glDisableClientState (GL_NORMAL_ARRAY); glColor4f (1.0f, 1.0f, 1.0f, alpha); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - const GLfloat clipX = (GLfloat) clip.getX(); const GLfloat clipY = (GLfloat) clip.getY(); const GLfloat clipR = (GLfloat) clip.getRight(); @@ -325,6 +402,87 @@ void OpenGLHelpers::fillRectWithTiledTexture (int textureWidth, int textureHeigh glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); } +//============================================================================== +struct OpenGLEdgeTableRenderer +{ + OpenGLEdgeTableRenderer (float r_, float g_, float b_, const Point& origin_) noexcept + : origin (origin_), r (r_), g (g_), b (b_), lastAlpha (-1) + { + } + + void draw (const EdgeTable& et) + { + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glEnableClientState (GL_VERTEX_ARRAY); + glVertexPointer (2, GL_FLOAT, 0, vertices); + + et.iterate (*this); + } + + void setEdgeTableYPos (const int y) noexcept + { + const int lineY = y + origin.getY(); + + vertices[1] = (GLfloat) lineY; + vertices[3] = (GLfloat) (lineY + 1); + vertices[5] = (GLfloat) lineY; + vertices[7] = (GLfloat) (lineY + 1); + } + + void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept + { + drawHorizontal (x, 1, alphaLevel); + } + + void handleEdgeTablePixelFull (const int x) noexcept + { + drawHorizontal (x, 1, 255); + } + + void handleEdgeTableLine (const int x, const int width, const int alphaLevel) noexcept + { + drawHorizontal (x, width, alphaLevel); + } + + void handleEdgeTableLineFull (const int x, const int width) noexcept + { + drawHorizontal (x, width, 255); + } + +private: + GLfloat vertices[8]; + const Point origin; + const float r, g, b; + int lastAlpha; + + void drawHorizontal (int x, const int w, const int alphaLevel) noexcept + { + x += origin.getX(); + + vertices[0] = (GLfloat) x; + vertices[2] = (GLfloat) x; + vertices[4] = (GLfloat) (x + w); + vertices[6] = (GLfloat) (x + w); + + if (lastAlpha != alphaLevel) + { + lastAlpha = alphaLevel; + glColor4f (r, g, b, alphaLevel / 255.0f); + } + + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + } + + JUCE_DECLARE_NON_COPYABLE (OpenGLEdgeTableRenderer); +}; + +void OpenGLHelpers::fillEdgeTable (const EdgeTable& edgeTable, + float red, float green, float blue, + const Point& offset) +{ + OpenGLEdgeTableRenderer etr (red, green, blue, offset); + etr.draw (edgeTable); +} //============================================================================== // This breaks down a path into a series of horizontal strips of trapezoids.. @@ -390,7 +548,7 @@ private: } else { - const int newX = x1 + (s->y1 - y1) * (x2 - x1) / (y2 - y1); + const int newX = x1 + (int) ((s->y1 - y1) * (int64) (x2 - x1) / (y2 - y1)); HorizontalSlice* const newSlice = new HorizontalSlice (s, x1, y1, newX, s->y1, winding); insert (last, newSlice); last = newSlice; @@ -411,7 +569,7 @@ private: if (y2 > s->y2) { const int newY = s->y2; - const int newX = x1 + (newY - y1) * (x2 - x1) / (y2 - y1); + const int newX = x1 + (int) ((newY - y1) * (int64) (x2 - x1) / (y2 - y1)); s->addLine (x1, newX, winding); x1 = newX; y1 = newY; @@ -467,7 +625,7 @@ private: if (dxDiff != 0) { - const int intersectionY = (dy * diff1) / dxDiff; + const int intersectionY = (int) ((dy * (int64) diff1) / dxDiff); if (intersectionY > 0 && intersectionY < dy) { @@ -505,7 +663,7 @@ private: for (int i = 0; i < segments.size(); ++i) { LineSegment& l = oldSegments[i]; - const int newX = l.x1 + dy1 * (l.x2 - l.x1) / dy2; + const int newX = l.x1 + (int) (dy1 * (int64) (l.x2 - l.x1) / dy2); newSegments[i].x1 = newX; l.x2 = newX; } diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h index ee96916b75..f04d6e4a94 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h @@ -86,6 +86,17 @@ public: const Rectangle& targetArea, const AffineTransform& transform, float alpha); + + /** Renders an edge-table into the current context. */ + static void fillEdgeTable (const EdgeTable& edgeTable, + float red, float green, float blue, + const Point& offset); + + /** Checks whether the current context supports the specified extension. */ + static bool isExtensionSupported (const char* extensionName); + + /** Returns the address of a named GL extension function */ + static void* getExtensionFunction (const char* functionName); }; //============================================================================== diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp index 562c5fea9d..4c7cdd14c3 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp @@ -25,8 +25,8 @@ BEGIN_JUCE_NAMESPACE -OpenGLFrameBufferImage::OpenGLFrameBufferImage (Image::PixelFormat format, int width, int height) - : Image::SharedImage (format, width, height), +OpenGLFrameBufferImage::OpenGLFrameBufferImage (int width, int height) + : Image::SharedImage (Image::ARGB, width, height), pixelStride (4), lineStride (width * pixelStride) { @@ -43,7 +43,7 @@ LowLevelGraphicsContext* OpenGLFrameBufferImage::createLowLevelContext() Image::SharedImage* OpenGLFrameBufferImage::clone() { - OpenGLFrameBufferImage* im = new OpenGLFrameBufferImage (getPixelFormat(), getWidth(), getHeight()); + OpenGLFrameBufferImage* im = new OpenGLFrameBufferImage (getWidth(), getHeight()); im->incReferenceCount(); { @@ -67,14 +67,15 @@ namespace OpenGLImageHelpers { Dummy (OpenGLFrameBuffer&, int, int, int, int) noexcept {} static void read (OpenGLFrameBuffer&, Image::BitmapData& , int, int) noexcept {} - static void write (const void*) noexcept {} + static void write (const PixelARGB*) noexcept {} }; struct Reader { static void read (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) { - frameBuffer.readPixels (bitmapData.data, Rectangle (x, y, bitmapData.width, bitmapData.height)); + frameBuffer.readPixels ((PixelARGB*) bitmapData.data, + Rectangle (x, y, bitmapData.width, bitmapData.height)); } }; @@ -84,9 +85,9 @@ namespace OpenGLImageHelpers : frameBuffer (frameBuffer_), area (x, y, w, h) {} - void write (const void* const data) const noexcept + void write (const PixelARGB* const data) const noexcept { - frameBuffer.writePixels (data, 4, area); + frameBuffer.writePixels (data, area); } OpenGLFrameBuffer& frameBuffer; @@ -98,8 +99,8 @@ namespace OpenGLImageHelpers template struct DataReleaser : public Image::BitmapData::BitmapDataReleaser { - DataReleaser (OpenGLFrameBuffer& frameBuffer, size_t dataSize, int x, int y, int w, int h) - : data (dataSize), + DataReleaser (OpenGLFrameBuffer& frameBuffer, int x, int y, int w, int h) + : data (w * h), writer (frameBuffer, x, y, w, h) {} @@ -110,15 +111,14 @@ namespace OpenGLImageHelpers static void initialise (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) { - DataReleaser* r = new DataReleaser (frameBuffer, bitmapData.lineStride * bitmapData.height, - x, y, bitmapData.width, bitmapData.height); + DataReleaser* r = new DataReleaser (frameBuffer, x, y, bitmapData.width, bitmapData.height); bitmapData.dataReleaser = r; - bitmapData.data = r->data + x * bitmapData.pixelStride + y * bitmapData.lineStride; + bitmapData.data = (uint8*) (r->data + (x + y * bitmapData.width)); ReaderType::read (frameBuffer, bitmapData, x, y); } - HeapBlock data; + HeapBlock data; WriterType writer; }; } diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.h b/modules/juce_opengl/opengl/juce_OpenGLImage.h index 3261b2850b..0796d2e523 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.h +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.h @@ -41,7 +41,7 @@ class JUCE_API OpenGLFrameBufferImage : public Image::SharedImage { public: - OpenGLFrameBufferImage (Image::PixelFormat format, int width, int height); + OpenGLFrameBufferImage (int width, int height); /** Destructor. */ ~OpenGLFrameBufferImage(); diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 7ad981ae42..aeff6ba370 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -47,7 +47,7 @@ bool OpenGLTexture::isValidSize (int width, int height) return isPowerOfTwo (width) && isPowerOfTwo (height); } -void OpenGLTexture::create (const int w, const int h) +void OpenGLTexture::create (const int w, const int h, const void* pixels) { // Texture objects can only be created when the current thread has an active OpenGL // context. You'll need to make an OpenGLComponent active before calling this. @@ -69,38 +69,73 @@ void OpenGLTexture::create (const int w, const int h) glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -void OpenGLTexture::load (const Image& image) -{ - create (image.getWidth(), image.getHeight()); - - { - Image::BitmapData srcData (image, Image::BitmapData::readOnly); - - glPixelStorei (GL_UNPACK_ALIGNMENT, srcData.pixelFormat); - - if (srcData.lineStride == image.getWidth() * srcData.pixelStride) - { - glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0, - srcData.pixelFormat == Image::RGB ? GL_RGB : GL_BGRA_EXT, - GL_UNSIGNED_BYTE, srcData.data); - return; - } - } - - load (Image (image.getSharedImage()->clone())); -} - -void OpenGLTexture::load (const PixelARGB* const pixels, const int w, const int h) -{ - create (w, h); glPixelStorei (GL_UNPACK_ALIGNMENT, 4); glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); } +void OpenGLTexture::load (const Image& image) +{ + const int imageW = image.getWidth(); + const int imageH = image.getHeight(); + const int textureW = nextPowerOfTwo (imageW); + const int textureH = nextPowerOfTwo (imageH); + + Image::BitmapData srcData (image, Image::BitmapData::readOnly); + const PixelARGB* data = (const PixelARGB*) srcData.data; + HeapBlock dataCopy; + + if (srcData.pixelFormat != Image::ARGB + || textureW != imageW + || textureH != imageH + || srcData.lineStride != imageW * srcData.pixelStride) + { + const int srcLineStride = (srcData.pixelStride * imageW + 3) & ~3; + dataCopy.malloc (textureW * textureH); + data = dataCopy; + + if (srcData.pixelFormat == Image::RGB) + { + for (int y = 0; y < imageH; ++y) + { + const PixelRGB* const src = (const PixelRGB*) addBytesToPointer (srcData.data, srcLineStride * y); + PixelARGB* const dst = (PixelARGB*) (dataCopy + textureW * y); + + for (int x = 0; x < imageW; ++x) + dst[x].set (src[x]); + } + } + else if (srcData.pixelFormat == Image::ARGB) + { + for (int y = 0; y < imageH; ++y) + memcpy (dataCopy + textureW * y, addBytesToPointer (srcData.data, srcLineStride * y), srcLineStride); + } + } + + create (textureW, textureH, data); +} + +void OpenGLTexture::load (const PixelARGB* pixels, const int w, const int h) +{ + const int textureW = nextPowerOfTwo (w); + const int textureH = nextPowerOfTwo (h); + + HeapBlock dataCopy; + + if (textureW != w || textureH != h) + { + dataCopy.malloc (textureW * textureH); + + for (int y = 0; y < h; ++y) + memcpy (dataCopy + textureW * y, pixels + w * y, w * 4); + + pixels = dataCopy; + } + + create (textureW, textureH, pixels); +} + void OpenGLTexture::release() { if (textureID != 0) diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.h b/modules/juce_opengl/opengl/juce_OpenGLTexture.h index cf7b6dc4d8..f773732e1f 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.h +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.h @@ -37,7 +37,8 @@ public: ~OpenGLTexture(); /** Creates a texture from the given image. - Note that the image's width and height must both be a power-of-two. + Note that if the image's dimensions aren't a power-of-two, the texture may + be created with a larger size. */ void load (const Image& image); @@ -70,6 +71,9 @@ public: /** Returns the GL texture ID number. */ GLuint getTextureID() const noexcept { return textureID; } + int getWidth() const noexcept { return width; } + int getHeight() const noexcept { return height; } + /** Returns true if a texture can be created with the given size. Some systems may require that the sizes are powers-of-two. */ @@ -79,7 +83,7 @@ private: GLuint textureID; int width, height; - void create (int w, int h); + void create (int w, int h, const void*); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture); };