From d1e4e9b9d0ffdd164aec98013c9a5b3b9f49aaeb Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 27 Oct 2011 16:40:25 +0100 Subject: [PATCH] OpenGL development (OpenGLRenderer now more-or-less works) --- modules/juce_graphics/images/juce_Image.cpp | 25 +- .../native/juce_RenderingHelpers.h | 2 +- modules/juce_gui_basics/juce_gui_basics.cpp | 4 + .../native/juce_mac_NSViewComponentPeer.mm | 128 +++++++++- .../native/juce_ios_OpenGLComponent.mm | 2 +- .../opengl/juce_OpenGLFrameBuffer.cpp | 21 +- .../opengl/juce_OpenGLGraphicsContext.cpp | 220 ++++++++++++------ .../opengl/juce_OpenGLGraphicsContext.h | 2 + .../juce_opengl/opengl/juce_OpenGLTexture.cpp | 76 +++--- 9 files changed, 342 insertions(+), 138 deletions(-) diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index f3e4b59074..e873713093 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -242,17 +242,28 @@ Image Image::convertedToFormat (PixelFormat newFormat) const for (int y = 0; y < h; ++y) { - const PixelARGB* src = (const PixelARGB*) srcData.getLinePointer(y); - uint8* dst = destData.getLinePointer (y); + const PixelARGB* const src = (const PixelARGB*) srcData.getLinePointer (y); + uint8* const dst = destData.getLinePointer (y); - for (int x = w; --x >= 0;) - { - *dst++ = src->getAlpha(); - ++src; - } + for (int x = 0; x < w; ++x) + dst[x] = src[x].getAlpha(); } } } + else if (image->format == SingleChannel && newFormat == Image::ARGB) + { + const BitmapData destData (newImage, 0, 0, w, h, BitmapData::writeOnly); + const BitmapData srcData (*this, 0, 0, w, h); + + for (int y = 0; y < h; ++y) + { + const PixelAlpha* const src = (const PixelAlpha*) srcData.getLinePointer (y); + PixelARGB* const dst = (PixelARGB*) destData.getLinePointer (y); + + for (int x = 0; x < w; ++x) + dst[x].set (src[x]); + } + } else { if (hasAlphaChannel()) diff --git a/modules/juce_graphics/native/juce_RenderingHelpers.h b/modules/juce_graphics/native/juce_RenderingHelpers.h index c1e96948dd..5ad206a16f 100644 --- a/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -345,7 +345,7 @@ struct FloatRectangleRasterisingInfo if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha); if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha); - callback (left, top, 1, bottom - top, 255); + callback (left, top, right - left, bottom - top, 255); } inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; } diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index 190516ec12..71ecfbb395 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -39,6 +39,10 @@ #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_gui_basics.h" +#if JUCE_MODULE_AVAILABLE_juce_opengl + #include "../juce_opengl/juce_opengl.h" +#endif + //============================================================================== #if JUCE_MAC #import diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 9775237825..d5c4069762 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -30,6 +30,10 @@ extern AppFocusChangeCallback appFocusChangeCallback; typedef bool (*CheckEventBlockedByModalComps) (NSEvent*); extern CheckEventBlockedByModalComps isEventBlockedByModalComps; +#if JUCE_MODULE_AVAILABLE_juce_opengl && ! defined (JUCE_OSX_OPENGL_RENDERER) + //#define JUCE_OSX_OPENGL_RENDERER 1 +#endif + //============================================================================== END_JUCE_NAMESPACE @@ -100,8 +104,6 @@ END_JUCE_NAMESPACE - (BOOL) resignFirstResponder; - (BOOL) acceptsFirstResponder; -- (void) asyncRepaint: (id) rect; - - (NSArray*) getSupportedDragTypes; - (BOOL) sendDragCallback: (int) type sender: (id ) sender; - (NSDragOperation) draggingEntered: (id ) sender; @@ -210,6 +212,10 @@ public: virtual bool isOpaque(); virtual void drawRect (NSRect r); + #if JUCE_OSX_OPENGL_RENDERER + virtual void drawOpenGL(); + #endif + virtual bool canBecomeKeyWindow(); virtual void becomeKeyWindow(); virtual bool windowShouldClose(); @@ -298,7 +304,7 @@ public: //============================================================================== NSWindow* window; JuceNSView* view; - bool isSharedWindow, fullScreen, insideDrawRect, usingCoreGraphics, recursiveToFrontCall; + bool isSharedWindow, fullScreen, insideDrawRect, usingCoreGraphics, usingOpenGL, recursiveToFrontCall; static ModifierKeys currentModifiers; static ComponentPeer* currentlyFocusedPeer; @@ -532,12 +538,6 @@ END_JUCE_NAMESPACE owner->viewMovedToWindow(); } -- (void) asyncRepaint: (id) rect -{ - NSRect* r = (NSRect*) [((NSData*) rect) bytes]; - [self setNeedsDisplayInRect: *r]; -} - //============================================================================== - (void) keyDown: (NSEvent*) ev { @@ -849,6 +849,49 @@ END_JUCE_NAMESPACE @end + +//============================================================================== +#if JUCE_OSX_OPENGL_RENDERER + +@interface JuceOpenGLLayer : CAOpenGLLayer +{ + NSViewComponentPeer* owner; +} + +- (JuceOpenGLLayer*) initWithPeer: (NSViewComponentPeer*) owner; +- (void) dealloc; + +- (void) drawInCGLContext: (CGLContextObj) glContext + pixelFormat: (CGLPixelFormatObj) pixelFormat + forLayerTime: (CFTimeInterval) timeInterval + displayTime: (const CVTimeStamp*) timeStamp; +@end + +@implementation JuceOpenGLLayer + +- (JuceOpenGLLayer*) initWithPeer: (NSViewComponentPeer*) owner_ +{ + [super init]; + owner = owner_; + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) drawInCGLContext: (CGLContextObj) glContext + pixelFormat: (CGLPixelFormatObj) pixelFormat + forLayerTime: (CFTimeInterval) timeInterval + displayTime: (const CVTimeStamp*) timeStamp +{ + owner->drawOpenGL(); +} + +@end +#endif + //============================================================================== //============================================================================== BEGIN_JUCE_NAMESPACE @@ -926,6 +969,7 @@ NSViewComponentPeer::NSViewComponentPeer (Component* const component_, #else usingCoreGraphics (false), #endif + usingOpenGL (false), recursiveToFrontCall (false) { appFocusChangeCallback = appFocusChanged; @@ -1051,7 +1095,13 @@ void NSViewComponentPeer::setBounds (int x, int y, int w, int h, bool isNowFullS if ([view frame].size.width != r.size.width || [view frame].size.height != r.size.height) - [view setNeedsDisplay: true]; + { + #if JUCE_OSX_OPENGL_RENDERER + if (usingOpenGL) + [[view layer] setNeedsDisplay: true]; + #endif + [view setNeedsDisplay: true]; + } [view setFrame: r]; } @@ -1061,6 +1111,11 @@ void NSViewComponentPeer::setBounds (int x, int y, int w, int h, bool isNowFullS [window setFrame: [window frameRectForContentRect: r] display: true]; + + #if JUCE_OSX_OPENGL_RENDERER + if (usingOpenGL) + [[view layer] setFrame: CGRectMake (0, 0, [view frame].size.width, [view frame].size.height)]; + #endif } } @@ -1611,6 +1666,21 @@ static void getClipRects (RectangleList& clip, NSView* view, roundToInt (rects[i].size.height)))); } +#if JUCE_OSX_OPENGL_RENDERER +void NSViewComponentPeer::drawOpenGL() +{ + if (! component->isOpaque()) + OpenGLHelpers::clear (Colours::transparentBlack); + + OpenGLRenderer context (OpenGLFrameBuffer::getCurrentFrameBufferTarget(), + component->getWidth(), component->getHeight()); + + insideDrawRect = true; + handlePaint (context); + insideDrawRect = false; +} +#endif + void NSViewComponentPeer::drawRect (NSRect r) { if (r.size.width < 1.0f || r.size.height < 1.0f) @@ -1672,16 +1742,44 @@ StringArray NSViewComponentPeer::getAvailableRenderingEngines() s.add ("CoreGraphics Renderer"); #endif + #if JUCE_OSX_OPENGL_RENDERER + s.add ("OpenGL Renderer"); + #endif + return s; } int NSViewComponentPeer::getCurrentRenderingEngine() const { + #if JUCE_OSX_OPENGL_RENDERER + if (usingOpenGL) return 2; + #endif + return usingCoreGraphics ? 1 : 0; } void NSViewComponentPeer::setCurrentRenderingEngine (int index) { + #if JUCE_OSX_OPENGL_RENDERER + if (index == 2) + { + usingCoreGraphics = false; + usingOpenGL = true; + + JuceOpenGLLayer* glLayer = [[JuceOpenGLLayer alloc] initWithPeer: this]; + [view setLayer: glLayer]; + [view setWantsLayer: YES]; + [glLayer release]; + } + else + { + usingOpenGL = false; + + [view setLayer: nil]; + [view setWantsLayer: NO]; + } + #endif + #if USE_COREGRAPHICS_RENDERING if (usingCoreGraphics != (index > 0)) { @@ -1802,8 +1900,14 @@ void NSViewComponentPeer::repaint (const Rectangle& area) } else { - [view setNeedsDisplayInRect: NSMakeRect ((CGFloat) area.getX(), [view frame].size.height - (CGFloat) area.getBottom(), - (CGFloat) area.getWidth(), (CGFloat) area.getHeight())]; + #if JUCE_OSX_OPENGL_RENDERER + if (usingOpenGL) + [[view layer] setNeedsDisplayInRect: CGRectMake ((CGFloat) area.getX(), [view frame].size.height - (CGFloat) area.getBottom(), + (CGFloat) area.getWidth(), (CGFloat) area.getHeight())]; + else + #endif + [view setNeedsDisplayInRect: NSMakeRect ((CGFloat) area.getX(), [view frame].size.height - (CGFloat) area.getBottom(), + (CGFloat) area.getWidth(), (CGFloat) area.getHeight())]; } } diff --git a/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm b/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm index 4654322e92..862a9e7812 100644 --- a/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm +++ b/modules/juce_opengl/native/juce_ios_OpenGLComponent.mm @@ -50,7 +50,7 @@ public: const GLESContext* const sharedContext, NSUInteger apiType) : component (component_), glLayer (nil), context (nil), - useDepthBuffer (pixelFormat_.depthBufferBits > 0), + useDepthBuffer (pixelFormat.depthBufferBits > 0), frameBufferHandle (0), colorBufferHandle (0), depthBufferHandle (0), lastWidth (0), lastHeight (0) { diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index c2066e3827..779881f069 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -30,6 +30,7 @@ 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, @@ -100,6 +101,7 @@ static void initialiseFrameBufferFunctions() #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 @@ -398,7 +400,6 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle #if JUCE_OPENGL_ES { - // GLES has no glDrawPixels function, so we have to create a texture and draw it.. glEnable (GL_TEXTURE_2D); glColor4f (1.0f, 1.0f, 1.0f, 1.0f); @@ -417,7 +418,23 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle OpenGLTexture tex; tex.load (data, area.getWidth(), area.getHeight()); - OpenGLHelpers::fillRectWithTexture (area.withSize (tex.getWidth(), tex.getHeight()), tex.getTextureID(), 1.0f); + 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); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); + + const int x = area.getX(); + const int texH = tex.getHeight(); + const int y = area.getY() - (texH - area.getHeight()); + const GLfloat x1 = (GLfloat) x; + const GLfloat y1 = (GLfloat) y; + const GLfloat x2 = (GLfloat) (x + tex.getWidth()); + const GLfloat y2 = (GLfloat) (y + texH); + const GLfloat vertices[] = { x1, y1, x2, y1, x1, y2, x2, y2 }; + const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f }; + + OpenGLHelpers::drawTriangleStrip (vertices, textureCoords, 4, tex.getTextureID()); } #endif diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index 2ab26e5325..0e36e5b685 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -25,16 +25,6 @@ BEGIN_JUCE_NAMESPACE -namespace -{ - void applyFlippedMatrix (int x, int y, int width, int height) - { - OpenGLHelpers::prepareFor2D (width, height); - OpenGLHelpers::applyTransform (AffineTransform::translation ((float) -x, (float) -y) - .followedBy (AffineTransform::verticalFlip ((float) height))); - } -} - struct OpenGLTarget { OpenGLTarget (GLuint frameBufferID_, int width_, int height_) noexcept @@ -69,6 +59,20 @@ struct OpenGLTarget OpenGLHelpers::enableScissorTest (r.withY (height - r.getBottom())); } + static void applyFlippedMatrix (const int x, const int y, const int width, const int height) + { + glMatrixMode (GL_PROJECTION); + glLoadIdentity(); + + #if JUCE_OPENGL_ES + glOrthof ((GLfloat) x, (GLfloat) (x + width), (GLfloat) (y + height), (GLfloat) y, 0.0f, 1.0f); + #else + glOrtho (x, x + width, y + height, y, 0, 1); + #endif + + glViewport (0, 0, width, height); + } + OpenGLFrameBuffer* frameBuffer; GLuint frameBufferID; int x, y, width, height; @@ -170,6 +174,11 @@ namespace 1.0f / textureHeight)); t.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); t.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); + + textureCoords[1] = 1.0f - textureCoords[1]; + textureCoords[3] = 1.0f - textureCoords[3]; + textureCoords[5] = 1.0f - textureCoords[5]; + textureCoords[7] = 1.0f - textureCoords[7]; } glVertexPointer (2, GL_FLOAT, 0, vertices); @@ -342,9 +351,6 @@ namespace void fillRectWithFillType (const OpenGLTarget& target, const Rectangle& rect, const FillType& fill, const bool replaceExistingContents) { - jassert (! fill.isInvisible()); - jassert (! fill.isColour()); - if (fill.isGradient()) { target.makeActiveFor2D(); @@ -370,13 +376,48 @@ namespace } } +//============================================================================== +class OpenGLRenderer::ScratchBufferManager +{ +public: + ScratchBufferManager() {} + + OpenGLFrameBuffer* get (int width, int height) + { + for (int i = 0; i < buffers.size(); ++i) + { + OpenGLFrameBuffer* b = buffers.getUnchecked(i); + + if (width <= b->getWidth() && height <= b->getHeight()) + return buffers.removeAndReturn (i); + } + + OpenGLFrameBuffer* b = new OpenGLFrameBuffer(); + b->initialise (width, height); + return b; + } + + void release (OpenGLFrameBuffer* buffer) + { + buffers.add (buffer); + + if (buffers.size() > 10) + buffers.remove (0); + } + +private: + OwnedArray buffers; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScratchBufferManager); +}; + class ClipRegion_Mask; //============================================================================== class ClipRegionBase : public SingleThreadedReferenceCountedObject { public: - ClipRegionBase() noexcept {} + ClipRegionBase (OpenGLRenderer::ScratchBufferManager& scratchBuffer_) noexcept : scratchBuffer (scratchBuffer_) {} virtual ~ClipRegionBase() {} typedef ReferenceCountedObjectPtr Ptr; @@ -394,6 +435,11 @@ public: virtual void fillAll (const OpenGLTarget&, const FillType& fill, bool replaceContents) = 0; virtual void fillRect (const OpenGLTarget&, const Rectangle& area, const FillType& fill, bool replaceContents) = 0; virtual void drawImage (const OpenGLTarget&, const OpenGLTextureFromImage&, float alpha, const Rectangle& targetArea) = 0; + + OpenGLRenderer::ScratchBufferManager& scratchBuffer; + +private: + JUCE_DECLARE_NON_COPYABLE (ClipRegionBase); }; //============================================================================== @@ -401,24 +447,31 @@ class ClipRegion_Mask : public ClipRegionBase { public: ClipRegion_Mask (const ClipRegion_Mask& other) - : clip (other.clip), - maskOrigin (other.maskOrigin) + : ClipRegionBase (other.scratchBuffer), + clip (other.clip), + maskOrigin (other.clip.getPosition()) { - const bool ok = mask.initialise (other.mask); - (void) ok; jassert (ok); + mask = scratchBuffer.get (clip.getWidth(), clip.getHeight()); + + OpenGLTarget m (*mask, maskOrigin); + m.makeActiveFor2D(); + glDisable (GL_BLEND); + setColour (1.0f); + drawFrameBuffer (*(other.mask), other.maskOrigin); } - explicit ClipRegion_Mask (const Rectangle& r) - : clip (r), + explicit ClipRegion_Mask (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) + : ClipRegionBase (scratchBuffer_), + clip (r), maskOrigin (r.getPosition()) { - const bool ok = mask.initialise (r.getWidth(), r.getHeight()); - (void) ok; jassert (ok); - mask.clear (Colours::white); + mask = scratchBuffer_.get (r.getWidth(), r.getHeight()); + mask->clear (Colours::white); } - explicit ClipRegion_Mask (const Rectangle& r) - : clip (r.getSmallestIntegerContainer()), + explicit ClipRegion_Mask (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) + : ClipRegionBase (scratchBuffer_), + clip (r.getSmallestIntegerContainer()), maskOrigin (clip.getPosition()) { initialiseClear(); @@ -431,27 +484,36 @@ public: fr.iterate (callback); } - explicit ClipRegion_Mask (const EdgeTable& e) - : clip (e.getMaximumBounds()), + explicit ClipRegion_Mask (const EdgeTable& e, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) + : ClipRegionBase (scratchBuffer_), + clip (e.getMaximumBounds()), maskOrigin (clip.getPosition()) { initialiseClear(); OpenGLHelpers::fillEdgeTable (e); } - ClipRegion_Mask (const Rectangle& bounds, const Path& p, const AffineTransform& transform, int oversamplingLevel) - : clip (bounds), maskOrigin (clip.getPosition()) + ClipRegion_Mask (OpenGLRenderer::ScratchBufferManager& scratchBuffer_, const Rectangle& bounds, + const Path& p, const AffineTransform& transform, int oversamplingLevel) + : ClipRegionBase (scratchBuffer_), + clip (bounds), maskOrigin (clip.getPosition()) { initialiseClear(); renderPath (p, transform, oversamplingLevel); } - static ClipRegion_Mask* createFromPath (Rectangle bounds, const Path& p, const AffineTransform& transform) + ~ClipRegion_Mask() + { + scratchBuffer.release (mask); + } + + static ClipRegion_Mask* createFromPath (OpenGLRenderer::ScratchBufferManager& scratchBuffer, Rectangle bounds, + const Path& p, const AffineTransform& transform) { bounds = bounds.getIntersection (p.getBoundsTransformed (transform).getSmallestIntegerContainer()); return bounds.isEmpty() ? nullptr - : new ClipRegion_Mask (bounds, p, transform, (int) defaultOversamplingLevel); + : new ClipRegion_Mask (scratchBuffer, bounds, p, transform, (int) defaultOversamplingLevel); } Ptr clone() const { return new ClipRegion_Mask (*this); } @@ -500,14 +562,14 @@ public: Ptr clipToPath (const Path& p, const AffineTransform& t) { - ClipRegion_Mask* tempMask = createFromPath (clip, p, t); + ClipRegion_Mask* tempMask = createFromPath (scratchBuffer, clip, p, t); const Ptr tempMaskPtr (tempMask); return tempMask == nullptr ? nullptr : clipToMask (tempMask); } Ptr clipToEdgeTable (const EdgeTable& et) { - ClipRegion_Mask* const tempMask = new ClipRegion_Mask (et); + ClipRegion_Mask* const tempMask = new ClipRegion_Mask (et, scratchBuffer); const Ptr tempMaskPtr (tempMask); return clipToMask (tempMask); } @@ -520,7 +582,7 @@ public: if (clip.isEmpty()) return nullptr; - clipFrameBuffers (OpenGLTarget (mask, maskOrigin), m->mask, m->maskOrigin); + clipFrameBuffers (OpenGLTarget (*mask, maskOrigin), *(m->mask), m->maskOrigin); return this; } @@ -529,9 +591,7 @@ public: makeMaskActive(); glEnable (GL_BLEND); glBlendFunc (GL_ZERO, GL_SRC_ALPHA); - glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); fillMaskWithSourceImage (image, transform); - glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); return this; } @@ -555,21 +615,22 @@ public: if (fill.isColour()) { target.makeActiveFor2D(); - setBlendMode (replaceContents); - OpenGLHelpers::setColour (fill.colour); + PixelARGB p (fill.colour.getARGB()); + p.premultiply(); + OpenGLHelpers::setColour (Colour (p.getARGB())); target.scissor (area); - drawFrameBuffer (mask, maskOrigin); + drawFrameBuffer (*mask, maskOrigin); glDisable (GL_SCISSOR_TEST); } else { - OpenGLFrameBuffer patternBuffer; - bool ok = patternBuffer.initialise (area.getWidth(), area.getHeight()); - (void) ok; jassert (ok); + OpenGLFrameBuffer* patternBuffer = scratchBuffer.get (area.getWidth(), area.getHeight()); - fillRectWithFillType (OpenGLTarget (patternBuffer, area.getPosition()), area, fill, true); - clipAndDraw (target, OpenGLTarget (patternBuffer, area.getPosition())); + fillRectWithFillType (OpenGLTarget (*patternBuffer, area.getPosition()), area, fill, true); + clipAndDraw (target, OpenGLTarget (*patternBuffer, area.getPosition()), area); + + scratchBuffer.release (patternBuffer); } } @@ -579,16 +640,16 @@ public: if (! bufferArea.isEmpty()) { - OpenGLFrameBuffer buffer; - bool ok = buffer.initialise (bufferArea.getWidth(), bufferArea.getHeight()); - (void) ok; jassert (ok); + OpenGLFrameBuffer* buffer = scratchBuffer.get (bufferArea.getWidth(), bufferArea.getHeight()); - OpenGLTarget bufferTarget (buffer, bufferArea.getPosition()); + OpenGLTarget bufferTarget (*buffer, bufferArea.getPosition()); bufferTarget.makeActiveFor2D(); glDisable (GL_BLEND); OpenGLHelpers::fillRectWithTexture (targetArea, source.textureID, alpha); - clipAndDraw (target, bufferTarget); + clipAndDraw (target, bufferTarget, bufferArea); + + scratchBuffer.release (buffer); } } @@ -603,22 +664,24 @@ public: target.makeActiveFor2D(); setColour (alpha); glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - drawFrameBuffer (mask, maskOrigin); + target.scissor (clip); + drawFrameBuffer (*mask, maskOrigin); + glDisable (GL_SCISSOR_TEST); } private: - OpenGLFrameBuffer mask; + OpenGLFrameBuffer* mask; Rectangle clip; Point maskOrigin; void prepareFor2D() const { - applyFlippedMatrix (maskOrigin.getX(), maskOrigin.getY(), mask.getWidth(), mask.getHeight()); + OpenGLTarget::applyFlippedMatrix (maskOrigin.getX(), maskOrigin.getY(), mask->getWidth(), mask->getHeight()); } void makeMaskActive() { - const bool b = mask.makeCurrentRenderingTarget(); + const bool b = mask->makeCurrentRenderingTarget(); (void) b; jassert (b); prepareFor2D(); } @@ -626,9 +689,8 @@ private: void initialiseClear() { jassert (! clip.isEmpty()); - bool ok = mask.initialise (clip.getWidth(), clip.getHeight()); - mask.makeCurrentAndClear(); - (void) ok; jassert (ok); + mask = scratchBuffer.get (clip.getWidth(), clip.getHeight()); + mask->makeCurrentAndClear(); glDisable (GL_TEXTURE_2D); glDisable (GL_BLEND); prepareFor2D(); @@ -650,16 +712,18 @@ private: } }; - void clipAndDraw (const OpenGLTarget& target, const OpenGLTarget& buffer) + void clipAndDraw (const OpenGLTarget& target, const OpenGLTarget& buffer, const Rectangle& clip) { - clipFrameBuffers (buffer, mask, maskOrigin); + clipFrameBuffers (buffer, *mask, maskOrigin); target.makeActiveFor2D(); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); setColour (1.0f); + target.scissor (clip); drawFrameBuffer (*buffer.frameBuffer, Point (buffer.x, buffer.y)); + glDisable (GL_SCISSOR_TEST); } void drawFrameBuffer (const OpenGLFrameBuffer& buffer, const Point& topLeft) @@ -679,8 +743,8 @@ private: const GLfloat l = (GLfloat) maskOrigin.getX(); const GLfloat t = (GLfloat) maskOrigin.getY(); - const GLfloat r = (GLfloat) (maskOrigin.getX() + mask.getWidth()); - const GLfloat b = (GLfloat) (maskOrigin.getY() + mask.getHeight()); + const GLfloat r = (GLfloat) (maskOrigin.getX() + mask->getWidth()); + const GLfloat b = (GLfloat) (maskOrigin.getY() + mask->getHeight()); const GLfloat vertices[] = { l, t, r, t, l, b, r, b }; GLfloat textureCoords[] = { l, t, r, t, l, b, r, b }; @@ -706,11 +770,12 @@ private: class ClipRegion_Rectangle : public ClipRegionBase { public: - explicit ClipRegion_Rectangle (const Rectangle& r) noexcept - : clip (r) + explicit ClipRegion_Rectangle (const Rectangle& r, OpenGLRenderer::ScratchBufferManager& scratchBuffer_) noexcept + : ClipRegionBase (scratchBuffer_), + clip (r) {} - Ptr clone() const { return new ClipRegion_Rectangle (clip); } + Ptr clone() const { return new ClipRegion_Rectangle (clip, scratchBuffer); } const Rectangle& getClipBounds() const { return clip; } Ptr applyClipTo (const Ptr& target) { return target->clipToRectangle (clip); } @@ -784,7 +849,7 @@ private: Ptr toMask() const { - return new ClipRegion_Mask (clip); + return new ClipRegion_Mask (clip, scratchBuffer); } ClipRegion_Rectangle& operator= (const ClipRegion_Rectangle&); @@ -795,8 +860,8 @@ private: class OpenGLRenderer::SavedState { public: - SavedState (const OpenGLTarget& target_) - : clip (new ClipRegion_Rectangle (Rectangle (target_.width, target_.height))), + SavedState (const OpenGLTarget& target_, ScratchBufferManager& scratchBuffer) + : clip (new ClipRegion_Rectangle (Rectangle (target_.width, target_.height), scratchBuffer)), transform (0, 0), interpolationQuality (Graphics::mediumResamplingQuality), target (target_), transparencyLayerAlpha (1.0f) { @@ -965,7 +1030,7 @@ public: if (transform.isOnlyTranslated) { fillShape (new ClipRegion_Mask (r.translated ((float) transform.xOffset, - (float) transform.yOffset)), false); + (float) transform.yOffset), clip->scratchBuffer), false); } else { @@ -980,7 +1045,7 @@ public: { if (clip != nullptr) { - ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->getClipBounds(), path, + ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->scratchBuffer, clip->getClipBounds(), path, transform.getTransformWith (t)); if (m != nullptr) @@ -1008,7 +1073,7 @@ public: .followedBy (t)))); if (et != nullptr) - fillShape (new ClipRegion_Mask (*et), false); + fillShape (new ClipRegion_Mask (*et, clip->scratchBuffer), false); } } } @@ -1017,7 +1082,7 @@ public: { EdgeTable et2 (et); et2.translate (x, y); - fillShape (new ClipRegion_Mask (et2), false); + fillShape (new ClipRegion_Mask (et2, clip->scratchBuffer), false); } void drawLine (const Line & line) @@ -1069,7 +1134,7 @@ public: Path p; p.addRectangle (image.getBounds()); - ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->getClipBounds(), p, t); + ClipRegion_Mask* m = ClipRegion_Mask::createFromPath (clip->scratchBuffer, clip->getClipBounds(), p, t); if (m != nullptr) { @@ -1152,20 +1217,23 @@ private: //============================================================================== OpenGLRenderer::OpenGLRenderer (OpenGLComponent& target) - : stack (new SavedState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()))) + : scratchBufferManager (new ScratchBufferManager()), + stack (new SavedState (OpenGLTarget (target.getFrameBufferID(), target.getWidth(), target.getHeight()), *scratchBufferManager)) { target.makeCurrentRenderingTarget(); } OpenGLRenderer::OpenGLRenderer (OpenGLFrameBuffer& target) - : stack (new SavedState (OpenGLTarget (target, Point()))) + : scratchBufferManager (new ScratchBufferManager()), + stack (new SavedState (OpenGLTarget (target, Point()), *scratchBufferManager)) { // This object can only be created and used when the current thread has an active OpenGL context. jassert (OpenGLHelpers::isContextActive()); } OpenGLRenderer::OpenGLRenderer (unsigned int frameBufferID, int width, int height) - : stack (new SavedState (OpenGLTarget (frameBufferID, width, height))) + : scratchBufferManager (new ScratchBufferManager()), + stack (new SavedState (OpenGLTarget (frameBufferID, width, height), *scratchBufferManager)) { // This object can only be created and used when the current thread has an active OpenGL context. jassert (OpenGLHelpers::isContextActive()); @@ -1195,8 +1263,8 @@ void OpenGLRenderer::setInterpolationQuality (Graphics::ResamplingQuality qualit void OpenGLRenderer::fillRect (const Rectangle& r, bool replace) { stack->fillRect (r, replace); } void OpenGLRenderer::fillPath (const Path& path, const AffineTransform& t) { stack->fillPath (path, t); } void OpenGLRenderer::drawImage (const Image& im, const AffineTransform& t) { stack->drawImage (im, t); } -void OpenGLRenderer::drawVerticalLine (int x, float top, float bottom) { stack->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } -void OpenGLRenderer::drawHorizontalLine (int y, float left, float right) { stack->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } +void OpenGLRenderer::drawVerticalLine (int x, float top, float bottom) { if (top < bottom) stack->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } +void OpenGLRenderer::drawHorizontalLine (int y, float left, float right) { if (left < right) stack->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } void OpenGLRenderer::drawGlyph (int glyphNumber, const AffineTransform& t) { stack->drawGlyph (glyphNumber, t); } void OpenGLRenderer::drawLine (const Line & line) { stack->drawLine (line); } void OpenGLRenderer::setFont (const Font& newFont) { stack->font = newFont; } diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h index e4b8bbfb33..2aa0d53f4c 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.h @@ -75,9 +75,11 @@ public: #ifndef DOXYGEN class SavedState; + class ScratchBufferManager; #endif private: + ScopedPointer scratchBufferManager; RenderingHelpers::SavedStateStack stack; }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 4891a613f6..1434acee88 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -75,6 +75,27 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels) GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); } +template +struct Flipper +{ + static void flip (HeapBlock& dataCopy, const uint8* srcData, const int lineStride, + const int w, const int h, const int textureW, const int textureH) + { + dataCopy.malloc (textureW * textureH); + + for (int y = 0; y < h; ++y) + { + const PixelType* src = (const PixelType*) srcData; + PixelARGB* const dst = (PixelARGB*) (dataCopy + textureW * (textureH - 1 - y)); + + for (int x = 0; x < w; ++x) + dst[x].set (src[x]); + + srcData += lineStride; + } + } +}; + void OpenGLTexture::load (const Image& image) { const int imageW = image.getWidth(); @@ -82,59 +103,36 @@ void OpenGLTexture::load (const Image& image) const int textureW = nextPowerOfTwo (imageW); const int textureH = nextPowerOfTwo (imageH); - Image::BitmapData srcData (image, Image::BitmapData::readOnly); - const PixelARGB* data = (const PixelARGB*) srcData.data; HeapBlock dataCopy; + Image::BitmapData srcData (image, Image::BitmapData::readOnly); - if (srcData.pixelFormat != Image::ARGB - || textureW != imageW - || textureH != imageH - || srcData.lineStride != imageW * srcData.pixelStride) + switch (srcData.pixelFormat) { - const int srcLineStride = (srcData.pixelStride * imageW + 3) & ~3; - dataCopy.malloc (textureW * textureH); - data = dataCopy; - const int yOffset = textureH - imageH; - - if (srcData.pixelFormat == Image::RGB) - { - for (int y = 0; y < imageH; ++y) - { - const PixelRGB* const src = (const PixelRGB*) addBytesToPointer (srcData.data, srcLineStride * y); - PixelARGB* const dst = (PixelARGB*) (dataCopy + textureW * (y + yOffset)); - - for (int x = 0; x < imageW; ++x) - dst[x].set (src[x]); - } - } - else if (srcData.pixelFormat == Image::ARGB) - { - for (int y = 0; y < imageH; ++y) - memcpy (dataCopy + textureW * (y + yOffset), addBytesToPointer (srcData.data, srcLineStride * y), srcLineStride); - } + case Image::ARGB: Flipper ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break; + case Image::RGB: Flipper ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break; + case Image::SingleChannel: Flipper::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break; + default: break; } - create (textureW, textureH, data); + create (textureW, textureH, dataCopy); } void OpenGLTexture::load (const PixelARGB* pixels, const int w, const int h) { const int textureW = nextPowerOfTwo (w); - const int textureH = nextPowerOfTwo (h); - - HeapBlock dataCopy; - if (textureW != w || textureH != h) + if (h == 1 && textureW == w) { - dataCopy.malloc (textureW * textureH); - - for (int y = 0; y < h; ++y) - memcpy (dataCopy + textureW * (y + textureH - h), pixels + w * y, w * 4); - - pixels = dataCopy; + create (w, 1, pixels); } + else + { + const int textureH = nextPowerOfTwo (h); - create (textureW, textureH, pixels); + HeapBlock dataCopy; + Flipper::flip (dataCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH); + create (textureW, textureH, dataCopy); + } } void OpenGLTexture::release()