From 115a171d7668078b6833d985746c25ade9ccdde3 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 1 Nov 2011 20:43:19 +0000 Subject: [PATCH] Big OpenGLRenderer rewrite, plus a revamped OpenGL demo page to demonstrate 2D rendering. --- extras/JuceDemo/Source/demos/OpenGLDemo.cpp | 146 ++- .../juce_graphics/geometry/juce_EdgeTable.cpp | 11 +- modules/juce_opengl/juce_opengl.cpp | 6 +- .../opengl/juce_OpenGLFrameBuffer.cpp | 171 +-- .../opengl/juce_OpenGLGraphicsContext.cpp | 1062 ++++++++++------- .../opengl/juce_OpenGLGraphicsContext.h | 5 +- .../juce_opengl/opengl/juce_OpenGLHelpers.cpp | 454 +------ .../juce_opengl/opengl/juce_OpenGLHelpers.h | 46 +- .../juce_opengl/opengl/juce_OpenGLTexture.cpp | 61 +- .../juce_opengl/opengl/juce_OpenGLTexture.h | 34 +- 10 files changed, 886 insertions(+), 1110 deletions(-) diff --git a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp index f5ddf2abc8..be81d2d098 100644 --- a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp +++ b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp @@ -34,29 +34,18 @@ class DemoOpenGLCanvas : public OpenGLComponent, public: DemoOpenGLCanvas() : rotation (0.0f), - delta (1.0f) + delta (1.0f), + textScrollPos (200) { startTimer (20); } // when the component creates a new internal context, this is called, and - // we'll use the opportunity to create the textures needed. + // we'll use the opportunity to create some images to use as textures. void newOpenGLContextCreated() { - texture1.load (createImage1()); - texture2.load (createImage2()); - - // (no need to call makeCurrentContextActive(), as that will have - // been done for us before the method call). - glDepthFunc (GL_LESS); - glEnable (GL_DEPTH_TEST); - glEnable (GL_TEXTURE_2D); - glEnable (GL_BLEND); - glShadeModel (GL_SMOOTH); - - glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); - glHint (GL_POINT_SMOOTH_HINT, GL_NICEST); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + logoImage = createLogoImage(); + dynamicTextureImage = Image (Image::ARGB, 128, 128, true, OpenGLImageType()); } void mouseDrag (const MouseEvent& e) @@ -68,76 +57,117 @@ public: void renderOpenGL() { OpenGLHelpers::clear (Colours::darkgrey.withAlpha (1.0f)); - OpenGLHelpers::prepareFor2D (getWidth(), getHeight()); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + updateTextureImage(); // this will update our dynamically-changing texture image. - texture1.draw2D (50.0f, getHeight() - 50.0f, - getWidth() - 50.0f, getHeight() - 50.0f, - getWidth() - 50.0f, 50.0f, - 50.0f, 50.0f, - Colours::white.withAlpha (fabsf (::sinf (rotation / 100.0f)))); + drawBackground2DStuff(); // draws some 2D content to demonstrate the OpenGLRenderer class - glClear (GL_DEPTH_BUFFER_BIT); + // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so + // we'll put back any important settings before doing our normal GL 3D drawing.. + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_TEXTURE_2D); OpenGLHelpers::setPerspective (45.0, getWidth() / (double) getHeight(), 0.1, 100.0); glTranslatef (0.0f, 0.0f, -5.0f); glRotatef (rotation, 0.5f, 1.0f, 0.0f); - // this draws the sides of our spinning cube.. - texture1.draw3D (-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, Colours::white); - texture1.draw3D (-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, Colours::white); - texture1.draw3D (-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Colours::white); - texture2.draw3D (-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, Colours::white); - texture2.draw3D ( 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, Colours::white); - texture2.draw3D (-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, Colours::white); + // logoImage and dynamicTextureImage are actually OpenGL images, so we can use this utility function to + // extract the frame buffer which is their backing store, and use it directly. + OpenGLFrameBuffer* tex1 = OpenGLImageType::getFrameBufferFrom (logoImage); + OpenGLFrameBuffer* tex2 = OpenGLImageType::getFrameBufferFrom (dynamicTextureImage); + + jassert (tex1 != nullptr && tex2 != nullptr); // (this would mean that our images weren't created correctly) + + // This draws the sides of our spinning cube. + // I've used some of the juce helper functions, but you can also just use normal GL calls here too. + tex1->draw3D (-1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, Colours::white); + tex1->draw3D (-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, Colours::white); + tex1->draw3D (-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, Colours::white); + tex2->draw3D (-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, Colours::white); + tex2->draw3D ( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, Colours::white); + tex2->draw3D (-1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Colours::white); + + drawForeground2DStuff(); // draws our scrolling text overlay + } + + void updateTextureImage() + { + // This image is a special framebuffer-backed image, so when we draw to it, the context + // will render directly into its framebuffer + + dynamicTextureImage.clear (dynamicTextureImage.getBounds(), Colours::red.withRotatedHue (fabsf (::sinf (rotation / 300.0f))).withAlpha (0.7f)); + + Graphics g (dynamicTextureImage); + + g.setFont (dynamicTextureImage.getHeight() / 3.0f); + g.setColour (Colours::black); + drawScrollingMessage (g, dynamicTextureImage.getHeight() / 2); + } + + void drawBackground2DStuff() + { + OpenGLRenderer glRenderer (*this); // Create an OpenGLRenderer that will draw into this GL window.. + Graphics g (&glRenderer); // ..and then wrap it in a normal Graphics object so we can draw with it. + + // This stuff just creates a spinning star shape and fills it.. + Path p; + const float scale = getHeight() * 0.4f; + p.addStar (getLocalBounds().getCentre().toFloat(), 7, + scale + ::cosf (rotation * 0.0021f) * scale / 2, + scale + ::sinf (rotation * 0.001f) * scale / 2, rotation / 50.0f); + + g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (fabsf (::sinf (rotation / 300.0f))), + 0, 0, + Colours::green.withRotatedHue (fabsf (::cosf (rotation / -431.0f))), + 0, (float) getHeight(), false)); + g.fillPath (p); + } + + void drawForeground2DStuff() + { + OpenGLRenderer glRenderer (*this); // Create an OpenGLRenderer that will draw into this GL window.. + Graphics g (&glRenderer); // ..and then wrap it in a normal Graphics object so we can draw with it. + + // Then, just draw our scolling text like we would in any other component. + g.setColour (Colours::blue.withAlpha (0.5f)); + g.setFont (30.0f, Font::bold); + drawScrollingMessage (g, getHeight() / 2); + } + + void drawScrollingMessage (Graphics& g, int y) const + { + g.drawSingleLineText ("The background, foreground and texture are all being drawn using the OpenGLRenderer class, which " + "lets you use a standard JUCE 2D graphics context to render directly onto an OpenGL window or framebuffer... ", + (int) -std::fmod (textScrollPos, 2500.0f), y); } void timerCallback() { rotation += delta; + textScrollPos += 1.4f; repaint(); } private: - OpenGLTexture texture1, texture2; - float rotation, delta; + Image logoImage, dynamicTextureImage; + float rotation, delta, textScrollPos; // Functions to create a couple of images to use as textures.. - static Image createImage1() + static Image createLogoImage() { - Image image (Image::ARGB, 256, 256, true); + Image image (Image::ARGB, 256, 256, true, OpenGLImageType()); Graphics g (image); - g.fillAll (Colours::white.withAlpha (0.7f)); + g.fillAll (Colours::lightgrey.withAlpha (0.8f)); g.drawImageWithin (ImageFileFormat::loadFrom (BinaryData::juce_png, BinaryData::juce_pngSize), 0, 0, image.getWidth(), image.getHeight(), RectanglePlacement::stretchToFit); drawRandomStars (g, image.getWidth(), image.getHeight()); - - return image; - } - - static Image createImage2() - { - Image image (Image::ARGB, 128, 128, true); - - Graphics g (image); - g.fillAll (Colours::darkred.withAlpha (0.7f)); - - Path p; - p.addStar (image.getBounds().getCentre().toFloat(), 11, image.getWidth() * 0.3f, image.getWidth() * 0.5f); - - g.setGradientFill (ColourGradient (Colours::blue, image.getWidth() * 0.5f, image.getHeight() * 0.5f, - Colours::green, image.getWidth() * 0.2f, image.getHeight() * 0.2f, - true)); - g.fillPath (p); - - drawRandomStars (g, image.getWidth(), image.getHeight()); - return image; } diff --git a/modules/juce_graphics/geometry/juce_EdgeTable.cpp b/modules/juce_graphics/geometry/juce_EdgeTable.cpp index 780520c873..c3a1709e52 100644 --- a/modules/juce_graphics/geometry/juce_EdgeTable.cpp +++ b/modules/juce_graphics/geometry/juce_EdgeTable.cpp @@ -44,10 +44,10 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, t += lineStrideElements; } - const int topLimit = bounds.getY() << 8; + const int topLimit = bounds.getY() << 8; const int heightLimit = bounds.getHeight() << 8; - const int leftLimit = bounds.getX() << 8; - const int rightLimit = bounds.getRight() << 8; + const int leftLimit = bounds.getX() << 8; + const int rightLimit = bounds.getRight() << 8; PathFlatteningIterator iter (path, transform); @@ -598,13 +598,10 @@ void EdgeTable::clipToRectangle (const Rectangle& r) if (bottom < bounds.getHeight()) bounds.setHeight (bottom); - if (clipped.getRight() < bounds.getRight()) - bounds.setRight (clipped.getRight()); - for (int i = top; --i >= 0;) table [lineStrideElements * i] = 0; - if (clipped.getX() > bounds.getX()) + if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight()) { const int x1 = clipped.getX() << 8; const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8; diff --git a/modules/juce_opengl/juce_opengl.cpp b/modules/juce_opengl/juce_opengl.cpp index 60893ebb85..6bbe12c9e7 100644 --- a/modules/juce_opengl/juce_opengl.cpp +++ b/modules/juce_opengl/juce_opengl.cpp @@ -57,9 +57,6 @@ #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment(lib, "vfw32.lib") #pragma comment(lib, "imm32.lib") - #endif - - #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment(lib, "OpenGL32.Lib") #pragma comment(lib, "GlU32.Lib") #endif @@ -128,6 +125,9 @@ typedef returnType (*type_ ## name) params; static type_ ## name name; #endif +#define JUCE_INSTANTIATE_GL_EXTENSION(name) \ + name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); + //============================================================================== // START_AUTOINCLUDE opengl/*.cpp #include "opengl/juce_OpenGLComponent.cpp" diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 3e0aa7f926..75f2d2ccf5 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -25,95 +25,98 @@ BEGIN_JUCE_NAMESPACE -#if JUCE_WINDOWS -enum +namespace { - GL_FRAMEBUFFER_EXT = 0x8D40, - GL_RENDERBUFFER_EXT = 0x8D41, - GL_FRAMEBUFFER_BINDING_EXT = 0x8CA6, - GL_COLOR_ATTACHMENT0_EXT = 0x8CE0, - GL_DEPTH_ATTACHMENT_EXT = 0x8D00, - GL_STENCIL_ATTACHMENT_EXT = 0x8D20, - GL_FRAMEBUFFER_COMPLETE_EXT = 0x8CD5, - GL_DEPTH24_STENCIL8_EXT = 0x88F0, - GL_RENDERBUFFER_DEPTH_SIZE_EXT = 0x8D54 -}; -#endif - -#if JUCE_WINDOWS || JUCE_LINUX - -#define FRAMEBUFFER_FUNCTION_LIST(USE_FUNCTION) \ - USE_FUNCTION (glIsRenderbufferEXT, GLboolean, (GLuint renderbuffer))\ - USE_FUNCTION (glBindRenderbufferEXT, void, (GLenum target, GLuint renderbuffer))\ - USE_FUNCTION (glDeleteRenderbuffersEXT, void, (GLsizei n, const GLuint *renderbuffers))\ - USE_FUNCTION (glGenRenderbuffersEXT, void, (GLsizei n, GLuint *renderbuffers))\ - USE_FUNCTION (glRenderbufferStorageEXT, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))\ - USE_FUNCTION (glGetRenderbufferParameterivEXT, void, (GLenum target, GLenum pname, GLint* params))\ - USE_FUNCTION (glIsFramebufferEXT, GLboolean, (GLuint framebuffer))\ - USE_FUNCTION (glBindFramebufferEXT, void, (GLenum target, GLuint framebuffer))\ - USE_FUNCTION (glDeleteFramebuffersEXT, void, (GLsizei n, const GLuint *framebuffers))\ - USE_FUNCTION (glGenFramebuffersEXT, void, (GLsizei n, GLuint *framebuffers))\ - USE_FUNCTION (glCheckFramebufferStatusEXT, GLenum, (GLenum target))\ - USE_FUNCTION (glFramebufferTexture1DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ - USE_FUNCTION (glFramebufferTexture2DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ - USE_FUNCTION (glFramebufferTexture3DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))\ - USE_FUNCTION (glFramebufferRenderbufferEXT, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))\ - USE_FUNCTION (glGetFramebufferAttachmentParameterivEXT, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\ - USE_FUNCTION (glGenerateMipmapEXT, void, (GLenum target))\ - -FRAMEBUFFER_FUNCTION_LIST (JUCE_DECLARE_GL_EXTENSION_FUNCTION) - -static bool framebufferFunctionsInitialised = false; - -static void initialiseFrameBufferFunctions() -{ - if (! framebufferFunctionsInitialised) + #if JUCE_WINDOWS + enum + { + GL_FRAMEBUFFER_EXT = 0x8D40, + GL_RENDERBUFFER_EXT = 0x8D41, + GL_FRAMEBUFFER_BINDING_EXT = 0x8CA6, + GL_COLOR_ATTACHMENT0_EXT = 0x8CE0, + GL_DEPTH_ATTACHMENT_EXT = 0x8D00, + GL_STENCIL_ATTACHMENT_EXT = 0x8D20, + GL_FRAMEBUFFER_COMPLETE_EXT = 0x8CD5, + GL_DEPTH24_STENCIL8_EXT = 0x88F0, + GL_RENDERBUFFER_DEPTH_SIZE_EXT = 0x8D54 + }; + #endif + + #if JUCE_WINDOWS || JUCE_LINUX + + #define FRAMEBUFFER_FUNCTION_LIST(USE_FUNCTION) \ + USE_FUNCTION (glIsRenderbufferEXT, GLboolean, (GLuint renderbuffer))\ + USE_FUNCTION (glBindRenderbufferEXT, void, (GLenum target, GLuint renderbuffer))\ + USE_FUNCTION (glDeleteRenderbuffersEXT, void, (GLsizei n, const GLuint *renderbuffers))\ + USE_FUNCTION (glGenRenderbuffersEXT, void, (GLsizei n, GLuint *renderbuffers))\ + USE_FUNCTION (glRenderbufferStorageEXT, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))\ + USE_FUNCTION (glGetRenderbufferParameterivEXT, void, (GLenum target, GLenum pname, GLint* params))\ + USE_FUNCTION (glIsFramebufferEXT, GLboolean, (GLuint framebuffer))\ + USE_FUNCTION (glBindFramebufferEXT, void, (GLenum target, GLuint framebuffer))\ + USE_FUNCTION (glDeleteFramebuffersEXT, void, (GLsizei n, const GLuint *framebuffers))\ + USE_FUNCTION (glGenFramebuffersEXT, void, (GLsizei n, GLuint *framebuffers))\ + USE_FUNCTION (glCheckFramebufferStatusEXT, GLenum, (GLenum target))\ + USE_FUNCTION (glFramebufferTexture1DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ + USE_FUNCTION (glFramebufferTexture2DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ + USE_FUNCTION (glFramebufferTexture3DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))\ + USE_FUNCTION (glFramebufferRenderbufferEXT, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))\ + USE_FUNCTION (glGetFramebufferAttachmentParameterivEXT, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\ + USE_FUNCTION (glGenerateMipmapEXT, void, (GLenum target))\ + + FRAMEBUFFER_FUNCTION_LIST (JUCE_DECLARE_GL_EXTENSION_FUNCTION) + + static bool framebufferFunctionsInitialised = false; + + void initialiseFrameBufferFunctions() { - framebufferFunctionsInitialised = true; + if (! framebufferFunctionsInitialised) + { + framebufferFunctionsInitialised = true; - #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); - FRAMEBUFFER_FUNCTION_LIST (FIND_FUNCTION) - #undef FIND_FUNCTION + #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); + FRAMEBUFFER_FUNCTION_LIST (FIND_FUNCTION) + #undef FIND_FUNCTION + } } -} -#undef FRAMEBUFFER_FUNCTION_LIST + #undef FRAMEBUFFER_FUNCTION_LIST + + //============================================================================== + #elif JUCE_OPENGL_ES + + #define glIsRenderbufferEXT glIsRenderbufferOES + #define glBindRenderbufferEXT glBindRenderbufferOES + #define glDeleteRenderbuffersEXT glDeleteRenderbuffersOES + #define glGenRenderbuffersEXT glGenRenderbuffersOES + #define glRenderbufferStorageEXT glRenderbufferStorageOES + #define glGetRenderbufferParameterivEXT glGetRenderbufferParameterivOES + #define glIsFramebufferEXT glIsFramebufferOES + #define glBindFramebufferEXT glBindFramebufferOES + #define glDeleteFramebuffersEXT glDeleteFramebuffersOES + #define glGenFramebuffersEXT glGenFramebuffersOES + #define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES + #define glFramebufferTexture1DEXT glFramebufferTexture1DOES + #define glFramebufferTexture2DEXT glFramebufferTexture2DOES + #define glFramebufferTexture3DEXT glFramebufferTexture3DOES + #define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES + #define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES + #define glGenerateMipmapEXT glGenerateMipmapOES + + #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES + #define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_OES + #define GL_RGBA8 GL_RGBA + #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES + #define GL_RENDERBUFFER_EXT GL_RENDERBUFFER_OES + #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES + #define GL_RENDERBUFFER_DEPTH_SIZE_EXT GL_RENDERBUFFER_DEPTH_SIZE_OES + #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT_OES + #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT_OES + #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE_OES + #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED_OES + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES -//============================================================================== -#elif JUCE_OPENGL_ES - -#define glIsRenderbufferEXT glIsRenderbufferOES -#define glBindRenderbufferEXT glBindRenderbufferOES -#define glDeleteRenderbuffersEXT glDeleteRenderbuffersOES -#define glGenRenderbuffersEXT glGenRenderbuffersOES -#define glRenderbufferStorageEXT glRenderbufferStorageOES -#define glGetRenderbufferParameterivEXT glGetRenderbufferParameterivOES -#define glIsFramebufferEXT glIsFramebufferOES -#define glBindFramebufferEXT glBindFramebufferOES -#define glDeleteFramebuffersEXT glDeleteFramebuffersOES -#define glGenFramebuffersEXT glGenFramebuffersOES -#define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES -#define glFramebufferTexture1DEXT glFramebufferTexture1DOES -#define glFramebufferTexture2DEXT glFramebufferTexture2DOES -#define glFramebufferTexture3DEXT glFramebufferTexture3DOES -#define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES -#define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES -#define glGenerateMipmapEXT glGenerateMipmapOES - -#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES -#define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_OES -#define GL_RGBA8 GL_RGBA -#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES -#define GL_RENDERBUFFER_EXT GL_RENDERBUFFER_OES -#define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES -#define GL_RENDERBUFFER_DEPTH_SIZE_EXT GL_RENDERBUFFER_DEPTH_SIZE_OES -#define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT_OES -#define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT_OES -#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE_OES -#define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED_OES -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES - -#endif + #endif +} //============================================================================== class OpenGLFrameBuffer::Pimpl @@ -399,7 +402,7 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle glDisable (GL_BLEND); OpenGLTexture tex; - tex.load (data, area.getWidth(), area.getHeight()); + tex.loadARGBFlipped (data, area.getWidth(), area.getHeight()); const int texH = tex.getHeight(); #if JUCE_OPENGL_ES diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 51a29d8226..25ac2ad7cb 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -25,12 +25,54 @@ BEGIN_JUCE_NAMESPACE +namespace +{ + #if JUCE_WINDOWS + enum + { + GL_OPERAND0_RGB = 0x8590, + GL_OPERAND1_RGB = 0x8591, + GL_OPERAND0_ALPHA = 0x8598, + GL_OPERAND1_ALPHA = 0x8599, + GL_SRC0_RGB = 0x8580, + GL_SRC1_RGB = 0x8581, + GL_SRC0_ALPHA = 0x8588, + GL_SRC1_ALPHA = 0x8589, + GL_TEXTURE0 = 0x84C0, + GL_TEXTURE1 = 0x84C1, + GL_TEXTURE2 = 0x84C2, + GL_COMBINE = 0x8570, + GL_COMBINE_RGB = 0x8571, + GL_COMBINE_ALPHA = 0x8572, + GL_PREVIOUS = 0x8578, + }; + #endif + + #if JUCE_WINDOWS || JUCE_LINUX + JUCE_DECLARE_GL_EXTENSION_FUNCTION (glActiveTexture, void, (GLenum)); + JUCE_DECLARE_GL_EXTENSION_FUNCTION (glClientActiveTexture, void, (GLenum)); + + void initialiseMultiTextureExtensions() + { + if (glActiveTexture == nullptr) + { + JUCE_INSTANTIATE_GL_EXTENSION (glActiveTexture); + JUCE_INSTANTIATE_GL_EXTENSION (glClientActiveTexture); + } + } + #else + void initialiseMultiTextureExtensions() {} + #endif +} + +//============================================================================== struct OpenGLTarget { OpenGLTarget (GLuint frameBufferID_, int width_, int height_) noexcept : frameBuffer (nullptr), frameBufferID (frameBufferID_), x (0), y (0), width (width_), height (height_) - {} + { + } OpenGLTarget (OpenGLFrameBuffer& frameBuffer_, const Point& origin) noexcept : frameBuffer (&frameBuffer_), frameBufferID (0), x (origin.getX()), y (origin.getY()), @@ -78,10 +120,168 @@ struct OpenGLTarget int x, y, width, height; }; +//============================================================================== +class PositionedTexture +{ +public: + PositionedTexture (OpenGLTexture& texture, EdgeTable& et, const Rectangle& clip_) + { + et.clipToRectangle (clip_); + + EdgeTableData data (et); + + texture.loadAlpha (data.data, data.area.getWidth(), data.area.getHeight()); + textureID = texture.getTextureID(); + + clip = et.getMaximumBounds(); + area = data.area; + } + + PositionedTexture (GLuint textureID_, const Rectangle area_, const Rectangle clip_) + : textureID (textureID_), area (area_), clip (clip_) + { + } + + template + void getTextureCoordAt (ValueType x, ValueType y, GLfloat& resultX, GLfloat& resultY) const noexcept + { + resultX = (x - area.getX()) / (float) area.getWidth(); + resultY = (area.getBottom() - y) / (float) area.getHeight(); + } + + void enable (GLenum multitextureIndex, const GLfloat* textureCoords) const + { + glActiveTexture (multitextureIndex); + glClientActiveTexture (multitextureIndex); + glBindTexture (GL_TEXTURE_2D, textureID); + glEnable (GL_TEXTURE_2D); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + } + + void enable (GLenum multitextureIndex, const Rectangle* const area, GLfloat* const textureCoords) const noexcept + { + if (area != nullptr) + { + getTextureCoordAt (area->getX(), area->getY(), textureCoords[0], textureCoords[1]); + getTextureCoordAt (area->getRight(), area->getY(), textureCoords[2], textureCoords[3]); + getTextureCoordAt (area->getX(), area->getBottom(), textureCoords[4], textureCoords[5]); + getTextureCoordAt (area->getRight(), area->getBottom(), textureCoords[6], textureCoords[7]); + } + + enable (multitextureIndex, textureCoords); + } + + GLuint textureID; + Rectangle area, clip; + + struct EdgeTableData + { + EdgeTableData (const EdgeTable& et) + : area (et.getMaximumBounds().withSize (nextPowerOfTwo (et.getMaximumBounds().getWidth()), + nextPowerOfTwo (et.getMaximumBounds().getHeight()))) + { + data.calloc (area.getWidth() * area.getHeight()); + et.iterate (*this); + } + + inline void setEdgeTableYPos (const int y) noexcept + { + currentLine = data + (area.getBottom() - 1 - y) * area.getWidth() - area.getX(); + } + + inline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept + { + currentLine[x] = (uint8) alphaLevel; + } + + inline void handleEdgeTablePixelFull (const int x) const noexcept + { + currentLine[x] = 255; + } + + inline void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept + { + memset (currentLine + x, (uint8) alphaLevel, width); + } + + inline void handleEdgeTableLineFull (int x, int width) const noexcept + { + memset (currentLine + x, 255, width); + } + + HeapBlock data; + const Rectangle area; + + private: + uint8* currentLine; + + JUCE_DECLARE_NON_COPYABLE (EdgeTableData); + }; +}; + +//============================================================================== +class GradientTexture +{ +public: + GradientTexture() : needsRefresh (true) {} + + enum { textureSize = 256 }; + + void reset() noexcept + { + needsRefresh = true; + } + + void bind (const ColourGradient& gradient) + { + if (needsRefresh) + { + needsRefresh = false; + PixelARGB lookup [textureSize]; + gradient.createLookupTable (lookup, textureSize); + texture.loadARGB (lookup, textureSize, 1); + } + + texture.bind(); + } + +private: + OpenGLTexture texture; + bool needsRefresh; +}; + //============================================================================== namespace { - enum { defaultOversamplingLevel = 4 }; + struct TargetSaver + { + TargetSaver() + : oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget()) + { + glGetIntegerv (GL_VIEWPORT, oldViewport); + glPushMatrix(); + } + + ~TargetSaver() + { + OpenGLFrameBuffer::setCurrentFrameBufferTarget (oldFramebuffer); + glPopMatrix(); + glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); + } + + private: + GLuint oldFramebuffer; + GLint oldViewport[4]; + }; + + struct TemporaryColourModulationMode + { + TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); } + ~TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); } + }; void fillRectangleList (const RectangleList& list) { @@ -107,25 +307,59 @@ namespace glColor4f (alpha, alpha, alpha, alpha); } - void clipFrameBuffers (const OpenGLTarget& dest, OpenGLFrameBuffer& source, const Point sourceOrigin) + inline void setPremultipliedColour (const Colour& c) noexcept { - dest.makeActiveFor2D(); - glEnable (GL_BLEND); - glBlendFunc (GL_ZERO, GL_SRC_ALPHA); - setColour (1.0f); - OpenGLHelpers::drawTextureQuad (source.getTextureID(), sourceOrigin.getX(), sourceOrigin.getY(), - source.getWidth(), source.getHeight()); + const PixelARGB p (c.getPixelARGB()); + OpenGLHelpers::setColour (Colour (p.getARGB())); } - void renderPath (const Path& path, const AffineTransform& transform, int oversamplingLevel) + void disableMultiTexture (GLenum level) { - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glActiveTexture (level); glDisable (GL_TEXTURE_2D); - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE); + } + + void disableMultiTexture() + { + disableMultiTexture (GL_TEXTURE2); + disableMultiTexture (GL_TEXTURE1); + disableMultiTexture (GL_TEXTURE0); + } + + void enableSingleTexture() + { + disableMultiTexture (GL_TEXTURE2); + disableMultiTexture (GL_TEXTURE1); + glActiveTexture (GL_TEXTURE0); + glClientActiveTexture (GL_TEXTURE0); + glEnable (GL_TEXTURE_2D); + } - TriangulatedPath (path, transform).draw (oversamplingLevel); + void resetMultiTextureMode (GLenum index, const bool forRGBTextures) + { + glActiveTexture (index); + glClientActiveTexture (index); + glDisable (GL_TEXTURE_2D); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, forRGBTextures ? GL_SRC_COLOR : GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + void resetMultiTextureModes (const bool forRGBTextures) + { + resetMultiTextureMode (GL_TEXTURE2, forRGBTextures); + resetMultiTextureMode (GL_TEXTURE1, forRGBTextures); + resetMultiTextureMode (GL_TEXTURE0, forRGBTextures); } void setPremultipliedBlendingMode() noexcept @@ -142,24 +376,57 @@ namespace setPremultipliedBlendingMode(); } - void fillRectWithTiledTexture (const OpenGLTarget& target, int textureWidth, int textureHeight, - const Rectangle& clip, const AffineTransform& transform, float alpha) + void prepareMasks (const PositionedTexture* const mask1, const PositionedTexture* const mask2, + GLfloat* const textureCoords1, GLfloat* const textureCoords2, const Rectangle* const area) { - glEnable (GL_TEXTURE_2D); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - 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); + if (mask1 != nullptr) + { + mask1->enable (GL_TEXTURE0, area, textureCoords1); - static bool canDoNonPowerOfTwos = OpenGLHelpers::isExtensionSupported ("GL_ARB_texture_non_power_of_two"); + if (mask2 != nullptr) + { + mask2->enable (GL_TEXTURE1, area, textureCoords2); - if (canDoNonPowerOfTwos || (isPowerOfTwo (textureWidth) && isPowerOfTwo (textureHeight))) + glActiveTexture (GL_TEXTURE2); + glClientActiveTexture (GL_TEXTURE2); + } + else + { + disableMultiTexture (GL_TEXTURE2); + glActiveTexture (GL_TEXTURE1); + glClientActiveTexture (GL_TEXTURE1); + } + } + else { - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + disableMultiTexture (GL_TEXTURE2); + disableMultiTexture (GL_TEXTURE1); + glActiveTexture (GL_TEXTURE0); + glClientActiveTexture (GL_TEXTURE0); + } + } + + void renderImage (const OpenGLTarget& target, const OpenGLTextureFromImage& image, + const Rectangle& clip, const AffineTransform& transform, float alpha, + const PositionedTexture* mask1, const PositionedTexture* mask2, + const bool replaceExistingContents, const bool isTiled) + { + setBlendMode (replaceExistingContents); + GLfloat textureCoords1[8], textureCoords2[8]; + + if ((! isTiled) || (isPowerOfTwo (image.imageWidth) && isPowerOfTwo (image.imageHeight))) + { + prepareMasks (mask1, mask2, textureCoords1, textureCoords2, &clip); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, image.textureID); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + TemporaryColourModulationMode tmm; + setColour (alpha); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, isTiled ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, isTiled ? GL_REPEAT : GL_CLAMP_TO_EDGE); const GLfloat clipX = (GLfloat) clip.getX(); const GLfloat clipY = (GLfloat) clip.getY(); @@ -170,8 +437,8 @@ namespace GLfloat textureCoords[] = { clipX, clipY, clipR, clipY, clipX, clipB, clipR, clipB }; { - const AffineTransform t (transform.inverted().scaled (1.0f / textureWidth, - 1.0f / textureHeight)); + const AffineTransform t (transform.inverted().scaled (image.fullWidthProportion / image.imageWidth, + image.fullHeightProportion / image.imageHeight)); t.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); t.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); @@ -188,7 +455,15 @@ namespace } else { - // For hardware that can't handle non-power-of-two textures, this is a fallback algorithm + prepareMasks (mask1, mask2, textureCoords1, textureCoords2, nullptr); + + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, image.textureID); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + TemporaryColourModulationMode tmm; + setColour (alpha); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); target.scissor (clip); @@ -196,29 +471,52 @@ namespace OpenGLHelpers::applyTransform (transform); GLfloat vertices[8]; - const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f }; + const GLfloat textureCoords[] = { 0, 1.0f, image.fullWidthProportion, 1.0f, + 0, 1.0f - image.fullHeightProportion, image.fullWidthProportion, 1.0f - image.fullHeightProportion }; glVertexPointer (2, GL_FLOAT, 0, vertices); glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); const Rectangle targetArea (clip.toFloat().transformed (transform.inverted()).getSmallestIntegerContainer()); - int x = targetArea.getX() - negativeAwareModulo (targetArea.getX(), textureWidth); - int y = targetArea.getY() - negativeAwareModulo (targetArea.getY(), textureHeight); + int x = targetArea.getX() - negativeAwareModulo (targetArea.getX(), image.imageWidth); + int y = targetArea.getY() - negativeAwareModulo (targetArea.getY(), image.imageHeight); const int right = targetArea.getRight(); const int bottom = targetArea.getBottom(); while (y < bottom) { vertices[1] = vertices[3] = (GLfloat) y; - vertices[5] = vertices[7] = (GLfloat) (y + textureHeight); + vertices[5] = vertices[7] = (GLfloat) (y + image.imageHeight); - for (int x1 = x; x1 < right; x1 += textureWidth) + for (int x1 = x; x1 < right; x1 += image.imageWidth) { vertices[0] = vertices[4] = (GLfloat) x1; - vertices[2] = vertices[6] = (GLfloat) (x1 + textureWidth); + vertices[2] = vertices[6] = (GLfloat) (x1 + image.imageWidth); + + if (mask1 != nullptr) + { + float t[] = { vertices[0], vertices[1], vertices[2], vertices[3], + vertices[4], vertices[5], vertices[6], vertices[7] }; + transform.transformPoints (t[0], t[1], t[2], t[3]); + transform.transformPoints (t[4], t[5], t[6], t[7]); + + mask1->getTextureCoordAt (t[0], t[1], textureCoords1[0], textureCoords1[1]); + mask1->getTextureCoordAt (t[2], t[3], textureCoords1[2], textureCoords1[3]); + mask1->getTextureCoordAt (t[4], t[5], textureCoords1[4], textureCoords1[5]); + mask1->getTextureCoordAt (t[6], t[7], textureCoords1[6], textureCoords1[7]); + + if (mask2 != nullptr) + { + mask2->getTextureCoordAt (t[0], t[1], textureCoords2[0], textureCoords2[1]); + mask2->getTextureCoordAt (t[2], t[3], textureCoords2[2], textureCoords2[3]); + mask2->getTextureCoordAt (t[4], t[5], textureCoords2[4], textureCoords2[5]); + mask2->getTextureCoordAt (t[6], t[7], textureCoords2[6], textureCoords2[7]); + } + } + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); } - y += textureHeight; + y += image.imageHeight; } glPopMatrix(); @@ -226,15 +524,13 @@ namespace } } - void fillWithLinearGradient (const Rectangle& rect, - const ColourGradient& grad, - const AffineTransform& transform, - const int textureSize) + void fillWithLinearGradient (GradientTexture& gradientTexture, const Rectangle& rect, const ColourGradient& grad, + const AffineTransform& transform, const PositionedTexture* mask1, const PositionedTexture* mask2) { const Point p1 (grad.point1.transformedBy (transform)); const Point p2 (grad.point2.transformedBy (transform)); - const Point p3 (Point (grad.point1.getX() - (grad.point2.getY() - grad.point1.getY()) / textureSize, - grad.point1.getY() + (grad.point2.getX() - grad.point1.getX()) / textureSize).transformedBy (transform)); + const Point p3 (Point (grad.point1.getX() - (grad.point2.getY() - grad.point1.getY()) / gradientTexture.textureSize, + grad.point1.getY() + (grad.point2.getX() - grad.point1.getX()) / gradientTexture.textureSize).transformedBy (transform)); const AffineTransform textureTransform (AffineTransform::fromTargetPoints (p1.getX(), p1.getY(), 0.0f, 0.0f, p2.getX(), p2.getY(), 1.0f, 0.0f, @@ -251,12 +547,19 @@ namespace textureTransform.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); textureTransform.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); + GLfloat textureCoords1[8], textureCoords2[8]; + prepareMasks (mask1, mask2, textureCoords1, textureCoords2, &rect); + TemporaryColourModulationMode tmm; + + gradientTexture.bind (grad); + setColour (1.0f); OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4); } - void fillWithRadialGradient (const OpenGLTarget& target, const Rectangle& rect, - const ColourGradient& grad, const AffineTransform& transform) + void fillWithRadialGradient (GradientTexture& gradientTexture, const OpenGLTarget& target, const Rectangle& rect, + const ColourGradient& grad, const AffineTransform& transform, + const PositionedTexture* mask1, const PositionedTexture* mask2) { const Point centre (grad.point1.transformedBy (transform)); @@ -270,11 +573,13 @@ namespace Point (0.0f, screenRadius).transformedBy (inverse).getDistanceFromOrigin()); const int numDivisions = 90; - GLfloat vertices [4 + numDivisions * 2]; - GLfloat textureCoords [4 + numDivisions * 2]; + GLfloat vertices [4 + numDivisions * 2]; + GLfloat textureCoords1 [4 + numDivisions * 2]; + GLfloat textureCoords2 [4 + numDivisions * 2]; + GLfloat textureCoords3 [4 + numDivisions * 2]; { - GLfloat* t = textureCoords; + GLfloat* t = textureCoords1; *t++ = 0.0f; *t++ = 0.0f; @@ -311,113 +616,98 @@ namespace *v++ = first.getY(); } + prepareMasks (mask1, mask2, textureCoords2, textureCoords3, nullptr); + + if (mask1 != nullptr) + { + for (int i = 0; i < 2 * (numDivisions + 2); i += 2) + mask1->getTextureCoordAt (vertices[i], vertices[i + 1], textureCoords2[i], textureCoords2[i + 1]); + + if (mask2 != nullptr) + for (int i = 0; i < 2 * (numDivisions + 2); i += 2) + mask2->getTextureCoordAt (vertices[i], vertices[i + 1], textureCoords3[i], textureCoords3[i + 1]); + } + + gradientTexture.bind (grad); + target.scissor (rect); glEnable (GL_TEXTURE_2D); + TemporaryColourModulationMode tmm; glEnableClientState (GL_VERTEX_ARRAY); glEnableClientState (GL_TEXTURE_COORD_ARRAY); - glDisableClientState (GL_COLOR_ARRAY); - glDisableClientState (GL_NORMAL_ARRAY); glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords1); setColour (1.0f); glDrawArrays (GL_TRIANGLE_FAN, 0, numDivisions + 2); glDisable (GL_SCISSOR_TEST); } - void fillRectWithColourGradient (const OpenGLTarget& target, const Rectangle& rect, - const ColourGradient& gradient, const AffineTransform& transform) + void fillTexture (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, GradientTexture& gradientTexture, + const PositionedTexture* mask1, const PositionedTexture* mask2, const bool replaceExistingContents) { - if (gradient.point1 == gradient.point2) - { - OpenGLHelpers::fillRectWithColour (rect, gradient.getColourAtPosition (1.0)); - } - else + jassert (! (mask1 == nullptr && mask2 != nullptr)); + + if (fill.isColour()) { - const int textureSize = 256; - OpenGLTexture texture; + GLfloat textureCoords1[8], textureCoords2[8]; + disableMultiTexture (GL_TEXTURE2); - HeapBlock lookup (textureSize); - gradient.createLookupTable (lookup, textureSize); - texture.load (lookup, textureSize, 1); - texture.bind(); + if (mask1 != nullptr) + { + setBlendMode (replaceExistingContents); + mask1->enable (GL_TEXTURE0, &area, textureCoords1); - if (gradient.isRadial) - fillWithRadialGradient (target, rect, gradient, transform); + if (mask2 != nullptr) + mask2->enable (GL_TEXTURE1, &area, textureCoords2); + else + disableMultiTexture (GL_TEXTURE1); + } else - fillWithLinearGradient (rect, gradient, transform, textureSize); - } - } + { + setBlendMode (replaceExistingContents || fill.colour.isOpaque()); + disableMultiTexture (GL_TEXTURE1); + disableMultiTexture (GL_TEXTURE0); + } - void fillRectWithFillType (const OpenGLTarget& target, const Rectangle& rect, - const FillType& fill, const bool replaceExistingContents) - { - if (fill.isGradient()) + setPremultipliedColour (fill.colour); + glEnableClientState (GL_VERTEX_ARRAY); + OpenGLHelpers::fillRect (area); + } + else if (fill.isGradient()) { - target.makeActiveFor2D(); - setBlendMode (replaceExistingContents); - ColourGradient g2 (*(fill.gradient)); g2.multiplyOpacity (fill.getOpacity()); - fillRectWithColourGradient (target, rect, g2, fill.transform); + if (g2.point1 == g2.point2) + { + fillTexture (target, area, g2.getColourAtPosition (1.0), gradientTexture, mask1, mask2, replaceExistingContents); + } + else + { + setBlendMode (replaceExistingContents || (mask1 == nullptr && fill.colour.isOpaque() && fill.gradient->isOpaque())); + + if (g2.isRadial) + fillWithRadialGradient (gradientTexture, target, area, g2, fill.transform, mask1, mask2); + else + fillWithLinearGradient (gradientTexture, area, g2, fill.transform, mask1, mask2); + } } else if (fill.isTiledImage()) { - OpenGLTextureFromImage t (fill.image); - - target.makeActiveFor2D(); - setBlendMode (replaceExistingContents); - - glBindTexture (GL_TEXTURE_2D, t.textureID); - fillRectWithTiledTexture (target, t.width, t.height, rect, - fill.transform, fill.colour.getFloatAlpha()); - glBindTexture (GL_TEXTURE_2D, 0); + renderImage (target, fill.image, area, fill.transform, fill.colour.getFloatAlpha(), + mask1, mask2, replaceExistingContents, true); } } } //============================================================================== -class OpenGLRenderer::ScratchBufferManager -{ -public: - ScratchBufferManager() {} - - OpenGLFrameBuffer* get (int width, int height) - { - for (int i = 0; i < buffers.size(); ++i) - { - OpenGLFrameBuffer* b = buffers.getUnchecked(i); - - if (width <= b->getWidth() && height <= b->getHeight()) - return buffers.removeAndReturn (i); - } - - OpenGLFrameBuffer* b = new OpenGLFrameBuffer(); - b->initialise (width, height); - return b; - } - - void release (OpenGLFrameBuffer* buffer) - { - buffers.add (buffer); - - if (buffers.size() > 10) - buffers.remove (0); - } - -private: - OwnedArray buffers; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScratchBufferManager); -}; - class ClipRegion_Mask; //============================================================================== class ClipRegionBase : public SingleThreadedReferenceCountedObject { public: - ClipRegionBase (OpenGLRenderer::ScratchBufferManager& scratchBuffer_) noexcept : scratchBuffer (scratchBuffer_) {} + ClipRegionBase() noexcept {} virtual ~ClipRegionBase() {} typedef ReferenceCountedObjectPtr Ptr; @@ -428,15 +718,13 @@ public: virtual Ptr clipToRectangleList (const RectangleList&) = 0; virtual Ptr excludeClipRectangle (const Rectangle&) = 0; virtual Ptr clipToPath (const Path& p, const AffineTransform&) = 0; - virtual Ptr clipToEdgeTable (const EdgeTable&) = 0; virtual Ptr clipToImageAlpha (const OpenGLTextureFromImage&, const AffineTransform&) = 0; - virtual Ptr clipToMask (ClipRegion_Mask*) = 0; - virtual const Rectangle& getClipBounds() const = 0; - virtual void fillAll (const OpenGLTarget&, const FillType& fill, bool replaceContents) = 0; - virtual void fillRect (const OpenGLTarget&, const Rectangle& area, const FillType& fill, bool replaceContents) = 0; - virtual void drawImage (const OpenGLTarget&, const OpenGLTextureFromImage&, float alpha, const Rectangle& targetArea) = 0; - - OpenGLRenderer::ScratchBufferManager& scratchBuffer; + virtual Ptr clipToTexture (const PositionedTexture&) = 0; + virtual Rectangle getClipBounds() const = 0; + virtual void fillRect (const OpenGLTarget&, const Rectangle& area, const FillType&, GradientTexture&, bool replaceContents) = 0; + virtual void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture&, const FillType&, GradientTexture&) = 0; + virtual void drawImage (const OpenGLTarget&, const OpenGLTextureFromImage&, const AffineTransform&, float alpha, + const Rectangle& clip, const PositionedTexture* mask) = 0; private: JUCE_DECLARE_NON_COPYABLE (ClipRegionBase); @@ -447,79 +735,40 @@ class ClipRegion_Mask : public ClipRegionBase { public: ClipRegion_Mask (const ClipRegion_Mask& other) - : ClipRegionBase (other.scratchBuffer), - clip (other.clip), + : clip (other.clip), maskOrigin (other.clip.getPosition()) { - mask = scratchBuffer.get (clip.getWidth(), clip.getHeight()); + TargetSaver ts; + mask.initialise (clip.getWidth(), clip.getHeight()); - OpenGLTarget m (*mask, maskOrigin); + OpenGLTarget m (mask, maskOrigin); m.makeActiveFor2D(); glDisable (GL_BLEND); setColour (1.0f); - drawFrameBuffer (*(other.mask), other.maskOrigin); + drawFrameBuffer (other.mask, other.maskOrigin); } - explicit ClipRegion_Mask (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) - : ClipRegionBase (scratchBuffer_), - clip (r), - maskOrigin (r.getPosition()) - { - mask = scratchBuffer_.get (r.getWidth(), r.getHeight()); - mask->clear (Colours::white); - } - - explicit ClipRegion_Mask (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) - : ClipRegionBase (scratchBuffer_), - clip (r.getSmallestIntegerContainer()), + explicit ClipRegion_Mask (const RectangleList& r) + : clip (r.getBounds()), maskOrigin (clip.getPosition()) { + TargetSaver ts; initialiseClear(); - glEnableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - - RenderingHelpers::FloatRectangleRasterisingInfo fr (r); - FillFloatRectCallback callback; - fr.iterate (callback); - } - - explicit ClipRegion_Mask (const EdgeTable& e, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) - : ClipRegionBase (scratchBuffer_), - clip (e.getMaximumBounds()), - maskOrigin (clip.getPosition()) - { - initialiseClear(); - OpenGLHelpers::fillEdgeTable (e); - } - - ClipRegion_Mask (OpenGLRenderer::ScratchBufferManager& scratchBuffer_, const Rectangle& bounds, - const Path& p, const AffineTransform& transform, int oversamplingLevel) - : ClipRegionBase (scratchBuffer_), - clip (bounds), maskOrigin (clip.getPosition()) - { - initialiseClear(); - renderPath (p, transform, oversamplingLevel); + disableMultiTexture(); + setColour (1.0f); + fillRectangleList (r); } - ~ClipRegion_Mask() - { - scratchBuffer.release (mask); - } + Ptr clone() const { return new ClipRegion_Mask (*this); } + Rectangle getClipBounds() const { return clip; } - static ClipRegion_Mask* createFromPath (OpenGLRenderer::ScratchBufferManager& scratchBuffer, Rectangle bounds, - const Path& p, const AffineTransform& transform) + Ptr applyClipTo (const Ptr& target) { - bounds = bounds.getIntersection (p.getBoundsTransformed (transform).getSmallestIntegerContainer()); - - return bounds.isEmpty() ? nullptr - : new ClipRegion_Mask (scratchBuffer, bounds, p, transform, (int) defaultOversamplingLevel); + return target->clipToTexture (PositionedTexture (mask.getTextureID(), Rectangle (maskOrigin.getX(), maskOrigin.getY(), + mask.getWidth(), mask.getHeight()), clip)); } - Ptr clone() const { return new ClipRegion_Mask (*this); } - const Rectangle& getClipBounds() const { return clip; } - Ptr applyClipTo (const Ptr& target) { return target->clipToMask (this); } - Ptr clipToRectangle (const Rectangle& r) { clip = clip.getIntersection (r); @@ -539,7 +788,9 @@ public: if (excluded.getNumRectangles() == 1) return excludeClipRectangle (excluded.getRectangle (0)); + TargetSaver ts; makeMaskActive(); + disableMultiTexture(); glDisable (GL_BLEND); setColour (0); fillRectangleList (excluded); @@ -553,7 +804,9 @@ public: if (r.contains (clip)) return nullptr; + TargetSaver ts; makeMaskActive(); + disableMultiTexture(); glDisable (GL_BLEND); setColour (0); OpenGLHelpers::fillRect (r); @@ -562,126 +815,116 @@ public: Ptr clipToPath (const Path& p, const AffineTransform& t) { - ClipRegion_Mask* tempMask = createFromPath (scratchBuffer, clip, p, t); - const Ptr tempMaskPtr (tempMask); - return tempMask == nullptr ? nullptr : clipToMask (tempMask); - } + EdgeTable et (clip, p, t); - Ptr clipToEdgeTable (const EdgeTable& et) - { - ClipRegion_Mask* const tempMask = new ClipRegion_Mask (et, scratchBuffer); - const Ptr tempMaskPtr (tempMask); - return clipToMask (tempMask); + if (! et.isEmpty()) + { + OpenGLTexture texture; + PositionedTexture pt (texture, et, et.getMaximumBounds()); + return clipToTexture (pt); + } + + return nullptr; } - Ptr clipToMask (ClipRegion_Mask* m) + Ptr clipToTexture (const PositionedTexture& pt) { - jassert (m != nullptr && m != this); - clip = clip.getIntersection (m->clip); + clip = clip.getIntersection (pt.clip); if (clip.isEmpty()) return nullptr; - clipFrameBuffers (OpenGLTarget (*mask, maskOrigin), *(m->mask), m->maskOrigin); + TargetSaver ts; + makeMaskActive(); + glEnable (GL_BLEND); + glBlendFunc (GL_ZERO, GL_SRC_ALPHA); + setColour (1.0f); + enableSingleTexture(); + OpenGLHelpers::drawTextureQuad (pt.textureID, pt.area); return this; } Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) { + TargetSaver ts; makeMaskActive(); glEnable (GL_BLEND); glBlendFunc (GL_ZERO, GL_SRC_ALPHA); - fillMaskWithSourceImage (image, transform); - return this; - } - void fillAll (const OpenGLTarget& target, const FillType& fill, bool replaceContents) - { - jassert (! replaceContents); - fillRectInternal (target, clip, fill, false); + setColour (1.0f); + glBindTexture (GL_TEXTURE_2D, image.textureID); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + const GLfloat l = (GLfloat) maskOrigin.getX(); + const GLfloat t = (GLfloat) maskOrigin.getY(); + const GLfloat r = (GLfloat) (maskOrigin.getX() + mask.getWidth()); + const GLfloat b = (GLfloat) (maskOrigin.getY() + mask.getHeight()); + const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; + GLfloat textureCoords[] = { l, t, r, t, l, b, r, b }; + + const AffineTransform inv (transform.inverted().scaled (image.fullWidthProportion / image.imageWidth, + image.fullHeightProportion / image.imageHeight)); + + inv.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); + inv.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); + + textureCoords[1] = 1.0f - textureCoords[1]; + textureCoords[3] = 1.0f - textureCoords[3]; + textureCoords[5] = 1.0f - textureCoords[5]; + textureCoords[7] = 1.0f - textureCoords[7]; + + OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4); + + return this; } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, bool replaceContents) + void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, GradientTexture& gradientTexture, bool replaceContents) { jassert (! replaceContents); const Rectangle r (clip.getIntersection (area)); if (! r.isEmpty()) - fillRectInternal (target, r, fill, false); + fillRectInternal (target, r, fill, gradientTexture, false); } - void fillRectInternal (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, bool replaceContents) + void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture& texture, const FillType& fill, GradientTexture& gradientTexture) { - if (fill.isColour()) - { - target.makeActiveFor2D(); - setBlendMode (replaceContents); - PixelARGB p (fill.colour.getARGB()); - p.premultiply(); - OpenGLHelpers::setColour (Colour (p.getARGB())); - target.scissor (area); - drawFrameBuffer (*mask, maskOrigin); - glDisable (GL_SCISSOR_TEST); - } - else - { - OpenGLFrameBuffer* patternBuffer = scratchBuffer.get (area.getWidth(), area.getHeight()); - - fillRectWithFillType (OpenGLTarget (*patternBuffer, area.getPosition()), area, fill, true); - clipAndDraw (target, OpenGLTarget (*patternBuffer, area.getPosition()), area); + PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.getX(), maskOrigin.getY(), mask.getWidth(), mask.getHeight()), area); + fillTexture (target, area, fill, gradientTexture, &texture, &pt, false); + } - scratchBuffer.release (patternBuffer); - } + void fillRectInternal (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, GradientTexture& gradientTexture, bool replaceContents) + { + PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.getX(), maskOrigin.getY(), mask.getWidth(), mask.getHeight()), area); + fillTexture (target, area, fill, gradientTexture, &pt, nullptr, replaceContents); } - void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, float alpha, const Rectangle& targetArea) + void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, const AffineTransform& transform, + float alpha, const Rectangle& clipArea, const PositionedTexture* mask1) { - const Rectangle bufferArea (targetArea.getIntersection (clip)); + const Rectangle bufferArea (clipArea.getIntersection (clip)); if (! bufferArea.isEmpty()) { - OpenGLFrameBuffer* buffer = scratchBuffer.get (bufferArea.getWidth(), bufferArea.getHeight()); - - OpenGLTarget bufferTarget (*buffer, bufferArea.getPosition()); - bufferTarget.makeActiveFor2D(); - glDisable (GL_BLEND); - OpenGLHelpers::fillRectWithTexture (targetArea, source.textureID, alpha); - - clipAndDraw (target, bufferTarget, bufferArea); - - scratchBuffer.release (buffer); + PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.getX(), maskOrigin.getY(), mask.getWidth(), mask.getHeight()), bufferArea); + renderImage (target, source, bufferArea, transform, alpha, mask1, &pt, false, false); } } - void drawImageSelfDestructively (const OpenGLTarget& target, const OpenGLTextureFromImage& source, - float alpha, const AffineTransform& transform) - { - makeMaskActive(); - glEnable (GL_BLEND); - glBlendFunc (GL_DST_ALPHA, GL_ZERO); - fillMaskWithSourceImage (source, transform); - - target.makeActiveFor2D(); - setColour (alpha); - glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - target.scissor (clip); - drawFrameBuffer (*mask, maskOrigin); - glDisable (GL_SCISSOR_TEST); - } - private: - OpenGLFrameBuffer* mask; + OpenGLFrameBuffer mask; Rectangle clip; Point maskOrigin; void prepareFor2D() const { - OpenGLTarget::applyFlippedMatrix (maskOrigin.getX(), maskOrigin.getY(), mask->getWidth(), mask->getHeight()); + OpenGLTarget::applyFlippedMatrix (maskOrigin.getX(), maskOrigin.getY(), mask.getWidth(), mask.getHeight()); } void makeMaskActive() { - const bool b = mask->makeCurrentRenderingTarget(); + const bool b = mask.makeCurrentRenderingTarget(); (void) b; jassert (b); prepareFor2D(); } @@ -689,77 +932,18 @@ private: void initialiseClear() { jassert (! clip.isEmpty()); - mask = scratchBuffer.get (clip.getWidth(), clip.getHeight()); - mask->makeCurrentAndClear(); + mask.initialise (clip.getWidth(), clip.getHeight()); + mask.makeCurrentAndClear(); glDisable (GL_TEXTURE_2D); glDisable (GL_BLEND); prepareFor2D(); } - struct FillFloatRectCallback - { - void operator() (const int x, const int y, const int w, const int h, const int alpha) const - { - const GLfloat l = (GLfloat) x; - const GLfloat t = (GLfloat) y; - const GLfloat r = (GLfloat) (x + w); - const GLfloat b = (GLfloat) (y + h); - - const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; - setColour (alpha / 255.0f); - glVertexPointer (2, GL_FLOAT, 0, vertices); - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); - } - }; - - void clipAndDraw (const OpenGLTarget& target, const OpenGLTarget& buffer, const Rectangle& clip) - { - clipFrameBuffers (buffer, *mask, maskOrigin); - - target.makeActiveFor2D(); - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - setColour (1.0f); - target.scissor (clip); - - drawFrameBuffer (*buffer.frameBuffer, Point (buffer.x, buffer.y)); - glDisable (GL_SCISSOR_TEST); - } - void drawFrameBuffer (const OpenGLFrameBuffer& buffer, const Point& topLeft) { - OpenGLHelpers::drawTextureQuad (buffer.getTextureID(), topLeft.getX(), topLeft.getY(), - buffer.getWidth(), buffer.getHeight()); - } - - void fillMaskWithSourceImage (const OpenGLTextureFromImage& image, const AffineTransform& transform) const - { - setColour (1.0f); - glBindTexture (GL_TEXTURE_2D, image.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); - - const GLfloat l = (GLfloat) maskOrigin.getX(); - const GLfloat t = (GLfloat) maskOrigin.getY(); - const GLfloat r = (GLfloat) (maskOrigin.getX() + mask->getWidth()); - const GLfloat b = (GLfloat) (maskOrigin.getY() + mask->getHeight()); - const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; - GLfloat textureCoords[] = { l, t, r, t, l, b, r, b }; - - const AffineTransform inv (transform.inverted().scaled (1.0f / image.width, - 1.0f / image.height)); - - inv.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); - inv.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); - - textureCoords[1] = 1.0f - textureCoords[1]; - textureCoords[3] = 1.0f - textureCoords[3]; - textureCoords[5] = 1.0f - textureCoords[5]; - textureCoords[7] = 1.0f - textureCoords[7]; - - OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4); + enableSingleTexture(); + OpenGLHelpers::drawTextureQuad (buffer.getTextureID(), Rectangle (topLeft.getX(), topLeft.getY(), + buffer.getWidth(), buffer.getHeight())); } ClipRegion_Mask& operator= (const ClipRegion_Mask&); @@ -767,92 +951,66 @@ private: //============================================================================== -class ClipRegion_Rectangle : public ClipRegionBase +class ClipRegion_RectangleList : public ClipRegionBase { public: - explicit ClipRegion_Rectangle (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) noexcept - : ClipRegionBase (scratchBuffer_), - clip (r) + explicit ClipRegion_RectangleList (const Rectangle& r) noexcept + : clip (r) {} - Ptr clone() const { return new ClipRegion_Rectangle (clip, scratchBuffer); } - const Rectangle& getClipBounds() const { return clip; } - Ptr applyClipTo (const Ptr& target) { return target->clipToRectangle (clip); } - - Ptr clipToRectangle (const Rectangle& r) - { - clip = clip.getIntersection (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr clipToRectangleList (const RectangleList& r) - { - if (r.getNumRectangles() <= 1) - return clipToRectangle (r.getRectangle (0)); + explicit ClipRegion_RectangleList (const RectangleList& r) noexcept + : clip (r) + {} - if (r.containsRectangle (clip)) - return this; + Ptr clone() const { return new ClipRegion_RectangleList (clip); } + Rectangle getClipBounds() const { return clip.getBounds(); } + Ptr applyClipTo (const Ptr& target) { return target->clipToRectangleList (clip); } - return toMask()->clipToRectangleList (r); - } + Ptr clipToRectangle (const Rectangle& r) { return clip.clipTo (r) ? this : nullptr; } + Ptr clipToRectangleList (const RectangleList& r) { return clip.clipTo (r) ? this : nullptr; } + Ptr excludeClipRectangle (const Rectangle& r) { clip.subtract (r); return clip.isEmpty() ? nullptr : this; } - Ptr excludeClipRectangle (const Rectangle& r) - { - return r.contains (clip) ? nullptr - : toMask()->excludeClipRectangle (r); - } - - Ptr clipToMask (ClipRegion_Mask* m) { return m->clipToRectangle (clip); } + Ptr clipToTexture (const PositionedTexture& t) { return toMask()->clipToTexture (t); } Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toMask()->clipToPath (p, transform); } - Ptr clipToEdgeTable (const EdgeTable& et) { return toMask()->clipToEdgeTable (et); } Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) { return toMask()->clipToImageAlpha (image, transform); } - void fillAll (const OpenGLTarget& target, const FillType& fill, bool replaceContents) + void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, GradientTexture& gradientTexture, bool replaceContents) { - fillRectInternal (target, clip, fill, replaceContents); + for (RectangleList::Iterator i (clip); i.next();) + { + const Rectangle r (i.getRectangle()->getIntersection (area)); + + if (! r.isEmpty()) + fillTexture (target, r, fill, gradientTexture, nullptr, nullptr, replaceContents); + } } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, bool replaceContents) + void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, const AffineTransform& transform, + float alpha, const Rectangle& clipArea, const PositionedTexture* mask) { - const Rectangle r (clip.getIntersection (area)); + for (RectangleList::Iterator i (clip); i.next();) + { + const Rectangle bufferArea (i.getRectangle()->getIntersection (clipArea)); - if (! r.isEmpty()) - fillRectInternal (target, r, fill, replaceContents); + if (! bufferArea.isEmpty()) + renderImage (target, source, bufferArea, transform, alpha, mask, nullptr, false, false); + } } - void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, float alpha, const Rectangle& targetArea) + void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture& texture, const FillType& fill, GradientTexture& gradientTexture) { - target.makeActiveFor2D(); - target.scissor (clip); - setPremultipliedBlendingMode(); - OpenGLHelpers::fillRectWithTexture (targetArea, source.textureID, alpha); - glDisable (GL_SCISSOR_TEST); + fillTexture (target, area, fill, gradientTexture, &texture, nullptr, false); } private: - Rectangle clip; - - void fillRectInternal (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, bool replaceContents) - { - if (fill.isColour()) - { - target.makeActiveFor2D(); - setBlendMode (replaceContents); - glDisable (GL_TEXTURE_2D); - OpenGLHelpers::fillRectWithColour (area, fill.colour); - } - else - { - fillRectWithFillType (target, area, fill, replaceContents); - } - } + RectangleList clip; Ptr toMask() const { - return new ClipRegion_Mask (clip, scratchBuffer); + return new ClipRegion_Mask (clip); } - ClipRegion_Rectangle& operator= (const ClipRegion_Rectangle&); + ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&); }; @@ -860,8 +1018,8 @@ private: class OpenGLRenderer::SavedState { public: - SavedState (const OpenGLTarget& target_, ScratchBufferManager& scratchBuffer) - : clip (new ClipRegion_Rectangle (Rectangle (target_.width, target_.height), scratchBuffer)), + SavedState (const OpenGLTarget& target_) + : clip (new ClipRegion_RectangleList (Rectangle (target_.width, target_.height))), transform (0, 0), interpolationQuality (Graphics::mediumResamplingQuality), target (target_), transparencyLayerAlpha (1.0f) { @@ -983,12 +1141,14 @@ public: if (clip != nullptr) { - const Rectangle& clipBounds = clip->getClipBounds(); + const Rectangle clipBounds (clip->getClipBounds()); s->transparencyLayer = Image (OpenGLImageType().create (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true)); s->target = OpenGLTarget (*OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); s->transparencyLayerAlpha = opacity; s->cloneClipIfMultiplyReferenced(); + + s->target.makeActiveFor2D(); } return s; @@ -997,8 +1157,14 @@ public: void endTransparencyLayer (SavedState& finishedLayerState) { if (clip != nullptr) + { + target.makeActiveFor2D(); + const Rectangle clipBounds (clip->getClipBounds()); + clip->drawImage (target, finishedLayerState.transparencyLayer, - finishedLayerState.transparencyLayerAlpha, clip->getClipBounds()); + AffineTransform::translation ((float) clipBounds.getX(), (float) clipBounds.getY()), + finishedLayerState.transparencyLayerAlpha, clipBounds, nullptr); + } } //============================================================================== @@ -1009,7 +1175,7 @@ public: if (transform.isOnlyTranslated) { clip->fillRect (target, r.translated (transform.xOffset, transform.yOffset), - getFillType(), replaceContents); + getFillType(), gradientTexture, replaceContents); } else { @@ -1026,8 +1192,14 @@ public: { if (transform.isOnlyTranslated) { - fillShape (new ClipRegion_Mask (r.translated ((float) transform.xOffset, - (float) transform.yOffset), clip->scratchBuffer), false); + const Rectangle c (r.translated ((float) transform.xOffset, (float) transform.yOffset) + .getIntersection (clip->getClipBounds().toFloat())); + + if (! c.isEmpty()) + { + EdgeTable et (c); + fillEdgeTable (et); + } } else { @@ -1042,11 +1214,8 @@ public: { if (clip != nullptr) { - ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->scratchBuffer, clip->getClipBounds(), path, - transform.getTransformWith (t)); - - if (m != nullptr) - fillShape (m, false); + EdgeTable et (clip->getClipBounds(), path, transform.getTransformWith (t)); + fillEdgeTable (et); } } @@ -1070,16 +1239,20 @@ public: .followedBy (t)))); if (et != nullptr) - fillShape (new ClipRegion_Mask (*et, clip->scratchBuffer), false); + fillEdgeTable (*et); } } } - void fillEdgeTable (const EdgeTable& et, float x, int y) + void fillEdgeTable (const EdgeTable& et, const float x, const int y) { - EdgeTable et2 (et); - et2.translate (x, y); - fillShape (new ClipRegion_Mask (et2, clip->scratchBuffer), false); + if (clip != nullptr) + { + EdgeTable et2 (et); + et2.translate (x, y); + + fillEdgeTable (et2); + } } void drawLine (const Line & line) @@ -1089,25 +1262,13 @@ public: fillPath (p, AffineTransform::identity); } - void fillShape (ClipRegionBase::Ptr shapeToFill, const bool replaceContents) - { - jassert (clip != nullptr && shapeToFill != nullptr); - - if (! fillType.isInvisible()) - { - shapeToFill = clip->applyClipTo (shapeToFill); - - if (shapeToFill != nullptr) - shapeToFill->fillAll (target, getFillType(), replaceContents); - } - } - //============================================================================== void drawImage (const Image& image, const AffineTransform& trans) { if (clip == nullptr || fillType.colour.isTransparent()) return; + const Rectangle clipBounds (clip->getClipBounds()); const AffineTransform t (transform.getTransformWith (trans)); const float alpha = fillType.colour.getFloatAlpha(); @@ -1121,43 +1282,42 @@ public: tx = ((tx + 128) >> 8); ty = ((ty + 128) >> 8); - clip->drawImage (target, image, alpha, Rectangle (tx, ty, image.getWidth(), image.getHeight())); + clip->drawImage (target, image, t, alpha, Rectangle (tx, ty, image.getWidth(), image.getHeight()), nullptr); return; } } - if (t.isSingularity()) - return; - - Path p; - p.addRectangle (image.getBounds()); - ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->scratchBuffer, clip->getClipBounds(), p, t); - - if (m != nullptr) + if (! t.isSingularity()) { - ClipRegionBase::Ptr c (clip->applyClipTo (m)); + Path p; + p.addRectangle (image.getBounds()); - if (c != nullptr) - { - m = dynamic_cast (c.getObject()); + OpenGLTexture texture; + EdgeTable et (clipBounds, p, t); + PositionedTexture pt (texture, et, clipBounds); - jassert (m != nullptr); - m->drawImageSelfDestructively (target, image, alpha, t); - } + clip->drawImage (target, image, t, alpha, clipBounds, &pt); } } + void setFillType (const FillType& newFill) + { + fillType = newFill; + gradientTexture.reset(); + } + //============================================================================== ClipRegionBase::Ptr clip; RenderingHelpers::TranslationOrTransform transform; Font font; FillType fillType; Graphics::ResamplingQuality interpolationQuality; + OpenGLTarget target; private: - OpenGLTarget target; float transparencyLayerAlpha; Image transparencyLayer; + GradientTexture gradientTexture; void cloneClipIfMultiplyReferenced() { @@ -1170,6 +1330,14 @@ private: return fillType.transformed (transform.getTransform()); } + void fillEdgeTable (EdgeTable& et) + { + OpenGLTexture texture; + PositionedTexture pt (texture, et, clip->getClipBounds()); + + clip->fillMask (target, pt.clip, pt, getFillType(), gradientTexture); + } + class CachedGlyphEdgeTable { public: @@ -1214,29 +1382,41 @@ private: //============================================================================== OpenGLRenderer::OpenGLRenderer (OpenGLComponent& target) - : scratchBufferManager (new ScratchBufferManager()), - stack (new SavedState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()), *scratchBufferManager)) + : stack (new SavedState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()))) { - target.makeCurrentRenderingTarget(); + initialise(); } OpenGLRenderer::OpenGLRenderer (OpenGLFrameBuffer& target) - : scratchBufferManager (new ScratchBufferManager()), - stack (new SavedState (OpenGLTarget (target, Point()), *scratchBufferManager)) + : stack (new SavedState (OpenGLTarget (target, Point()))) { - // This object can only be created and used when the current thread has an active OpenGL context. - jassert (OpenGLHelpers::isContextActive()); + initialise(); } OpenGLRenderer::OpenGLRenderer (unsigned int frameBufferID, int width, int height) - : scratchBufferManager (new ScratchBufferManager()), - stack (new SavedState (OpenGLTarget (frameBufferID, width, height), *scratchBufferManager)) + : stack (new SavedState (OpenGLTarget (frameBufferID, width, height))) +{ + initialise(); +} + +void OpenGLRenderer::initialise() { // This object can only be created and used when the current thread has an active OpenGL context. jassert (OpenGLHelpers::isContextActive()); + + previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget(); + initialiseMultiTextureExtensions(); + stack->target.makeActiveFor2D(); + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + resetMultiTextureModes (false); } -OpenGLRenderer::~OpenGLRenderer() {} +OpenGLRenderer::~OpenGLRenderer() +{ + OpenGLFrameBuffer::setCurrentFrameBufferTarget (previousFrameBufferTarget); + resetMultiTextureModes (true); +} bool OpenGLRenderer::isVectorDevice() const { return false; } void OpenGLRenderer::setOrigin (int x, int y) { stack->transform.setOrigin (x, y); } @@ -1254,7 +1434,7 @@ void OpenGLRenderer::saveState() void OpenGLRenderer::restoreState() { stack.restore(); } void OpenGLRenderer::beginTransparencyLayer (float opacity) { stack.beginTransparencyLayer (opacity); } void OpenGLRenderer::endTransparencyLayer() { stack.endTransparencyLayer(); } -void OpenGLRenderer::setFill (const FillType& fillType) { stack->fillType = fillType; } +void OpenGLRenderer::setFill (const FillType& fillType) { stack->setFillType (fillType); } void OpenGLRenderer::setOpacity (float newOpacity) { stack->fillType.setOpacity (newOpacity); } void OpenGLRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) { stack->interpolationQuality = quality; } void OpenGLRenderer::fillRect (const Rectangle& r, bool replace) { stack->fillRect (r, replace); } diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h index 2aa0d53f4c..9783eeb724 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h @@ -75,12 +75,13 @@ public: #ifndef DOXYGEN class SavedState; - class ScratchBufferManager; #endif private: - ScopedPointer scratchBufferManager; RenderingHelpers::SavedStateStack stack; + GLuint previousFrameBufferTarget; + + void initialise(); }; #endif // __JUCE_OPENGLGRAPHICSCONTEXT_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index ad96e484b2..cc4c887eef 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -234,12 +234,12 @@ void OpenGLHelpers::drawTriangleStrip (const GLfloat* const vertices, const GLfl glBindTexture (GL_TEXTURE_2D, 0); } -void OpenGLHelpers::drawTextureQuad (GLuint textureID, int x, int y, int w, int h) +void OpenGLHelpers::drawTextureQuad (GLuint textureID, const Rectangle& rect) { - const GLfloat l = (GLfloat) x; - const GLfloat t = (GLfloat) y; - const GLfloat r = (GLfloat) (x + w); - const GLfloat b = (GLfloat) (y + h); + const GLfloat l = (GLfloat) rect.getX(); + const GLfloat t = (GLfloat) rect.getY(); + const GLfloat r = (GLfloat) rect.getRight(); + const GLfloat b = (GLfloat) rect.getBottom(); const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; const GLfloat textureCoords[] = { 0, 1.0f, 1.0f, 1.0f, 0, 0, 1.0f, 0 }; @@ -255,7 +255,7 @@ void OpenGLHelpers::fillRectWithTexture (const Rectangle& rect, GLuint text glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glColor4f (alpha, alpha, alpha, alpha); - drawTextureQuad (textureID, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + drawTextureQuad (textureID, rect); } //============================================================================== @@ -280,449 +280,27 @@ void OpenGLHelpers::fillRect (const Rectangle& rect) glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); } -//============================================================================== -struct OpenGLEdgeTableRenderer -{ - OpenGLEdgeTableRenderer() noexcept - : 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 - { - vertices[1] = vertices[5] = (GLfloat) y; - vertices[3] = vertices[7] = (GLfloat) (y + 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]; - int lastAlpha; - - void drawHorizontal (int x, const int w, const int alphaLevel) noexcept - { - vertices[0] = vertices[2] = (GLfloat) x; - vertices[4] = vertices[6] = (GLfloat) (x + w); - - if (lastAlpha != alphaLevel) - { - lastAlpha = alphaLevel; - const float a = alphaLevel / 255.0f; - glColor4f (a, a, a, a); - } - - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); - } - - JUCE_DECLARE_NON_COPYABLE (OpenGLEdgeTableRenderer); -}; - -void OpenGLHelpers::fillEdgeTable (const EdgeTable& edgeTable) -{ - OpenGLEdgeTableRenderer etr; - etr.draw (edgeTable); -} - -//============================================================================== -// This breaks down a path into a series of horizontal strips of trapezoids.. -class TriangulatedPath::TrapezoidedPath -{ -public: - TrapezoidedPath (const Path& p, const AffineTransform& transform) - : firstSlice (nullptr), - windingMask (p.isUsingNonZeroWinding() ? -1 : 1) - { - for (PathFlatteningIterator iter (p, transform); iter.next();) - addLine (floatToInt (iter.x1), floatToInt (iter.y1), - floatToInt (iter.x2), floatToInt (iter.y2)); - } - - ~TrapezoidedPath() - { - for (HorizontalSlice* s = firstSlice; s != nullptr;) - { - const ScopedPointer deleter (s); - s = s->next; - } - } - - template - void iterate (Consumer& consumer) const - { - for (HorizontalSlice* s = firstSlice; s != nullptr; s = s->next) - s->iterate (consumer, windingMask); - } - -private: - void addLine (int x1, int y1, int x2, int y2) - { - int winding = 1; - - if (y2 < y1) - { - std::swap (x1, x2); - std::swap (y1, y2); - winding = -1; - } - - HorizontalSlice* last = nullptr; - HorizontalSlice* s = firstSlice; - - while (y2 > y1) - { - if (s == nullptr) - { - insert (last, new HorizontalSlice (nullptr, x1, y1, x2, y2, winding)); - break; - } - - if (s->y2 > y1) - { - if (y1 < s->y1) - { - if (y2 <= s->y1) - { - insert (last, new HorizontalSlice (s, x1, y1, x2, y2, winding)); - break; - } - else - { - 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; - x1 = newX; - y1 = s->y1; - continue; - } - } - else if (y1 > s->y1) - { - s->split (y1); - s = s->next; - jassert (s != nullptr); - } - - jassert (y1 == s->y1); - - if (y2 > s->y2) - { - const int newY = s->y2; - const int newX = x1 + (int) ((newY - y1) * (int64) (x2 - x1) / (y2 - y1)); - s->addLine (x1, newX, winding); - x1 = newX; - y1 = newY; - } - else - { - if (y2 < s->y2) - s->split (y2); - - jassert (y2 == s->y2); - s->addLine (x1, x2, winding); - break; - } - } - - last = s; - s = s->next; - } - } - - struct HorizontalSlice - { - HorizontalSlice (const HorizontalSlice& other, HorizontalSlice* const next_, int y1_, int y2_) - : next (next_), y1 (y1_), y2 (y2_), segments (other.segments) - { - } - - HorizontalSlice (HorizontalSlice* const next_, int x1, int y1_, int x2, int y2_, int winding) - : next (next_), y1 (y1_), y2 (y2_) - { - jassert (next != this); - jassert (y2 > y1); - segments.ensureStorageAllocated (32); - segments.add (LineSegment (x1, x2, winding)); - } - - void addLine (const int x1, const int x2, int winding) - { - const int dy = y2 - y1; - - for (int i = 0; i < segments.size(); ++i) - { - const LineSegment& l = segments.getReference (i); - - const int diff1 = l.x1 - x1; - const int diff2 = l.x2 - x2; - - if ((diff1 < 0) == (diff2 > 0)) - { - const int dx1 = l.x2 - l.x1; - const int dx2 = x2 - x1; - const int dxDiff = dx2 - dx1; - - if (dxDiff != 0) - { - const int intersectionY = (int) ((dy * (int64) diff1) / dxDiff); - - if (intersectionY > 0 && intersectionY < dy) - { - const int intersectionX = x1 + (intersectionY * dx2) / dy; - split (intersectionY + y1); - next->addLine (intersectionX, x2, winding); - addLine (x1, intersectionX, winding); - return; - } - } - } - - if (diff1 + diff2 > 0) - { - segments.insert (i, LineSegment (x1, x2, winding)); - return; - } - } - - segments.add (LineSegment (x1, x2, winding)); - } - - void split (const int newY) - { - jassert (newY > y1 && newY < y2); - - const int dy1 = newY - y1; - const int dy2 = y2 - y1; - next = new HorizontalSlice (*this, next, newY, y2); - y2 = newY; - - LineSegment* const oldSegments = segments.getRawDataPointer(); - LineSegment* const newSegments = next->segments.getRawDataPointer(); - - for (int i = 0; i < segments.size(); ++i) - { - LineSegment& l = oldSegments[i]; - const int newX = l.x1 + (int) (dy1 * (int64) (l.x2 - l.x1) / dy2); - newSegments[i].x1 = newX; - l.x2 = newX; - } - } - - template - void iterate (Consumer& consumer, const int windingMask) - { - jassert (segments.size() > 0); - - const float fy1 = intToFloat (y1); - const float fy2 = intToFloat (y2); - - const LineSegment* s1 = segments.getRawDataPointer(); - const LineSegment* s2 = s1; - int winding = s1->winding; - - for (int i = segments.size(); --i > 0;) - { - ++s2; - winding += s2->winding; - - if ((winding & windingMask) == 0) - { - const float ax1 = intToFloat (s1->x1); - const float ax2 = intToFloat (s1->x2); - - if (s1->x1 == s2->x1) - consumer.addTriangle (ax1, fy1, ax2, fy2, intToFloat (s2->x2), fy2); - else if (s1->x2 == s2->x2) - consumer.addTriangle (ax1, fy1, intToFloat (s2->x1), fy1, ax2, fy2); - else - consumer.addTrapezoid (fy1, fy2, ax1, ax2, intToFloat (s2->x1), intToFloat (s2->x2)); - - s1 = s2 + 1; - } - } - } - - HorizontalSlice* next; - int y1, y2; - - private: - struct LineSegment - { - inline LineSegment (int x1_, int x2_, int winding_) noexcept - : x1 (x1_), x2 (x2_), winding (winding_) {} - - int x1, x2; - int winding; - }; - - Array segments; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HorizontalSlice); - }; - - HorizontalSlice* firstSlice; - const int windingMask; - - inline void insert (HorizontalSlice* const last, HorizontalSlice* const newOne) noexcept - { - if (last == nullptr) - firstSlice = newOne; - else - last->next = newOne; - } - - enum { factor = 128 }; - static inline int floatToInt (const float n) noexcept { return roundToInt (n * (float) factor); } - static inline float intToFloat (const int n) noexcept { return n * (1.0f / (float) factor); } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrapezoidedPath); -}; - -//============================================================================== -struct TriangulatedPath::TriangleBlock -{ - TriangleBlock() noexcept - : numVertices (0), - triangles (maxVerticesPerBlock) - {} - - void draw() const - { - glVertexPointer (2, GL_FLOAT, 0, triangles); - glDrawArrays (GL_TRIANGLES, 0, numVertices / 2); - } - - inline GLfloat* getNextTriangle() noexcept { return triangles + numVertices; } - void optimiseStorage() { triangles.realloc (numVertices); } - - // Some GL implementations can't take very large triangle lists, so store - // the list as a series of blocks containing this max number of triangles. - enum { maxVerticesPerBlock = 256 * 6 }; - - unsigned int numVertices; - HeapBlock triangles; -}; - -TriangulatedPath::TriangulatedPath (const Path& path, const AffineTransform& transform) -{ - startNewBlock(); - TrapezoidedPath (path, transform).iterate (*this); -} - -TriangulatedPath::~TriangulatedPath() {} - -void TriangulatedPath::draw (const int oversamplingLevel) const -{ - const float a = 1.0f / (oversamplingLevel * oversamplingLevel); - glColor4f (a, a, a, a); - - glPushMatrix(); - glTranslatef (-0.5f, -0.5f, 0.0f); - const float inc = 1.0f / oversamplingLevel; - - for (int y = oversamplingLevel; --y >= 0;) - { - for (int x = oversamplingLevel; --x >= 0;) - { - glTranslatef (inc, 0.0f, 0.0f); - - for (int i = 0; i < blocks.size(); ++i) - blocks.getUnchecked(i)->draw(); - } - - glTranslatef (-1.0f, inc, 0.0f); - } - - glPopMatrix(); -} - -void TriangulatedPath::optimiseStorage() -{ - currentBlock->optimiseStorage(); -} - -void TriangulatedPath::startNewBlock() -{ - currentBlock = new TriangleBlock(); - blocks.add (currentBlock); -} - -void TriangulatedPath::addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3) -{ - if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock) - startNewBlock(); - - GLfloat* t = currentBlock->getNextTriangle(); - *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y3; - - currentBlock->numVertices += 6; -} - -void TriangulatedPath::addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4) -{ - if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock - 6) - startNewBlock(); - - GLfloat* t = currentBlock->getNextTriangle(); - *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; - *t++ = x4; *t++ = y2; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; - - currentBlock->numVertices += 12; -} - //============================================================================== OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) - : width (image.getWidth()), - height (image.getHeight()) + : imageWidth (image.getWidth()), + imageHeight (image.getHeight()) { OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image); if (fb != nullptr) { textureID = fb->getTextureID(); + fullWidthProportion = 1.0f; + fullHeightProportion = 1.0f; } else { - if (OpenGLTexture::isValidSize (width, height)) - { - texture = new OpenGLTexture(); - texture->load (image); - textureID = texture->getTextureID(); - } - else - { - frameBuffer = new OpenGLFrameBuffer(); - frameBuffer->initialise (image); - textureID = frameBuffer->getTextureID(); - } + texture = new OpenGLTexture(); + texture->loadImage (image); + textureID = texture->getTextureID(); + + fullWidthProportion = imageWidth / (float) texture->getWidth(); + fullHeightProportion = imageHeight / (float) texture->getHeight(); } } diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h index 1287b70eb4..78a83aa6c7 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h @@ -78,7 +78,7 @@ public: static void drawTriangleStrip (const GLfloat* const vertices, const GLfloat* const textureCoords, const int numVertices, const GLuint textureID) noexcept; - static void drawTextureQuad (GLuint textureID, int x, int y, int w, int h); + static void drawTextureQuad (GLuint textureID, const Rectangle& rect); static void fillRectWithTexture (const Rectangle& rect, GLuint textureID, const float alpha); @@ -88,9 +88,6 @@ public: static void fillRectWithColour (const Rectangle& rect, const Colour& colour); - /** Renders an edge-table into the current context. */ - static void fillEdgeTable (const EdgeTable& edgeTable); - /** Checks whether the current context supports the specified extension. */ static bool isExtensionSupported (const char* extensionName); @@ -98,43 +95,6 @@ public: static void* getExtensionFunction (const char* functionName); }; -//============================================================================== -/** Holds a set of OpenGL triangles, having generated them from a Path object. -*/ -class JUCE_API TriangulatedPath -{ -public: - TriangulatedPath (const Path& path, const AffineTransform& transform); - - /** Destructor. */ - ~TriangulatedPath(); - - /** Renders the path, using a jittered oversampling method. - The oversampling level is the square root of the number of times it - should be oversampled, so 3 or 4 might be reasonable. - */ - void draw (int oversamplingLevel) const; - - /** Reduces the memory footprint of this object to the minimum possible. */ - void optimiseStorage(); - -private: - class TrapezoidedPath; - friend class TrapezoidedPath; - - void startNewBlock(); - void addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3); - void addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4); - - struct TriangleBlock; - friend class OwnedArray; - OwnedArray blocks; - TriangleBlock* currentBlock; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangulatedPath); -}; - - //============================================================================== /** Used as a local object while rendering, this will create a temporary texture ID @@ -150,11 +110,11 @@ public: ~OpenGLTextureFromImage(); GLuint textureID; - const int width, height; + const int imageWidth, imageHeight; + float fullWidthProportion, fullHeightProportion; private: ScopedPointer texture; - ScopedPointer frameBuffer; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTextureFromImage); }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 1434acee88..4820e95528 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -25,12 +25,6 @@ BEGIN_JUCE_NAMESPACE -#if JUCE_OPENGL_ES - enum { internalGLTextureFormat = GL_RGBA }; -#else - enum { internalGLTextureFormat = 4 }; -#endif - OpenGLTexture::OpenGLTexture() : textureID (0), width (0), height (0) @@ -47,7 +41,7 @@ bool OpenGLTexture::isValidSize (int width, int height) return isPowerOfTwo (width) && isPowerOfTwo (height); } -void OpenGLTexture::create (const int w, const int h, const void* pixels) +void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum type) { // 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. @@ -55,24 +49,26 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels) jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? - release(); + if (width != w || height != h) + { + release(); - width = w; - height = h; + width = w; + height = h; - glGenTextures (1, &textureID); - glBindTexture (GL_TEXTURE_2D, textureID); + glGenTextures (1, &textureID); + } - glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + 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); - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0, - GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glTexImage2D (GL_TEXTURE_2D, 0, type == GL_ALPHA ? GL_ALPHA : GL_RGBA, + w, h, 0, type, GL_UNSIGNED_BYTE, pixels); } template @@ -96,7 +92,7 @@ struct Flipper } }; -void OpenGLTexture::load (const Image& image) +void OpenGLTexture::loadImage (const Image& image) { const int imageW = image.getWidth(); const int imageH = image.getHeight(); @@ -114,25 +110,30 @@ void OpenGLTexture::load (const Image& image) default: break; } - create (textureW, textureH, dataCopy); + create (textureW, textureH, dataCopy, GL_BGRA_EXT); +} + +void OpenGLTexture::loadARGB (const PixelARGB* pixels, const int w, const int h) +{ + jassert (isValidSize (w, h)); + create (w, h, pixels, GL_BGRA_EXT); +} + +void OpenGLTexture::loadAlpha (const uint8* pixels, int w, int h) +{ + jassert (isValidSize (w, h)); + create (w, h, pixels, GL_ALPHA); } -void OpenGLTexture::load (const PixelARGB* pixels, const int w, const int h) +void OpenGLTexture::loadARGBFlipped (const PixelARGB* pixels, int w, int h) { const int textureW = nextPowerOfTwo (w); + const int textureH = nextPowerOfTwo (h); - if (h == 1 && textureW == w) - { - create (w, 1, pixels); - } - else - { - const int textureH = nextPowerOfTwo (h); + HeapBlock flippedCopy; + Flipper::flip (flippedCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH); - HeapBlock dataCopy; - Flipper::flip (dataCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH); - create (textureW, textureH, dataCopy); - } + loadARGB (flippedCopy, textureW, textureH); } void OpenGLTexture::release() diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.h b/modules/juce_opengl/opengl/juce_OpenGLTexture.h index f773732e1f..180caa030f 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.h +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.h @@ -37,13 +37,39 @@ public: ~OpenGLTexture(); /** Creates a texture from the given image. + Note that if the image's dimensions aren't a power-of-two, the texture may be created with a larger size. + + The image will be arranged so that its top-left corner is at texture + coordinate (0, 1). + */ + void loadImage (const Image& image); + + /** Creates a texture from a raw array of pixels. + The width and height provided must be valid - i.e. power-of-two unless + the underlying GL system allows otherwise. + The data is sent directly to the OpenGL driver without being flipped vertically, + so the first pixel will be mapped onto texture coordinate (0, 0). + bottom-left corner of the texture */ - void load (const Image& image); + void loadARGB (const PixelARGB* pixels, int width, int height); - /** Creates a texture from a raw array of pixels. */ - void load (const PixelARGB* pixels, int width, int height); + /** Creates a texture from a raw array of pixels. + This is like loadARGB, but will vertically flip the data so that the first + pixel ends up at texture coordinate (0, 1), and if the width and height are + not powers-of-two, it will compensate by using a larger texture size. + */ + void loadARGBFlipped (const PixelARGB* pixels, int width, int height); + + /** Creates an alpha-channel texture from an array of alpha values. + The width and height provided must be valid - i.e. power-of-two unless + the underlying GL system allows otherwise. + The data is sent directly to the OpenGL driver without being flipped vertically, + so the first pixel will be mapped onto texture coordinate (0, 0). + bottom-left corner of the texture + */ + void loadAlpha (const uint8* pixels, int width, int height); /** Frees the texture, if there is one. */ void release(); @@ -83,7 +109,7 @@ private: GLuint textureID; int width, height; - void create (int w, int h, const void*); + void create (int w, int h, const void*, GLenum type); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture); };