From 9bccfebea73e889d56b10b9457b870a0ec671b47 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 15 Nov 2011 17:10:48 +0000 Subject: [PATCH] OpenGL development. --- .../opengl/juce_OpenGLGraphicsContext.cpp | 1718 +++++++++++------ .../opengl/juce_OpenGLGraphicsContext.h | 5 +- .../juce_opengl/opengl/juce_OpenGLTexture.cpp | 26 +- 3 files changed, 1156 insertions(+), 593 deletions(-) diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 26e744d32c..5b644250ff 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -71,8 +71,7 @@ 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.x), y (origin.y), @@ -115,6 +114,27 @@ struct OpenGLTarget glViewport (0, 0, width, height); } + 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]; + }; + OpenGLFrameBuffer* frameBuffer; GLuint frameBufferID; int x, y, width, height; @@ -128,19 +148,18 @@ public: { et.clipToRectangle (clip_); - EdgeTableData data (et); + EdgeTableAlphaMap alphaMap (et); - texture.loadAlpha (data.data, data.area.getWidth(), data.area.getHeight()); + texture.loadAlpha (alphaMap.data, alphaMap.area.getWidth(), alphaMap.area.getHeight()); textureID = texture.getTextureID(); clip = et.getMaximumBounds(); - area = data.area; + area = alphaMap.area; } - PositionedTexture (GLuint textureID_, const Rectangle area_, const Rectangle clip_) + PositionedTexture (GLuint textureID_, const Rectangle area_, const Rectangle clip_) noexcept : textureID (textureID_), area (area_), clip (clip_) - { - } + {} template void getTextureCoordAt (ValueType x, ValueType y, GLfloat& resultX, GLfloat& resultY) const noexcept @@ -149,19 +168,7 @@ public: 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 + void prepareTextureCoords (const Rectangle* const area, GLfloat* const textureCoords) const noexcept { if (area != nullptr) { @@ -171,16 +178,16 @@ public: getTextureCoordAt (area->getRight(), area->getBottom(), textureCoords[6], textureCoords[7]); } - enable (multitextureIndex, textureCoords); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); } GLuint textureID; Rectangle area, clip; private: - struct EdgeTableData + struct EdgeTableAlphaMap { - EdgeTableData (const EdgeTable& et) + EdgeTableAlphaMap (const EdgeTable& et) : area (et.getMaximumBounds().withSize (nextPowerOfTwo (et.getMaximumBounds().getWidth()), nextPowerOfTwo (et.getMaximumBounds().getHeight()))) { @@ -219,265 +226,706 @@ private: private: uint8* currentLine; - JUCE_DECLARE_NON_COPYABLE (EdgeTableData); + JUCE_DECLARE_NON_COPYABLE (EdgeTableAlphaMap); }; }; //============================================================================== -class TextureCache : public SingleThreadedReferenceCountedObject +namespace +{ + struct TemporaryColourModulationMode + { + TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); } + ~TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); } + }; +} + +//============================================================================== +#if JUCE_USE_OPENGL_SHADERS +class GLShaderProgram { public: - TextureCache() : activeGradientIndex (0), gradientNeedsRefresh (true) {} + GLShaderProgram() noexcept + : program (glCreateProgram()) + { + } - enum { gradientTextureSize = 256, numTexturesToCache = 8, numGradientTexturesToCache = 6 }; + GLShaderProgram (const GLchar* fragment) noexcept + : program (glCreateProgram()) + { + addShader (fragment, GL_FRAGMENT_SHADER); + link(); + } - void resetGradient() noexcept + ~GLShaderProgram() noexcept { - gradientNeedsRefresh = true; + glDeleteProgram (program); } - void bindTextureForGradient (const ColourGradient& gradient) + void addShader (const GLchar* const code, GLenum type) { - if (gradientNeedsRefresh) - { - gradientNeedsRefresh = false; + GLuint shaderID = glCreateShader (type); + glShaderSource (shaderID, 1, (const GLchar**) &code, nullptr); + glCompileShader (shaderID); - if (gradientTextures.size() < numGradientTexturesToCache) - { - activeGradientIndex = gradientTextures.size(); - gradientTextures.add (new OpenGLTexture()); - } - else - { - activeGradientIndex = (activeGradientIndex + 1) % numGradientTexturesToCache; - } + #if JUCE_DEBUG + GLint status = 0; + glGetShaderiv (shaderID, GL_COMPILE_STATUS, &status); - PixelARGB lookup [gradientTextureSize]; - gradient.createLookupTable (lookup, gradientTextureSize); - gradientTextures.getUnchecked (activeGradientIndex)->loadARGB (lookup, gradientTextureSize, 1); + if (status == GL_FALSE) + { + GLchar infoLog [16384]; + GLsizei infologLength = 0; + glGetShaderInfoLog (shaderID, sizeof (infoLog), &infologLength, infoLog); + DBG (String (infoLog, infologLength)); + jassertfalse; } + #endif - gradientTextures.getUnchecked (activeGradientIndex)->bind(); - } + glAttachShader (program, shaderID); + glDeleteShader (shaderID); + } - OpenGLTexture* getTexture (const int w, const int h) + void link() noexcept { - if (textures.size() < numTexturesToCache) - return new OpenGLTexture(); + glLinkProgram (program); - for (int i = 0; i < numTexturesToCache - 1; ++i) + #if JUCE_DEBUG + GLint status = 0; + glGetProgramiv (program, GL_LINK_STATUS, &status); + jassert (status != GL_FALSE); + #endif + } + + struct UniformLocation + { + UniformLocation (const GLShaderProgram& program, const GLchar* name) + : uniformID (glGetUniformLocation (program.program, name)) { - const OpenGLTexture* const t = textures.getUnchecked(i); - if (t->getWidth() == w && t->getHeight() == h) - return textures.removeAndReturn (i); + jassert (uniformID >= 0); } - return textures.removeAndReturn (0); - } + void set (GLfloat n1) const noexcept { glUniform1f (uniformID, n1); } + void set (GLint n1) const noexcept { glUniform1i (uniformID, n1); } + void set (GLfloat n1, GLfloat n2) const noexcept { glUniform2f (uniformID, n1, n2); } + void set (GLfloat n1, GLfloat n2, GLfloat n3) const noexcept { glUniform3f (uniformID, n1, n2, n3); } + void set (GLfloat n1, GLfloat n2, GLfloat n3, float n4) const noexcept { glUniform4f (uniformID, n1, n2, n3, n4); } + void set (GLint n1, GLint n2, GLint n3, GLint n4) const noexcept { glUniform4i (uniformID, n1, n2, n3, n4); } - void release (OpenGLTexture* texture) - { - textures.add (texture); - } + void set (const AffineTransform& t) const noexcept + { + const GLfloat f[] = { t.mat00, t.mat01, t.mat02, t.mat10, t.mat11, t.mat12 }; + glUniformMatrix2x3fv (uniformID, 1, false, f); + } - typedef ReferenceCountedObjectPtr Ptr; + GLint uniformID; + }; -private: - OwnedArray textures, gradientTextures; - int activeGradientIndex; - bool gradientNeedsRefresh; + GLuint program; }; -//============================================================================== -namespace +struct ShaderPrograms { - struct TargetSaver - { - TargetSaver() - : oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget()) + ShaderPrograms() + { + String v ((const char*) glGetString (GL_SHADING_LANGUAGE_VERSION)); + v = v.upToFirstOccurrenceOf (" ", false, false); + areShadersSupported = (v.getDoubleValue() >= 1.199); + } + + bool areShadersSupported; + + struct SolidColourMaskedProgram + { + SolidColourMaskedProgram() + : program ("#version 120\n" + "uniform sampler2D maskTexture;" + "uniform ivec4 maskBounds;" + "const float textureY = 0.5;" + "" + "void main()" + "{" + " vec2 maskPos;" + " maskPos.x = (gl_FragCoord.x - maskBounds.x) / maskBounds.z;" + " maskPos.y = 1.0 - (gl_FragCoord.y - maskBounds.y) / maskBounds.w;" + " gl_FragColor = gl_Color * texture2D (maskTexture, maskPos).w;" + "}"), + maskTexture (program, "maskTexture"), + maskBounds (program, "maskBounds") { - glGetIntegerv (GL_VIEWPORT, oldViewport); - glPushMatrix(); } - ~TargetSaver() + GLShaderProgram program; + GLShaderProgram::UniformLocation maskTexture, maskBounds; + }; + + struct RadialGradientProgram + { + RadialGradientProgram() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform mat2x3 matrix;" + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = length (vec2 (matrix[0][0] * gl_FragCoord.x + matrix[0][1] * gl_FragCoord.y + matrix[0][2]," + " matrix[1][0] * gl_FragCoord.x + matrix[1][1] * gl_FragCoord.y + matrix[1][2]));" + " gl_FragColor = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "}"), + gradientTexture (program, "gradientTexture"), + matrix (program, "matrix") { - OpenGLFrameBuffer::setCurrentFrameBufferTarget (oldFramebuffer); - glPopMatrix(); - glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); } - private: - GLuint oldFramebuffer; - GLint oldViewport[4]; + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, matrix; }; - struct TemporaryColourModulationMode - { - TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); } - ~TemporaryColourModulationMode() { glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); } + struct RadialGradientMaskedProgram + { + RadialGradientMaskedProgram() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform mat2x3 matrix;" + "uniform sampler2D maskTexture;" + "uniform ivec4 maskBounds;" + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = length (vec2 (matrix[0][0] * gl_FragCoord.x + matrix[0][1] * gl_FragCoord.y + matrix[0][2]," + " matrix[1][0] * gl_FragCoord.x + matrix[1][1] * gl_FragCoord.y + matrix[1][2]));" + " vec4 result = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "" + " vec2 maskPos;" + " maskPos.x = (gl_FragCoord.x - maskBounds.x) / maskBounds.z;" + " maskPos.y = 1.0 - (gl_FragCoord.y - maskBounds.y) / maskBounds.w;" + " result *= texture2D (maskTexture, maskPos).w;" + "" + " gl_FragColor = result;" + "}"), + gradientTexture (program, "gradientTexture"), + matrix (program, "matrix"), + maskTexture (program, "maskTexture"), + maskBounds (program, "maskBounds") + { + } + + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, matrix; + GLShaderProgram::UniformLocation maskTexture, maskBounds; }; - void fillRectangleList (const RectangleList& list) - { - GLshort vertices [8]; - glEnableClientState (GL_VERTEX_ARRAY); - glVertexPointer (2, GL_SHORT, 0, vertices); + struct LinearGradient1Program + { + LinearGradient1Program() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform vec4 gradientInfo;" // x = x1, y = y1, z = (y2 - y1) / (x2 - x1), w = length + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = (gl_FragCoord.y - (gradientInfo.y + (gradientInfo.z * (gl_FragCoord.x - gradientInfo.x)))) / gradientInfo.w;" + " gl_FragColor = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "}"), + gradientTexture (program, "gradientTexture"), + gradientInfo (program, "gradientInfo") + { + } - for (RectangleList::Iterator i (list); i.next();) + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, gradientInfo; + }; + + struct LinearGradient1MaskedProgram + { + LinearGradient1MaskedProgram() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform vec3 gradientInfo;" // x = (x2 - x1) / (y2 - y1), y = x1, z = length + "uniform sampler2D maskTexture;" + "uniform ivec4 maskBounds;" + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = (gl_FragCoord.y - (gradientInfo.y + (gradientInfo.x * (gl_FragCoord.x - gradientInfo.y)))) / gradientInfo.z;" + " vec4 result = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "" + " vec2 maskPos;" + " maskPos.x = (gl_FragCoord.x - maskBounds.x) / maskBounds.z;" + " maskPos.y = 1.0 - (gl_FragCoord.y - maskBounds.y) / maskBounds.w;" + " result *= texture2D (maskTexture, maskPos).w;" + " gl_FragColor = result;" + "}"), + gradientTexture (program, "gradientTexture"), + gradientInfo (program, "gradientInfo"), + maskTexture (program, "maskTexture"), + maskBounds (program, "maskBounds") { - vertices[0] = vertices[4] = (GLshort) i.getRectangle()->getX(); - vertices[1] = vertices[3] = (GLshort) i.getRectangle()->getY(); - vertices[2] = vertices[6] = (GLshort) i.getRectangle()->getRight(); - vertices[5] = vertices[7] = (GLshort) i.getRectangle()->getBottom(); + } - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, gradientInfo; + GLShaderProgram::UniformLocation maskTexture, maskBounds; + }; + + struct LinearGradient2Program + { + LinearGradient2Program() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform vec4 gradientInfo;" // x = x1, y = y1, z = (x2 - x1) / (y2 - y1), y = y1, w = length + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = (gl_FragCoord.x - (gradientInfo.x + (gradientInfo.z * (gl_FragCoord.y - gradientInfo.y)))) / gradientInfo.w;" + " gl_FragColor = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "}"), + gradientTexture (program, "gradientTexture"), + gradientInfo (program, "gradientInfo") + { } - } - void fillRectangleList (const RectangleList& list, const Rectangle& clip) - { - GLshort vertices [8]; - glEnableClientState (GL_VERTEX_ARRAY); - glVertexPointer (2, GL_SHORT, 0, vertices); + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, gradientInfo; + }; - for (RectangleList::Iterator i (list); i.next();) + struct LinearGradient2MaskedProgram + { + LinearGradient2MaskedProgram() + : program ("#version 120\n" + "uniform sampler2D gradientTexture;" + "uniform vec3 gradientInfo;" // x = (y2 - y1) / (x2 - x1), y = y1, z = length + "uniform sampler2D maskTexture;" + "uniform ivec4 maskBounds;" + "const float textureY = 0.5;" + "" + "void main()" + "{" + " float dist = (gl_FragCoord.x - (gradientInfo.y + (gradientInfo.x * (gl_FragCoord.y - gradientInfo.y)))) / gradientInfo.z;" + " vec4 result = gl_Color.w * texture2D (gradientTexture, vec2 (dist, textureY));" + "" + " vec2 maskPos;" + " maskPos.x = (gl_FragCoord.x - maskBounds.x) / maskBounds.z;" + " maskPos.y = 1.0 - (gl_FragCoord.y - maskBounds.y) / maskBounds.w;" + " result *= texture2D (maskTexture, maskPos).w;" + " gl_FragColor = result;" + "}"), + gradientTexture (program, "gradientTexture"), + gradientInfo (program, "gradientInfo"), + maskTexture (program, "maskTexture"), + maskBounds (program, "maskBounds") { - const Rectangle r (i.getRectangle()->getIntersection (clip)); - vertices[0] = vertices[4] = (GLshort) r.getX(); - vertices[1] = vertices[3] = (GLshort) r.getY(); - vertices[2] = vertices[6] = (GLshort) r.getRight(); - vertices[5] = vertices[7] = (GLshort) r.getBottom(); + } - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + GLShaderProgram program; + GLShaderProgram::UniformLocation gradientTexture, gradientInfo; + GLShaderProgram::UniformLocation maskTexture, maskBounds; + }; + + SolidColourMaskedProgram solidColourMasked; + RadialGradientProgram radialGradient; + RadialGradientMaskedProgram radialGradientMasked; + LinearGradient1Program linearGradient1; + LinearGradient1MaskedProgram linearGradient1Masked; + LinearGradient2Program linearGradient2; + LinearGradient2MaskedProgram linearGradient2Masked; +}; + +//static ScopedPointer programs; +#endif + +class OpenGLRenderer::GLState +{ +public: + GLState (const OpenGLTarget& target_) noexcept + : target (target_), + previousFrameBufferTarget (OpenGLFrameBuffer::getCurrentFrameBufferTarget()), + texturesEnabled (0), + currentActiveTexture (0), + blendingEnabled (false), + blendEnabled (false), + srcFunction (0), dstFunction (0), + currentColour (0), + quadQueueActive (false), + numVertices (0), + activeGradientIndex (0), + gradientNeedsRefresh (true) + #if JUCE_USE_OPENGL_SHADERS + , activeShader (nullptr) + #endif + { + // This object can only be created and used when the current thread has an active OpenGL context. + jassert (OpenGLHelpers::isContextActive()); + + initialiseMultiTextureExtensions(); + target.makeActiveFor2D(); + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + glEnableClientState (GL_VERTEX_ARRAY); + glDisable (GL_BLEND); + glColor4f (0, 0, 0, 0); + + for (int i = 3; --i >= 0;) + { + setActiveTexture (i); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); } + + clearSelectedTextures(); + 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); + + #if JUCE_USE_OPENGL_SHADERS + if (programs == nullptr) + programs = new ShaderPrograms(); + + canUseShaders = programs->areShadersSupported; + #endif } - inline void setColour (const float alpha) noexcept + ~GLState() { - glColor4f (alpha, alpha, alpha, alpha); + flushQuads(); + OpenGLFrameBuffer::setCurrentFrameBufferTarget (previousFrameBufferTarget); + resetMultiTextureModes (true); + glFlush(); } - inline void setPremultipliedColour (const Colour& c) noexcept + #if JUCE_USE_OPENGL_SHADERS + bool shadersAvailable() const { - const PixelARGB p (c.getPixelARGB()); - OpenGLHelpers::setColour (Colour (p.getARGB())); + return canUseShaders; } + #endif - void disableMultiTexture (GLenum level) + void setPremultipliedColour (const Colour& c) noexcept { - glActiveTexture (level); - glDisable (GL_TEXTURE_2D); + setColour (c.getPixelARGB()); + } + + void setColour (const float alpha) noexcept + { + const uint8 v = jmin (255, (int) (alpha * 255.0f)); + setColour (PixelARGB (v, v, v, v)); } - void disableMultiTexture() + void setColour (const PixelARGB& c) noexcept { - disableMultiTexture (GL_TEXTURE2); - disableMultiTexture (GL_TEXTURE1); - disableMultiTexture (GL_TEXTURE0); + 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 enableSingleTexture() + void setSolidColour() noexcept { - disableMultiTexture (GL_TEXTURE2); - disableMultiTexture (GL_TEXTURE1); - glActiveTexture (GL_TEXTURE0); - glClientActiveTexture (GL_TEXTURE0); - glEnable (GL_TEXTURE_2D); + if (currentColour.getARGB() != 0xffffffff) + { + currentColour = PixelARGB (0xffffffff); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); + } } - void resetMultiTextureMode (GLenum index, const bool forRGBTextures) + void setPremultipliedBlendingMode() noexcept { - 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); + setBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } - void resetMultiTextureModes (const bool forRGBTextures) + void setBlendFunc (GLenum src, GLenum dst) { - resetMultiTextureMode (GL_TEXTURE2, forRGBTextures); - resetMultiTextureMode (GL_TEXTURE1, forRGBTextures); - resetMultiTextureMode (GL_TEXTURE0, forRGBTextures); + if (! blendingEnabled) + { + flushQuads(); + blendingEnabled = true; + glEnable (GL_BLEND); + } + + if (srcFunction != src || dstFunction != dst) + { + flushQuads(); + srcFunction = src; + dstFunction = dst; + glBlendFunc (src, dst); + } } - void setPremultipliedBlendingMode() noexcept + void disableBlend() noexcept { - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + if (blendingEnabled) + { + flushQuads(); + blendingEnabled = false; + glDisable (GL_BLEND); + } } void setBlendMode (const bool replaceExistingContents) noexcept { if (replaceExistingContents) - glDisable (GL_BLEND); + disableBlend(); else setPremultipliedBlendingMode(); } + void scissor (const Rectangle& r) + { + flushQuads(); + target.scissor (r); + } + + void disableScissor() + { + flushQuads(); + glDisable (GL_SCISSOR_TEST); + } + + OpenGLTexture* getTexture (int w, int h) + { + if (textures.size() < numTexturesToCache) + { + clearSelectedTextures(); + return new OpenGLTexture(); + } + + for (int i = 0; i < numTexturesToCache - 2; ++i) + { + const OpenGLTexture* const t = textures.getUnchecked(i); + if (t->getWidth() == w && t->getHeight() == h) + return textures.removeAndReturn (i); + } + + return textures.removeAndReturn (0); + } + + void releaseTexture (OpenGLTexture* texture) + { + currentTextureID [currentActiveTexture] = 0; + textures.add (texture); + } + + void resetGradient() noexcept + { + gradientNeedsRefresh = true; + } + + void bindTexture (const GLuint textureID) noexcept + { + if (currentTextureID [currentActiveTexture] != textureID) + { + currentTextureID [currentActiveTexture] = textureID; + glBindTexture (GL_TEXTURE_2D, textureID); + } + else + { + GLint t = 0; + glGetIntegerv (GL_TEXTURE_BINDING_2D, &t); + jassert (t == textureID); + } + } + + void bindTextureForGradient (const ColourGradient& gradient) + { + if (gradientNeedsRefresh) + { + gradientNeedsRefresh = false; + + if (gradientTextures.size() < numGradientTexturesToCache) + { + activeGradientIndex = gradientTextures.size(); + clearSelectedTextures(); + gradientTextures.add (new OpenGLTexture()); + } + else + { + activeGradientIndex = (activeGradientIndex + 1) % numGradientTexturesToCache; + } + + PixelARGB lookup [gradientTextureSize]; + gradient.createLookupTable (lookup, gradientTextureSize); + gradientTextures.getUnchecked (activeGradientIndex)->loadARGB (lookup, gradientTextureSize, 1); + } + + bindTexture (gradientTextures.getUnchecked (activeGradientIndex)->getTextureID()); + } + + void setTexturesEnabled (const int textureIndexMask) noexcept + { + if (texturesEnabled != textureIndexMask) + { + flushQuads(); + + for (int i = 3; --i >= 0;) + { + if ((texturesEnabled & (1 << i)) != (textureIndexMask & (1 << i))) + { + setActiveTexture (i); + + if ((textureIndexMask & (1 << i)) != 0) + glEnable (GL_TEXTURE_2D); + else + glDisable (GL_TEXTURE_2D); + } + } + + texturesEnabled = textureIndexMask; + } + } + + void setActiveTexture (const int index) noexcept + { + if (currentActiveTexture != index) + { + currentActiveTexture = index; + glActiveTexture (GL_TEXTURE0 + index); + glClientActiveTexture (GL_TEXTURE0 + index); + } + } + + void setSingleTextureMode() noexcept + { + setTexturesEnabled (1); + setActiveTexture (0); + } + void prepareMasks (const PositionedTexture* const mask1, const PositionedTexture* const mask2, GLfloat* const textureCoords1, GLfloat* const textureCoords2, const Rectangle* const area) { if (mask1 != nullptr) { - mask1->enable (GL_TEXTURE0, area, textureCoords1); + setTexturesEnabled (mask2 != nullptr ? 7 : 3); + setActiveTexture (0); + mask1->prepareTextureCoords (area, textureCoords1); + bindTexture (mask1->textureID); + setActiveTexture (1); if (mask2 != nullptr) { - mask2->enable (GL_TEXTURE1, area, textureCoords2); - - glActiveTexture (GL_TEXTURE2); - glClientActiveTexture (GL_TEXTURE2); - } - else - { - disableMultiTexture (GL_TEXTURE2); - glActiveTexture (GL_TEXTURE1); - glClientActiveTexture (GL_TEXTURE1); + mask2->prepareTextureCoords (area, textureCoords2); + bindTexture (mask2->textureID); + setActiveTexture (2); } } else { - disableMultiTexture (GL_TEXTURE2); - disableMultiTexture (GL_TEXTURE1); - glActiveTexture (GL_TEXTURE0); - glClientActiveTexture (GL_TEXTURE0); + setSingleTextureMode(); } } - void renderImage (const OpenGLTarget& target, const OpenGLTextureFromImage& image, + void checkQuadQueueActive() + { + if (! quadQueueActive) + { + jassert (numVertices == 0); + setTexturesEnabled (0); + glEnableClientState (GL_COLOR_ARRAY); + glVertexPointer (2, GL_SHORT, 0, vertices); + glColorPointer (4, GL_UNSIGNED_BYTE, 0, colours); + setSolidColour(); + quadQueueActive = true; // (careful to do this last, as the preceding calls may change it) + } + } + + void addQuadToList (const int x, const int y, const int w, const int h, const PixelARGB& colour) noexcept + { + jassert (quadQueueActive && w > 0 && h > 0); + + const GLshort x1 = (GLshort) x; + const GLshort y1 = (GLshort) y; + const GLshort x2 = (GLshort) (x + w); + const GLshort y2 = (GLshort) (y + h); + + GLshort* const v = vertices + numVertices * 2; + v[0] = x1; v[1] = y1; v[2] = x2; v[3] = y1; v[4] = x1; v[5] = y2; + v[6] = x2; v[7] = y1; v[8] = x1; v[9] = y2; v[10] = x2; v[11] = y2; + + const uint32 rgba = colour.getInRGBAMemoryOrder(); + + uint32* const c = colours + numVertices; + for (int i = 0; i < 6; ++i) + c[i] = rgba; + + numVertices += 6; + + if (numVertices > maxVerticesPerBlock - 6) + { + glDrawArrays (GL_TRIANGLES, 0, numVertices); + numVertices = 0; + } + } + + void fillRect (const Rectangle& r, const PixelARGB& colour) noexcept + { + jassert (! r.isEmpty()); + + FloatRectangleRenderer frr (*this, colour); + RenderingHelpers::FloatRectangleRasterisingInfo (r).iterate (frr); + } + + void fillRectangleList (const RectangleList& list, const PixelARGB& colour) + { + checkQuadQueueActive(); + + for (RectangleList::Iterator i (list); i.next();) + addQuadToList (i.getRectangle()->getX(), i.getRectangle()->getY(), + i.getRectangle()->getWidth(), i.getRectangle()->getHeight(), colour); + } + + void fillRectangleList (const RectangleList& list, const Rectangle& clip, const PixelARGB& colour) + { + checkQuadQueueActive(); + + for (RectangleList::Iterator i (list); i.next();) + { + const Rectangle r (i.getRectangle()->getIntersection (clip)); + + if (! r.isEmpty()) + addQuadToList (r.getX(), r.getY(), r.getWidth(), r.getHeight(), 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); + } + + void fillEdgeTable (const EdgeTable& et, const PixelARGB& colour) + { + EdgeTableRenderer etr (*this, colour); + et.iterate (etr); + } + + 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) { + flushQuads(); setBlendMode (replaceExistingContents); + setColour (alpha); 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); + bindTexture (image.textureID); 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); + if (isTiled) + { + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } const GLfloat clipX = (GLfloat) clip.getX(); const GLfloat clipY = (GLfloat) clip.getY(); @@ -503,21 +951,21 @@ namespace 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); + } } else { 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); + bindTexture (image.textureID); 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); + scissor (clip); glPushMatrix(); OpenGLHelpers::applyTransform (transform); @@ -555,33 +1003,299 @@ namespace 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]); - } - } + 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 += image.imageHeight; + } + + 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) + { + setBlendMode (replaceExistingContents); + setTexturesEnabled (mask2 != nullptr ? 3 : 1); + + setActiveTexture (0); + mask1->prepareTextureCoords (&area, textureCoords1); + bindTexture (mask1->textureID); + + if (mask2 != nullptr) + { + setActiveTexture (1); + mask2->prepareTextureCoords (&area, textureCoords2); + bindTexture (mask2->textureID); + } + } + else + { + setBlendMode (replaceExistingContents || fill.colour.isOpaque()); + setTexturesEnabled (0); + } + + 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 + { + setBlendMode (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); + } + } + + #if JUCE_USE_OPENGL_SHADERS + void setShaderForGradientFill (const FillType& fill) + { + setPremultipliedBlendingMode(); + setSingleTextureMode(); + setSolidColour(); + + const ColourGradient& g = *fill.gradient; + bindTextureForGradient (g); + + const AffineTransform t (fill.transform.followedBy (AffineTransform::verticalFlip (target.height))); + 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)); + + if (g.isRadial) + { + setShader (&(programs->radialGradient.program)); + programs->radialGradient.matrix.set (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)); + } + else + { + p1 = Line (p1, p3).findNearestPointTo (p2); + const Point delta (p2.x - p1.x, p1.y - p2.y); + + if (std::abs (delta.x) < std::abs (delta.y)) + { + setShader (&(programs->linearGradient1.program)); + const float grad = delta.x / delta.y; + programs->linearGradient1.gradientInfo.set (p1.x, p1.y, grad, (p2.y - grad * p2.x) - (p1.y - grad * p1.x)); + } + else + { + setShader (&(programs->linearGradient2.program)); + const float grad = delta.y / delta.x; + programs->linearGradient2.gradientInfo.set (p1.x, p1.y, grad, (p2.x - grad * p2.y) - (p1.x - grad * p1.y)); + } + } + } + #endif + + void flushQuads() noexcept + { + if (quadQueueActive) + { + if (numVertices > 0) + { + glDrawArrays (GL_TRIANGLES, 0, numVertices); + numVertices = 0; + } + + quadQueueActive = false; + glDisableClientState (GL_COLOR_ARRAY); + } + } + + void clearSelectedTextures() noexcept + { + for (int i = 0; i < numElementsInArray (currentTextureID); ++i) + currentTextureID[i] = 0; + } + + #if JUCE_USE_OPENGL_SHADERS + void setShader (GLShaderProgram* newShader) + { + if (activeShader != newShader) + { + flushQuads(); + activeShader = newShader; + glUseProgram (newShader != nullptr ? newShader->program : 0); + } + } + #endif + + OpenGLTarget target; + +private: + enum + { + maxVerticesPerBlock = 192, + gradientTextureSize = 256, + numTexturesToCache = 8, + numGradientTexturesToCache = 6 + }; + + GLuint previousFrameBufferTarget; + GLuint currentTextureID [3]; + int texturesEnabled, currentActiveTexture; + bool blendingEnabled, blendEnabled; + GLenum srcFunction, dstFunction; + PixelARGB currentColour; + + bool quadQueueActive; + GLshort vertices [maxVerticesPerBlock * 2]; + uint32 colours [maxVerticesPerBlock]; + int numVertices; + + OwnedArray textures, gradientTextures; + int activeGradientIndex; + bool gradientNeedsRefresh; + + #if JUCE_USE_OPENGL_SHADERS + GLShaderProgram* activeShader; + bool canUseShaders; + #endif + + void resetMultiTextureMode (int index, const bool forRGBTextures) + { + setActiveTexture (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); + 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); + } + + struct EdgeTableRenderer + { + EdgeTableRenderer (GLState& state_, const PixelARGB& colour_) noexcept + : state (state_), colour (colour_) + { + state.checkQuadQueueActive(); + } + + void setEdgeTableYPos (const int y) noexcept + { + currentY = y; + } + + void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept + { + PixelARGB c (colour); + c.multiplyAlpha (alphaLevel); + state.addQuadToList (x, currentY, 1, 1, c); + } + + void handleEdgeTablePixelFull (const int x) noexcept + { + state.addQuadToList (x, currentY, 1, 1, colour); + } + + void handleEdgeTableLine (const int x, const int width, const int alphaLevel) noexcept + { + PixelARGB c (colour); + c.multiplyAlpha (alphaLevel); + state.addQuadToList (x, currentY, width, 1, c); + } + + void handleEdgeTableLineFull (const int x, const int width) noexcept + { + state.addQuadToList (x, currentY, width, 1, colour); + } + + private: + GLState& state; + const PixelARGB colour; + int currentY; - glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); - } + JUCE_DECLARE_NON_COPYABLE (EdgeTableRenderer); + }; - y += image.imageHeight; - } + struct FloatRectangleRenderer + { + FloatRectangleRenderer (GLState& state_, const PixelARGB& colour_) noexcept + : state (state_), colour (colour_) + { + state.checkQuadQueueActive(); + } - glPopMatrix(); - glDisable (GL_SCISSOR_TEST); + void operator() (const int x, const int y, const int w, const int h, const int alpha) noexcept + { + if (w > 0 && h > 0) + { + PixelARGB c (colour); + c.multiplyAlpha (alpha); + state.addQuadToList (x, y, w, h, c); + } } - } - void fillWithLinearGradient (TextureCache& textureCache, const Rectangle& rect, const ColourGradient& grad, - const AffineTransform& transform, const PositionedTexture* mask1, const PositionedTexture* mask2) + private: + GLState& state; + const PixelARGB colour; + + JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); + }; + + 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) / textureCache.gradientTextureSize, - grad.point1.y + (grad.point2.x - grad.point1.x) / textureCache.gradientTextureSize).transformedBy (transform)); + const Point p3 (Point (grad.point1.x - (grad.point2.y - grad.point1.y) / gradientTextureSize, + grad.point1.y + (grad.point2.x - grad.point1.x) / gradientTextureSize).transformedBy (transform)); const AffineTransform textureTransform (AffineTransform::fromTargetPoints (p1.x, p1.y, 0.0f, 0.0f, p2.x, p2.y, 1.0f, 0.0f, @@ -602,14 +1316,13 @@ namespace prepareMasks (mask1, mask2, textureCoords1, textureCoords2, &rect); TemporaryColourModulationMode tmm; - textureCache.bindTextureForGradient (grad); + bindTextureForGradient (grad); - setColour (1.0f); - OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4); + setSolidColour(); + drawTriangleStrip (vertices, textureCoords, 4); } - void fillWithRadialGradient (TextureCache& textureCache, const OpenGLTarget& target, const Rectangle& rect, - const ColourGradient& grad, const AffineTransform& transform, + void fillWithRadialGradient (const Rectangle& rect, const ColourGradient& grad, const AffineTransform& transform, const PositionedTexture* mask1, const PositionedTexture* mask2) { const Point centre (grad.point1.transformedBy (transform)); @@ -679,213 +1392,16 @@ namespace mask2->getTextureCoordAt (vertices[i], vertices[i + 1], textureCoords3[i], textureCoords3[i + 1]); } - textureCache.bindTextureForGradient (grad); - - target.scissor (rect); - glEnable (GL_TEXTURE_2D); + scissor (rect); + bindTextureForGradient (grad); + setSolidColour(); TemporaryColourModulationMode tmm; - glEnableClientState (GL_VERTEX_ARRAY); - glEnableClientState (GL_TEXTURE_COORD_ARRAY); glVertexPointer (2, GL_FLOAT, 0, vertices); glTexCoordPointer (2, GL_FLOAT, 0, textureCoords1); - setColour (1.0f); glDrawArrays (GL_TRIANGLE_FAN, 0, numDivisions + 2); - glDisable (GL_SCISSOR_TEST); - } - - void fillTexture (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache, - const PositionedTexture* mask1, const PositionedTexture* mask2, const bool replaceExistingContents) - { - jassert (! (mask1 == nullptr && mask2 != nullptr)); - - if (fill.isColour()) - { - GLfloat textureCoords1[8], textureCoords2[8]; - disableMultiTexture (GL_TEXTURE2); - - if (mask1 != nullptr) - { - setBlendMode (replaceExistingContents); - mask1->enable (GL_TEXTURE0, &area, textureCoords1); - - if (mask2 != nullptr) - mask2->enable (GL_TEXTURE1, &area, textureCoords2); - else - disableMultiTexture (GL_TEXTURE1); - } - else - { - setBlendMode (replaceExistingContents || fill.colour.isOpaque()); - disableMultiTexture (GL_TEXTURE1); - disableMultiTexture (GL_TEXTURE0); - } - - setPremultipliedColour (fill.colour); - glEnableClientState (GL_VERTEX_ARRAY); - OpenGLHelpers::fillRect (area); - } - else if (fill.isGradient()) - { - ColourGradient g2 (*(fill.gradient)); - g2.multiplyOpacity (fill.getOpacity()); - - if (g2.point1 == g2.point2) - { - fillTexture (target, area, g2.getColourAtPosition (1.0), textureCache, mask1, mask2, replaceExistingContents); - } - else - { - setBlendMode (replaceExistingContents || (mask1 == nullptr && fill.colour.isOpaque() && fill.gradient->isOpaque())); - - if (g2.isRadial) - fillWithRadialGradient (textureCache, target, area, g2, fill.transform, mask1, mask2); - else - fillWithLinearGradient (textureCache, area, g2, fill.transform, mask1, mask2); - } - } - else if (fill.isTiledImage()) - { - renderImage (target, fill.image, area, fill.transform, fill.colour.getFloatAlpha(), - mask1, mask2, replaceExistingContents, true); - } + disableScissor(); } - - //============================================================================== - struct MultipleQuadList - { - public: - MultipleQuadList (const Colour& colour) noexcept - : colour (colour.getPixelARGB()), - numVertices (0) - {} - - void begin() - { - glDisableClientState (GL_TEXTURE_COORD_ARRAY); - glEnableClientState (GL_VERTEX_ARRAY); - glEnableClientState (GL_COLOR_ARRAY); - glVertexPointer (2, GL_SHORT, 0, vertices); - glColorPointer (4, GL_UNSIGNED_BYTE, 0, colours); - setColour (1.0f); - } - - void end() - { - if (numVertices > 0) - glDrawArrays (GL_TRIANGLES, 0, numVertices); - - glDisableClientState (GL_COLOR_ARRAY); - } - - void addQuad (const int x, const int y, const int w, const int h, const int alpha) - { - if (numVertices > maxVerticesPerBlock - 6) - { - glDrawArrays (GL_TRIANGLES, 0, numVertices); - numVertices = 0; - } - - const GLshort x1 = (GLshort) x; - const GLshort y1 = (GLshort) y; - const GLshort x2 = (GLshort) (x + w); - const GLshort y2 = (GLshort) (y + h); - - GLshort* const v = vertices + numVertices * 2; - v[0] = x1; v[1] = y1; v[2] = x2; v[3] = y1; v[4] = x1; v[5] = y2; - v[6] = x2; v[7] = y1; v[8] = x1; v[9] = y2; v[10] = x2; v[11] = y2; - - PixelARGB p (colour); - p.multiplyAlpha (alpha); - const uint32 rgba = p.getInRGBAMemoryOrder(); - - uint32* const c = colours + numVertices; - for (int i = 0; i < 6; ++i) - c[i] = rgba; - - numVertices += 6; - } - - private: - const PixelARGB colour; - enum { maxVerticesPerBlock = 192 }; - GLshort vertices [maxVerticesPerBlock * 2]; - uint32 colours [maxVerticesPerBlock]; - int numVertices; - - JUCE_DECLARE_NON_COPYABLE (MultipleQuadList); - }; - - //============================================================================== - struct EdgeTableRenderer - { - EdgeTableRenderer (const Colour& colour) noexcept - : quadList (colour) - {} - - void draw (const EdgeTable& et) - { - quadList.begin(); - et.iterate (*this); - quadList.end(); - } - - void setEdgeTableYPos (const int y) noexcept - { - currentY = y; - } - - void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept - { - quadList.addQuad (x, currentY, 1, 1, alphaLevel); - } - - void handleEdgeTablePixelFull (const int x) noexcept - { - quadList.addQuad (x, currentY, 1, 1, 255); - } - - void handleEdgeTableLine (const int x, const int width, const int alphaLevel) noexcept - { - quadList.addQuad (x, currentY, width, 1, alphaLevel); - } - - void handleEdgeTableLineFull (const int x, const int width) noexcept - { - quadList.addQuad (x, currentY, width, 1, 255); - } - - private: - MultipleQuadList quadList; - int currentY; - - JUCE_DECLARE_NON_COPYABLE (EdgeTableRenderer); - }; - - //============================================================================== - struct FloatRectangleRenderer - { - FloatRectangleRenderer (const Colour& c) noexcept - : quadList (c) - {} - - void draw (const Rectangle& r) - { - quadList.begin(); - RenderingHelpers::FloatRectangleRasterisingInfo (r).iterate (*this); - quadList.end(); - } - - void operator() (const int x, const int y, const int w, const int h, const int alpha) - { - quadList.addQuad (x, y, w, h, alpha); - } - - private: - MultipleQuadList quadList; - - JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); - }; -} +}; //============================================================================== class ClipRegion_Mask; @@ -894,7 +1410,7 @@ class ClipRegion_Mask; class ClipRegionBase : public SingleThreadedReferenceCountedObject { public: - ClipRegionBase() noexcept {} + ClipRegionBase (OpenGLRenderer::GLState& state_) noexcept : state (state_) {} virtual ~ClipRegionBase() {} typedef ReferenceCountedObjectPtr Ptr; @@ -908,13 +1424,14 @@ public: virtual Ptr clipToImageAlpha (const OpenGLTextureFromImage&, const AffineTransform&) = 0; virtual Ptr clipToTexture (const PositionedTexture&) = 0; virtual Rectangle getClipBounds() const = 0; - virtual void fillRect (const OpenGLTarget&, const Rectangle& area, const FillType&, TextureCache&, bool replaceContents) = 0; - virtual void fillRect (const OpenGLTarget&, const Rectangle& area, const FillType&, TextureCache&) = 0; - virtual void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture&, const FillType&, TextureCache&) = 0; - virtual void fillEdgeTable (const OpenGLTarget& target, EdgeTable& et, const FillType& fill, TextureCache&) = 0; - virtual void drawImage (const OpenGLTarget&, const OpenGLTextureFromImage&, const AffineTransform&, float alpha, + 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 OpenGLTextureFromImage&, const AffineTransform&, float alpha, const Rectangle& clip, const PositionedTexture* mask) = 0; + OpenGLRenderer::GLState& state; + private: JUCE_DECLARE_NON_COPYABLE (ClipRegionBase); }; @@ -924,38 +1441,42 @@ class ClipRegion_Mask : public ClipRegionBase { public: ClipRegion_Mask (const ClipRegion_Mask& other) - : clip (other.clip), + : ClipRegionBase (other.state), + clip (other.clip), maskOrigin (other.clip.getPosition()) { - TargetSaver ts; + OpenGLTarget::TargetSaver ts; + state.flushQuads(); + state.setSingleTextureMode(); + state.clearSelectedTextures(); mask.initialise (clip.getWidth(), clip.getHeight()); OpenGLTarget m (mask, maskOrigin); m.makeActiveFor2D(); - glDisable (GL_BLEND); - setColour (1.0f); - drawFrameBuffer (other.mask, other.maskOrigin); + state.disableBlend(); + state.setSolidColour(); + state.setSingleTextureMode(); + OpenGLHelpers::drawTextureQuad (other.mask.getTextureID(), other.getMaskArea()); } - explicit ClipRegion_Mask (const RectangleList& r) - : clip (r.getBounds()), + explicit ClipRegion_Mask (OpenGLRenderer::GLState& state_, const RectangleList& r) + : ClipRegionBase (state_), + clip (r.getBounds()), maskOrigin (clip.getPosition()) { - TargetSaver ts; + OpenGLTarget::TargetSaver ts; initialiseClear(); - - disableMultiTexture(); - setColour (1.0f); - fillRectangleList (r); + state.disableBlend(); + state.fillRectangleList (r, PixelARGB (0xffffffff)); + state.flushQuads(); } - Ptr clone() const { return new ClipRegion_Mask (*this); } - Rectangle getClipBounds() const { return clip; } + Ptr clone() const { return new ClipRegion_Mask (*this); } + Rectangle getClipBounds() const { return clip; } Ptr applyClipTo (const Ptr& target) { - return target->clipToTexture (PositionedTexture (mask.getTextureID(), Rectangle (maskOrigin.x, maskOrigin.y, - mask.getWidth(), mask.getHeight()), clip)); + return target->clipToTexture (PositionedTexture (mask.getTextureID(), getMaskArea(), clip)); } Ptr clipToRectangle (const Rectangle& r) @@ -977,12 +1498,11 @@ public: if (excluded.getNumRectangles() == 1) return excludeClipRectangle (excluded.getRectangle (0)); - TargetSaver ts; + OpenGLTarget::TargetSaver ts; makeMaskActive(); - disableMultiTexture(); - glDisable (GL_BLEND); - setColour (0); - fillRectangleList (excluded); + state.disableBlend(); + state.fillRectangleList (excluded, PixelARGB (0)); + state.flushQuads(); } return this; @@ -993,11 +1513,11 @@ public: if (r.contains (clip)) return nullptr; - TargetSaver ts; + OpenGLTarget::TargetSaver ts; makeMaskActive(); - disableMultiTexture(); - glDisable (GL_BLEND); - setColour (0); + state.setTexturesEnabled (0); + state.disableBlend(); + state.setColour (PixelARGB (0)); OpenGLHelpers::fillRect (r); return this; } @@ -1023,27 +1543,23 @@ public: if (clip.isEmpty()) return nullptr; - TargetSaver ts; + OpenGLTarget::TargetSaver ts; makeMaskActive(); - glEnable (GL_BLEND); - glBlendFunc (GL_ZERO, GL_SRC_ALPHA); - setColour (1.0f); - enableSingleTexture(); + state.setBlendFunc (GL_ZERO, GL_SRC_ALPHA); + state.setSolidColour(); + state.setSingleTextureMode(); OpenGLHelpers::drawTextureQuad (pt.textureID, pt.area); return this; } Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) { - TargetSaver ts; + OpenGLTarget::TargetSaver ts; makeMaskActive(); - glEnable (GL_BLEND); - glBlendFunc (GL_ZERO, GL_SRC_ALPHA); - - 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); + state.setBlendFunc (GL_ZERO, GL_SRC_ALPHA); + state.setSolidColour(); + state.setSingleTextureMode(); + state.bindTexture (image.textureID); const GLfloat l = (GLfloat) maskOrigin.x; const GLfloat t = (GLfloat) maskOrigin.y; @@ -1063,55 +1579,62 @@ public: textureCoords[5] = 1.0f - textureCoords[5]; textureCoords[7] = 1.0f - textureCoords[7]; - OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4); - + state.drawTriangleStrip (vertices, textureCoords, 4); return this; } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache, bool replaceContents) + void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) { jassert (! replaceContents); const Rectangle r (clip.getIntersection (area)); if (! r.isEmpty()) - fillRectInternal (target, r, fill, textureCache, false); + fillRectInternal (r, fill, false); } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache) + void fillRect (const Rectangle& area, const FillType& fill) { - EdgeTable et (area); - fillEdgeTable (target, et, fill, textureCache); + if (fill.isColour()) + { + FloatRectangleRenderer frr (*this, fill); + RenderingHelpers::FloatRectangleRasterisingInfo (area).iterate (frr); + } + else + { + EdgeTable et (area); + fillEdgeTable (et, fill); + } } - void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture& texture, const FillType& fill, TextureCache& textureCache) + void fillEdgeTable (EdgeTable& et, const FillType& fill) { - PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()), area); - fillTexture (target, area, fill, textureCache, &texture, &pt, false); - } + const Rectangle r (et.getMaximumBounds().getIntersection (clip)); - void fillEdgeTable (const OpenGLTarget& target, EdgeTable& et, const FillType& fill, TextureCache& textureCache) - { - OpenGLTexture* texture = textureCache.getTexture (clip.getWidth(), clip.getHeight()); - PositionedTexture pt (*texture, et, clip); - fillMask (target, pt.clip, pt, fill, textureCache); - textureCache.release (texture); + if (! r.isEmpty()) + { + OpenGLTexture* texture = state.getTexture (r.getWidth(), r.getHeight()); + PositionedTexture pt1 (*texture, et, r); + PositionedTexture pt2 (mask.getTextureID(), getMaskArea(), r); + state.fillTexture (r, fill, &pt2, &pt1, false); + state.releaseTexture (texture); + } } - void fillRectInternal (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache, bool replaceContents) + void fillRectInternal (const Rectangle& area, const FillType& fill, bool replaceContents) { - PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()), area); - fillTexture (target, area, fill, textureCache, &pt, nullptr, replaceContents); + PositionedTexture pt (mask.getTextureID(), getMaskArea(), area); + state.fillTexture (area, fill, &pt, nullptr, replaceContents); } - void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, const AffineTransform& transform, + void drawImage (const OpenGLTextureFromImage& source, const AffineTransform& transform, float alpha, const Rectangle& clipArea, const PositionedTexture* mask1) { const Rectangle bufferArea (clipArea.getIntersection (clip)); if (! bufferArea.isEmpty()) { - PositionedTexture pt (mask.getTextureID(), Rectangle (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()), bufferArea); - renderImage (target, source, bufferArea, transform, alpha, mask1, &pt, false, false); + PositionedTexture pt (mask.getTextureID(), getMaskArea(), bufferArea); + state.renderImage (source, bufferArea, transform, alpha, &pt, mask1, false, false); } } @@ -1120,13 +1643,12 @@ private: Rectangle clip; Point maskOrigin; - void prepareFor2D() const - { - OpenGLTarget::applyFlippedMatrix (maskOrigin.x, maskOrigin.y, mask.getWidth(), mask.getHeight()); - } + 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()); } void makeMaskActive() { + state.flushQuads(); const bool b = mask.makeCurrentRenderingTarget(); (void) b; jassert (b); prepareFor2D(); @@ -1134,20 +1656,39 @@ private: void initialiseClear() { + state.flushQuads(); jassert (! clip.isEmpty()); + state.setSingleTextureMode(); + state.clearSelectedTextures(); mask.initialise (clip.getWidth(), clip.getHeight()); mask.makeCurrentAndClear(); - glDisable (GL_TEXTURE_2D); - glDisable (GL_BLEND); + state.setTexturesEnabled (0); + state.disableBlend(); prepareFor2D(); } - void drawFrameBuffer (const OpenGLFrameBuffer& buffer, const Point& topLeft) + struct FloatRectangleRenderer { - enableSingleTexture(); - OpenGLHelpers::drawTextureQuad (buffer.getTextureID(), Rectangle (topLeft.x, topLeft.y, - buffer.getWidth(), buffer.getHeight())); - } + FloatRectangleRenderer (ClipRegion_Mask& mask_, const FillType& fill_) noexcept + : mask (mask_), fill (fill_), originalColour (fill_.colour) + {} + + void operator() (const int x, const int y, const int w, const int h, const int alpha) noexcept + { + if (w > 0 && h > 0) + { + fill.colour = originalColour.withMultipliedAlpha (alpha / 255.0f); + mask.fillRect (Rectangle (x, y, w, h), fill, false); + } + } + + private: + ClipRegion_Mask& mask; + FillType fill; + const Colour originalColour; + + JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer); + }; ClipRegion_Mask& operator= (const ClipRegion_Mask&); }; @@ -1157,15 +1698,15 @@ private: class ClipRegion_RectangleList : public ClipRegionBase { public: - explicit ClipRegion_RectangleList (const Rectangle& r) noexcept - : clip (r) + explicit ClipRegion_RectangleList (OpenGLRenderer::GLState& state_, const Rectangle& r) noexcept + : ClipRegionBase (state_), clip (r) {} - explicit ClipRegion_RectangleList (const RectangleList& r) noexcept - : clip (r) + explicit ClipRegion_RectangleList (OpenGLRenderer::GLState& state_, const RectangleList& r) noexcept + : ClipRegionBase (state_), clip (r) {} - Ptr clone() const { return new ClipRegion_RectangleList (clip); } + Ptr clone() const { return new ClipRegion_RectangleList (state, clip); } Rectangle getClipBounds() const { return clip.getBounds(); } Ptr applyClipTo (const Ptr& target) { return target->clipToRectangleList (clip); } @@ -1173,19 +1714,26 @@ public: 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 clipToTexture (const PositionedTexture& t) { return toMask()->clipToTexture (t); } - Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toMask()->clipToPath (p, transform); } + 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); } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache, bool replaceContents) + void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) { if (fill.isColour()) { - disableMultiTexture(); - setBlendMode (replaceContents || fill.colour.isOpaque()); - setPremultipliedColour (fill.colour); - fillRectangleList (clip, area); + state.setTexturesEnabled (0); + state.setBlendMode (replaceContents || fill.colour.isOpaque()); + state.fillRectangleList (clip, area, fill.colour.getPixelARGB()); } + #if JUCE_USE_OPENGL_SHADERS + else if (state.shadersAvailable() && fill.isGradient()) + { + state.setShaderForGradientFill (fill); + state.fillRectangleList (clip, area, fill.colour.getPixelARGB()); + state.setShader (nullptr); + } + #endif else { for (RectangleList::Iterator i (clip); i.next();) @@ -1193,38 +1741,48 @@ public: const Rectangle r (i.getRectangle()->getIntersection (area)); if (! r.isEmpty()) - fillTexture (target, r, fill, textureCache, nullptr, nullptr, replaceContents); + state.fillTexture (r, fill, nullptr, nullptr, replaceContents); } } } - void fillRect (const OpenGLTarget& target, const Rectangle& area, const FillType& fill, TextureCache& textureCache) + void fillRect (const Rectangle& area, const FillType& fill) { if (fill.isColour()) { - disableMultiTexture(); - setPremultipliedBlendingMode(); - setPremultipliedColour (fill.colour); + state.setTexturesEnabled (0); + state.setPremultipliedBlendingMode(); for (RectangleList::Iterator i (clip); i.next();) { const Rectangle r (i.getRectangle()->toFloat().getIntersection (area)); + if (! r.isEmpty()) + state.fillRect (r, fill.colour.getPixelARGB()); + } + } + #if JUCE_USE_OPENGL_SHADERS + else if (state.shadersAvailable() && fill.isGradient()) + { + state.setShaderForGradientFill (fill); + for (RectangleList::Iterator i (clip); i.next();) + { + const Rectangle r (i.getRectangle()->toFloat().getIntersection (area)); if (! r.isEmpty()) - { - FloatRectangleRenderer frr (fill.colour); - frr.draw (r); - } + state.fillRect (r, fill.colour.getPixelARGB()); } + + state.setShader (nullptr); } + #endif else { EdgeTable et (area); - fillEdgeTable (target, et, fill, textureCache); + fillEdgeTable (et, fill); } } - void drawImage (const OpenGLTarget& target, const OpenGLTextureFromImage& source, const AffineTransform& transform, + void drawImage (const OpenGLTextureFromImage& source, const AffineTransform& transform, float alpha, const Rectangle& clipArea, const PositionedTexture* mask) { for (RectangleList::Iterator i (clip); i.next();) @@ -1232,46 +1790,60 @@ public: const Rectangle bufferArea (i.getRectangle()->getIntersection (clipArea)); if (! bufferArea.isEmpty()) - renderImage (target, source, bufferArea, transform, alpha, mask, nullptr, false, false); + state.renderImage (source, bufferArea, transform, alpha, mask, nullptr, false, false); } } - void fillEdgeTable (const OpenGLTarget& target, EdgeTable& et, const FillType& fill, TextureCache& textureCache) + void fillEdgeTable (EdgeTable& et, const FillType& fill) { if (fill.isColour()) { - setPremultipliedBlendingMode(); - disableMultiTexture (GL_TEXTURE2); - disableMultiTexture (GL_TEXTURE1); - disableMultiTexture (GL_TEXTURE0); + state.setPremultipliedBlendingMode(); - for (RectangleList::Iterator i (clip); i.next();) + if (clip.containsRectangle (et.getMaximumBounds())) { - const Rectangle r (i.getRectangle()->getIntersection (et.getMaximumBounds())); - - if (! r.isEmpty()) - { - target.scissor (r); + state.fillEdgeTable (et, fill.colour.getPixelARGB()); + } + else + { + EdgeTable et2 (clip); + et2.clipToEdgeTable (et); + state.fillEdgeTable (et2, fill.colour.getPixelARGB()); + } + } + #if JUCE_USE_OPENGL_SHADERS + else if (state.shadersAvailable() && fill.isGradient()) + { + state.setShaderForGradientFill (fill); - EdgeTableRenderer etr (fill.colour); - etr.draw (et); - } + if (clip.containsRectangle (et.getMaximumBounds())) + { + state.fillEdgeTable (et, fill.colour.getPixelARGB()); + } + else + { + EdgeTable et2 (clip); + et2.clipToEdgeTable (et); + state.fillEdgeTable (et2, fill.colour.getPixelARGB()); } - glDisable (GL_SCISSOR_TEST); + state.setShader (nullptr); } + #endif else { - OpenGLTexture* texture = textureCache.getTexture (clip.getBounds().getWidth(), clip.getBounds().getHeight()); + OpenGLTexture* texture = state.getTexture (clip.getBounds().getWidth(), clip.getBounds().getHeight()); PositionedTexture pt (*texture, et, clip.getBounds()); - fillMask (target, pt.clip, pt, fill, textureCache); - textureCache.release (texture); - } - } - void fillMask (const OpenGLTarget& target, const Rectangle& area, const PositionedTexture& texture, const FillType& fill, TextureCache& textureCache) - { - fillTexture (target, area, fill, textureCache, &texture, nullptr, false); + 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); + } + + state.releaseTexture (texture); + } } private: @@ -1279,7 +1851,7 @@ private: Ptr toMask() const { - return new ClipRegion_Mask (clip); + return new ClipRegion_Mask (state, clip); } ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&); @@ -1290,20 +1862,19 @@ private: class OpenGLRenderer::SavedState { public: - SavedState (const OpenGLTarget& target_) - : clip (new ClipRegion_RectangleList (Rectangle (target_.width, target_.height))), + SavedState (GLState* const state_) + : clip (new ClipRegion_RectangleList (*state_, Rectangle (state_->target.width, state_->target.height))), transform (0, 0), interpolationQuality (Graphics::mediumResamplingQuality), - target (target_), transparencyLayerAlpha (1.0f), - textureCache (new TextureCache()) + state (state_), transparencyLayerAlpha (1.0f) { } SavedState (const SavedState& other) : clip (other.clip), transform (other.transform), font (other.font), fillType (other.fillType), interpolationQuality (other.interpolationQuality), - target (other.target), transparencyLayerAlpha (other.transparencyLayerAlpha), + state (other.state), transparencyLayerAlpha (other.transparencyLayerAlpha), transparencyLayer (other.transparencyLayer), - textureCache (other.textureCache) + previousTarget (other.previousTarget != nullptr ? new OpenGLTarget (*other.previousTarget) : nullptr) { } @@ -1417,12 +1988,14 @@ public: { const Rectangle clipBounds (clip->getClipBounds()); + state->flushQuads(); s->transparencyLayer = Image (OpenGLImageType().create (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true)); - s->target = OpenGLTarget (*OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); + s->previousTarget = new OpenGLTarget (state->target); + state->target = OpenGLTarget (*OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition()); s->transparencyLayerAlpha = opacity; s->cloneClipIfMultiplyReferenced(); - s->target.makeActiveFor2D(); + s->state->target.makeActiveFor2D(); } return s; @@ -1432,10 +2005,16 @@ public: { if (clip != nullptr) { - target.makeActiveFor2D(); + jassert (finishedLayerState.previousTarget != nullptr); + + state->flushQuads(); + state->target = *finishedLayerState.previousTarget; + finishedLayerState.previousTarget = nullptr; + + state->target.makeActiveFor2D(); const Rectangle clipBounds (clip->getClipBounds()); - clip->drawImage (target, finishedLayerState.transparencyLayer, + clip->drawImage (finishedLayerState.transparencyLayer, AffineTransform::translation ((float) clipBounds.getX(), (float) clipBounds.getY()), finishedLayerState.transparencyLayerAlpha, clipBounds, nullptr); } @@ -1448,8 +2027,8 @@ public: { if (transform.isOnlyTranslated) { - clip->fillRect (target, r.translated (transform.xOffset, transform.yOffset), - getFillType(), *textureCache, replaceContents); + clip->fillRect (r.translated (transform.xOffset, transform.yOffset), + getFillType(), replaceContents); } else { @@ -1470,7 +2049,7 @@ public: .getIntersection (clip->getClipBounds().toFloat())); if (! c.isEmpty()) - clip->fillRect (target, c, getFillType(), *textureCache); + clip->fillRect (c, getFillType()); } else { @@ -1553,7 +2132,7 @@ public: tx = ((tx + 128) >> 8); ty = ((ty + 128) >> 8); - clip->drawImage (target, image, t, alpha, Rectangle (tx, ty, image.getWidth(), image.getHeight()), nullptr); + clip->drawImage (image, t, alpha, Rectangle (tx, ty, image.getWidth(), image.getHeight()), nullptr); return; } } @@ -1563,19 +2142,19 @@ public: Path p; p.addRectangle (image.getBounds()); - OpenGLTexture* texture = textureCache->getTexture (clipBounds.getWidth(), clipBounds.getHeight()); + OpenGLTexture* texture = state->getTexture (clipBounds.getWidth(), clipBounds.getHeight()); EdgeTable et (clipBounds, p, t); PositionedTexture pt (*texture, et, clipBounds); - clip->drawImage (target, image, t, alpha, clipBounds, &pt); - textureCache->release (texture); + clip->drawImage (image, t, alpha, clipBounds, &pt); + state->releaseTexture (texture); } } void setFillType (const FillType& newFill) { fillType = newFill; - textureCache->resetGradient(); + state->resetGradient(); } //============================================================================== @@ -1584,12 +2163,12 @@ public: Font font; FillType fillType; Graphics::ResamplingQuality interpolationQuality; - OpenGLTarget target; + GLState* state; private: float transparencyLayerAlpha; Image transparencyLayer; - TextureCache::Ptr textureCache; + ScopedPointer previousTarget; void cloneClipIfMultiplyReferenced() { @@ -1604,7 +2183,7 @@ private: void fillEdgeTable (EdgeTable& et) { - clip->fillEdgeTable (target, et, getFillType(), *textureCache); + clip->fillEdgeTable (et, getFillType()); } class CachedGlyphEdgeTable @@ -1651,40 +2230,25 @@ private: //============================================================================== OpenGLRenderer::OpenGLRenderer (OpenGLComponent& target) - : stack (new SavedState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()))) + : glState (new GLState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()))), + stack (new SavedState (glState)) { - initialise(); } OpenGLRenderer::OpenGLRenderer (OpenGLFrameBuffer& target) - : stack (new SavedState (OpenGLTarget (target, Point()))) + : glState (new GLState (OpenGLTarget (target, Point()))), + stack (new SavedState (glState)) { - initialise(); } OpenGLRenderer::OpenGLRenderer (unsigned int frameBufferID, int width, int height) - : stack (new SavedState (OpenGLTarget (frameBufferID, width, height))) -{ - initialise(); -} - -void OpenGLRenderer::initialise() + : glState (new GLState (OpenGLTarget (frameBufferID, width, height))), + stack (new SavedState (glState)) { - // 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() { - OpenGLFrameBuffer::setCurrentFrameBufferTarget (previousFrameBufferTarget); - resetMultiTextureModes (true); } bool OpenGLRenderer::isVectorDevice() const { return false; } diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h index 9783eeb724..4390bb4b86 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h @@ -75,13 +75,12 @@ public: #ifndef DOXYGEN class SavedState; + class GLState; #endif private: + ScopedPointer glState; RenderingHelpers::SavedStateStack stack; - GLuint previousFrameBufferTarget; - - void initialise(); }; #endif // __JUCE_OPENGLGRAPHICSCONTEXT_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 4820e95528..0716d35db6 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -49,22 +49,22 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? - if (width != w || height != h) - { - release(); - - width = w; - height = h; + width = w; + height = h; + if (textureID == 0) + { glGenTextures (1, &textureID); + glBindTexture (GL_TEXTURE_2D, textureID); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else + { + glBindTexture (GL_TEXTURE_2D, textureID); } - - 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, 1); glTexImage2D (GL_TEXTURE_2D, 0, type == GL_ALPHA ? GL_ALPHA : GL_RGBA,