| @@ -120,6 +120,14 @@ | |||
| #include <GLES/glext.h> | |||
| #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" | |||
| @@ -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<int>& bounds) | |||
| { | |||
| @@ -146,6 +146,11 @@ public: | |||
| return renderContext; | |||
| } | |||
| unsigned int getFrameBufferID() const | |||
| { | |||
| return 0; | |||
| } | |||
| void updateWindowPosition (const Rectangle<int>& bounds) | |||
| { | |||
| ScopedXLock xlock; | |||
| @@ -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<int>&) {} | |||
| @@ -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 }; | |||
| @@ -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 | |||
| @@ -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. | |||
| @@ -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. | |||
| @@ -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); | |||
| 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); | |||
| glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, textureType, textureID, 0); | |||
| 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<int> (0, 0, w, h)); | |||
| buffer.readPixels (data, Rectangle<int> (w, h)); | |||
| } | |||
| bool restore (OpenGLFrameBuffer& buffer) | |||
| { | |||
| if (buffer.initialise (width, height)) | |||
| { | |||
| buffer.writePixels (data, 4, Rectangle<int> (0, 0, width, height)); | |||
| buffer.writePixels (data, Rectangle<int> (width, height)); | |||
| return true; | |||
| } | |||
| @@ -270,7 +253,7 @@ public: | |||
| private: | |||
| const int width, height; | |||
| HeapBlock <int32> data; | |||
| HeapBlock <PixelARGB> 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 (bitmap.lineStride == image.getWidth() * bitmap.pixelStride) | |||
| return writePixels (bitmap.data, bitmap.pixelStride, image.getBounds()); | |||
| } | |||
| if (! image.isARGB()) | |||
| return initialise (image.convertedToFormat (Image::ARGB)); | |||
| return initialise (Image (image.getSharedImage()->clone())); | |||
| } | |||
| Image::BitmapData bitmap (image, Image::BitmapData::readOnly); | |||
| 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<int>& area) | |||
| bool OpenGLFrameBuffer::readPixels (PixelARGB* target, const Rectangle<int>& 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<int>& area) | |||
| bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>& 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); | |||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||
| OpenGLTexture temp; | |||
| temp.load (data, area.getWidth(), area.getHeight()); | |||
| temp.bind(); | |||
| 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; | |||
| } | |||
| @@ -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<int>& sourceArea); | |||
| bool readPixels (PixelARGB* targetData, const Rectangle<int>& 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<int>& targetArea); | |||
| bool writePixels (const PixelARGB* srcData, const Rectangle<int>& targetArea); | |||
| private: | |||
| class Pimpl; | |||
| @@ -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<int>& 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<int> 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<int>& 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; | |||
| } | |||
| @@ -86,6 +86,17 @@ public: | |||
| const Rectangle<int>& 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<int>& 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); | |||
| }; | |||
| //============================================================================== | |||
| @@ -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<int> (x, y, bitmapData.width, bitmapData.height)); | |||
| frameBuffer.readPixels ((PixelARGB*) bitmapData.data, | |||
| Rectangle<int> (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 <class ReaderType, class WriterType> | |||
| 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<uint8> data; | |||
| HeapBlock<PixelARGB> data; | |||
| WriterType writer; | |||
| }; | |||
| } | |||
| @@ -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(); | |||
| @@ -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,36 +69,71 @@ 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); | |||
| 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) | |||
| { | |||
| create (image.getWidth(), image.getHeight()); | |||
| 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<PixelARGB> dataCopy; | |||
| if (srcData.pixelFormat != Image::ARGB | |||
| || textureW != imageW | |||
| || textureH != imageH | |||
| || srcData.lineStride != imageW * srcData.pixelStride) | |||
| { | |||
| Image::BitmapData srcData (image, Image::BitmapData::readOnly); | |||
| const int srcLineStride = (srcData.pixelStride * imageW + 3) & ~3; | |||
| dataCopy.malloc (textureW * textureH); | |||
| data = dataCopy; | |||
| glPixelStorei (GL_UNPACK_ALIGNMENT, srcData.pixelFormat); | |||
| if (srcData.lineStride == image.getWidth() * srcData.pixelStride) | |||
| 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) | |||
| { | |||
| glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0, | |||
| srcData.pixelFormat == Image::RGB ? GL_RGB : GL_BGRA_EXT, | |||
| GL_UNSIGNED_BYTE, srcData.data); | |||
| return; | |||
| for (int y = 0; y < imageH; ++y) | |||
| memcpy (dataCopy + textureW * y, addBytesToPointer (srcData.data, srcLineStride * y), srcLineStride); | |||
| } | |||
| } | |||
| load (Image (image.getSharedImage()->clone())); | |||
| create (textureW, textureH, data); | |||
| } | |||
| void OpenGLTexture::load (const PixelARGB* const pixels, const int w, const int h) | |||
| void OpenGLTexture::load (const PixelARGB* pixels, const int w, const int h) | |||
| { | |||
| create (w, h); | |||
| const int textureW = nextPowerOfTwo (w); | |||
| const int textureH = nextPowerOfTwo (h); | |||
| glPixelStorei (GL_UNPACK_ALIGNMENT, 4); | |||
| glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0, | |||
| GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); | |||
| HeapBlock<PixelARGB> 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() | |||
| @@ -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); | |||
| }; | |||