diff --git a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp index 254e0d0f78..0ca20ea01d 100644 --- a/extras/JuceDemo/Source/demos/OpenGLDemo.cpp +++ b/extras/JuceDemo/Source/demos/OpenGLDemo.cpp @@ -157,8 +157,9 @@ public: void drawBackground2DStuff() { - OpenGLGraphicsContext glRenderer (*this); // Create an OpenGLGraphicsContext 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. + // Create an OpenGLGraphicsContext that will draw into this GL window.. + ScopedPointer glRenderer (createOpenGLGraphicsContext (*this)); + Graphics g (glRenderer); // This stuff just creates a spinning star shape and fills it.. Path p; diff --git a/modules/juce_gui_basics/windows/juce_ResizableWindow.h b/modules/juce_gui_basics/windows/juce_ResizableWindow.h index 9996b1b453..1926b6d377 100644 --- a/modules/juce_gui_basics/windows/juce_ResizableWindow.h +++ b/modules/juce_gui_basics/windows/juce_ResizableWindow.h @@ -322,7 +322,7 @@ protected: //============================================================================== /** @internal */ void paint (Graphics& g); - /** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */ + /** (if overriding this, make sure you call ResizableWindow::moved() in your subclass) */ void moved(); /** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */ void resized(); diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp index 803062e341..bb53373626 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp @@ -121,6 +121,26 @@ public: return frameBuffer; } + void clearRegionInFrameBuffer (const RectangleList& list) + { + glClearColor (0, 0, 0, 0); + glEnable (GL_SCISSOR_TEST); + + const GLuint previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget(); + frameBuffer.makeCurrentRenderingTarget(); + + for (RectangleList::Iterator i (list); i.next();) + { + const Rectangle& r = *i.getRectangle(); + glScissor (r.getX(), owner.getHeight() - r.getBottom(), r.getWidth(), r.getHeight()); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } + + glDisable (GL_SCISSOR_TEST); + owner.getCurrentContext()->extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget); + JUCE_CHECK_OPENGL_ERROR + } + RectangleList validArea; private: @@ -152,7 +172,9 @@ public: void componentVisibilityChanged() { - if (! owner.isShowing()) + if (owner.isShowing()) + owner.triggerRepaint(); + else owner.stopRenderThread(); } @@ -172,34 +194,54 @@ public: { } - void run() + virtual void initialise() { #if JUCE_LINUX - { - MessageManagerLock mml (this); - - if (! mml.lockWasGained()) - return; + MessageManagerLock mml (this); + if (mml.lockWasGained()) + { owner.updateContext(); owner.updateContextPosition(); } #endif + } + + virtual void shutdown() + { + #if JUCE_LINUX + owner.deleteContext(); + #endif + } + + virtual bool renderFrame() + { + return owner.performRender(); + } + + virtual void waitForNextFrame (const uint32 frameRenderStartTime) + { + const int defaultFPS = 60; + + const int elapsed = (int) (Time::getMillisecondCounter() - frameRenderStartTime); + Thread::sleep (jmax (1, (1000 / defaultFPS) - elapsed)); + } + + void run() + { + initialise(); while (! threadShouldExit()) { - const uint32 startOfRendering = Time::getMillisecondCounter(); + const uint32 frameRenderStartTime = Time::getMillisecondCounter(); - if (! owner.performRender()) + if (! renderFrame()) break; - const int elapsed = (int) (Time::getMillisecondCounter() - startOfRendering); - Thread::sleep (jmax (1, (1000 / 60) - elapsed)); + waitForNextFrame (frameRenderStartTime); } - #if JUCE_LINUX - owner.deleteContext(); - #endif + shutdown(); } private: @@ -458,17 +500,14 @@ bool OpenGLComponent::performRender() { jassert (getCurrentContext() != nullptr); + cachedImage->clearRegionInFrameBuffer (invalid); + { - OpenGLGraphicsContext g (*getCurrentContext(), frameBuffer); + ScopedPointer g (createOpenGLGraphicsContext (*getCurrentContext(), frameBuffer)); JUCE_CHECK_OPENGL_ERROR - g.clipToRectangleList (invalid); - g.setFill (Colours::transparentBlack); - g.fillRect (bounds, true); - g.setFill (Colours::black); - - JUCE_CHECK_OPENGL_ERROR - paintSelf (g); + g->clipToRectangleList (invalid); + paintSelf (*g); JUCE_CHECK_OPENGL_ERROR } @@ -484,7 +523,7 @@ bool OpenGLComponent::performRender() glBindTexture (GL_TEXTURE_2D, frameBuffer.getTextureID()); jassert (bounds.getPosition() == Point()); - context->copyTexture (bounds, bounds); + context->copyTexture (bounds, bounds, context->getWidth(), context->getHeight()); glBindTexture (GL_TEXTURE_2D, 0); JUCE_CHECK_OPENGL_ERROR } @@ -495,9 +534,9 @@ bool OpenGLComponent::performRender() return true; } -void OpenGLComponent::paintSelf (OpenGLGraphicsContext& glRenderer) +void OpenGLComponent::paintSelf (LowLevelGraphicsContext& context) { - Graphics g (&glRenderer); + Graphics g (&context); #if JUCE_ENABLE_REPAINT_DEBUGGING g.saveState(); diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.h b/modules/juce_opengl/opengl/juce_OpenGLComponent.h index 90eb1c1c40..7e0edf7aa8 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.h +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.h @@ -289,7 +289,7 @@ private: void updateEmbeddedPosition (const Rectangle&); void startRenderThread(); bool performRender(); - void paintSelf (OpenGLGraphicsContext&); + void paintSelf (LowLevelGraphicsContext&); int renderAndSwapBuffers(); // (This method has been deprecated) diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index 01b9197aef..928c9624cc 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -127,7 +127,8 @@ bool OpenGLContext::areShadersAvailable() const } void OpenGLContext::copyTexture (const Rectangle& targetClipArea, - const Rectangle& anchorPosAndTextureSize) + const Rectangle& anchorPosAndTextureSize, + int contextWidth, int contextHeight) { glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); @@ -138,9 +139,7 @@ void OpenGLContext::copyTexture (const Rectangle& targetClipArea, struct OverlayShaderProgram : public ReferenceCountedObject { OverlayShaderProgram (OpenGLContext& context) - : program (context), - builder (program), - params (program) + : program (context), builder (program), params (program) {} static const OverlayShaderProgram& select (OpenGLContext& context) @@ -225,7 +224,7 @@ void OpenGLContext::copyTexture (const Rectangle& targetClipArea, const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top }; const OverlayShaderProgram& program = OverlayShaderProgram::select (*this); - program.params.set ((float) getWidth(), (float) getHeight(), anchorPosAndTextureSize.toFloat()); + program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat()); extensions.glVertexAttribPointer (program.params.positionAttribute.attributeID, 2, GL_SHORT, GL_FALSE, 4, vertices); extensions.glEnableVertexAttribArray (program.params.positionAttribute.attributeID); @@ -241,26 +240,29 @@ void OpenGLContext::copyTexture (const Rectangle& targetClipArea, #if JUCE_USE_OPENGL_FIXED_FUNCTION { - (void) anchorPosAndTextureSize; // xxx need to scissor - const GLshort left = (GLshort) targetClipArea.getX(); - const GLshort right = (GLshort) targetClipArea.getRight(); - const GLshort top = (GLshort) (getHeight() - targetClipArea.getY()); - const GLshort bottom = (GLshort) (getHeight() - targetClipArea.getBottom()); - const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top }; - - OpenGLHelpers::prepareFor2D (getWidth(), getHeight()); + glEnable (GL_SCISSOR_TEST); + glScissor (targetClipArea.getX(), contextHeight - targetClipArea.getBottom(), + targetClipArea.getWidth(), targetClipArea.getHeight()); glColor4f (1.0f, 1.0f, 1.0f, 1.0f); glDisableClientState (GL_COLOR_ARRAY); glDisableClientState (GL_NORMAL_ARRAY); glEnableClientState (GL_VERTEX_ARRAY); glEnableClientState (GL_TEXTURE_COORD_ARRAY); + OpenGLHelpers::prepareFor2D (contextWidth, contextHeight); const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f }; glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + + const GLshort left = (GLshort) anchorPosAndTextureSize.getX(); + const GLshort right = (GLshort) anchorPosAndTextureSize.getRight(); + const GLshort top = (GLshort) (contextHeight - anchorPosAndTextureSize.getY()); + const GLshort bottom = (GLshort) (contextHeight - anchorPosAndTextureSize.getBottom()); + const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top }; glVertexPointer (2, GL_SHORT, 0, vertices); glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + glDisable (GL_SCISSOR_TEST); } #endif } diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.h b/modules/juce_opengl/opengl/juce_OpenGLContext.h index 501c581ec3..deb366919c 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.h @@ -112,9 +112,14 @@ public: @param anchorPosAndTextureSize the position of this rectangle is the texture's top-left anchor position in the target space, and the size must be the total size of the texture. + @param contextWidth the width of the context or framebuffer that is being drawn into, + used for scaling of the coordinates. + @param contextHeight the height of the context or framebuffer that is being drawn into, + used for vertical flipping of the y coordinates. */ void copyTexture (const Rectangle& targetClipArea, - const Rectangle& anchorPosAndTextureSize); + const Rectangle& anchorPosAndTextureSize, + int contextWidth, int contextHeight); protected: //============================================================================== diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index 91befdc97d..2f5a5789f4 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -32,7 +32,7 @@ public: width (width_), height (height_), textureID (0), - frameBufferHandle (0), + frameBufferID (0), depthOrStencilBuffer (0), hasDepthBuffer (false), hasStencilBuffer (false), @@ -47,8 +47,8 @@ public: return; #endif - context.extensions.glGenFramebuffers (1, &frameBufferHandle); - context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle); + context.extensions.glGenFramebuffers (1, &frameBufferID); + context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID); JUCE_CHECK_OPENGL_ERROR glGenTextures (1, &textureID); @@ -102,15 +102,15 @@ public: if (depthOrStencilBuffer != 0) context.extensions.glDeleteRenderbuffers (1, &depthOrStencilBuffer); - if (frameBufferHandle != 0) - context.extensions.glDeleteFramebuffers (1, &frameBufferHandle); + if (frameBufferID != 0) + context.extensions.glDeleteFramebuffers (1, &frameBufferID); JUCE_CHECK_OPENGL_ERROR } void bind() { - context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle); + context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID); JUCE_CHECK_OPENGL_ERROR } @@ -122,7 +122,7 @@ public: OpenGLContext& context; const int width, height; - GLuint textureID, frameBufferHandle, depthOrStencilBuffer; + GLuint textureID, frameBufferID, depthOrStencilBuffer; bool hasDepthBuffer, hasStencilBuffer, ok; private: @@ -214,7 +214,7 @@ bool OpenGLFrameBuffer::initialise (OpenGLFrameBuffer& other) glEnable (GL_TEXTURE_2D); #endif glBindTexture (GL_TEXTURE_2D, p->textureID); - pimpl->context.copyTexture (area, area); + pimpl->context.copyTexture (area, area, area.getWidth(), area.getHeight()); glBindTexture (GL_TEXTURE_2D, 0); JUCE_CHECK_OPENGL_ERROR @@ -272,6 +272,11 @@ bool OpenGLFrameBuffer::makeCurrentRenderingTarget() return true; } +GLuint OpenGLFrameBuffer::getFrameBufferID() const +{ + return pimpl != nullptr ? pimpl->frameBufferID : 0; +} + GLuint OpenGLFrameBuffer::getCurrentFrameBufferTarget() { GLint fb; @@ -339,7 +344,7 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight()); glBindTexture (GL_TEXTURE_2D, 0); #else - pimpl->context.copyTexture (area, area); + pimpl->context.copyTexture (area, area, pimpl->width, pimpl->height); #endif pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0); diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h index 44b694c9bd..f2dea30ed9 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h @@ -95,6 +95,9 @@ public: /** Deselects this buffer as the current OpenGL rendering target. */ void releaseAsRenderingTarget(); + /** Returns the ID of this framebuffer, or 0 if it isn't initialised. */ + GLuint getFrameBufferID() const; + /** Returns the current frame buffer ID for the current context. */ static GLuint getCurrentFrameBufferTarget(); diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 637b7ca3bd..5217572010 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -23,75 +23,47 @@ ============================================================================== */ -//============================================================================== -struct OpenGLTarget +namespace OpenGLRendering +{ + +struct Target { - OpenGLTarget (OpenGLContext& context_, GLuint frameBufferID_, int width, int height) noexcept - : context (context_), frameBuffer (nullptr), frameBufferID (frameBufferID_), bounds (width, height) + Target (OpenGLContext& context_, GLuint frameBufferID_, int width, int height) noexcept + : context (context_), frameBufferID (frameBufferID_), bounds (width, height) {} - OpenGLTarget (OpenGLContext& context_, OpenGLFrameBuffer& frameBuffer_, const Point& origin) noexcept - : context (context_), frameBuffer (&frameBuffer_), frameBufferID (0), + Target (OpenGLContext& context_, OpenGLFrameBuffer& frameBuffer_, const Point& origin) noexcept + : context (context_), frameBufferID (frameBuffer_.getFrameBufferID()), bounds (origin.x, origin.y, frameBuffer_.getWidth(), frameBuffer_.getHeight()) - {} + { + jassert (frameBufferID != 0); // trying to render into an uninitialised framebuffer object. + } - OpenGLTarget (const OpenGLTarget& other) noexcept - : context (other.context), frameBuffer (other.frameBuffer), - frameBufferID (other.frameBufferID), bounds (other.bounds) + Target (const Target& other) noexcept + : context (other.context), frameBufferID (other.frameBufferID), bounds (other.bounds) {} - OpenGLTarget& operator= (const OpenGLTarget& other) + Target& operator= (const Target& other) noexcept { - frameBuffer = other.frameBuffer; frameBufferID = other.frameBufferID; bounds = other.bounds; return *this; } - void makeActiveFor2D() const + void makeActive() const noexcept { - if (frameBuffer != nullptr) - frameBuffer->makeCurrentRenderingTarget(); - else - context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID); - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - applyFlippedMatrix (bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); - #else + context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID); glViewport (0, 0, bounds.getWidth(), bounds.getHeight()); - #endif glDisable (GL_DEPTH_TEST); } - #if JUCE_USE_OPENGL_FIXED_FUNCTION - void scissor (Rectangle r) const - { - r -= bounds.getPosition(); - OpenGLHelpers::enableScissorTest (r.withY (bounds.getHeight() - r.getBottom())); - } - - static void applyFlippedMatrix (const int x, const int y, const int width, const int height) - { - glMatrixMode (GL_PROJECTION); - glLoadIdentity(); - - #if JUCE_OPENGL_ES - glOrthof ((GLfloat) x, (GLfloat) (x + width), (GLfloat) (y + height), (GLfloat) y, 0.0f, 1.0f); - #else - glOrtho (x, x + width, y + height, y, 0, 1); - #endif - - glViewport (0, 0, width, height); - } - #endif - OpenGLContext& context; - OpenGLFrameBuffer* frameBuffer; GLuint frameBufferID; - Rectangle bounds; }; +#if JUCE_USE_OPENGL_SHADERS + //============================================================================== class PositionedTexture { @@ -115,28 +87,6 @@ public: : textureID (textureID_), area (area_), clip (clip_) {} - #if JUCE_USE_OPENGL_FIXED_FUNCTION - 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 prepareTextureCoords (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]); - } - - glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); - } - #endif - GLuint textureID; Rectangle area, clip; @@ -195,7 +145,6 @@ private: }; //============================================================================== -#if JUCE_USE_OPENGL_SHADERS class ShaderPrograms : public ReferenceCountedObject { public: @@ -288,7 +237,7 @@ public: maskBounds (program, "maskBounds") {} - void setBounds (const Rectangle& area, const OpenGLTarget& target, const GLint textureIndex) const + void setBounds (const Rectangle& area, const Target& target, const GLint textureIndex) const { maskTexture.set (textureIndex); maskBounds.set (area.getX() - target.bounds.getX(), @@ -307,10 +256,7 @@ public: { SolidColourProgram (OpenGLContext& context) : ShaderBase (context, JUCE_DECLARE_VARYING_COLOUR - "void main()" - "{" - " gl_FragColor = frontColour;" - "}") + "void main() { gl_FragColor = frontColour; }") {} }; @@ -331,8 +277,7 @@ public: SolidColourMaskedProgram (OpenGLContext& context) : ShaderBase (context, JUCE_DECLARE_MASK_UNIFORMS JUCE_DECLARE_VARYING_COLOUR JUCE_DECLARE_VARYING_PIXELPOS - "void main()" - "{" + "void main() {" "gl_FragColor = frontColour * " JUCE_GET_MASK_ALPHA ";" "}"), maskParams (program) @@ -651,8 +596,6 @@ public: MaskTextureProgram maskTexture; }; -#endif - //============================================================================== struct StateHelpers { @@ -721,55 +664,6 @@ struct StateHelpers GLenum srcFunction, dstFunction; }; - //============================================================================== - #if JUCE_USE_OPENGL_FIXED_FUNCTION - struct CurrentColour - { - CurrentColour() noexcept - : currentColour (0xffffffff) - {} - - void resync() noexcept - { - currentColour = PixelARGB (0xffffffff); - glColor4f (1.0f, 1.0f, 1.0f, 1.0f); - } - - void setPremultipliedColour (const Colour& c) noexcept - { - setColour (c.getPixelARGB()); - } - - void setColour (const float alpha) noexcept - { - const uint8 v = (uint8) jmin (255, (int) (alpha * 255.0f)); - setColour (PixelARGB (v, v, v, v)); - } - - void setColour (const PixelARGB& c) noexcept - { - if (currentColour.getARGB() != c.getARGB()) - { - currentColour = c; - glColor4f (c.getRed() / 255.0f, c.getGreen() / 255.0f, - c.getBlue() / 255.0f, c.getAlpha() / 255.0f); - } - } - - void setSolidColour() noexcept - { - if (currentColour.getARGB() != 0xffffffff) - { - currentColour = PixelARGB (0xffffffff); - glColor4f (1.0f, 1.0f, 1.0f, 1.0f); - } - } - - private: - PixelARGB currentColour; - }; - #endif - //============================================================================== template struct EdgeTableRenderer @@ -839,94 +733,6 @@ struct StateHelpers JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); }; - #if JUCE_USE_OPENGL_FIXED_FUNCTION - struct QuadQueue - { - QuadQueue() noexcept - : numIndices (0), numVertices (0), isActive (false) - {} - - void prepare (ActiveTextures& activeTextures, CurrentColour& currentColour) - { - if (! isActive) - { - jassert (numIndices == 0 && numVertices == 0); - activeTextures.disableTextures (*this); - glEnableClientState (GL_COLOR_ARRAY); - glVertexPointer (2, GL_SHORT, 0, vertices); - glColorPointer (4, GL_UNSIGNED_BYTE, 0, colours); - currentColour.setSolidColour(); - isActive = true; // (careful to do this last, as the preceding calls may change it) - } - } - - void add (const int x, const int y, const int w, const int h, const PixelARGB& colour) noexcept - { - jassert (isActive && w > 0 && h > 0); - - GLshort* const v = vertices + numVertices * 2; - v[0] = v[4] = (GLshort) x; - v[1] = v[3] = (GLshort) y; - v[2] = v[6] = (GLshort) (x + w); - v[5] = v[7] = (GLshort) (y + h); - - uint32* const c = colours + numVertices; - c[0] = c[1] = c[2] = c[3] = colour.getInRGBAMemoryOrder(); - - GLubyte* const i = indices + numIndices; - i[0] = (GLubyte) numVertices; - i[1] = i[3] = (GLubyte) (numVertices + 1); - i[2] = i[4] = (GLubyte) (numVertices + 2); - i[5] = (GLubyte) (numVertices + 3); - - numVertices += 4; - numIndices += 6; - - if (numIndices > maxVerticesPerBlock - 6) - draw(); - } - - void add (const Rectangle& r, const PixelARGB& colour) noexcept - { - FloatRectangleRenderer frr (*this, colour); - RenderingHelpers::FloatRectangleRasterisingInfo (r).iterate (frr); - } - - void flush() noexcept - { - if (isActive) - { - if (numIndices > 0) - draw(); - - isActive = false; - glDisableClientState (GL_COLOR_ARRAY); - } - } - - void add (const EdgeTable& et, const PixelARGB& colour) - { - EdgeTableRenderer etr (*this, colour); - et.iterate (etr); - } - - private: - enum { maxVerticesPerBlock = 192 }; // must not go over 256 because the indices are 8-bit. - GLshort vertices [maxVerticesPerBlock * 2]; - GLubyte indices [maxVerticesPerBlock]; - uint32 colours [maxVerticesPerBlock]; - int numIndices, numVertices; - bool isActive; - - void draw() noexcept - { - glDrawElements (GL_TRIANGLES, numIndices, GL_UNSIGNED_BYTE, indices); - numIndices = 0; - numVertices = 0; - } - }; - #endif - //============================================================================== struct ActiveTextures { @@ -1019,11 +825,6 @@ struct StateHelpers currentActiveTexture = index; context.extensions.glActiveTexture (GL_TEXTURE0 + index); JUCE_CHECK_OPENGL_ERROR - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - context.extensions.glClientActiveTexture (GL_TEXTURE0 + index); - JUCE_CHECK_OPENGL_ERROR - #endif } } @@ -1124,7 +925,6 @@ struct StateHelpers bool gradientNeedsRefresh; }; - #if JUCE_USE_OPENGL_SHADERS //============================================================================== struct ShaderQuadQueue { @@ -1141,7 +941,6 @@ struct StateHelpers void initialise() noexcept { JUCE_CHECK_OPENGL_ERROR - for (int i = 0, v = 0; i < numQuads * 6; i += 6, v += 4) { indexData[i] = (GLushort) v; @@ -1154,7 +953,6 @@ struct StateHelpers context.extensions.glBindBuffer (GL_ARRAY_BUFFER, buffers[0]); context.extensions.glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, buffers[1]); context.extensions.glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indexData), indexData, GL_STATIC_DRAW); - JUCE_CHECK_OPENGL_ERROR } @@ -1254,13 +1052,12 @@ struct StateHelpers { CurrentShader (OpenGLContext& context_) noexcept : context (context_), - canUseShaders (context.areShadersAvailable()), activeShader (nullptr) { const Identifier programValueID ("GraphicsContextPrograms"); programs = dynamic_cast (context.properties [programValueID].getObject()); - if (programs == nullptr && canUseShaders) + if (programs == nullptr) { programs = new ShaderPrograms (context); context.properties.set (programValueID, var (programs)); @@ -1288,7 +1085,7 @@ struct StateHelpers } } - void setShader (OpenGLTarget& target, ShaderQuadQueue& quadQueue, ShaderPrograms::ShaderBase& shader) + void setShader (Target& target, ShaderQuadQueue& quadQueue, ShaderPrograms::ShaderBase& shader) { setShader (target.bounds, quadQueue, shader); } @@ -1306,7 +1103,6 @@ struct StateHelpers OpenGLContext& context; ShaderPrograms::Ptr programs; - bool canUseShaders; private: ShaderPrograms::ShaderBase* activeShader; @@ -1314,458 +1110,143 @@ struct StateHelpers CurrentShader& operator= (const CurrentShader&); }; - #endif }; //============================================================================== -class OpenGLGraphicsContext::GLState +class GLState { public: - GLState (const OpenGLTarget& target_) noexcept + GLState (const Target& target_) noexcept : target (target_), activeTextures (target_.context), - #if JUCE_USE_OPENGL_SHADERS currentShader (target_.context), shaderQuadQueue (target_.context), - #endif previousFrameBufferTarget (OpenGLFrameBuffer::getCurrentFrameBufferTarget()) { // This object can only be created and used when the current thread has an active OpenGL context. jassert (OpenGLHelpers::isContextActive()); JUCE_CHECK_OPENGL_ERROR - target.makeActiveFor2D(); + target.makeActive(); blendMode.resync(); - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - currentColour.resync(); - #endif - JUCE_CHECK_OPENGL_ERROR #ifdef GL_COLOR_ARRAY glDisableClientState (GL_COLOR_ARRAY); glDisableClientState (GL_NORMAL_ARRAY); + glDisableClientState (GL_VERTEX_ARRAY); + glDisableClientState (GL_INDEX_ARRAY); - #if JUCE_USE_OPENGL_SHADERS - if (currentShader.canUseShaders) - { - glDisableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_INDEX_ARRAY); - - for (int i = 3; --i >= 0;) - { - activeTextures.setActiveTexture (i); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - } - } - else - #endif + for (int i = 3; --i >= 0;) { - glEnableClientState (GL_VERTEX_ARRAY); - - for (int i = 3; --i >= 0;) - { - activeTextures.setActiveTexture (i); - glEnableClientState (GL_TEXTURE_COORD_ARRAY); - } + activeTextures.setActiveTexture (i); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); } #endif JUCE_CHECK_OPENGL_ERROR activeTextures.clear(); - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - resetMultiTextureModes (false); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - #endif - - #if JUCE_USE_OPENGL_SHADERS shaderQuadQueue.initialise(); - #endif - JUCE_CHECK_OPENGL_ERROR } ~GLState() { flush(); - target.context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget); - #if JUCE_USE_OPENGL_FIXED_FUNCTION - resetMultiTextureModes (true); - #endif - #if JUCE_USE_OPENGL_SHADERS && defined (GL_INDEX_ARRAY) + #if defined (GL_INDEX_ARRAY) glDisableClientState (GL_INDEX_ARRAY); #endif } void flush() { - #if JUCE_USE_OPENGL_SHADERS currentShader.clearShader (shaderQuadQueue); shaderQuadQueue.flush(); - #endif - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - quadQueue.flush(); - #endif - JUCE_CHECK_OPENGL_ERROR } - #if JUCE_USE_OPENGL_FIXED_FUNCTION - void scissor (const Rectangle& r) + void setShader (ShaderPrograms::ShaderBase& shader) { - quadQueue.flush(); - target.scissor (r); + currentShader.setShader (target, shaderQuadQueue, shader); + JUCE_CHECK_OPENGL_ERROR } - void disableScissor() + void setShaderForGradientFill (const ColourGradient& g, const AffineTransform& transform, + const int maskTextureID, const Rectangle* const maskArea) { - quadQueue.flush(); - glDisable (GL_SCISSOR_TEST); - } + JUCE_CHECK_OPENGL_ERROR + activeTextures.disableTextures (shaderQuadQueue); + blendMode.setPremultipliedBlendingMode (shaderQuadQueue); + JUCE_CHECK_OPENGL_ERROR - void prepareMasks (const PositionedTexture* const mask1, const PositionedTexture* const mask2, - GLfloat* const textureCoords1, GLfloat* const textureCoords2, const Rectangle* const area) - { - if (mask1 != nullptr) + if (maskArea != nullptr) { - activeTextures.setTexturesEnabled (quadQueue, mask2 != nullptr ? 7 : 3); - activeTextures.setActiveTexture (0); - mask1->prepareTextureCoords (area, textureCoords1); - activeTextures.bindTexture (mask1->textureID); + activeTextures.setTexturesEnabled (shaderQuadQueue, 3); activeTextures.setActiveTexture (1); - - if (mask2 != nullptr) - { - mask2->prepareTextureCoords (area, textureCoords2); - activeTextures.bindTexture (mask2->textureID); - activeTextures.setActiveTexture (2); - } + activeTextures.bindTexture (maskTextureID); + activeTextures.setActiveTexture (0); + textureCache.bindTextureForGradient (activeTextures, g); } else { - activeTextures.setSingleTextureMode (quadQueue); - } - } - - void fillRect (const Rectangle& r, const PixelARGB& colour) noexcept - { - jassert (! r.isEmpty()); - - quadQueue.prepare (activeTextures, currentColour); - quadQueue.add (r.getX(), r.getY(), r.getWidth(), r.getHeight(), colour); - } - - void fillRect (const Rectangle& r, const PixelARGB& colour) noexcept - { - jassert (! r.isEmpty()); - - quadQueue.prepare (activeTextures, currentColour); - quadQueue.add (r, colour); - } - - void fillRectangleList (const RectangleList& list, const PixelARGB& colour) - { - quadQueue.prepare (activeTextures, currentColour); - - for (RectangleList::Iterator i (list); i.next();) - quadQueue.add (i.getRectangle()->getX(), i.getRectangle()->getY(), - i.getRectangle()->getWidth(), i.getRectangle()->getHeight(), colour); - } - - void fillRectangleList (const RectangleList& list, const Rectangle& clip, const PixelARGB& colour) - { - quadQueue.prepare (activeTextures, currentColour); - - for (RectangleList::Iterator i (list); i.next();) - { - const Rectangle r (i.getRectangle()->getIntersection (clip)); - - if (! r.isEmpty()) - quadQueue.add (r.getX(), r.getY(), r.getWidth(), r.getHeight(), colour); + activeTextures.setSingleTextureMode (shaderQuadQueue); + textureCache.bindTextureForGradient (activeTextures, g); } - } - - void fillEdgeTable (const EdgeTable& et, const PixelARGB& colour) - { - quadQueue.prepare (activeTextures, currentColour); - quadQueue.add (et, colour); - } - void drawTriangleStrip (const GLfloat* const vertices, const GLfloat* const textureCoords, const int numVertices) noexcept - { - glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); - glDrawArrays (GL_TRIANGLE_STRIP, 0, numVertices); - } + const AffineTransform t (transform.translated ((float) -target.bounds.getX(), (float) -target.bounds.getY())); + Point p1 (g.point1.transformedBy (t)); + const Point p2 (g.point2.transformedBy (t)); + const Point p3 (Point (g.point1.x + (g.point2.y - g.point1.y), + g.point1.y - (g.point2.x - g.point1.x)).transformedBy (t)); - void renderImage (const OpenGLTextureFromImage& image, - const Rectangle& clip, const AffineTransform& transform, float alpha, - const PositionedTexture* mask1, const PositionedTexture* mask2, - const bool replaceExistingContents, const bool isTiled) - { - quadQueue.flush(); - blendMode.setBlendMode (quadQueue, replaceExistingContents); - currentColour.setColour (alpha); - GLfloat textureCoords1[8], textureCoords2[8]; + ShaderPrograms* const programs = currentShader.programs; + const ShaderPrograms::MaskedShaderParams* maskParams = nullptr; - if ((! isTiled) || (isPowerOfTwo (image.imageWidth) && isPowerOfTwo (image.imageHeight))) + if (g.isRadial) { - prepareMasks (mask1, mask2, textureCoords1, textureCoords2, &clip); - - activeTextures.bindTexture (image.textureID); - TemporaryColourModulationMode tmm; + ShaderPrograms::RadialGradientParams* gradientParams; - if (isTiled) + if (maskArea == nullptr) { - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + setShader (programs->radialGradient); + gradientParams = &programs->radialGradient.gradientParams; } - - const GLfloat clipX = (GLfloat) clip.getX(); - const GLfloat clipY = (GLfloat) clip.getY(); - const GLfloat clipR = (GLfloat) clip.getRight(); - const GLfloat clipB = (GLfloat) clip.getBottom(); - - const GLfloat vertices[] = { clipX, clipY, clipR, clipY, clipX, clipB, clipR, clipB }; - GLfloat textureCoords[] = { clipX, clipY, clipR, clipY, clipX, clipB, clipR, clipB }; - + else { - 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]); - - textureCoords[1] = 1.0f - textureCoords[1]; - textureCoords[3] = 1.0f - textureCoords[3]; - textureCoords[5] = 1.0f - textureCoords[5]; - textureCoords[7] = 1.0f - textureCoords[7]; + setShader (programs->radialGradientMasked); + gradientParams = &programs->radialGradientMasked.gradientParams; + maskParams = &programs->radialGradientMasked.maskParams; } - glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); - - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); - - if (isTiled) - { - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + gradientParams->setMatrix (p1, p2, p3); } else { - prepareMasks (mask1, mask2, textureCoords1, textureCoords2, nullptr); - - activeTextures.bindTexture (image.textureID); - TemporaryColourModulationMode tmm; - - scissor (clip); - glPushMatrix(); - OpenGLHelpers::applyTransform (transform); - - GLfloat vertices[8]; - 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(), image.imageWidth); - int y = targetArea.getY() - negativeAwareModulo (targetArea.getY(), image.imageHeight); - const int right = targetArea.getRight(); - const int bottom = targetArea.getBottom(); + p1 = Line (p1, p3).findNearestPointTo (p2); + const Point delta (p2.x - p1.x, p1.y - p2.y); + const ShaderPrograms::LinearGradientParams* gradientParams; + float grad, length; - while (y < bottom) + if (std::abs (delta.x) < std::abs (delta.y)) { - vertices[1] = vertices[3] = (GLfloat) y; - vertices[5] = vertices[7] = (GLfloat) (y + image.imageHeight); - - for (int x1 = x; x1 < right; x1 += image.imageWidth) + if (maskArea == nullptr) { - vertices[0] = vertices[4] = (GLfloat) x1; - 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); + setShader (programs->linearGradient1); + gradientParams = &(programs->linearGradient1.gradientParams); + } + else + { + setShader (programs->linearGradient1Masked); + gradientParams = &(programs->linearGradient1Masked.gradientParams); + maskParams = &programs->linearGradient1Masked.maskParams; } - y += image.imageHeight; + grad = delta.x / delta.y; + length = (p2.y - grad * p2.x) - (p1.y - grad * p1.x); } - - glPopMatrix(); - disableScissor(); - } - } - - void fillTexture (const Rectangle& area, const FillType& fill, - const PositionedTexture* mask1, const PositionedTexture* mask2, - const bool replaceExistingContents) - { - jassert (! (mask1 == nullptr && mask2 != nullptr)); - - if (fill.isColour()) - { - GLfloat textureCoords1[8], textureCoords2[8]; - - if (mask1 != nullptr) - { - blendMode.setBlendMode (quadQueue, replaceExistingContents); - activeTextures.setTexturesEnabled (quadQueue, mask2 != nullptr ? 3 : 1); - - activeTextures.setActiveTexture (0); - mask1->prepareTextureCoords (&area, textureCoords1); - activeTextures.bindTexture (mask1->textureID); - - if (mask2 != nullptr) - { - activeTextures.setActiveTexture (1); - mask2->prepareTextureCoords (&area, textureCoords2); - activeTextures.bindTexture (mask2->textureID); - } - } - else - { - blendMode.setBlendMode (quadQueue, replaceExistingContents || fill.colour.isOpaque()); - activeTextures.disableTextures (quadQueue); - } - - currentColour.setPremultipliedColour (fill.colour); - OpenGLHelpers::fillRect (area); - } - else if (fill.isGradient()) - { - ColourGradient g2 (*(fill.gradient)); - g2.multiplyOpacity (fill.getOpacity()); - - if (g2.point1 == g2.point2) - { - fillTexture (area, g2.getColourAtPosition (1.0), mask1, mask2, replaceExistingContents); - } - else - { - blendMode.setBlendMode (quadQueue, replaceExistingContents || (mask1 == nullptr && fill.colour.isOpaque() && fill.gradient->isOpaque())); - - if (g2.isRadial) - fillWithRadialGradient (area, g2, fill.transform, mask1, mask2); - else - fillWithLinearGradient (area, g2, fill.transform, mask1, mask2); - } - } - else if (fill.isTiledImage()) - { - renderImage (fill.image, area, fill.transform, fill.colour.getFloatAlpha(), - mask1, mask2, replaceExistingContents, true); - } - } - #endif - - #if JUCE_USE_OPENGL_SHADERS - void setShader (ShaderPrograms::ShaderBase& shader) - { - currentShader.setShader (target, shaderQuadQueue, shader); - JUCE_CHECK_OPENGL_ERROR - } - - void setShaderForGradientFill (const ColourGradient& g, const AffineTransform& transform, - const int maskTextureID, const Rectangle* const maskArea) - { - JUCE_CHECK_OPENGL_ERROR - activeTextures.disableTextures (shaderQuadQueue); - blendMode.setPremultipliedBlendingMode (shaderQuadQueue); - JUCE_CHECK_OPENGL_ERROR - - if (maskArea != nullptr) - { - activeTextures.setTexturesEnabled (shaderQuadQueue, 3); - activeTextures.setActiveTexture (1); - activeTextures.bindTexture (maskTextureID); - activeTextures.setActiveTexture (0); - textureCache.bindTextureForGradient (activeTextures, g); - } - else - { - activeTextures.setSingleTextureMode (shaderQuadQueue); - textureCache.bindTextureForGradient (activeTextures, g); - } - - const AffineTransform t (transform.translated ((float) -target.bounds.getX(), (float) -target.bounds.getY())); - Point p1 (g.point1.transformedBy (t)); - const Point p2 (g.point2.transformedBy (t)); - const Point p3 (Point (g.point1.x + (g.point2.y - g.point1.y), - g.point1.y - (g.point2.x - g.point1.x)).transformedBy (t)); - - ShaderPrograms* const programs = currentShader.programs; - const ShaderPrograms::MaskedShaderParams* maskParams = nullptr; - - if (g.isRadial) - { - ShaderPrograms::RadialGradientParams* gradientParams; - - if (maskArea == nullptr) - { - setShader (programs->radialGradient); - gradientParams = &programs->radialGradient.gradientParams; - } - else - { - setShader (programs->radialGradientMasked); - gradientParams = &programs->radialGradientMasked.gradientParams; - maskParams = &programs->radialGradientMasked.maskParams; - } - - gradientParams->setMatrix (p1, p2, p3); - } - else - { - p1 = Line (p1, p3).findNearestPointTo (p2); - const Point delta (p2.x - p1.x, p1.y - p2.y); - const ShaderPrograms::LinearGradientParams* gradientParams; - float grad, length; - - if (std::abs (delta.x) < std::abs (delta.y)) - { - if (maskArea == nullptr) - { - setShader (programs->linearGradient1); - gradientParams = &(programs->linearGradient1.gradientParams); - } - else - { - setShader (programs->linearGradient1Masked); - gradientParams = &(programs->linearGradient1Masked.gradientParams); - maskParams = &programs->linearGradient1Masked.maskParams; - } - - grad = delta.x / delta.y; - length = (p2.y - grad * p2.x) - (p1.y - grad * p1.x); - } - else + else { if (maskArea == nullptr) { @@ -1773,721 +1254,121 @@ public: gradientParams = &(programs->linearGradient2.gradientParams); } else - { - setShader (programs->linearGradient2Masked); - gradientParams = &(programs->linearGradient2Masked.gradientParams); - maskParams = &programs->linearGradient2Masked.maskParams; - } - - grad = delta.y / delta.x; - length = (p2.x - grad * p2.y) - (p1.x - grad * p1.y); - } - - gradientParams->gradientInfo.set (p1.x, p1.y, grad, length); - } - - if (maskParams != nullptr) - maskParams->setBounds (*maskArea, target, 1); - - JUCE_CHECK_OPENGL_ERROR - } - - void setShaderForTiledImageFill (const OpenGLTextureFromImage& image, const AffineTransform& transform, - const int maskTextureID, const Rectangle* const maskArea, const bool clampTiledImages) - { - blendMode.setPremultipliedBlendingMode (shaderQuadQueue); - - ShaderPrograms* const programs = currentShader.programs; - - const ShaderPrograms::MaskedShaderParams* maskParams = nullptr; - const ShaderPrograms::ImageParams* imageParams; - - if (maskArea != nullptr) - { - activeTextures.setTwoTextureMode (shaderQuadQueue, image.textureID, maskTextureID); - - if (clampTiledImages) - { - setShader (programs->imageMasked); - imageParams = &programs->imageMasked.imageParams; - maskParams = &programs->imageMasked.maskParams; - } - else - { - setShader (programs->tiledImageMasked); - imageParams = &programs->tiledImageMasked.imageParams; - maskParams = &programs->tiledImageMasked.maskParams; - } - } - else - { - activeTextures.setSingleTextureMode (shaderQuadQueue); - activeTextures.bindTexture (image.textureID); - - if (clampTiledImages) - { - setShader (programs->image); - imageParams = &programs->image.imageParams; - } - else - { - setShader (programs->tiledImage); - imageParams = &programs->tiledImage.imageParams; - } - } - - imageParams->setMatrix (transform, image, (float) target.bounds.getX(), (float) target.bounds.getY()); - - if (maskParams != nullptr) - maskParams->setBounds (*maskArea, target, 1); - } - #endif - - OpenGLTarget target; - - StateHelpers::BlendingMode blendMode; - StateHelpers::ActiveTextures activeTextures; - StateHelpers::TextureCache textureCache; - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - StateHelpers::CurrentColour currentColour; - StateHelpers::QuadQueue quadQueue; - #endif - - #if JUCE_USE_OPENGL_SHADERS - StateHelpers::CurrentShader currentShader; - StateHelpers::ShaderQuadQueue shaderQuadQueue; - #endif - -private: - GLuint previousFrameBufferTarget; - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - void resetMultiTextureMode (int index, const bool forRGBTextures) - { - activeTextures.setActiveTexture (index); - glDisable (GL_TEXTURE_2D); - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - 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); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - void resetMultiTextureModes (const bool forRGBTextures) - { - resetMultiTextureMode (2, forRGBTextures); - resetMultiTextureMode (1, forRGBTextures); - resetMultiTextureMode (0, forRGBTextures); - } - - void fillWithLinearGradient (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.x - (grad.point2.y - grad.point1.y) / StateHelpers::TextureCache::gradientTextureSize, - grad.point1.y + (grad.point2.x - grad.point1.x) / StateHelpers::TextureCache::gradientTextureSize) - .transformedBy (transform)); - - const AffineTransform textureTransform (AffineTransform::fromTargetPoints (p1.x, p1.y, 0.0f, 0.0f, - p2.x, p2.y, 1.0f, 0.0f, - p3.x, p3.y, 0.0f, 1.0f)); - - const GLfloat l = (GLfloat) rect.getX(); - const GLfloat r = (GLfloat) rect.getRight(); - const GLfloat t = (GLfloat) rect.getY(); - const GLfloat b = (GLfloat) rect.getBottom(); - - const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; - GLfloat textureCoords[] = { l, t, r, t, l, b, r, b }; - - 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; - - textureCache.bindTextureForGradient (activeTextures, grad); - - currentColour.setSolidColour(); - drawTriangleStrip (vertices, textureCoords, 4); - } - - void fillWithRadialGradient (const Rectangle& rect, const ColourGradient& grad, const AffineTransform& transform, - const PositionedTexture* mask1, const PositionedTexture* mask2) - { - const Point centre (grad.point1.transformedBy (transform)); - - const float screenRadius = centre.getDistanceFrom (rect.getCentre().toFloat()) - + Point (rect.getWidth() / 2, - rect.getHeight() / 2).getDistanceFromOrigin() - + 8.0f; - - const AffineTransform inverse (transform.inverted()); - const float sourceRadius = jmax (Point (screenRadius, 0.0f).transformedBy (inverse).getDistanceFromOrigin(), - Point (0.0f, screenRadius).transformedBy (inverse).getDistanceFromOrigin()); - - const int numDivisions = 90; - GLfloat vertices [4 + numDivisions * 2]; - GLfloat textureCoords1 [4 + numDivisions * 2]; - GLfloat textureCoords2 [4 + numDivisions * 2]; - GLfloat textureCoords3 [4 + numDivisions * 2]; - - { - GLfloat* t = textureCoords1; - *t++ = 0.0f; - *t++ = 0.0f; - - const GLfloat texturePos = sourceRadius / grad.point1.getDistanceFrom (grad.point2); - - for (int i = numDivisions + 1; --i >= 0;) - { - *t++ = texturePos; - *t++ = 0.0f; - } - } - - { - GLfloat* v = vertices; - *v++ = centre.x; - *v++ = centre.y; - - const Point first (grad.point1.translated (0, -sourceRadius) - .transformedBy (transform)); - *v++ = first.x; - *v++ = first.y; - - for (int i = 1; i < numDivisions; ++i) - { - const float angle = i * (float_Pi * 2.0f / numDivisions); - const Point p (grad.point1.translated (std::sin (angle) * sourceRadius, - std::cos (angle) * -sourceRadius) - .transformedBy (transform)); - *v++ = p.x; - *v++ = p.y; - } - - *v++ = first.x; - *v++ = first.y; - } - - 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]); - } - - scissor (rect); - textureCache.bindTextureForGradient (activeTextures, grad); - currentColour.setSolidColour(); - TemporaryColourModulationMode tmm; - glVertexPointer (2, GL_FLOAT, 0, vertices); - glTexCoordPointer (2, GL_FLOAT, 0, textureCoords1); - glDrawArrays (GL_TRIANGLE_FAN, 0, numDivisions + 2); - disableScissor(); - } - - struct TemporaryColourModulationMode - { - TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); } - ~TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); } - }; - #endif -}; - -//============================================================================== -class ClipRegion_Mask; - -//============================================================================== -class ClipRegionBase : public SingleThreadedReferenceCountedObject -{ -public: - ClipRegionBase (OpenGLGraphicsContext::GLState& state_) noexcept : state (state_) {} - virtual ~ClipRegionBase() {} - - typedef ReferenceCountedObjectPtr Ptr; - - virtual Ptr clone() const = 0; - virtual Ptr clipToRectangle (const Rectangle&) = 0; - virtual Ptr clipToRectangleList (const RectangleList&) = 0; - virtual Ptr excludeClipRectangle (const Rectangle&) = 0; - virtual Ptr clipToPath (const Path& p, const AffineTransform&) = 0; - virtual Ptr clipToImageAlpha (const OpenGLTextureFromImage&, const AffineTransform&) = 0; - virtual Ptr clipToTexture (const PositionedTexture&) = 0; - virtual Rectangle getClipBounds() const = 0; - virtual void fillRect (const Rectangle& area, const FillType&, bool replaceContents) = 0; - virtual void fillRect (const Rectangle& area, const FillType&) = 0; - virtual void fillEdgeTable (EdgeTable& et, const FillType& fill) = 0; - virtual void drawImage (const Image&, const AffineTransform&, float alpha, - const Rectangle& clip, EdgeTable* mask) = 0; - - OpenGLGraphicsContext::GLState& state; - -private: - JUCE_DECLARE_NON_COPYABLE (ClipRegionBase); -}; - -//============================================================================== -class ClipRegion_RectangleListBase : public ClipRegionBase -{ -public: - ClipRegion_RectangleListBase (OpenGLGraphicsContext::GLState& state_, const Rectangle& r) noexcept - : ClipRegionBase (state_), clip (r) - {} - - ClipRegion_RectangleListBase (OpenGLGraphicsContext::GLState& state_, const RectangleList& r) noexcept - : ClipRegionBase (state_), clip (r) - {} - - Rectangle getClipBounds() const { return clip.getBounds(); } - - 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; } - -protected: - RectangleList clip; -}; - - -#if JUCE_USE_OPENGL_FIXED_FUNCTION -//============================================================================== -class ClipRegion_Mask : public ClipRegionBase -{ -public: - ClipRegion_Mask (const ClipRegion_Mask& other) - : ClipRegionBase (other.state), - clip (other.clip), - maskOrigin (other.clip.getPosition()) - { - TargetSaver ts (state.target.context); - state.flush(); - state.activeTextures.setSingleTextureMode (state.quadQueue); - state.activeTextures.clear(); - mask.initialise (state.target.context, clip.getWidth(), clip.getHeight()); - - OpenGLTarget m (state.target.context, mask, maskOrigin); - m.makeActiveFor2D(); - state.blendMode.disableBlend (state.quadQueue); - state.currentColour.setSolidColour(); - state.activeTextures.setSingleTextureMode (state.quadQueue); - OpenGLHelpers::drawTextureQuad (other.mask.getTextureID(), other.getMaskArea()); - } - - ClipRegion_Mask (OpenGLGraphicsContext::GLState& state_, const RectangleList& r) - : ClipRegionBase (state_), - clip (r.getBounds()), - maskOrigin (clip.getPosition()) - { - TargetSaver ts (state.target.context); - initialiseClear(); - state.blendMode.disableBlend (state.quadQueue); - state.fillRectangleList (r, PixelARGB (0xffffffff)); - state.quadQueue.flush(); - } - - Ptr clone() const { return new ClipRegion_Mask (*this); } - Rectangle getClipBounds() const { return clip; } - - Ptr clipToRectangle (const Rectangle& r) - { - clip = clip.getIntersection (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr clipToRectangleList (const RectangleList& r) - { - clip = clip.getIntersection (r.getBounds()); - if (clip.isEmpty()) - return nullptr; - - RectangleList excluded (clip); - - if (excluded.subtract (r)) - { - if (excluded.getNumRectangles() == 1) - return excludeClipRectangle (excluded.getRectangle (0)); - - TargetSaver ts (state.target.context); - makeMaskActive(); - state.blendMode.disableBlend (state.quadQueue); - state.fillRectangleList (excluded, PixelARGB (0)); - state.quadQueue.flush(); - } - - return this; - } - - Ptr excludeClipRectangle (const Rectangle& r) - { - if (r.contains (clip)) - return nullptr; - - TargetSaver ts (state.target.context); - makeMaskActive(); - state.activeTextures.disableTextures (state.quadQueue); - state.blendMode.disableBlend (state.quadQueue); - state.currentColour.setColour (PixelARGB (0)); - OpenGLHelpers::fillRect (r); - return this; - } - - Ptr clipToPath (const Path& p, const AffineTransform& t) - { - EdgeTable et (clip, p, t); - - if (! et.isEmpty()) - { - OpenGLTexture texture; - PositionedTexture pt (texture, et, et.getMaximumBounds()); - return clipToTexture (pt); - } - - return nullptr; - } - - Ptr clipToTexture (const PositionedTexture& pt) - { - clip = clip.getIntersection (pt.clip); - - if (clip.isEmpty()) - return nullptr; - - TargetSaver ts (state.target.context); - makeMaskActive(); - state.blendMode.setBlendFunc (state.quadQueue, GL_ZERO, GL_SRC_ALPHA); - state.currentColour.setSolidColour(); - state.activeTextures.setSingleTextureMode (state.quadQueue); - OpenGLHelpers::drawTextureQuad (pt.textureID, pt.area); - return this; - } - - Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) - { - TargetSaver ts (state.target.context); - makeMaskActive(); - state.blendMode.setBlendFunc (state.quadQueue, GL_ZERO, GL_SRC_ALPHA); - state.currentColour.setSolidColour(); - state.activeTextures.setSingleTextureMode (state.quadQueue); - state.activeTextures.bindTexture (image.textureID); - - const GLfloat l = (GLfloat) maskOrigin.x; - const GLfloat t = (GLfloat) maskOrigin.y; - const GLfloat r = (GLfloat) (maskOrigin.x + mask.getWidth()); - const GLfloat b = (GLfloat) (maskOrigin.y + 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]; - - state.drawTriangleStrip (vertices, textureCoords, 4); - return this; - } - - void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) - { - (void) replaceContents; jassert (! replaceContents); - const Rectangle r (clip.getIntersection (area)); - - if (! r.isEmpty()) - fillRectInternal (r, fill, false); - } - - void fillRect (const Rectangle& area, const FillType& fill) - { - if (fill.isColour()) - { - FloatRectangleRenderer frr (*this, fill); - RenderingHelpers::FloatRectangleRasterisingInfo (area).iterate (frr); - } - else - { - EdgeTable et (area); - fillEdgeTable (et, fill); - } - } - - void fillEdgeTable (EdgeTable& et, const FillType& fill) - { - const Rectangle r (et.getMaximumBounds().getIntersection (clip)); - - if (! r.isEmpty()) - { - OpenGLTexture* texture = state.textureCache.getTexture (state.activeTextures, r.getWidth(), r.getHeight()); - PositionedTexture pt1 (*texture, et, r); - PositionedTexture pt2 (mask.getTextureID(), getMaskArea(), r); - state.fillTexture (r, fill, &pt2, &pt1, false); - state.textureCache.releaseTexture (state.activeTextures, texture); - } - } - - void fillRectInternal (const Rectangle& area, const FillType& fill, bool replaceContents) - { - PositionedTexture pt (mask.getTextureID(), getMaskArea(), area); - state.fillTexture (area, fill, &pt, nullptr, replaceContents); - } - - void drawImage (const Image& image, const AffineTransform& transform, - float alpha, const Rectangle& clipArea, EdgeTable* et) - { - const OpenGLTextureFromImage source (image); - const Rectangle bufferArea (clipArea.getIntersection (clip)); - - if (! bufferArea.isEmpty()) - { - PositionedTexture pt (mask.getTextureID(), getMaskArea(), bufferArea); - - if (et != nullptr) - { - OpenGLTexture* texture = state.textureCache.getTexture (state.activeTextures, clipArea.getWidth(), clipArea.getHeight()); - PositionedTexture mask1 (*texture, *et, clipArea); - - state.renderImage (source, bufferArea, transform, alpha, &pt, &mask1, false, false); + { + setShader (programs->linearGradient2Masked); + gradientParams = &(programs->linearGradient2Masked.gradientParams); + maskParams = &programs->linearGradient2Masked.maskParams; + } - state.textureCache.releaseTexture (state.activeTextures, texture); - } - else - { - state.renderImage (source, bufferArea, transform, alpha, &pt, nullptr, false, false); + grad = delta.y / delta.x; + length = (p2.x - grad * p2.y) - (p1.x - grad * p1.y); } - } - } -protected: - OpenGLFrameBuffer mask; - Rectangle clip; - Point maskOrigin; - - Rectangle getMaskArea() const noexcept { return Rectangle (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()); } - void prepareFor2D() const { OpenGLTarget::applyFlippedMatrix (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()); } + gradientParams->gradientInfo.set (p1.x, p1.y, grad, length); + } - void makeMaskActive() - { - state.flush(); - const bool b = mask.makeCurrentRenderingTarget(); - (void) b; jassert (b); - prepareFor2D(); - } + if (maskParams != nullptr) + maskParams->setBounds (*maskArea, target, 1); - void initialiseClear() - { - state.flush(); - jassert (! clip.isEmpty()); - state.activeTextures.setSingleTextureMode (state.quadQueue); - state.activeTextures.clear(); - mask.initialise (state.target.context, clip.getWidth(), clip.getHeight()); - mask.makeCurrentAndClear(); - state.activeTextures.disableTextures (state.quadQueue); - state.blendMode.disableBlend (state.quadQueue); - prepareFor2D(); + JUCE_CHECK_OPENGL_ERROR } - struct TargetSaver + void setShaderForTiledImageFill (const OpenGLTextureFromImage& image, const AffineTransform& transform, + const int maskTextureID, const Rectangle* const maskArea, const bool clampTiledImages) { - TargetSaver (const OpenGLContext& context_) - : context (context_), oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget()) - { - glGetIntegerv (GL_VIEWPORT, oldViewport); - glPushMatrix(); - } - - ~TargetSaver() - { - context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, oldFramebuffer); - - glPopMatrix(); - glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); - } - - private: - const OpenGLContext& context; - GLuint oldFramebuffer; - GLint oldViewport[4]; + blendMode.setPremultipliedBlendingMode (shaderQuadQueue); - TargetSaver& operator= (const TargetSaver&); - }; + ShaderPrograms* const programs = currentShader.programs; - struct FloatRectangleRenderer - { - FloatRectangleRenderer (ClipRegion_Mask& owner_, const FillType& fill_) noexcept - : owner (owner_), fill (fill_), originalColour (fill_.colour) - {} + const ShaderPrograms::MaskedShaderParams* maskParams = nullptr; + const ShaderPrograms::ImageParams* imageParams; - void operator() (const int x, const int y, const int w, const int h, const int alpha) noexcept + if (maskArea != nullptr) { - if (w > 0 && h > 0) - { - fill.colour = originalColour.withMultipliedAlpha (alpha / 255.0f); - owner.fillRect (Rectangle (x, y, w, h), fill, false); - } - } - - private: - ClipRegion_Mask& owner; - FillType fill; - const Colour originalColour; - - JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); - }; - - ClipRegion_Mask& operator= (const ClipRegion_Mask&); -}; - -//============================================================================== -class ClipRegion_RectangleList : public ClipRegion_RectangleListBase -{ -public: - ClipRegion_RectangleList (OpenGLGraphicsContext::GLState& state_, const Rectangle& r) noexcept - : ClipRegion_RectangleListBase (state_, r) - {} - - ClipRegion_RectangleList (OpenGLGraphicsContext::GLState& state_, const RectangleList& r) noexcept - : ClipRegion_RectangleListBase (state_, r) - {} - - Ptr clone() const { return new ClipRegion_RectangleList (state, clip); } - - Ptr clipToTexture (const PositionedTexture& t) { return toMask()->clipToTexture (t); } - Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toMask()->clipToPath (p, transform); } - Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) { return toMask()->clipToImageAlpha (image, transform); } + activeTextures.setTwoTextureMode (shaderQuadQueue, image.textureID, maskTextureID); - void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) - { - if (fill.isColour()) - { - state.activeTextures.disableTextures (state.quadQueue); - state.blendMode.setBlendMode (state.quadQueue, replaceContents || fill.colour.isOpaque()); - state.fillRectangleList (clip, area, fill.colour.getPixelARGB()); - } - else - { - for (RectangleList::Iterator i (clip); i.next();) + if (clampTiledImages) { - const Rectangle r (i.getRectangle()->getIntersection (area)); - - if (! r.isEmpty()) - state.fillTexture (r, fill, nullptr, nullptr, replaceContents); + setShader (programs->imageMasked); + imageParams = &programs->imageMasked.imageParams; + maskParams = &programs->imageMasked.maskParams; } - } - } - - void fillRect (const Rectangle& area, const FillType& fill) - { - if (fill.isColour()) - { - state.activeTextures.disableTextures (state.quadQueue); - state.blendMode.setPremultipliedBlendingMode (state.quadQueue); - - for (RectangleList::Iterator i (clip); i.next();) + else { - const Rectangle r (i.getRectangle()->toFloat().getIntersection (area)); - if (! r.isEmpty()) - state.fillRect (r, fill.colour.getPixelARGB()); + setShader (programs->tiledImageMasked); + imageParams = &programs->tiledImageMasked.imageParams; + maskParams = &programs->tiledImageMasked.maskParams; } } else { - EdgeTable et (area); - fillEdgeTable (et, fill); - } - } - - void drawImage (const Image& image, const AffineTransform& transform, - float alpha, const Rectangle& clipArea, EdgeTable* et) - { - const OpenGLTextureFromImage source (image); - - for (RectangleList::Iterator i (clip); i.next();) - { - const Rectangle bufferArea (i.getRectangle()->getIntersection (clipArea)); + activeTextures.setSingleTextureMode (shaderQuadQueue); + activeTextures.bindTexture (image.textureID); - if (! bufferArea.isEmpty()) + if (clampTiledImages) { - if (et != nullptr) - { - OpenGLTexture* texture = state.textureCache.getTexture (state.activeTextures, clipArea.getWidth(), clipArea.getHeight()); - PositionedTexture mask (*texture, *et, clipArea); - - state.renderImage (source, bufferArea, transform, alpha, &mask, nullptr, false, false); - - state.textureCache.releaseTexture (state.activeTextures, texture); - } - else - { - state.renderImage (source, bufferArea, transform, alpha, nullptr, nullptr, false, false); - } + setShader (programs->image); + imageParams = &programs->image.imageParams; + } + else + { + setShader (programs->tiledImage); + imageParams = &programs->tiledImage.imageParams; } } + + imageParams->setMatrix (transform, image, (float) target.bounds.getX(), (float) target.bounds.getY()); + + if (maskParams != nullptr) + maskParams->setBounds (*maskArea, target, 1); } - void fillEdgeTable (EdgeTable& et, const FillType& fill) - { - if (fill.isColour()) - { - state.blendMode.setPremultipliedBlendingMode (state.quadQueue); + Target target; - if (! clip.containsRectangle (et.getMaximumBounds())) - et.clipToEdgeTable (EdgeTable (clip)); + StateHelpers::BlendingMode blendMode; + StateHelpers::ActiveTextures activeTextures; + StateHelpers::TextureCache textureCache; + StateHelpers::CurrentShader currentShader; + StateHelpers::ShaderQuadQueue shaderQuadQueue; - state.fillEdgeTable (et, fill.colour.getPixelARGB()); - } - else - { - OpenGLTexture* texture = state.textureCache.getTexture (state.activeTextures, - clip.getBounds().getWidth(), clip.getBounds().getHeight()); - PositionedTexture pt (*texture, et, clip.getBounds()); +private: + GLuint previousFrameBufferTarget; +}; - for (RectangleList::Iterator i (clip); i.next();) - { - const Rectangle r (i.getRectangle()->getIntersection (pt.clip)); - if (! r.isEmpty()) - state.fillTexture (r, fill, &pt, nullptr, false); - } +//============================================================================== +class ClipRegionBase : public SingleThreadedReferenceCountedObject +{ +public: + ClipRegionBase (GLState& state_) noexcept : state (state_) {} + virtual ~ClipRegionBase() {} - state.textureCache.releaseTexture (state.activeTextures, texture); - } - } + typedef ReferenceCountedObjectPtr Ptr; -protected: - Ptr toMask() const { return new ClipRegion_Mask (state, clip); } + virtual Ptr clone() const = 0; + virtual Ptr clipToRectangle (const Rectangle&) = 0; + virtual Ptr clipToRectangleList (const RectangleList&) = 0; + virtual Ptr excludeClipRectangle (const Rectangle&) = 0; + virtual Ptr clipToPath (const Path& p, const AffineTransform&) = 0; + virtual Ptr clipToImageAlpha (const OpenGLTextureFromImage&, const AffineTransform&) = 0; + virtual Ptr clipToTexture (const PositionedTexture&) = 0; + virtual Rectangle getClipBounds() const = 0; + virtual void fillRect (const Rectangle& area, const FillType&, bool replaceContents) = 0; + virtual void fillRect (const Rectangle& area, const FillType&) = 0; + virtual void fillEdgeTable (EdgeTable& et, const FillType& fill) = 0; + virtual void drawImage (const Image&, const AffineTransform&, float alpha, + const Rectangle& clip, EdgeTable* mask) = 0; - JUCE_DECLARE_NON_COPYABLE (ClipRegion_RectangleList); + GLState& state; + + JUCE_DECLARE_NON_COPYABLE (ClipRegionBase); }; -#endif -//============================================================================== -#if JUCE_USE_OPENGL_SHADERS -class ClipRegion_Mask_Shader : public ClipRegionBase +//============================================================================== +class ClipRegion_Mask : public ClipRegionBase { public: - ClipRegion_Mask_Shader (const ClipRegion_Mask_Shader& other) + ClipRegion_Mask (const ClipRegion_Mask& other) : ClipRegionBase (other.state), clip (other.clip), maskArea (other.clip) @@ -2516,7 +1397,7 @@ public: state.shaderQuadQueue.flush(); } - ClipRegion_Mask_Shader (OpenGLGraphicsContext::GLState& state_, const RectangleList& r) + ClipRegion_Mask (GLState& state_, const RectangleList& r) : ClipRegionBase (state_), clip (r.getBounds()), maskArea (clip) @@ -2535,7 +1416,7 @@ public: state.shaderQuadQueue.flush(); } - Ptr clone() const { return new ClipRegion_Mask_Shader (*this); } + Ptr clone() const { return new ClipRegion_Mask (*this); } Rectangle getClipBounds() const { return clip; } Ptr clipToRectangle (const Rectangle& r) @@ -2708,7 +1589,7 @@ private: struct ShaderFillOperation { - ShaderFillOperation (const ClipRegion_Mask_Shader& clip, const FillType& fill, const bool clampTiledImages) + ShaderFillOperation (const ClipRegion_Mask& clip, const FillType& fill, const bool clampTiledImages) : state (clip.state) { const GLuint maskTextureID = clip.mask.getTextureID(); @@ -2739,7 +1620,7 @@ private: state.shaderQuadQueue.flush(); } - OpenGLGraphicsContext::GLState& state; + GLState& state; ScopedPointer image; JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation); @@ -2777,7 +1658,7 @@ private: struct FloatRectangleRenderer { - FloatRectangleRenderer (ClipRegion_Mask_Shader& owner_, const FillType& fill_) noexcept + FloatRectangleRenderer (ClipRegion_Mask& owner_, const FillType& fill_) noexcept : owner (owner_), originalColour (fill_.colour.getPixelARGB()) {} @@ -2792,28 +1673,28 @@ private: } private: - ClipRegion_Mask_Shader& owner; + ClipRegion_Mask& owner; const PixelARGB originalColour; JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); }; - ClipRegion_Mask_Shader& operator= (const ClipRegion_Mask_Shader&); + ClipRegion_Mask& operator= (const ClipRegion_Mask&); }; //============================================================================== -class ClipRegion_RectangleList_Shaders : public ClipRegion_RectangleListBase +class ClipRegion_RectangleList : public ClipRegionBase { public: - ClipRegion_RectangleList_Shaders (OpenGLGraphicsContext::GLState& state_, const Rectangle& r) noexcept - : ClipRegion_RectangleListBase (state_, r) + ClipRegion_RectangleList (GLState& state_, const Rectangle& r) noexcept + : ClipRegionBase (state_), clip (r) {} - ClipRegion_RectangleList_Shaders (OpenGLGraphicsContext::GLState& state_, const RectangleList& r) noexcept - : ClipRegion_RectangleListBase (state_, r) + ClipRegion_RectangleList (GLState& state_, const RectangleList& r) noexcept + : ClipRegionBase (state_), clip (r) {} - Ptr clone() const { return new ClipRegion_RectangleList_Shaders (state, clip); } + Ptr clone() const { return new ClipRegion_RectangleList (state, clip); } Ptr clipToTexture (const PositionedTexture& t) { return toMask()->clipToTexture (t); } Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toMask()->clipToPath (p, transform); } @@ -2873,12 +1754,19 @@ public: } } + Rectangle getClipBounds() const { return clip.getBounds(); } + 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; } + private: - Ptr toMask() const { return new ClipRegion_Mask_Shader (state, clip); } + RectangleList clip; + + Ptr toMask() const { return new ClipRegion_Mask (state, clip); } struct ShaderFillOperation { - ShaderFillOperation (const ClipRegion_RectangleList_Shaders& clip, const FillType& fill, + ShaderFillOperation (const ClipRegion_RectangleList& clip, const FillType& fill, const bool replaceContents, const bool clampTiledImages) : state (clip.state) { @@ -2906,50 +1794,31 @@ private: state.shaderQuadQueue.flush(); } - OpenGLGraphicsContext::GLState& state; + GLState& state; ScopedPointer image; JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation); }; - JUCE_DECLARE_NON_COPYABLE (ClipRegion_RectangleList_Shaders); + JUCE_DECLARE_NON_COPYABLE (ClipRegion_RectangleList); }; -#endif //============================================================================== -class OpenGLGraphicsContext::SavedState +class SavedState { public: - SavedState (OpenGLGraphicsContext::GLState* const state_) - : clip (createRectangleClip (*state_, state_->target.bounds)), + SavedState (GLState* const state_) + : clip (new ClipRegion_RectangleList (*state_, state_->target.bounds)), transform (0, 0), interpolationQuality (Graphics::mediumResamplingQuality), state (state_), transparencyLayerAlpha (1.0f) - { - } + {} SavedState (const SavedState& other) : clip (other.clip), transform (other.transform), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality), state (other.state), transparencyLayerAlpha (other.transparencyLayerAlpha), transparencyLayer (other.transparencyLayer), previousTarget (other.previousTarget.createCopy()) - { - } - - static ClipRegionBase* createRectangleClip (OpenGLGraphicsContext::GLState& state, const Rectangle& clip) - { - #if JUCE_USE_OPENGL_SHADERS - if (state.currentShader.canUseShaders) - return new ClipRegion_RectangleList_Shaders (state, clip); - #endif - - #if JUCE_USE_OPENGL_FIXED_FUNCTION - return new ClipRegion_RectangleList (state, clip); - #else - // there's no shader hardware, but we're compiling without the fixed-function pipeline available! - jassertfalse; - return nullptr; - #endif - } + {} bool clipToRectangle (const Rectangle& r) { @@ -3055,7 +1924,7 @@ public: SavedState* beginTransparencyLayer (float opacity) { - SavedState* s = new SavedState (*this); + SavedState* const s = new SavedState (*this); if (clip != nullptr) { @@ -3063,12 +1932,12 @@ public: state->flush(); s->transparencyLayer = Image (OpenGLImageType().create (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true)); - s->previousTarget = new OpenGLTarget (state->target); - state->target = OpenGLTarget (state->target.context, *OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); + s->previousTarget = new Target (state->target); + state->target = Target (state->target.context, *OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); s->transparencyLayerAlpha = opacity; s->cloneClipIfMultiplyReferenced(); - s->state->target.makeActiveFor2D(); + s->state->target.makeActive(); } return s; @@ -3084,7 +1953,7 @@ public: state->target = *finishedLayerState.previousTarget; finishedLayerState.previousTarget = nullptr; - state->target.makeActiveFor2D(); + state->target.makeActive(); const Rectangle clipBounds (clip->getClipBounds()); clip->drawImage (finishedLayerState.transparencyLayer, @@ -3148,7 +2017,7 @@ public: { if (transform.isOnlyTranslated && t.isOnlyTranslation()) { - RenderingHelpers::GlyphCache , SavedState>::getInstance() + RenderingHelpers::GlyphCache , SavedState>::getInstance() .drawGlyph (*this, font, glyphNumber, transform.xOffset + t.getTranslationX(), transform.yOffset + t.getTranslationY()); @@ -3237,7 +2106,7 @@ public: private: float transparencyLayerAlpha; Image transparencyLayer; - ScopedPointer previousTarget; + ScopedPointer previousTarget; void cloneClipIfMultiplyReferenced() { @@ -3259,52 +2128,118 @@ private: }; //============================================================================== -OpenGLGraphicsContext::OpenGLGraphicsContext (OpenGLComponent& target) - : glState (new GLState (OpenGLTarget (*target.getCurrentContext(), target.getFrameBufferID(), target.getWidth(), target.getHeight()))), - stack (new SavedState (glState)) +class ShaderContext : public LowLevelGraphicsContext { - jassert (target.getCurrentContext() != nullptr); // must have a valid context when this is called! +public: + ShaderContext (const Target& target) + : glState (target), stack (new SavedState (&glState)) + {} + + bool isVectorDevice() const { return false; } + void setOrigin (int x, int y) { stack->transform.setOrigin (x, y); } + void addTransform (const AffineTransform& t) { stack->transform.addTransform (t); } + float getScaleFactor() { return stack->transform.getScaleFactor(); } + Rectangle getClipBounds() const { return stack->getClipBounds(); } + bool isClipEmpty() const { return stack->clip == nullptr; } + bool clipRegionIntersects (const Rectangle& r) { return stack->clipRegionIntersects (r); } + bool clipToRectangle (const Rectangle& r) { return stack->clipToRectangle (r); } + bool clipToRectangleList (const RectangleList& r) { return stack->clipToRectangleList (r); } + void excludeClipRectangle (const Rectangle& r) { stack->excludeClipRectangle (r); } + void clipToPath (const Path& path, const AffineTransform& t) { stack->clipToPath (path, t); } + void clipToImageAlpha (const Image& im, const AffineTransform& t) { stack->clipToImageAlpha (im, t); } + void saveState() { stack.save(); } + void restoreState() { stack.restore(); } + void beginTransparencyLayer (float opacity) { stack.beginTransparencyLayer (opacity); } + void endTransparencyLayer() { stack.endTransparencyLayer(); } + void setFill (const FillType& fillType) { stack->setFillType (fillType); } + void setOpacity (float newOpacity) { stack->fillType.setOpacity (newOpacity); } + void setInterpolationQuality (Graphics::ResamplingQuality quality) { stack->interpolationQuality = quality; } + void fillRect (const Rectangle& r, bool replace) { stack->fillRect (r, replace); } + void fillPath (const Path& path, const AffineTransform& t) { stack->fillPath (path, t); } + void drawImage (const Image& im, const AffineTransform& t) { stack->drawImage (im, t); } + void drawVerticalLine (int x, float top, float bottom) { if (top < bottom) stack->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } + void drawHorizontalLine (int y, float left, float right) { if (left < right) stack->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } + void drawGlyph (int glyphNumber, const AffineTransform& t) { stack->drawGlyph (glyphNumber, t); } + void drawLine (const Line & line) { stack->drawLine (line); } + void setFont (const Font& newFont) { stack->font = newFont; } + const Font& getFont() { return stack->font; } + +private: + GLState glState; + RenderingHelpers::SavedStateStack stack; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShaderContext); +}; + +#endif + +class NonShaderContext : public LowLevelGraphicsSoftwareRenderer +{ +public: + NonShaderContext (const Target& target_, const Image& image_) + : LowLevelGraphicsSoftwareRenderer (image_), target (target_), image (image_) + {} + + ~NonShaderContext() + { + JUCE_CHECK_OPENGL_ERROR + const GLuint previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget(); + + #if ! JUCE_ANDROID + target.context.extensions.glActiveTexture (GL_TEXTURE0); + glEnable (GL_TEXTURE_2D); + #endif + + OpenGLTexture texture; + texture.loadImage (image); + texture.bind(); + + target.makeActive(); + target.context.copyTexture (target.bounds, Rectangle (texture.getWidth(), + texture.getHeight()), + target.bounds.getWidth(), target.bounds.getHeight()); + glBindTexture (GL_TEXTURE_2D, 0); + target.context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget); + JUCE_CHECK_OPENGL_ERROR + } + +private: + Target target; + Image image; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NonShaderContext); +}; + +LowLevelGraphicsContext* createOpenGLContext (const Target& target) +{ + #if JUCE_USE_OPENGL_SHADERSxxx + if (target.context.areShadersAvailable()) + return new ShaderContext (target); + #endif + + Image tempImage (Image::ARGB, target.bounds.getWidth(), target.bounds.getHeight(), true); + return new NonShaderContext (target, tempImage); +} + } -OpenGLGraphicsContext::OpenGLGraphicsContext (OpenGLContext& context, OpenGLFrameBuffer& target) - : glState (new GLState (OpenGLTarget (context, target, Point()))), - stack (new SavedState (glState)) +//============================================================================== +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLComponent& target) { + jassert (target.getCurrentContext() != nullptr); // must have a valid context when this is called! + + return createOpenGLGraphicsContext (*target.getCurrentContext(), target.getFrameBufferID(), + target.getWidth(), target.getHeight()); } -OpenGLGraphicsContext::OpenGLGraphicsContext (OpenGLContext& context, unsigned int frameBufferID, int width, int height) - : glState (new GLState (OpenGLTarget (context, frameBufferID, width, height))), - stack (new SavedState (glState)) +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, OpenGLFrameBuffer& target) { + using namespace OpenGLRendering; + return createOpenGLContext (Target (context, target, Point())); } -OpenGLGraphicsContext::~OpenGLGraphicsContext() {} - -bool OpenGLGraphicsContext::isVectorDevice() const { return false; } -void OpenGLGraphicsContext::setOrigin (int x, int y) { stack->transform.setOrigin (x, y); } -void OpenGLGraphicsContext::addTransform (const AffineTransform& t) { stack->transform.addTransform (t); } -float OpenGLGraphicsContext::getScaleFactor() { return stack->transform.getScaleFactor(); } -Rectangle OpenGLGraphicsContext::getClipBounds() const { return stack->getClipBounds(); } -bool OpenGLGraphicsContext::isClipEmpty() const { return stack->clip == nullptr; } -bool OpenGLGraphicsContext::clipRegionIntersects (const Rectangle& r) { return stack->clipRegionIntersects (r); } -bool OpenGLGraphicsContext::clipToRectangle (const Rectangle& r) { return stack->clipToRectangle (r); } -bool OpenGLGraphicsContext::clipToRectangleList (const RectangleList& r) { return stack->clipToRectangleList (r); } -void OpenGLGraphicsContext::excludeClipRectangle (const Rectangle& r) { stack->excludeClipRectangle (r); } -void OpenGLGraphicsContext::clipToPath (const Path& path, const AffineTransform& t) { stack->clipToPath (path, t); } -void OpenGLGraphicsContext::clipToImageAlpha (const Image& im, const AffineTransform& t) { stack->clipToImageAlpha (im, t); } -void OpenGLGraphicsContext::saveState() { stack.save(); } -void OpenGLGraphicsContext::restoreState() { stack.restore(); } -void OpenGLGraphicsContext::beginTransparencyLayer (float opacity) { stack.beginTransparencyLayer (opacity); } -void OpenGLGraphicsContext::endTransparencyLayer() { stack.endTransparencyLayer(); } -void OpenGLGraphicsContext::setFill (const FillType& fillType) { stack->setFillType (fillType); } -void OpenGLGraphicsContext::setOpacity (float newOpacity) { stack->fillType.setOpacity (newOpacity); } -void OpenGLGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality quality) { stack->interpolationQuality = quality; } -void OpenGLGraphicsContext::fillRect (const Rectangle& r, bool replace) { stack->fillRect (r, replace); } -void OpenGLGraphicsContext::fillPath (const Path& path, const AffineTransform& t) { stack->fillPath (path, t); } -void OpenGLGraphicsContext::drawImage (const Image& im, const AffineTransform& t) { stack->drawImage (im, t); } -void OpenGLGraphicsContext::drawVerticalLine (int x, float top, float bottom) { if (top < bottom) stack->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } -void OpenGLGraphicsContext::drawHorizontalLine (int y, float left, float right) { if (left < right) stack->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } -void OpenGLGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& t) { stack->drawGlyph (glyphNumber, t); } -void OpenGLGraphicsContext::drawLine (const Line & line) { stack->drawLine (line); } -void OpenGLGraphicsContext::setFont (const Font& newFont) { stack->font = newFont; } -const Font& OpenGLGraphicsContext::getFont() { return stack->font; } +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, unsigned int frameBufferID, int width, int height) +{ + using namespace OpenGLRendering; + return createOpenGLContext (Target (context, frameBufferID, width, height)); +} diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h index 3868db7379..f5c941f4d8 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h @@ -28,59 +28,23 @@ //============================================================================== -/** A LowLevelGraphicsContext for rendering into an OpenGL framebuffer or window. +/** Creates a graphics context object that will render into the given OpenGL target. + The caller is responsible for deleting this object when no longer needed. */ -class JUCE_API OpenGLGraphicsContext : public LowLevelGraphicsContext -{ -public: - explicit OpenGLGraphicsContext (OpenGLComponent& target); - OpenGLGraphicsContext (OpenGLContext& context, OpenGLFrameBuffer& target); - OpenGLGraphicsContext (OpenGLContext& context, unsigned int frameBufferID, int width, int height); - ~OpenGLGraphicsContext(); +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLComponent& target); - bool isVectorDevice() const; - void setOrigin (int x, int y); - void addTransform (const AffineTransform&); - float getScaleFactor(); - bool clipToRectangle (const Rectangle&); - bool clipToRectangleList (const RectangleList&); - void excludeClipRectangle (const Rectangle&); - void clipToPath (const Path& path, const AffineTransform&); - void clipToImageAlpha (const Image& sourceImage, const AffineTransform&); - bool clipRegionIntersects (const Rectangle&); - Rectangle getClipBounds() const; - bool isClipEmpty() const; - - void saveState(); - void restoreState(); - - void beginTransparencyLayer (float opacity); - void endTransparencyLayer(); - - void setFill (const FillType& fillType); - void setOpacity (float newOpacity); - void setInterpolationQuality (Graphics::ResamplingQuality); - - void fillRect (const Rectangle& r, bool replaceExistingContents); - void fillPath (const Path& path, const AffineTransform& transform); - void drawImage (const Image& sourceImage, const AffineTransform& transform); - void drawLine (const Line & line); - void drawVerticalLine (int x, float top, float bottom); - void drawHorizontalLine (int y, float left, float right); - - void setFont (const Font&); - const Font& getFont(); - - void drawGlyph (int glyphNumber, const AffineTransform&); +/** Creates a graphics context object that will render into the given OpenGL target. + The caller is responsible for deleting this object when no longer needed. +*/ +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, + OpenGLFrameBuffer& target); - #ifndef DOXYGEN - class SavedState; - class GLState; - #endif +/** Creates a graphics context object that will render into the given OpenGL target. + The caller is responsible for deleting this object when no longer needed. +*/ +LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, + unsigned int frameBufferID, + int width, int height); -private: - ScopedPointer glState; - RenderingHelpers::SavedStateStack stack; -}; #endif // __JUCE_OPENGLGRAPHICSCONTEXT_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp index 8de003d86d..7d9581b2ea 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp @@ -38,10 +38,10 @@ public: LowLevelGraphicsContext* createLowLevelContext() { - return new OpenGLGraphicsContext (context, frameBuffer); + return createOpenGLGraphicsContext (context, frameBuffer); } - ImageType* createType() const { return new OpenGLImageType(); } + ImageType* createType() const { return new OpenGLImageType(); } ImagePixelData* clone() {