| @@ -34,29 +34,18 @@ class DemoOpenGLCanvas : public OpenGLComponent, | |||||
| public: | public: | ||||
| DemoOpenGLCanvas() | DemoOpenGLCanvas() | ||||
| : rotation (0.0f), | : rotation (0.0f), | ||||
| delta (1.0f) | |||||
| delta (1.0f), | |||||
| textScrollPos (200) | |||||
| { | { | ||||
| startTimer (20); | startTimer (20); | ||||
| } | } | ||||
| // when the component creates a new internal context, this is called, and | // when the component creates a new internal context, this is called, and | ||||
| // we'll use the opportunity to create the textures needed. | |||||
| // we'll use the opportunity to create some images to use as textures. | |||||
| void newOpenGLContextCreated() | void newOpenGLContextCreated() | ||||
| { | { | ||||
| texture1.load (createImage1()); | |||||
| texture2.load (createImage2()); | |||||
| // (no need to call makeCurrentContextActive(), as that will have | |||||
| // been done for us before the method call). | |||||
| glDepthFunc (GL_LESS); | |||||
| glEnable (GL_DEPTH_TEST); | |||||
| glEnable (GL_TEXTURE_2D); | |||||
| glEnable (GL_BLEND); | |||||
| glShadeModel (GL_SMOOTH); | |||||
| glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); | |||||
| glHint (GL_POINT_SMOOTH_HINT, GL_NICEST); | |||||
| glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||||
| logoImage = createLogoImage(); | |||||
| dynamicTextureImage = Image (Image::ARGB, 128, 128, true, OpenGLImageType()); | |||||
| } | } | ||||
| void mouseDrag (const MouseEvent& e) | void mouseDrag (const MouseEvent& e) | ||||
| @@ -68,76 +57,117 @@ public: | |||||
| void renderOpenGL() | void renderOpenGL() | ||||
| { | { | ||||
| OpenGLHelpers::clear (Colours::darkgrey.withAlpha (1.0f)); | OpenGLHelpers::clear (Colours::darkgrey.withAlpha (1.0f)); | ||||
| OpenGLHelpers::prepareFor2D (getWidth(), getHeight()); | |||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
| updateTextureImage(); // this will update our dynamically-changing texture image. | |||||
| texture1.draw2D (50.0f, getHeight() - 50.0f, | |||||
| getWidth() - 50.0f, getHeight() - 50.0f, | |||||
| getWidth() - 50.0f, 50.0f, | |||||
| 50.0f, 50.0f, | |||||
| Colours::white.withAlpha (fabsf (::sinf (rotation / 100.0f)))); | |||||
| drawBackground2DStuff(); // draws some 2D content to demonstrate the OpenGLRenderer class | |||||
| glClear (GL_DEPTH_BUFFER_BIT); | |||||
| // Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so | |||||
| // we'll put back any important settings before doing our normal GL 3D drawing.. | |||||
| glEnable (GL_DEPTH_TEST); | |||||
| glDepthFunc (GL_LESS); | |||||
| glEnable (GL_BLEND); | |||||
| glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||||
| glEnable (GL_TEXTURE_2D); | |||||
| OpenGLHelpers::setPerspective (45.0, getWidth() / (double) getHeight(), 0.1, 100.0); | OpenGLHelpers::setPerspective (45.0, getWidth() / (double) getHeight(), 0.1, 100.0); | ||||
| glTranslatef (0.0f, 0.0f, -5.0f); | glTranslatef (0.0f, 0.0f, -5.0f); | ||||
| glRotatef (rotation, 0.5f, 1.0f, 0.0f); | glRotatef (rotation, 0.5f, 1.0f, 0.0f); | ||||
| // this draws the sides of our spinning cube.. | |||||
| texture1.draw3D (-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, Colours::white); | |||||
| texture1.draw3D (-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, Colours::white); | |||||
| texture1.draw3D (-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Colours::white); | |||||
| texture2.draw3D (-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, Colours::white); | |||||
| texture2.draw3D ( 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, Colours::white); | |||||
| texture2.draw3D (-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, Colours::white); | |||||
| // logoImage and dynamicTextureImage are actually OpenGL images, so we can use this utility function to | |||||
| // extract the frame buffer which is their backing store, and use it directly. | |||||
| OpenGLFrameBuffer* tex1 = OpenGLImageType::getFrameBufferFrom (logoImage); | |||||
| OpenGLFrameBuffer* tex2 = OpenGLImageType::getFrameBufferFrom (dynamicTextureImage); | |||||
| jassert (tex1 != nullptr && tex2 != nullptr); // (this would mean that our images weren't created correctly) | |||||
| // This draws the sides of our spinning cube. | |||||
| // I've used some of the juce helper functions, but you can also just use normal GL calls here too. | |||||
| tex1->draw3D (-1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, Colours::white); | |||||
| tex1->draw3D (-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, Colours::white); | |||||
| tex1->draw3D (-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, Colours::white); | |||||
| tex2->draw3D (-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, Colours::white); | |||||
| tex2->draw3D ( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, Colours::white); | |||||
| tex2->draw3D (-1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, Colours::white); | |||||
| drawForeground2DStuff(); // draws our scrolling text overlay | |||||
| } | |||||
| void updateTextureImage() | |||||
| { | |||||
| // This image is a special framebuffer-backed image, so when we draw to it, the context | |||||
| // will render directly into its framebuffer | |||||
| dynamicTextureImage.clear (dynamicTextureImage.getBounds(), Colours::red.withRotatedHue (fabsf (::sinf (rotation / 300.0f))).withAlpha (0.7f)); | |||||
| Graphics g (dynamicTextureImage); | |||||
| g.setFont (dynamicTextureImage.getHeight() / 3.0f); | |||||
| g.setColour (Colours::black); | |||||
| drawScrollingMessage (g, dynamicTextureImage.getHeight() / 2); | |||||
| } | |||||
| void drawBackground2DStuff() | |||||
| { | |||||
| OpenGLRenderer glRenderer (*this); // Create an OpenGLRenderer that will draw into this GL window.. | |||||
| Graphics g (&glRenderer); // ..and then wrap it in a normal Graphics object so we can draw with it. | |||||
| // This stuff just creates a spinning star shape and fills it.. | |||||
| Path p; | |||||
| const float scale = getHeight() * 0.4f; | |||||
| p.addStar (getLocalBounds().getCentre().toFloat(), 7, | |||||
| scale + ::cosf (rotation * 0.0021f) * scale / 2, | |||||
| scale + ::sinf (rotation * 0.001f) * scale / 2, rotation / 50.0f); | |||||
| g.setGradientFill (ColourGradient (Colours::green.withRotatedHue (fabsf (::sinf (rotation / 300.0f))), | |||||
| 0, 0, | |||||
| Colours::green.withRotatedHue (fabsf (::cosf (rotation / -431.0f))), | |||||
| 0, (float) getHeight(), false)); | |||||
| g.fillPath (p); | |||||
| } | |||||
| void drawForeground2DStuff() | |||||
| { | |||||
| OpenGLRenderer glRenderer (*this); // Create an OpenGLRenderer that will draw into this GL window.. | |||||
| Graphics g (&glRenderer); // ..and then wrap it in a normal Graphics object so we can draw with it. | |||||
| // Then, just draw our scolling text like we would in any other component. | |||||
| g.setColour (Colours::blue.withAlpha (0.5f)); | |||||
| g.setFont (30.0f, Font::bold); | |||||
| drawScrollingMessage (g, getHeight() / 2); | |||||
| } | |||||
| void drawScrollingMessage (Graphics& g, int y) const | |||||
| { | |||||
| g.drawSingleLineText ("The background, foreground and texture are all being drawn using the OpenGLRenderer class, which " | |||||
| "lets you use a standard JUCE 2D graphics context to render directly onto an OpenGL window or framebuffer... ", | |||||
| (int) -std::fmod (textScrollPos, 2500.0f), y); | |||||
| } | } | ||||
| void timerCallback() | void timerCallback() | ||||
| { | { | ||||
| rotation += delta; | rotation += delta; | ||||
| textScrollPos += 1.4f; | |||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| private: | private: | ||||
| OpenGLTexture texture1, texture2; | |||||
| float rotation, delta; | |||||
| Image logoImage, dynamicTextureImage; | |||||
| float rotation, delta, textScrollPos; | |||||
| // Functions to create a couple of images to use as textures.. | // Functions to create a couple of images to use as textures.. | ||||
| static Image createImage1() | |||||
| static Image createLogoImage() | |||||
| { | { | ||||
| Image image (Image::ARGB, 256, 256, true); | |||||
| Image image (Image::ARGB, 256, 256, true, OpenGLImageType()); | |||||
| Graphics g (image); | Graphics g (image); | ||||
| g.fillAll (Colours::white.withAlpha (0.7f)); | |||||
| g.fillAll (Colours::lightgrey.withAlpha (0.8f)); | |||||
| g.drawImageWithin (ImageFileFormat::loadFrom (BinaryData::juce_png, BinaryData::juce_pngSize), | g.drawImageWithin (ImageFileFormat::loadFrom (BinaryData::juce_png, BinaryData::juce_pngSize), | ||||
| 0, 0, image.getWidth(), image.getHeight(), RectanglePlacement::stretchToFit); | 0, 0, image.getWidth(), image.getHeight(), RectanglePlacement::stretchToFit); | ||||
| drawRandomStars (g, image.getWidth(), image.getHeight()); | drawRandomStars (g, image.getWidth(), image.getHeight()); | ||||
| return image; | |||||
| } | |||||
| static Image createImage2() | |||||
| { | |||||
| Image image (Image::ARGB, 128, 128, true); | |||||
| Graphics g (image); | |||||
| g.fillAll (Colours::darkred.withAlpha (0.7f)); | |||||
| Path p; | |||||
| p.addStar (image.getBounds().getCentre().toFloat(), 11, image.getWidth() * 0.3f, image.getWidth() * 0.5f); | |||||
| g.setGradientFill (ColourGradient (Colours::blue, image.getWidth() * 0.5f, image.getHeight() * 0.5f, | |||||
| Colours::green, image.getWidth() * 0.2f, image.getHeight() * 0.2f, | |||||
| true)); | |||||
| g.fillPath (p); | |||||
| drawRandomStars (g, image.getWidth(), image.getHeight()); | |||||
| return image; | return image; | ||||
| } | } | ||||
| @@ -44,10 +44,10 @@ EdgeTable::EdgeTable (const Rectangle<int>& bounds_, | |||||
| t += lineStrideElements; | t += lineStrideElements; | ||||
| } | } | ||||
| const int topLimit = bounds.getY() << 8; | |||||
| const int topLimit = bounds.getY() << 8; | |||||
| const int heightLimit = bounds.getHeight() << 8; | const int heightLimit = bounds.getHeight() << 8; | ||||
| const int leftLimit = bounds.getX() << 8; | |||||
| const int rightLimit = bounds.getRight() << 8; | |||||
| const int leftLimit = bounds.getX() << 8; | |||||
| const int rightLimit = bounds.getRight() << 8; | |||||
| PathFlatteningIterator iter (path, transform); | PathFlatteningIterator iter (path, transform); | ||||
| @@ -598,13 +598,10 @@ void EdgeTable::clipToRectangle (const Rectangle<int>& r) | |||||
| if (bottom < bounds.getHeight()) | if (bottom < bounds.getHeight()) | ||||
| bounds.setHeight (bottom); | bounds.setHeight (bottom); | ||||
| if (clipped.getRight() < bounds.getRight()) | |||||
| bounds.setRight (clipped.getRight()); | |||||
| for (int i = top; --i >= 0;) | for (int i = top; --i >= 0;) | ||||
| table [lineStrideElements * i] = 0; | table [lineStrideElements * i] = 0; | ||||
| if (clipped.getX() > bounds.getX()) | |||||
| if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight()) | |||||
| { | { | ||||
| const int x1 = clipped.getX() << 8; | const int x1 = clipped.getX() << 8; | ||||
| const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8; | const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8; | ||||
| @@ -57,9 +57,6 @@ | |||||
| #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | ||||
| #pragma comment(lib, "vfw32.lib") | #pragma comment(lib, "vfw32.lib") | ||||
| #pragma comment(lib, "imm32.lib") | #pragma comment(lib, "imm32.lib") | ||||
| #endif | |||||
| #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||||
| #pragma comment(lib, "OpenGL32.Lib") | #pragma comment(lib, "OpenGL32.Lib") | ||||
| #pragma comment(lib, "GlU32.Lib") | #pragma comment(lib, "GlU32.Lib") | ||||
| #endif | #endif | ||||
| @@ -128,6 +125,9 @@ | |||||
| typedef returnType (*type_ ## name) params; static type_ ## name name; | typedef returnType (*type_ ## name) params; static type_ ## name name; | ||||
| #endif | #endif | ||||
| #define JUCE_INSTANTIATE_GL_EXTENSION(name) \ | |||||
| name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); | |||||
| //============================================================================== | //============================================================================== | ||||
| // START_AUTOINCLUDE opengl/*.cpp | // START_AUTOINCLUDE opengl/*.cpp | ||||
| #include "opengl/juce_OpenGLComponent.cpp" | #include "opengl/juce_OpenGLComponent.cpp" | ||||
| @@ -25,95 +25,98 @@ | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| #if JUCE_WINDOWS | |||||
| enum | |||||
| namespace | |||||
| { | { | ||||
| GL_FRAMEBUFFER_EXT = 0x8D40, | |||||
| GL_RENDERBUFFER_EXT = 0x8D41, | |||||
| GL_FRAMEBUFFER_BINDING_EXT = 0x8CA6, | |||||
| GL_COLOR_ATTACHMENT0_EXT = 0x8CE0, | |||||
| GL_DEPTH_ATTACHMENT_EXT = 0x8D00, | |||||
| GL_STENCIL_ATTACHMENT_EXT = 0x8D20, | |||||
| GL_FRAMEBUFFER_COMPLETE_EXT = 0x8CD5, | |||||
| GL_DEPTH24_STENCIL8_EXT = 0x88F0, | |||||
| GL_RENDERBUFFER_DEPTH_SIZE_EXT = 0x8D54 | |||||
| }; | |||||
| #endif | |||||
| #if JUCE_WINDOWS || JUCE_LINUX | |||||
| #define FRAMEBUFFER_FUNCTION_LIST(USE_FUNCTION) \ | |||||
| USE_FUNCTION (glIsRenderbufferEXT, GLboolean, (GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glBindRenderbufferEXT, void, (GLenum target, GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glDeleteRenderbuffersEXT, void, (GLsizei n, const GLuint *renderbuffers))\ | |||||
| USE_FUNCTION (glGenRenderbuffersEXT, void, (GLsizei n, GLuint *renderbuffers))\ | |||||
| USE_FUNCTION (glRenderbufferStorageEXT, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))\ | |||||
| USE_FUNCTION (glGetRenderbufferParameterivEXT, void, (GLenum target, GLenum pname, GLint* params))\ | |||||
| USE_FUNCTION (glIsFramebufferEXT, GLboolean, (GLuint framebuffer))\ | |||||
| USE_FUNCTION (glBindFramebufferEXT, void, (GLenum target, GLuint framebuffer))\ | |||||
| USE_FUNCTION (glDeleteFramebuffersEXT, void, (GLsizei n, const GLuint *framebuffers))\ | |||||
| USE_FUNCTION (glGenFramebuffersEXT, void, (GLsizei n, GLuint *framebuffers))\ | |||||
| USE_FUNCTION (glCheckFramebufferStatusEXT, GLenum, (GLenum target))\ | |||||
| USE_FUNCTION (glFramebufferTexture1DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ | |||||
| USE_FUNCTION (glFramebufferTexture2DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ | |||||
| USE_FUNCTION (glFramebufferTexture3DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))\ | |||||
| USE_FUNCTION (glFramebufferRenderbufferEXT, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glGetFramebufferAttachmentParameterivEXT, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\ | |||||
| USE_FUNCTION (glGenerateMipmapEXT, void, (GLenum target))\ | |||||
| FRAMEBUFFER_FUNCTION_LIST (JUCE_DECLARE_GL_EXTENSION_FUNCTION) | |||||
| static bool framebufferFunctionsInitialised = false; | |||||
| static void initialiseFrameBufferFunctions() | |||||
| { | |||||
| if (! framebufferFunctionsInitialised) | |||||
| #if JUCE_WINDOWS | |||||
| enum | |||||
| { | |||||
| GL_FRAMEBUFFER_EXT = 0x8D40, | |||||
| GL_RENDERBUFFER_EXT = 0x8D41, | |||||
| GL_FRAMEBUFFER_BINDING_EXT = 0x8CA6, | |||||
| GL_COLOR_ATTACHMENT0_EXT = 0x8CE0, | |||||
| GL_DEPTH_ATTACHMENT_EXT = 0x8D00, | |||||
| GL_STENCIL_ATTACHMENT_EXT = 0x8D20, | |||||
| GL_FRAMEBUFFER_COMPLETE_EXT = 0x8CD5, | |||||
| GL_DEPTH24_STENCIL8_EXT = 0x88F0, | |||||
| GL_RENDERBUFFER_DEPTH_SIZE_EXT = 0x8D54 | |||||
| }; | |||||
| #endif | |||||
| #if JUCE_WINDOWS || JUCE_LINUX | |||||
| #define FRAMEBUFFER_FUNCTION_LIST(USE_FUNCTION) \ | |||||
| USE_FUNCTION (glIsRenderbufferEXT, GLboolean, (GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glBindRenderbufferEXT, void, (GLenum target, GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glDeleteRenderbuffersEXT, void, (GLsizei n, const GLuint *renderbuffers))\ | |||||
| USE_FUNCTION (glGenRenderbuffersEXT, void, (GLsizei n, GLuint *renderbuffers))\ | |||||
| USE_FUNCTION (glRenderbufferStorageEXT, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))\ | |||||
| USE_FUNCTION (glGetRenderbufferParameterivEXT, void, (GLenum target, GLenum pname, GLint* params))\ | |||||
| USE_FUNCTION (glIsFramebufferEXT, GLboolean, (GLuint framebuffer))\ | |||||
| USE_FUNCTION (glBindFramebufferEXT, void, (GLenum target, GLuint framebuffer))\ | |||||
| USE_FUNCTION (glDeleteFramebuffersEXT, void, (GLsizei n, const GLuint *framebuffers))\ | |||||
| USE_FUNCTION (glGenFramebuffersEXT, void, (GLsizei n, GLuint *framebuffers))\ | |||||
| USE_FUNCTION (glCheckFramebufferStatusEXT, GLenum, (GLenum target))\ | |||||
| USE_FUNCTION (glFramebufferTexture1DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ | |||||
| USE_FUNCTION (glFramebufferTexture2DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))\ | |||||
| USE_FUNCTION (glFramebufferTexture3DEXT, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))\ | |||||
| USE_FUNCTION (glFramebufferRenderbufferEXT, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))\ | |||||
| USE_FUNCTION (glGetFramebufferAttachmentParameterivEXT, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))\ | |||||
| USE_FUNCTION (glGenerateMipmapEXT, void, (GLenum target))\ | |||||
| FRAMEBUFFER_FUNCTION_LIST (JUCE_DECLARE_GL_EXTENSION_FUNCTION) | |||||
| static bool framebufferFunctionsInitialised = false; | |||||
| void initialiseFrameBufferFunctions() | |||||
| { | { | ||||
| framebufferFunctionsInitialised = true; | |||||
| if (! framebufferFunctionsInitialised) | |||||
| { | |||||
| framebufferFunctionsInitialised = true; | |||||
| #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); | |||||
| FRAMEBUFFER_FUNCTION_LIST (FIND_FUNCTION) | |||||
| #undef FIND_FUNCTION | |||||
| #define FIND_FUNCTION(name, returnType, params) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name); | |||||
| FRAMEBUFFER_FUNCTION_LIST (FIND_FUNCTION) | |||||
| #undef FIND_FUNCTION | |||||
| } | |||||
| } | } | ||||
| } | |||||
| #undef FRAMEBUFFER_FUNCTION_LIST | |||||
| #undef FRAMEBUFFER_FUNCTION_LIST | |||||
| //============================================================================== | |||||
| #elif JUCE_OPENGL_ES | |||||
| #define glIsRenderbufferEXT glIsRenderbufferOES | |||||
| #define glBindRenderbufferEXT glBindRenderbufferOES | |||||
| #define glDeleteRenderbuffersEXT glDeleteRenderbuffersOES | |||||
| #define glGenRenderbuffersEXT glGenRenderbuffersOES | |||||
| #define glRenderbufferStorageEXT glRenderbufferStorageOES | |||||
| #define glGetRenderbufferParameterivEXT glGetRenderbufferParameterivOES | |||||
| #define glIsFramebufferEXT glIsFramebufferOES | |||||
| #define glBindFramebufferEXT glBindFramebufferOES | |||||
| #define glDeleteFramebuffersEXT glDeleteFramebuffersOES | |||||
| #define glGenFramebuffersEXT glGenFramebuffersOES | |||||
| #define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES | |||||
| #define glFramebufferTexture1DEXT glFramebufferTexture1DOES | |||||
| #define glFramebufferTexture2DEXT glFramebufferTexture2DOES | |||||
| #define glFramebufferTexture3DEXT glFramebufferTexture3DOES | |||||
| #define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES | |||||
| #define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES | |||||
| #define glGenerateMipmapEXT glGenerateMipmapOES | |||||
| #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES | |||||
| #define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_OES | |||||
| #define GL_RGBA8 GL_RGBA | |||||
| #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES | |||||
| #define GL_RENDERBUFFER_EXT GL_RENDERBUFFER_OES | |||||
| #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES | |||||
| #define GL_RENDERBUFFER_DEPTH_SIZE_EXT GL_RENDERBUFFER_DEPTH_SIZE_OES | |||||
| #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT_OES | |||||
| #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT_OES | |||||
| #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE_OES | |||||
| #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED_OES | |||||
| #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES | |||||
| //============================================================================== | |||||
| #elif JUCE_OPENGL_ES | |||||
| #define glIsRenderbufferEXT glIsRenderbufferOES | |||||
| #define glBindRenderbufferEXT glBindRenderbufferOES | |||||
| #define glDeleteRenderbuffersEXT glDeleteRenderbuffersOES | |||||
| #define glGenRenderbuffersEXT glGenRenderbuffersOES | |||||
| #define glRenderbufferStorageEXT glRenderbufferStorageOES | |||||
| #define glGetRenderbufferParameterivEXT glGetRenderbufferParameterivOES | |||||
| #define glIsFramebufferEXT glIsFramebufferOES | |||||
| #define glBindFramebufferEXT glBindFramebufferOES | |||||
| #define glDeleteFramebuffersEXT glDeleteFramebuffersOES | |||||
| #define glGenFramebuffersEXT glGenFramebuffersOES | |||||
| #define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES | |||||
| #define glFramebufferTexture1DEXT glFramebufferTexture1DOES | |||||
| #define glFramebufferTexture2DEXT glFramebufferTexture2DOES | |||||
| #define glFramebufferTexture3DEXT glFramebufferTexture3DOES | |||||
| #define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES | |||||
| #define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES | |||||
| #define glGenerateMipmapEXT glGenerateMipmapOES | |||||
| #define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES | |||||
| #define GL_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_OES | |||||
| #define GL_RGBA8 GL_RGBA | |||||
| #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES | |||||
| #define GL_RENDERBUFFER_EXT GL_RENDERBUFFER_OES | |||||
| #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES | |||||
| #define GL_RENDERBUFFER_DEPTH_SIZE_EXT GL_RENDERBUFFER_DEPTH_SIZE_OES | |||||
| #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT_OES | |||||
| #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT_OES | |||||
| #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE_OES | |||||
| #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED_OES | |||||
| #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES | |||||
| #endif | |||||
| #endif | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class OpenGLFrameBuffer::Pimpl | class OpenGLFrameBuffer::Pimpl | ||||
| @@ -399,7 +402,7 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int> | |||||
| glDisable (GL_BLEND); | glDisable (GL_BLEND); | ||||
| OpenGLTexture tex; | OpenGLTexture tex; | ||||
| tex.load (data, area.getWidth(), area.getHeight()); | |||||
| tex.loadARGBFlipped (data, area.getWidth(), area.getHeight()); | |||||
| const int texH = tex.getHeight(); | const int texH = tex.getHeight(); | ||||
| #if JUCE_OPENGL_ES | #if JUCE_OPENGL_ES | ||||
| @@ -75,12 +75,13 @@ public: | |||||
| #ifndef DOXYGEN | #ifndef DOXYGEN | ||||
| class SavedState; | class SavedState; | ||||
| class ScratchBufferManager; | |||||
| #endif | #endif | ||||
| private: | private: | ||||
| ScopedPointer<ScratchBufferManager> scratchBufferManager; | |||||
| RenderingHelpers::SavedStateStack<SavedState> stack; | RenderingHelpers::SavedStateStack<SavedState> stack; | ||||
| GLuint previousFrameBufferTarget; | |||||
| void initialise(); | |||||
| }; | }; | ||||
| #endif // __JUCE_OPENGLGRAPHICSCONTEXT_JUCEHEADER__ | #endif // __JUCE_OPENGLGRAPHICSCONTEXT_JUCEHEADER__ | ||||
| @@ -234,12 +234,12 @@ void OpenGLHelpers::drawTriangleStrip (const GLfloat* const vertices, const GLfl | |||||
| glBindTexture (GL_TEXTURE_2D, 0); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| } | } | ||||
| void OpenGLHelpers::drawTextureQuad (GLuint textureID, int x, int y, int w, int h) | |||||
| void OpenGLHelpers::drawTextureQuad (GLuint textureID, const Rectangle<int>& rect) | |||||
| { | { | ||||
| const GLfloat l = (GLfloat) x; | |||||
| const GLfloat t = (GLfloat) y; | |||||
| const GLfloat r = (GLfloat) (x + w); | |||||
| const GLfloat b = (GLfloat) (y + h); | |||||
| const GLfloat l = (GLfloat) rect.getX(); | |||||
| const GLfloat t = (GLfloat) rect.getY(); | |||||
| const GLfloat r = (GLfloat) rect.getRight(); | |||||
| const GLfloat b = (GLfloat) rect.getBottom(); | |||||
| const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; | const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; | ||||
| const GLfloat textureCoords[] = { 0, 1.0f, 1.0f, 1.0f, 0, 0, 1.0f, 0 }; | const GLfloat textureCoords[] = { 0, 1.0f, 1.0f, 1.0f, 0, 0, 1.0f, 0 }; | ||||
| @@ -255,7 +255,7 @@ void OpenGLHelpers::fillRectWithTexture (const Rectangle<int>& rect, GLuint text | |||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||
| glColor4f (alpha, alpha, alpha, alpha); | glColor4f (alpha, alpha, alpha, alpha); | ||||
| drawTextureQuad (textureID, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); | |||||
| drawTextureQuad (textureID, rect); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -280,449 +280,27 @@ void OpenGLHelpers::fillRect (const Rectangle<int>& rect) | |||||
| glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); | glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); | ||||
| } | } | ||||
| //============================================================================== | |||||
| struct OpenGLEdgeTableRenderer | |||||
| { | |||||
| OpenGLEdgeTableRenderer() noexcept | |||||
| : lastAlpha (-1) | |||||
| { | |||||
| } | |||||
| void draw (const EdgeTable& et) | |||||
| { | |||||
| glDisableClientState (GL_TEXTURE_COORD_ARRAY); | |||||
| glEnableClientState (GL_VERTEX_ARRAY); | |||||
| glVertexPointer (2, GL_FLOAT, 0, vertices); | |||||
| et.iterate (*this); | |||||
| } | |||||
| void setEdgeTableYPos (const int y) noexcept | |||||
| { | |||||
| vertices[1] = vertices[5] = (GLfloat) y; | |||||
| vertices[3] = vertices[7] = (GLfloat) (y + 1); | |||||
| } | |||||
| void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept | |||||
| { | |||||
| drawHorizontal (x, 1, alphaLevel); | |||||
| } | |||||
| void handleEdgeTablePixelFull (const int x) noexcept | |||||
| { | |||||
| drawHorizontal (x, 1, 255); | |||||
| } | |||||
| void handleEdgeTableLine (const int x, const int width, const int alphaLevel) noexcept | |||||
| { | |||||
| drawHorizontal (x, width, alphaLevel); | |||||
| } | |||||
| void handleEdgeTableLineFull (const int x, const int width) noexcept | |||||
| { | |||||
| drawHorizontal (x, width, 255); | |||||
| } | |||||
| private: | |||||
| GLfloat vertices[8]; | |||||
| int lastAlpha; | |||||
| void drawHorizontal (int x, const int w, const int alphaLevel) noexcept | |||||
| { | |||||
| vertices[0] = vertices[2] = (GLfloat) x; | |||||
| vertices[4] = vertices[6] = (GLfloat) (x + w); | |||||
| if (lastAlpha != alphaLevel) | |||||
| { | |||||
| lastAlpha = alphaLevel; | |||||
| const float a = alphaLevel / 255.0f; | |||||
| glColor4f (a, a, a, a); | |||||
| } | |||||
| glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE (OpenGLEdgeTableRenderer); | |||||
| }; | |||||
| void OpenGLHelpers::fillEdgeTable (const EdgeTable& edgeTable) | |||||
| { | |||||
| OpenGLEdgeTableRenderer etr; | |||||
| etr.draw (edgeTable); | |||||
| } | |||||
| //============================================================================== | |||||
| // This breaks down a path into a series of horizontal strips of trapezoids.. | |||||
| class TriangulatedPath::TrapezoidedPath | |||||
| { | |||||
| public: | |||||
| TrapezoidedPath (const Path& p, const AffineTransform& transform) | |||||
| : firstSlice (nullptr), | |||||
| windingMask (p.isUsingNonZeroWinding() ? -1 : 1) | |||||
| { | |||||
| for (PathFlatteningIterator iter (p, transform); iter.next();) | |||||
| addLine (floatToInt (iter.x1), floatToInt (iter.y1), | |||||
| floatToInt (iter.x2), floatToInt (iter.y2)); | |||||
| } | |||||
| ~TrapezoidedPath() | |||||
| { | |||||
| for (HorizontalSlice* s = firstSlice; s != nullptr;) | |||||
| { | |||||
| const ScopedPointer<HorizontalSlice> deleter (s); | |||||
| s = s->next; | |||||
| } | |||||
| } | |||||
| template <class Consumer> | |||||
| void iterate (Consumer& consumer) const | |||||
| { | |||||
| for (HorizontalSlice* s = firstSlice; s != nullptr; s = s->next) | |||||
| s->iterate (consumer, windingMask); | |||||
| } | |||||
| private: | |||||
| void addLine (int x1, int y1, int x2, int y2) | |||||
| { | |||||
| int winding = 1; | |||||
| if (y2 < y1) | |||||
| { | |||||
| std::swap (x1, x2); | |||||
| std::swap (y1, y2); | |||||
| winding = -1; | |||||
| } | |||||
| HorizontalSlice* last = nullptr; | |||||
| HorizontalSlice* s = firstSlice; | |||||
| while (y2 > y1) | |||||
| { | |||||
| if (s == nullptr) | |||||
| { | |||||
| insert (last, new HorizontalSlice (nullptr, x1, y1, x2, y2, winding)); | |||||
| break; | |||||
| } | |||||
| if (s->y2 > y1) | |||||
| { | |||||
| if (y1 < s->y1) | |||||
| { | |||||
| if (y2 <= s->y1) | |||||
| { | |||||
| insert (last, new HorizontalSlice (s, x1, y1, x2, y2, winding)); | |||||
| break; | |||||
| } | |||||
| else | |||||
| { | |||||
| const int newX = x1 + (int) ((s->y1 - y1) * (int64) (x2 - x1) / (y2 - y1)); | |||||
| HorizontalSlice* const newSlice = new HorizontalSlice (s, x1, y1, newX, s->y1, winding); | |||||
| insert (last, newSlice); | |||||
| last = newSlice; | |||||
| x1 = newX; | |||||
| y1 = s->y1; | |||||
| continue; | |||||
| } | |||||
| } | |||||
| else if (y1 > s->y1) | |||||
| { | |||||
| s->split (y1); | |||||
| s = s->next; | |||||
| jassert (s != nullptr); | |||||
| } | |||||
| jassert (y1 == s->y1); | |||||
| if (y2 > s->y2) | |||||
| { | |||||
| const int newY = s->y2; | |||||
| const int newX = x1 + (int) ((newY - y1) * (int64) (x2 - x1) / (y2 - y1)); | |||||
| s->addLine (x1, newX, winding); | |||||
| x1 = newX; | |||||
| y1 = newY; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (y2 < s->y2) | |||||
| s->split (y2); | |||||
| jassert (y2 == s->y2); | |||||
| s->addLine (x1, x2, winding); | |||||
| break; | |||||
| } | |||||
| } | |||||
| last = s; | |||||
| s = s->next; | |||||
| } | |||||
| } | |||||
| struct HorizontalSlice | |||||
| { | |||||
| HorizontalSlice (const HorizontalSlice& other, HorizontalSlice* const next_, int y1_, int y2_) | |||||
| : next (next_), y1 (y1_), y2 (y2_), segments (other.segments) | |||||
| { | |||||
| } | |||||
| HorizontalSlice (HorizontalSlice* const next_, int x1, int y1_, int x2, int y2_, int winding) | |||||
| : next (next_), y1 (y1_), y2 (y2_) | |||||
| { | |||||
| jassert (next != this); | |||||
| jassert (y2 > y1); | |||||
| segments.ensureStorageAllocated (32); | |||||
| segments.add (LineSegment (x1, x2, winding)); | |||||
| } | |||||
| void addLine (const int x1, const int x2, int winding) | |||||
| { | |||||
| const int dy = y2 - y1; | |||||
| for (int i = 0; i < segments.size(); ++i) | |||||
| { | |||||
| const LineSegment& l = segments.getReference (i); | |||||
| const int diff1 = l.x1 - x1; | |||||
| const int diff2 = l.x2 - x2; | |||||
| if ((diff1 < 0) == (diff2 > 0)) | |||||
| { | |||||
| const int dx1 = l.x2 - l.x1; | |||||
| const int dx2 = x2 - x1; | |||||
| const int dxDiff = dx2 - dx1; | |||||
| if (dxDiff != 0) | |||||
| { | |||||
| const int intersectionY = (int) ((dy * (int64) diff1) / dxDiff); | |||||
| if (intersectionY > 0 && intersectionY < dy) | |||||
| { | |||||
| const int intersectionX = x1 + (intersectionY * dx2) / dy; | |||||
| split (intersectionY + y1); | |||||
| next->addLine (intersectionX, x2, winding); | |||||
| addLine (x1, intersectionX, winding); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (diff1 + diff2 > 0) | |||||
| { | |||||
| segments.insert (i, LineSegment (x1, x2, winding)); | |||||
| return; | |||||
| } | |||||
| } | |||||
| segments.add (LineSegment (x1, x2, winding)); | |||||
| } | |||||
| void split (const int newY) | |||||
| { | |||||
| jassert (newY > y1 && newY < y2); | |||||
| const int dy1 = newY - y1; | |||||
| const int dy2 = y2 - y1; | |||||
| next = new HorizontalSlice (*this, next, newY, y2); | |||||
| y2 = newY; | |||||
| LineSegment* const oldSegments = segments.getRawDataPointer(); | |||||
| LineSegment* const newSegments = next->segments.getRawDataPointer(); | |||||
| for (int i = 0; i < segments.size(); ++i) | |||||
| { | |||||
| LineSegment& l = oldSegments[i]; | |||||
| const int newX = l.x1 + (int) (dy1 * (int64) (l.x2 - l.x1) / dy2); | |||||
| newSegments[i].x1 = newX; | |||||
| l.x2 = newX; | |||||
| } | |||||
| } | |||||
| template <class Consumer> | |||||
| void iterate (Consumer& consumer, const int windingMask) | |||||
| { | |||||
| jassert (segments.size() > 0); | |||||
| const float fy1 = intToFloat (y1); | |||||
| const float fy2 = intToFloat (y2); | |||||
| const LineSegment* s1 = segments.getRawDataPointer(); | |||||
| const LineSegment* s2 = s1; | |||||
| int winding = s1->winding; | |||||
| for (int i = segments.size(); --i > 0;) | |||||
| { | |||||
| ++s2; | |||||
| winding += s2->winding; | |||||
| if ((winding & windingMask) == 0) | |||||
| { | |||||
| const float ax1 = intToFloat (s1->x1); | |||||
| const float ax2 = intToFloat (s1->x2); | |||||
| if (s1->x1 == s2->x1) | |||||
| consumer.addTriangle (ax1, fy1, ax2, fy2, intToFloat (s2->x2), fy2); | |||||
| else if (s1->x2 == s2->x2) | |||||
| consumer.addTriangle (ax1, fy1, intToFloat (s2->x1), fy1, ax2, fy2); | |||||
| else | |||||
| consumer.addTrapezoid (fy1, fy2, ax1, ax2, intToFloat (s2->x1), intToFloat (s2->x2)); | |||||
| s1 = s2 + 1; | |||||
| } | |||||
| } | |||||
| } | |||||
| HorizontalSlice* next; | |||||
| int y1, y2; | |||||
| private: | |||||
| struct LineSegment | |||||
| { | |||||
| inline LineSegment (int x1_, int x2_, int winding_) noexcept | |||||
| : x1 (x1_), x2 (x2_), winding (winding_) {} | |||||
| int x1, x2; | |||||
| int winding; | |||||
| }; | |||||
| Array<LineSegment> segments; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HorizontalSlice); | |||||
| }; | |||||
| HorizontalSlice* firstSlice; | |||||
| const int windingMask; | |||||
| inline void insert (HorizontalSlice* const last, HorizontalSlice* const newOne) noexcept | |||||
| { | |||||
| if (last == nullptr) | |||||
| firstSlice = newOne; | |||||
| else | |||||
| last->next = newOne; | |||||
| } | |||||
| enum { factor = 128 }; | |||||
| static inline int floatToInt (const float n) noexcept { return roundToInt (n * (float) factor); } | |||||
| static inline float intToFloat (const int n) noexcept { return n * (1.0f / (float) factor); } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrapezoidedPath); | |||||
| }; | |||||
| //============================================================================== | |||||
| struct TriangulatedPath::TriangleBlock | |||||
| { | |||||
| TriangleBlock() noexcept | |||||
| : numVertices (0), | |||||
| triangles (maxVerticesPerBlock) | |||||
| {} | |||||
| void draw() const | |||||
| { | |||||
| glVertexPointer (2, GL_FLOAT, 0, triangles); | |||||
| glDrawArrays (GL_TRIANGLES, 0, numVertices / 2); | |||||
| } | |||||
| inline GLfloat* getNextTriangle() noexcept { return triangles + numVertices; } | |||||
| void optimiseStorage() { triangles.realloc (numVertices); } | |||||
| // Some GL implementations can't take very large triangle lists, so store | |||||
| // the list as a series of blocks containing this max number of triangles. | |||||
| enum { maxVerticesPerBlock = 256 * 6 }; | |||||
| unsigned int numVertices; | |||||
| HeapBlock<GLfloat> triangles; | |||||
| }; | |||||
| TriangulatedPath::TriangulatedPath (const Path& path, const AffineTransform& transform) | |||||
| { | |||||
| startNewBlock(); | |||||
| TrapezoidedPath (path, transform).iterate (*this); | |||||
| } | |||||
| TriangulatedPath::~TriangulatedPath() {} | |||||
| void TriangulatedPath::draw (const int oversamplingLevel) const | |||||
| { | |||||
| const float a = 1.0f / (oversamplingLevel * oversamplingLevel); | |||||
| glColor4f (a, a, a, a); | |||||
| glPushMatrix(); | |||||
| glTranslatef (-0.5f, -0.5f, 0.0f); | |||||
| const float inc = 1.0f / oversamplingLevel; | |||||
| for (int y = oversamplingLevel; --y >= 0;) | |||||
| { | |||||
| for (int x = oversamplingLevel; --x >= 0;) | |||||
| { | |||||
| glTranslatef (inc, 0.0f, 0.0f); | |||||
| for (int i = 0; i < blocks.size(); ++i) | |||||
| blocks.getUnchecked(i)->draw(); | |||||
| } | |||||
| glTranslatef (-1.0f, inc, 0.0f); | |||||
| } | |||||
| glPopMatrix(); | |||||
| } | |||||
| void TriangulatedPath::optimiseStorage() | |||||
| { | |||||
| currentBlock->optimiseStorage(); | |||||
| } | |||||
| void TriangulatedPath::startNewBlock() | |||||
| { | |||||
| currentBlock = new TriangleBlock(); | |||||
| blocks.add (currentBlock); | |||||
| } | |||||
| void TriangulatedPath::addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3) | |||||
| { | |||||
| if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock) | |||||
| startNewBlock(); | |||||
| GLfloat* t = currentBlock->getNextTriangle(); | |||||
| *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y3; | |||||
| currentBlock->numVertices += 6; | |||||
| } | |||||
| void TriangulatedPath::addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4) | |||||
| { | |||||
| if (currentBlock->numVertices >= TriangleBlock::maxVerticesPerBlock - 6) | |||||
| startNewBlock(); | |||||
| GLfloat* t = currentBlock->getNextTriangle(); | |||||
| *t++ = x1; *t++ = y1; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; | |||||
| *t++ = x4; *t++ = y2; *t++ = x2; *t++ = y2; *t++ = x3; *t++ = y1; | |||||
| currentBlock->numVertices += 12; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) | OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) | ||||
| : width (image.getWidth()), | |||||
| height (image.getHeight()) | |||||
| : imageWidth (image.getWidth()), | |||||
| imageHeight (image.getHeight()) | |||||
| { | { | ||||
| OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image); | OpenGLFrameBuffer* const fb = OpenGLImageType::getFrameBufferFrom (image); | ||||
| if (fb != nullptr) | if (fb != nullptr) | ||||
| { | { | ||||
| textureID = fb->getTextureID(); | textureID = fb->getTextureID(); | ||||
| fullWidthProportion = 1.0f; | |||||
| fullHeightProportion = 1.0f; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (OpenGLTexture::isValidSize (width, height)) | |||||
| { | |||||
| texture = new OpenGLTexture(); | |||||
| texture->load (image); | |||||
| textureID = texture->getTextureID(); | |||||
| } | |||||
| else | |||||
| { | |||||
| frameBuffer = new OpenGLFrameBuffer(); | |||||
| frameBuffer->initialise (image); | |||||
| textureID = frameBuffer->getTextureID(); | |||||
| } | |||||
| texture = new OpenGLTexture(); | |||||
| texture->loadImage (image); | |||||
| textureID = texture->getTextureID(); | |||||
| fullWidthProportion = imageWidth / (float) texture->getWidth(); | |||||
| fullHeightProportion = imageHeight / (float) texture->getHeight(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -78,7 +78,7 @@ public: | |||||
| static void drawTriangleStrip (const GLfloat* const vertices, const GLfloat* const textureCoords, | static void drawTriangleStrip (const GLfloat* const vertices, const GLfloat* const textureCoords, | ||||
| const int numVertices, const GLuint textureID) noexcept; | const int numVertices, const GLuint textureID) noexcept; | ||||
| static void drawTextureQuad (GLuint textureID, int x, int y, int w, int h); | |||||
| static void drawTextureQuad (GLuint textureID, const Rectangle<int>& rect); | |||||
| static void fillRectWithTexture (const Rectangle<int>& rect, GLuint textureID, const float alpha); | static void fillRectWithTexture (const Rectangle<int>& rect, GLuint textureID, const float alpha); | ||||
| @@ -88,9 +88,6 @@ public: | |||||
| static void fillRectWithColour (const Rectangle<int>& rect, | static void fillRectWithColour (const Rectangle<int>& rect, | ||||
| const Colour& colour); | const Colour& colour); | ||||
| /** Renders an edge-table into the current context. */ | |||||
| static void fillEdgeTable (const EdgeTable& edgeTable); | |||||
| /** Checks whether the current context supports the specified extension. */ | /** Checks whether the current context supports the specified extension. */ | ||||
| static bool isExtensionSupported (const char* extensionName); | static bool isExtensionSupported (const char* extensionName); | ||||
| @@ -98,43 +95,6 @@ public: | |||||
| static void* getExtensionFunction (const char* functionName); | static void* getExtensionFunction (const char* functionName); | ||||
| }; | }; | ||||
| //============================================================================== | |||||
| /** Holds a set of OpenGL triangles, having generated them from a Path object. | |||||
| */ | |||||
| class JUCE_API TriangulatedPath | |||||
| { | |||||
| public: | |||||
| TriangulatedPath (const Path& path, const AffineTransform& transform); | |||||
| /** Destructor. */ | |||||
| ~TriangulatedPath(); | |||||
| /** Renders the path, using a jittered oversampling method. | |||||
| The oversampling level is the square root of the number of times it | |||||
| should be oversampled, so 3 or 4 might be reasonable. | |||||
| */ | |||||
| void draw (int oversamplingLevel) const; | |||||
| /** Reduces the memory footprint of this object to the minimum possible. */ | |||||
| void optimiseStorage(); | |||||
| private: | |||||
| class TrapezoidedPath; | |||||
| friend class TrapezoidedPath; | |||||
| void startNewBlock(); | |||||
| void addTriangle (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3); | |||||
| void addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat x2, GLfloat x3, GLfloat x4); | |||||
| struct TriangleBlock; | |||||
| friend class OwnedArray<TriangleBlock>; | |||||
| OwnedArray<TriangleBlock> blocks; | |||||
| TriangleBlock* currentBlock; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TriangulatedPath); | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| Used as a local object while rendering, this will create a temporary texture ID | Used as a local object while rendering, this will create a temporary texture ID | ||||
| @@ -150,11 +110,11 @@ public: | |||||
| ~OpenGLTextureFromImage(); | ~OpenGLTextureFromImage(); | ||||
| GLuint textureID; | GLuint textureID; | ||||
| const int width, height; | |||||
| const int imageWidth, imageHeight; | |||||
| float fullWidthProportion, fullHeightProportion; | |||||
| private: | private: | ||||
| ScopedPointer<OpenGLTexture> texture; | ScopedPointer<OpenGLTexture> texture; | ||||
| ScopedPointer<OpenGLFrameBuffer> frameBuffer; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTextureFromImage); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTextureFromImage); | ||||
| }; | }; | ||||
| @@ -25,12 +25,6 @@ | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| #if JUCE_OPENGL_ES | |||||
| enum { internalGLTextureFormat = GL_RGBA }; | |||||
| #else | |||||
| enum { internalGLTextureFormat = 4 }; | |||||
| #endif | |||||
| OpenGLTexture::OpenGLTexture() | OpenGLTexture::OpenGLTexture() | ||||
| : textureID (0), width (0), height (0) | : textureID (0), width (0), height (0) | ||||
| @@ -47,7 +41,7 @@ bool OpenGLTexture::isValidSize (int width, int height) | |||||
| return isPowerOfTwo (width) && isPowerOfTwo (height); | return isPowerOfTwo (width) && isPowerOfTwo (height); | ||||
| } | } | ||||
| void OpenGLTexture::create (const int w, const int h, const void* pixels) | |||||
| void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum type) | |||||
| { | { | ||||
| // Texture objects can only be created when the current thread has an active OpenGL | // Texture objects can only be created when the current thread has an active OpenGL | ||||
| // context. You'll need to make an OpenGLComponent active before calling this. | // context. You'll need to make an OpenGLComponent active before calling this. | ||||
| @@ -55,24 +49,26 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels) | |||||
| jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? | jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? | ||||
| release(); | |||||
| if (width != w || height != h) | |||||
| { | |||||
| release(); | |||||
| width = w; | |||||
| height = h; | |||||
| width = w; | |||||
| height = h; | |||||
| glGenTextures (1, &textureID); | |||||
| glBindTexture (GL_TEXTURE_2D, textureID); | |||||
| glGenTextures (1, &textureID); | |||||
| } | |||||
| glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |||||
| glBindTexture (GL_TEXTURE_2D, textureID); | |||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE); | ||||
| glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||
| glPixelStorei (GL_UNPACK_ALIGNMENT, 4); | |||||
| glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0, | |||||
| GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); | |||||
| glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | |||||
| glTexImage2D (GL_TEXTURE_2D, 0, type == GL_ALPHA ? GL_ALPHA : GL_RGBA, | |||||
| w, h, 0, type, GL_UNSIGNED_BYTE, pixels); | |||||
| } | } | ||||
| template <class PixelType> | template <class PixelType> | ||||
| @@ -96,7 +92,7 @@ struct Flipper | |||||
| } | } | ||||
| }; | }; | ||||
| void OpenGLTexture::load (const Image& image) | |||||
| void OpenGLTexture::loadImage (const Image& image) | |||||
| { | { | ||||
| const int imageW = image.getWidth(); | const int imageW = image.getWidth(); | ||||
| const int imageH = image.getHeight(); | const int imageH = image.getHeight(); | ||||
| @@ -114,25 +110,30 @@ void OpenGLTexture::load (const Image& image) | |||||
| default: break; | default: break; | ||||
| } | } | ||||
| create (textureW, textureH, dataCopy); | |||||
| create (textureW, textureH, dataCopy, GL_BGRA_EXT); | |||||
| } | |||||
| void OpenGLTexture::loadARGB (const PixelARGB* pixels, const int w, const int h) | |||||
| { | |||||
| jassert (isValidSize (w, h)); | |||||
| create (w, h, pixels, GL_BGRA_EXT); | |||||
| } | |||||
| void OpenGLTexture::loadAlpha (const uint8* pixels, int w, int h) | |||||
| { | |||||
| jassert (isValidSize (w, h)); | |||||
| create (w, h, pixels, GL_ALPHA); | |||||
| } | } | ||||
| void OpenGLTexture::load (const PixelARGB* pixels, const int w, const int h) | |||||
| void OpenGLTexture::loadARGBFlipped (const PixelARGB* pixels, int w, int h) | |||||
| { | { | ||||
| const int textureW = nextPowerOfTwo (w); | const int textureW = nextPowerOfTwo (w); | ||||
| const int textureH = nextPowerOfTwo (h); | |||||
| if (h == 1 && textureW == w) | |||||
| { | |||||
| create (w, 1, pixels); | |||||
| } | |||||
| else | |||||
| { | |||||
| const int textureH = nextPowerOfTwo (h); | |||||
| HeapBlock<PixelARGB> flippedCopy; | |||||
| Flipper<PixelARGB>::flip (flippedCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH); | |||||
| HeapBlock<PixelARGB> dataCopy; | |||||
| Flipper<PixelARGB>::flip (dataCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH); | |||||
| create (textureW, textureH, dataCopy); | |||||
| } | |||||
| loadARGB (flippedCopy, textureW, textureH); | |||||
| } | } | ||||
| void OpenGLTexture::release() | void OpenGLTexture::release() | ||||
| @@ -37,13 +37,39 @@ public: | |||||
| ~OpenGLTexture(); | ~OpenGLTexture(); | ||||
| /** Creates a texture from the given image. | /** Creates a texture from the given image. | ||||
| Note that if the image's dimensions aren't a power-of-two, the texture may | Note that if the image's dimensions aren't a power-of-two, the texture may | ||||
| be created with a larger size. | be created with a larger size. | ||||
| The image will be arranged so that its top-left corner is at texture | |||||
| coordinate (0, 1). | |||||
| */ | |||||
| void loadImage (const Image& image); | |||||
| /** Creates a texture from a raw array of pixels. | |||||
| The width and height provided must be valid - i.e. power-of-two unless | |||||
| the underlying GL system allows otherwise. | |||||
| The data is sent directly to the OpenGL driver without being flipped vertically, | |||||
| so the first pixel will be mapped onto texture coordinate (0, 0). | |||||
| bottom-left corner of the texture | |||||
| */ | */ | ||||
| void load (const Image& image); | |||||
| void loadARGB (const PixelARGB* pixels, int width, int height); | |||||
| /** Creates a texture from a raw array of pixels. */ | |||||
| void load (const PixelARGB* pixels, int width, int height); | |||||
| /** Creates a texture from a raw array of pixels. | |||||
| This is like loadARGB, but will vertically flip the data so that the first | |||||
| pixel ends up at texture coordinate (0, 1), and if the width and height are | |||||
| not powers-of-two, it will compensate by using a larger texture size. | |||||
| */ | |||||
| void loadARGBFlipped (const PixelARGB* pixels, int width, int height); | |||||
| /** Creates an alpha-channel texture from an array of alpha values. | |||||
| The width and height provided must be valid - i.e. power-of-two unless | |||||
| the underlying GL system allows otherwise. | |||||
| The data is sent directly to the OpenGL driver without being flipped vertically, | |||||
| so the first pixel will be mapped onto texture coordinate (0, 0). | |||||
| bottom-left corner of the texture | |||||
| */ | |||||
| void loadAlpha (const uint8* pixels, int width, int height); | |||||
| /** Frees the texture, if there is one. */ | /** Frees the texture, if there is one. */ | ||||
| void release(); | void release(); | ||||
| @@ -83,7 +109,7 @@ private: | |||||
| GLuint textureID; | GLuint textureID; | ||||
| int width, height; | int width, height; | ||||
| void create (int w, int h, const void*); | |||||
| void create (int w, int h, const void*, GLenum type); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture); | ||||
| }; | }; | ||||