mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
New internal class CachedComponentImage, which is used internally to manage component buffering. I've used this to rebuild the OpenGLComponent's rendering algorithm so that it now supports sub-components and can have 2D content drawn in its paint method. Updated the openGL demo to show this in action.
This commit is contained in:
parent
f29aa4c468
commit
ea6df1c8f1
43 changed files with 3570 additions and 2496 deletions
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__
|
||||
#define __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__
|
||||
|
||||
class Component;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class used internally for structures that can store cached images of
|
||||
component state.
|
||||
|
||||
Most people are unlikely to ever need to know about this class - it's really
|
||||
only for power-users!
|
||||
|
||||
@see Component::setCachedComponentImage
|
||||
*/
|
||||
class JUCE_API CachedComponentImage
|
||||
{
|
||||
public:
|
||||
CachedComponentImage() noexcept {}
|
||||
virtual ~CachedComponentImage() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Called as part of the parent component's paint method, this must draw
|
||||
the given component into the target graphics context, using the cached
|
||||
version where possible.
|
||||
*/
|
||||
virtual void paint (Graphics&) = 0;
|
||||
|
||||
/** Invalidates all cached image data. */
|
||||
virtual void invalidateAll() = 0;
|
||||
|
||||
/** Invalidates a section of the cached image data. */
|
||||
virtual void invalidate (const Rectangle<int>& area) = 0;
|
||||
|
||||
/** Called to indicate that the component is no longer active, so
|
||||
any cached data should be released if possible.
|
||||
*/
|
||||
virtual void releaseResources() = 0;
|
||||
};
|
||||
|
||||
#endif // __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__
|
||||
|
|
@ -35,7 +35,7 @@ Component* Component::currentlyFocusedComponent = nullptr;
|
|||
class Component::MouseListenerList
|
||||
{
|
||||
public:
|
||||
MouseListenerList()
|
||||
MouseListenerList() noexcept
|
||||
: numDeepMouseListeners (0)
|
||||
{
|
||||
}
|
||||
|
|
@ -377,6 +377,73 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class StandardCachedComponentImage : public CachedComponentImage
|
||||
{
|
||||
public:
|
||||
StandardCachedComponentImage (Component& owner_) noexcept : owner (owner_) {}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
const Rectangle<int> bounds (owner.getLocalBounds());
|
||||
|
||||
if (image.isNull() || image.getBounds() != bounds)
|
||||
{
|
||||
image = Image (owner.isOpaque() ? Image::RGB : Image::ARGB,
|
||||
jmax (1, bounds.getWidth()), jmax (1, bounds.getHeight()), ! owner.isOpaque());
|
||||
|
||||
validArea.clear();
|
||||
}
|
||||
|
||||
{
|
||||
Graphics imG (image);
|
||||
LowLevelGraphicsContext* const lg = imG.getInternalContext();
|
||||
|
||||
for (RectangleList::Iterator i (validArea); i.next();)
|
||||
lg->excludeClipRectangle (*i.getRectangle());
|
||||
|
||||
if (! lg->isClipEmpty())
|
||||
{
|
||||
if (! owner.isOpaque())
|
||||
{
|
||||
lg->setFill (Colours::transparentBlack);
|
||||
lg->fillRect (bounds, true);
|
||||
lg->setFill (Colours::black);
|
||||
}
|
||||
|
||||
owner.paintEntireComponent (imG, true);
|
||||
}
|
||||
}
|
||||
|
||||
validArea = bounds;
|
||||
|
||||
g.setColour (Colours::black.withAlpha (owner.getAlpha()));
|
||||
g.drawImageAt (image, 0, 0);
|
||||
}
|
||||
|
||||
void invalidateAll()
|
||||
{
|
||||
validArea.clear();
|
||||
}
|
||||
|
||||
void invalidate (const Rectangle<int>& area)
|
||||
{
|
||||
validArea.subtract (area);
|
||||
}
|
||||
|
||||
void releaseResources()
|
||||
{
|
||||
image = Image::null;
|
||||
}
|
||||
|
||||
private:
|
||||
Image image;
|
||||
RectangleList validArea;
|
||||
Component& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Component::Component()
|
||||
|
|
@ -461,16 +528,21 @@ void Component::setVisible (bool shouldBeVisible)
|
|||
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
|
||||
CHECK_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
WeakReference<Component> safePointer (this);
|
||||
|
||||
const WeakReference<Component> safePointer (this);
|
||||
flags.visibleFlag = shouldBeVisible;
|
||||
|
||||
internalRepaint (0, 0, getWidth(), getHeight());
|
||||
if (shouldBeVisible)
|
||||
repaint();
|
||||
else
|
||||
repaintParent();
|
||||
|
||||
sendFakeMouseMove();
|
||||
|
||||
if (! shouldBeVisible)
|
||||
{
|
||||
if (cachedImage != nullptr)
|
||||
cachedImage->releaseResources();
|
||||
|
||||
if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
if (parentComponent != nullptr)
|
||||
|
|
@ -567,7 +639,7 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
|
|||
|
||||
if (styleWanted != currentStyleFlags || ! flags.hasHeavyweightPeerFlag)
|
||||
{
|
||||
WeakReference<Component> safePointer (this);
|
||||
const WeakReference<Component> safePointer (this);
|
||||
|
||||
#if JUCE_LINUX
|
||||
// it's wise to give the component a non-zero size before
|
||||
|
|
@ -701,12 +773,27 @@ bool Component::isOpaque() const noexcept
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void Component::setCachedComponentImage (CachedComponentImage* newCachedImage)
|
||||
{
|
||||
cachedImage = newCachedImage;
|
||||
}
|
||||
|
||||
void Component::setBufferedToImage (const bool shouldBeBuffered)
|
||||
{
|
||||
if (shouldBeBuffered != flags.bufferToImageFlag)
|
||||
// This assertion means that this component is already using a custom CachedComponentImage,
|
||||
// so by calling setBufferedToImage, you'll be deleting the custom one - this is almost certainly
|
||||
// not what you wanted to happen... If you really do know what you're doing here, and want to
|
||||
// avoid this assertion, just call setCachedComponentImage (nullptr) before setBufferedToImage().
|
||||
jassert (cachedImage == nullptr || dynamic_cast <StandardCachedComponentImage*> (cachedImage.get()) != nullptr);
|
||||
|
||||
if (shouldBeBuffered)
|
||||
{
|
||||
bufferedImage = Image::null;
|
||||
flags.bufferToImageFlag = shouldBeBuffered;
|
||||
if (cachedImage == nullptr)
|
||||
cachedImage = new StandardCachedComponentImage (*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
cachedImage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -935,17 +1022,17 @@ Rectangle<int> Component::localAreaToGlobal (const Rectangle<int>& area) const
|
|||
}
|
||||
|
||||
/* Deprecated methods... */
|
||||
const Point<int> Component::relativePositionToGlobal (const Point<int>& relativePosition) const
|
||||
Point<int> Component::relativePositionToGlobal (const Point<int>& relativePosition) const
|
||||
{
|
||||
return localPointToGlobal (relativePosition);
|
||||
}
|
||||
|
||||
const Point<int> Component::globalPositionToRelative (const Point<int>& screenPosition) const
|
||||
Point<int> Component::globalPositionToRelative (const Point<int>& screenPosition) const
|
||||
{
|
||||
return getLocalPoint (nullptr, screenPosition);
|
||||
}
|
||||
|
||||
const Point<int> Component::relativePositionToOtherComponent (const Component* const targetComponent, const Point<int>& positionRelativeToThis) const
|
||||
Point<int> Component::relativePositionToOtherComponent (const Component* const targetComponent, const Point<int>& positionRelativeToThis) const
|
||||
{
|
||||
return targetComponent == nullptr ? localPointToGlobal (positionRelativeToThis)
|
||||
: targetComponent->getLocalPoint (this, positionRelativeToThis);
|
||||
|
|
@ -991,9 +1078,9 @@ void Component::setBounds (const int x, const int y, int w, int h)
|
|||
else if (! flags.hasHeavyweightPeerFlag)
|
||||
repaintParent();
|
||||
}
|
||||
else
|
||||
else if (cachedImage != nullptr)
|
||||
{
|
||||
bufferedImage = Image::null;
|
||||
cachedImage->invalidateAll();
|
||||
}
|
||||
|
||||
if (flags.hasHeavyweightPeerFlag)
|
||||
|
|
@ -1375,12 +1462,17 @@ Component* Component::removeChildComponent (const int index, bool sendParentEven
|
|||
if (sendParentEvents)
|
||||
{
|
||||
sendFakeMouseMove();
|
||||
child->repaintParent();
|
||||
|
||||
if (child->isVisible())
|
||||
child->repaintParent();
|
||||
}
|
||||
|
||||
childComponentList.remove (index);
|
||||
child->parentComponent = nullptr;
|
||||
|
||||
if (child->cachedImage != nullptr)
|
||||
child->cachedImage->releaseResources();
|
||||
|
||||
// (NB: there are obscure situations where child->isShowing() = false, but it still has the focus)
|
||||
if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
|
|
@ -1702,112 +1794,76 @@ float Component::getAlpha() const
|
|||
return (255 - componentTransparency) / 255.0f;
|
||||
}
|
||||
|
||||
void Component::repaintParent()
|
||||
{
|
||||
if (flags.visibleFlag)
|
||||
internalRepaint (0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Component::repaint()
|
||||
{
|
||||
repaint (0, 0, getWidth(), getHeight());
|
||||
internalRepaintUnchecked (getLocalBounds(), true);
|
||||
}
|
||||
|
||||
void Component::repaint (const int x, const int y,
|
||||
const int w, const int h)
|
||||
void Component::repaint (const int x, const int y, const int w, const int h)
|
||||
{
|
||||
bufferedImage = Image::null;
|
||||
|
||||
if (flags.visibleFlag)
|
||||
internalRepaint (x, y, w, h);
|
||||
internalRepaint (Rectangle<int> (x, y, w, h));
|
||||
}
|
||||
|
||||
void Component::repaint (const Rectangle<int>& area)
|
||||
{
|
||||
repaint (area.getX(), area.getY(), area.getWidth(), area.getHeight());
|
||||
internalRepaint (area);
|
||||
}
|
||||
|
||||
void Component::internalRepaint (int x, int y, int w, int h)
|
||||
void Component::repaintParent()
|
||||
{
|
||||
if (parentComponent != nullptr)
|
||||
parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, getLocalBounds()));
|
||||
}
|
||||
|
||||
void Component::internalRepaint (const Rectangle<int>& area)
|
||||
{
|
||||
const Rectangle<int> r (area.getIntersection (getLocalBounds()));
|
||||
|
||||
if (! r.isEmpty())
|
||||
internalRepaintUnchecked (r, false);
|
||||
}
|
||||
|
||||
void Component::internalRepaintUnchecked (const Rectangle<int>& area, const bool isEntireComponent)
|
||||
{
|
||||
// if component methods are being called from threads other than the message
|
||||
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
|
||||
CHECK_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
if (x < 0)
|
||||
if (flags.visibleFlag)
|
||||
{
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (x + w > getWidth())
|
||||
w = getWidth() - x;
|
||||
|
||||
if (w > 0)
|
||||
{
|
||||
if (y < 0)
|
||||
if (flags.hasHeavyweightPeerFlag)
|
||||
{
|
||||
h += y;
|
||||
y = 0;
|
||||
ComponentPeer* const peer = getPeer();
|
||||
|
||||
if (peer != nullptr)
|
||||
peer->repaint (area);
|
||||
}
|
||||
|
||||
if (y + h > getHeight())
|
||||
h = getHeight() - y;
|
||||
|
||||
if (h > 0)
|
||||
else
|
||||
{
|
||||
if (parentComponent != nullptr)
|
||||
if (cachedImage != nullptr)
|
||||
{
|
||||
if (parentComponent->flags.visibleFlag)
|
||||
{
|
||||
if (affineTransform == nullptr)
|
||||
{
|
||||
parentComponent->internalRepaint (x + getX(), y + getY(), w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Rectangle<int> r (ComponentHelpers::convertToParentSpace (*this, Rectangle<int> (x, y, w, h)));
|
||||
parentComponent->internalRepaint (r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||
}
|
||||
}
|
||||
if (isEntireComponent)
|
||||
cachedImage->invalidateAll();
|
||||
else
|
||||
cachedImage->invalidate (area);
|
||||
}
|
||||
else if (flags.hasHeavyweightPeerFlag)
|
||||
{
|
||||
ComponentPeer* const peer = getPeer();
|
||||
|
||||
if (peer != nullptr)
|
||||
peer->repaint (Rectangle<int> (x, y, w, h));
|
||||
}
|
||||
if (parentComponent != nullptr)
|
||||
parentComponent->internalRepaint (ComponentHelpers::convertToParentSpace (*this, area));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Component::paintComponent (Graphics& g)
|
||||
{
|
||||
if (flags.bufferToImageFlag)
|
||||
{
|
||||
if (bufferedImage.isNull())
|
||||
{
|
||||
bufferedImage = Image (flags.opaqueFlag ? Image::RGB : Image::ARGB,
|
||||
getWidth(), getHeight(), ! flags.opaqueFlag);
|
||||
|
||||
Graphics imG (bufferedImage);
|
||||
paint (imG);
|
||||
}
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.drawImageAt (bufferedImage, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
paint (g);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::paintWithinParentContext (Graphics& g)
|
||||
{
|
||||
g.setOrigin (getX(), getY());
|
||||
paintEntireComponent (g, false);
|
||||
|
||||
if (cachedImage != nullptr)
|
||||
cachedImage->paint (g);
|
||||
else
|
||||
paintEntireComponent (g, false);
|
||||
}
|
||||
|
||||
void Component::paintComponentAndChildren (Graphics& g)
|
||||
|
|
@ -1816,7 +1872,7 @@ void Component::paintComponentAndChildren (Graphics& g)
|
|||
|
||||
if (flags.dontClipGraphicsFlag)
|
||||
{
|
||||
paintComponent (g);
|
||||
paint (g);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1824,7 +1880,7 @@ void Component::paintComponentAndChildren (Graphics& g)
|
|||
ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point<int>());
|
||||
|
||||
if (! g.isClipEmpty())
|
||||
paintComponent (g);
|
||||
paint (g);
|
||||
|
||||
g.restoreState();
|
||||
}
|
||||
|
|
@ -1884,8 +1940,6 @@ void Component::paintComponentAndChildren (Graphics& g)
|
|||
|
||||
void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel)
|
||||
{
|
||||
jassert (! g.isClipEmpty());
|
||||
|
||||
#if JUCE_DEBUG
|
||||
flags.isInsidePaintCall = true;
|
||||
#endif
|
||||
|
|
@ -1990,7 +2044,7 @@ void Component::sendLookAndFeelChange()
|
|||
{
|
||||
repaint();
|
||||
|
||||
WeakReference<Component> safePointer (this);
|
||||
const WeakReference<Component> safePointer (this);
|
||||
|
||||
lookAndFeelChanged();
|
||||
|
||||
|
|
@ -2592,7 +2646,7 @@ void Component::focusLost (FocusChangeType)
|
|||
|
||||
void Component::internalFocusLoss (const FocusChangeType cause)
|
||||
{
|
||||
WeakReference<Component> safePointer (this);
|
||||
const WeakReference<Component> safePointer (this);
|
||||
|
||||
focusLost (focusChangedDirectly);
|
||||
|
||||
|
|
@ -2645,7 +2699,7 @@ void Component::setEnabled (const bool shouldBeEnabled)
|
|||
|
||||
void Component::sendEnablementChangeMessage()
|
||||
{
|
||||
WeakReference<Component> safePointer (this);
|
||||
const WeakReference<Component> safePointer (this);
|
||||
|
||||
enablementChanged();
|
||||
|
||||
|
|
@ -2731,14 +2785,12 @@ void Component::takeKeyboardFocus (const FocusChangeType cause)
|
|||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
WeakReference<Component> safePointer (this);
|
||||
|
||||
const WeakReference<Component> safePointer (this);
|
||||
peer->grabFocus();
|
||||
|
||||
if (peer->isFocused() && currentlyFocusedComponent != this)
|
||||
{
|
||||
WeakReference<Component> componentLosingFocus (currentlyFocusedComponent);
|
||||
|
||||
currentlyFocusedComponent = this;
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
|
|
@ -2827,7 +2879,7 @@ void Component::moveKeyboardFocusToSibling (const bool moveToNext)
|
|||
{
|
||||
if (nextComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
WeakReference<Component> nextCompPointer (nextComp);
|
||||
const WeakReference<Component> nextCompPointer (nextComp);
|
||||
internalModalInputAttempt();
|
||||
|
||||
if (nextCompPointer == nullptr || nextComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ class MouseInputSourceInternal;
|
|||
class ComponentPeer;
|
||||
class MarkerList;
|
||||
class RelativeRectangle;
|
||||
class CachedComponentImage;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
|
@ -958,8 +960,9 @@ public:
|
|||
There's no guarantee about how soon after calling repaint() the redraw will actually
|
||||
happen, and other queued events may be delivered before a redraw is done.
|
||||
|
||||
If the setBufferedToImage() method has been used to cause this component
|
||||
to use a buffer, the repaint() call will invalidate the component's buffer.
|
||||
If the setBufferedToImage() method has been used to cause this component to use a
|
||||
buffer, the repaint() call will invalidate the cached buffer. If setCachedComponentImage()
|
||||
has been used to provide a custom image cache, that cache will be invalidated appropriately.
|
||||
|
||||
To redraw just a subsection of the component rather than the whole thing,
|
||||
use the repaint (int, int, int, int) method.
|
||||
|
|
@ -2227,12 +2230,24 @@ public:
|
|||
*/
|
||||
void setPositioner (Positioner* newPositioner);
|
||||
|
||||
/** Gives the component a CachedComponentImage that should be used to buffer its painting.
|
||||
The object that is passed-in will be owned by this component, and will be deleted automatically
|
||||
later on.
|
||||
@see setBufferedToImage
|
||||
*/
|
||||
void setCachedComponentImage (CachedComponentImage* newCachedImage);
|
||||
|
||||
/** Returns the object that was set by setCachedComponentImage().
|
||||
@see setCachedComponentImage
|
||||
*/
|
||||
CachedComponentImage* getCachedComponentImage() const noexcept { return cachedImage; }
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// These methods are deprecated - use localPointToGlobal, getLocalPoint, getLocalPoint, etc instead.
|
||||
JUCE_DEPRECATED (const Point<int> relativePositionToGlobal (const Point<int>&) const);
|
||||
JUCE_DEPRECATED (const Point<int> globalPositionToRelative (const Point<int>&) const);
|
||||
JUCE_DEPRECATED (const Point<int> relativePositionToOtherComponent (const Component*, const Point<int>&) const);
|
||||
JUCE_DEPRECATED (Point<int> relativePositionToGlobal (const Point<int>&) const);
|
||||
JUCE_DEPRECATED (Point<int> globalPositionToRelative (const Point<int>&) const);
|
||||
JUCE_DEPRECATED (Point<int> relativePositionToOtherComponent (const Component*, const Point<int>&) const);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
|
@ -2254,7 +2269,7 @@ private:
|
|||
LookAndFeel* lookAndFeel;
|
||||
MouseCursor cursor;
|
||||
ImageEffectFilter* effect;
|
||||
Image bufferedImage;
|
||||
ScopedPointer <CachedComponentImage> cachedImage;
|
||||
|
||||
class MouseListenerList;
|
||||
friend class MouseListenerList;
|
||||
|
|
@ -2315,10 +2330,11 @@ private:
|
|||
void internalModifierKeysChanged();
|
||||
void internalChildrenChanged();
|
||||
void internalHierarchyChanged();
|
||||
void internalRepaint (const Rectangle<int>&);
|
||||
void internalRepaintUnchecked (const Rectangle<int>&, bool);
|
||||
Component* removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents);
|
||||
void moveChildInternal (int sourceIndex, int destIndex);
|
||||
void paintComponentAndChildren (Graphics&);
|
||||
void paintComponent (Graphics&);
|
||||
void paintWithinParentContext (Graphics&);
|
||||
void sendMovedResizedMessages (bool wasMoved, bool wasResized);
|
||||
void repaintParent();
|
||||
|
|
@ -2353,8 +2369,6 @@ private:
|
|||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
virtual void internalRepaint (int x, int y, int w, int h);
|
||||
/** @internal */
|
||||
virtual ComponentPeer* createNewPeer (int styleFlags, void* nativeWindowToAttachTo);
|
||||
#endif
|
||||
};
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ BEGIN_JUCE_NAMESPACE
|
|||
// START_AUTOINCLUDE components, mouse, keyboard, buttons, drawables,
|
||||
// filebrowser, layout, lookandfeel, menus, positioning, properties,
|
||||
// widgets, windows, commands, application, misc
|
||||
#ifndef __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__
|
||||
#include "components/juce_CachedComponentImage.h"
|
||||
#endif
|
||||
#ifndef __JUCE_COMPONENT_JUCEHEADER__
|
||||
#include "components/juce_Component.h"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -34,11 +34,6 @@ void* OpenGLComponent::getNativeWindowHandle() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
|
||||
{
|
||||
Component::internalRepaint (x, y, w, h);
|
||||
}
|
||||
|
||||
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>&)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,8 @@ public:
|
|||
glGenRenderbuffersOES (1, &depthBufferHandle);
|
||||
|
||||
glBindRenderbufferOES (GL_RENDERBUFFER_OES, colorBufferHandle);
|
||||
[context renderbufferStorage: GL_RENDERBUFFER_OES fromDrawable: glLayer];
|
||||
bool ok = [context renderbufferStorage: GL_RENDERBUFFER_OES fromDrawable: glLayer];
|
||||
jassert (ok);
|
||||
|
||||
GLint width, height;
|
||||
glGetRenderbufferParameterivOES (GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
|
||||
|
|
@ -178,6 +179,8 @@ public:
|
|||
|
||||
void freeGLBuffers()
|
||||
{
|
||||
[context renderbufferStorage: GL_RENDERBUFFER_OES fromDrawable: nil];
|
||||
|
||||
if (frameBufferHandle != 0)
|
||||
{
|
||||
glDeleteFramebuffersOES (1, &frameBufferHandle);
|
||||
|
|
@ -197,7 +200,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
WeakReference<Component> component;
|
||||
JuceGLView* view;
|
||||
|
|
@ -208,7 +210,6 @@ private:
|
|||
int numFrames;
|
||||
int lastWidth, lastHeight;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GLESContext);
|
||||
};
|
||||
|
||||
|
|
@ -221,16 +222,12 @@ OpenGLContext* OpenGLComponent::createContext()
|
|||
if (peer != nullptr)
|
||||
return new GLESContext ((UIView*) peer->getNativeHandle(), this, preferredPixelFormat,
|
||||
dynamic_cast <const GLESContext*> (contextToShareListsWith),
|
||||
type == openGLES2 ? kEAGLRenderingAPIOpenGLES2 : kEAGLRenderingAPIOpenGLES1);
|
||||
(flags & openGLES2) == 0 ? kEAGLRenderingAPIOpenGLES1
|
||||
: kEAGLRenderingAPIOpenGLES2);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
|
||||
{
|
||||
Component::internalRepaint (x, y, w, h);
|
||||
}
|
||||
|
||||
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
|
||||
{
|
||||
if (context != nullptr)
|
||||
|
|
|
|||
|
|
@ -190,11 +190,6 @@ OpenGLContext* OpenGLComponent::createContext()
|
|||
return (c->renderContext != 0) ? c.release() : nullptr;
|
||||
}
|
||||
|
||||
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
|
||||
{
|
||||
Component::internalRepaint (x, y, w, h);
|
||||
}
|
||||
|
||||
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
|
||||
{
|
||||
if (context != nullptr)
|
||||
|
|
|
|||
|
|
@ -245,22 +245,6 @@ void* OpenGLComponent::getNativeWindowHandle() const
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
|
||||
{
|
||||
Component::internalRepaint (x, y, w, h);
|
||||
|
||||
if (context != nullptr)
|
||||
{
|
||||
NSView* const v = static_cast<WindowedGLContext*> (context.get())->view;
|
||||
|
||||
// bit of a bodge here.. if we only invalidate the area of the gl component,
|
||||
// it's completely covered by the NSOpenGLView, so the OS throws away the
|
||||
// repaint message, thus never causing our paint() callback, and never repainting
|
||||
// the comp. So invalidating just a little bit around the edge helps..
|
||||
[[v superview] setNeedsDisplayInRect: NSInsetRect ([v frame], -2.0f, -2.0f)];
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>&)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,17 +324,6 @@ void* OpenGLComponent::getNativeWindowHandle() const
|
|||
return context != nullptr ? static_cast<WindowedGLContext*> (context.get())->getNativeWindowHandle() : nullptr;
|
||||
}
|
||||
|
||||
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
|
||||
{
|
||||
Component::internalRepaint (x, y, w, h);
|
||||
|
||||
if (context != nullptr)
|
||||
{
|
||||
ComponentPeer* peer = static_cast<WindowedGLContext*> (context.get())->nativeWindow;
|
||||
peer->repaint (peer->getBounds().withPosition (Point<int>()));
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLComponent::updateEmbeddedPosition (const Rectangle<int>& bounds)
|
||||
{
|
||||
if (context != nullptr)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ BEGIN_JUCE_NAMESPACE
|
|||
OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent,
|
||||
const int alphaBits_,
|
||||
const int depthBufferBits_,
|
||||
const int stencilBufferBits_)
|
||||
const int stencilBufferBits_) noexcept
|
||||
: redBits (bitsPerRGBComponent),
|
||||
greenBits (bitsPerRGBComponent),
|
||||
blueBits (bitsPerRGBComponent),
|
||||
|
|
@ -45,7 +45,7 @@ OpenGLPixelFormat::OpenGLPixelFormat (const int bitsPerRGBComponent,
|
|||
{
|
||||
}
|
||||
|
||||
OpenGLPixelFormat::OpenGLPixelFormat (const OpenGLPixelFormat& other)
|
||||
OpenGLPixelFormat::OpenGLPixelFormat (const OpenGLPixelFormat& other) noexcept
|
||||
: redBits (other.redBits),
|
||||
greenBits (other.greenBits),
|
||||
blueBits (other.blueBits),
|
||||
|
|
@ -60,7 +60,7 @@ OpenGLPixelFormat::OpenGLPixelFormat (const OpenGLPixelFormat& other)
|
|||
{
|
||||
}
|
||||
|
||||
OpenGLPixelFormat& OpenGLPixelFormat::operator= (const OpenGLPixelFormat& other)
|
||||
OpenGLPixelFormat& OpenGLPixelFormat::operator= (const OpenGLPixelFormat& other) noexcept
|
||||
{
|
||||
redBits = other.redBits;
|
||||
greenBits = other.greenBits;
|
||||
|
|
@ -76,7 +76,7 @@ OpenGLPixelFormat& OpenGLPixelFormat::operator= (const OpenGLPixelFormat& other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const
|
||||
bool OpenGLPixelFormat::operator== (const OpenGLPixelFormat& other) const noexcept
|
||||
{
|
||||
return redBits == other.redBits
|
||||
&& greenBits == other.greenBits
|
||||
|
|
@ -121,30 +121,30 @@ OpenGLContext* OpenGLContext::getCurrentContext()
|
|||
class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
OpenGLComponentWatcher (OpenGLComponent* const owner_)
|
||||
: ComponentMovementWatcher (owner_),
|
||||
OpenGLComponentWatcher (OpenGLComponent& owner_)
|
||||
: ComponentMovementWatcher (&owner_),
|
||||
owner (owner_)
|
||||
{
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
|
||||
{
|
||||
owner->updateContextPosition();
|
||||
owner.updateContextPosition();
|
||||
}
|
||||
|
||||
void componentPeerChanged()
|
||||
{
|
||||
owner->recreateContextAsync();
|
||||
owner.recreateContextAsync();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged()
|
||||
{
|
||||
if (! owner->isShowing())
|
||||
owner->stopBackgroundThread();
|
||||
if (! owner.isShowing())
|
||||
owner.stopBackgroundThread();
|
||||
}
|
||||
|
||||
private:
|
||||
OpenGLComponent* const owner;
|
||||
OpenGLComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponentWatcher);
|
||||
};
|
||||
|
|
@ -217,16 +217,175 @@ void OpenGLComponent::stopRenderThread()
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
OpenGLComponent::OpenGLComponent (const OpenGLType type_, const bool useBackgroundThread)
|
||||
: type (type_),
|
||||
class OpenGLComponent::OpenGLCachedComponentImage : public CachedComponentImage,
|
||||
public Timer // N.B. using a Timer rather than an AsyncUpdater
|
||||
// to avoid scheduling problems on Windows
|
||||
{
|
||||
public:
|
||||
OpenGLCachedComponentImage (OpenGLComponent& owner_)
|
||||
: owner (owner_)
|
||||
{
|
||||
}
|
||||
|
||||
void paint (Graphics&)
|
||||
{
|
||||
ComponentPeer* const peer = owner.getPeer();
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
const Point<int> topLeft (owner.getScreenPosition() - peer->getScreenPosition());
|
||||
peer->addMaskedRegion (topLeft.x, topLeft.y, owner.getWidth(), owner.getHeight());
|
||||
}
|
||||
|
||||
if (owner.isUsingDedicatedThread())
|
||||
{
|
||||
if (peer != nullptr && owner.isShowing())
|
||||
{
|
||||
#if ! JUCE_LINUX
|
||||
owner.updateContext();
|
||||
#endif
|
||||
|
||||
if (! owner.threadStarted)
|
||||
{
|
||||
owner.threadStarted = true;
|
||||
owner.startRenderThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
owner.updateContext();
|
||||
|
||||
if (isTimerRunning())
|
||||
timerCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void invalidateAll()
|
||||
{
|
||||
validArea.clear();
|
||||
triggerRepaint();
|
||||
}
|
||||
|
||||
void invalidate (const Rectangle<int>& area)
|
||||
{
|
||||
validArea.subtract (area);
|
||||
triggerRepaint();
|
||||
}
|
||||
|
||||
void releaseResources()
|
||||
{
|
||||
frameBuffer.release();
|
||||
}
|
||||
|
||||
void timerCallback()
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
if (! owner.makeCurrentRenderingTarget())
|
||||
return;
|
||||
|
||||
const Rectangle<int> bounds (owner.getLocalBounds());
|
||||
|
||||
const int fbW = frameBuffer.getWidth();
|
||||
const int fbH = frameBuffer.getHeight();
|
||||
|
||||
if (fbW < bounds.getWidth()
|
||||
|| fbH < bounds.getHeight()
|
||||
|| fbW > bounds.getWidth() + 128
|
||||
|| fbH > bounds.getHeight() + 128
|
||||
|| ! frameBuffer.isValid())
|
||||
{
|
||||
frameBuffer.initialise (bounds.getWidth(), bounds.getHeight());
|
||||
validArea.clear();
|
||||
}
|
||||
|
||||
owner.renderOpenGL();
|
||||
|
||||
if ((owner.flags & allowSubComponents) != 0)
|
||||
{
|
||||
{
|
||||
RectangleList invalid (bounds);
|
||||
invalid.subtract (validArea);
|
||||
validArea = bounds;
|
||||
|
||||
if (! invalid.isEmpty())
|
||||
{
|
||||
OpenGLRenderer g (frameBuffer);
|
||||
g.clipToRectangleList (invalid);
|
||||
|
||||
g.setFill (Colours::transparentBlack);
|
||||
g.fillRect (bounds, true);
|
||||
g.setFill (Colours::black);
|
||||
|
||||
paintOwner (g);
|
||||
}
|
||||
}
|
||||
|
||||
owner.makeCurrentRenderingTarget();
|
||||
OpenGLHelpers::prepareFor2D (bounds.getWidth(), bounds.getHeight());
|
||||
glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable (GL_BLEND);
|
||||
glColor4f (1.0f, 1.0f, 1.0f, owner.getAlpha());
|
||||
frameBuffer.drawAt (0, (float) (bounds.getHeight() - frameBuffer.getHeight()));
|
||||
}
|
||||
|
||||
owner.swapBuffers();
|
||||
owner.releaseAsRenderingTarget();
|
||||
}
|
||||
|
||||
void triggerRepaint()
|
||||
{
|
||||
if (! owner.isUsingDedicatedThread())
|
||||
startTimer (1000 / 70);
|
||||
}
|
||||
|
||||
private:
|
||||
void paintOwner (OpenGLRenderer& glRenderer)
|
||||
{
|
||||
Graphics g (&glRenderer);
|
||||
|
||||
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
g.saveState();
|
||||
#endif
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
owner.paintEntireComponent (g, false);
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
// enabling this code will fill all areas that get repainted with a colour overlay, to show
|
||||
// clearly when things are being repainted.
|
||||
g.restoreState();
|
||||
|
||||
static Random rng;
|
||||
g.fillAll (Colour ((uint8) rng.nextInt (255),
|
||||
(uint8) rng.nextInt (255),
|
||||
(uint8) rng.nextInt (255),
|
||||
(uint8) 0x50));
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLComponent& owner;
|
||||
OpenGLFrameBuffer frameBuffer;
|
||||
RectangleList validArea;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLCachedComponentImage);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
OpenGLComponent::OpenGLComponent (const int flags_)
|
||||
: flags (flags_),
|
||||
contextToShareListsWith (nullptr),
|
||||
needToUpdateViewport (true),
|
||||
needToDeleteContext (false),
|
||||
threadStarted (false),
|
||||
useThread (useBackgroundThread)
|
||||
threadStarted (false)
|
||||
{
|
||||
setOpaque (true);
|
||||
componentWatcher = new OpenGLComponentWatcher (this);
|
||||
componentWatcher = new OpenGLComponentWatcher (*this);
|
||||
setCachedComponentImage (new OpenGLCachedComponentImage (*this));
|
||||
}
|
||||
|
||||
OpenGLComponent::~OpenGLComponent()
|
||||
|
|
@ -321,6 +480,7 @@ void OpenGLComponent::deleteContext()
|
|||
}
|
||||
|
||||
context = nullptr;
|
||||
setCachedComponentImage (nullptr);
|
||||
}
|
||||
|
||||
needToDeleteContext = false;
|
||||
|
|
@ -351,40 +511,6 @@ void OpenGLComponent::stopBackgroundThread()
|
|||
}
|
||||
}
|
||||
|
||||
void OpenGLComponent::paint (Graphics&)
|
||||
{
|
||||
ComponentPeer* const peer = getPeer();
|
||||
|
||||
if (useThread)
|
||||
{
|
||||
if (peer != nullptr && isShowing())
|
||||
{
|
||||
#if ! JUCE_LINUX
|
||||
updateContext();
|
||||
#endif
|
||||
|
||||
if (! threadStarted)
|
||||
{
|
||||
threadStarted = true;
|
||||
startRenderThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updateContext();
|
||||
|
||||
if (! renderAndSwapBuffers())
|
||||
return;
|
||||
}
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
const Point<int> topLeft (getScreenPosition() - peer->getScreenPosition());
|
||||
peer->addMaskedRegion (topLeft.x, topLeft.y, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLComponent::renderAndSwapBuffers()
|
||||
{
|
||||
const ScopedLock sl (contextLock);
|
||||
|
|
@ -411,6 +537,21 @@ bool OpenGLComponent::renderAndSwapBuffers()
|
|||
return true;
|
||||
}
|
||||
|
||||
void OpenGLComponent::triggerRepaint()
|
||||
{
|
||||
OpenGLCachedComponentImage* const c
|
||||
= dynamic_cast<OpenGLCachedComponentImage*> (getCachedComponentImage());
|
||||
|
||||
jassert (c != nullptr); // you mustn't set your own cached image object for an OpenGLComponent!
|
||||
|
||||
if (c != nullptr)
|
||||
c->triggerRepaint();
|
||||
}
|
||||
|
||||
void OpenGLComponent::paint (Graphics&)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int OpenGLComponent::getFrameBufferID() const
|
||||
{
|
||||
return context != nullptr ? context->getFrameBufferID() : 0;
|
||||
|
|
|
|||
|
|
@ -47,26 +47,42 @@ class JUCE_API OpenGLComponent : public OpenGLBaseType,
|
|||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Used to select the type of openGL API to use, if more than one choice is available
|
||||
on a particular platform.
|
||||
/** These flags can be combined and passed to the OpenGLComponent constructor to
|
||||
specify various options.
|
||||
*/
|
||||
enum OpenGLType
|
||||
enum OpenGLFlags
|
||||
{
|
||||
openGLDefault = 0,
|
||||
/** This value can be used if you want your OpenGLComponent to use the
|
||||
default settings.
|
||||
*/
|
||||
openGLDefault = 8,
|
||||
|
||||
#if JUCE_IOS
|
||||
openGLES1, /**< On the iPhone, this selects openGL ES 1.0 */
|
||||
openGLES2 /**< On the iPhone, this selects openGL ES 2.0 */
|
||||
openGLES1 = 1, /**< On the iPhone, this selects openGL ES 1.0 */
|
||||
openGLES2 = 2, /**< On the iPhone, this selects openGL ES 2.0 */
|
||||
#endif
|
||||
|
||||
/** If this flag is enabled, the component will launch a background thread to
|
||||
perform the rendering. If this flag is not enabled, then renderOpenGL()
|
||||
will be invoked on the main event thread when the component has been told to
|
||||
repaint, or after triggerRepaint() has been called.
|
||||
*/
|
||||
useBackgroundThread = 4,
|
||||
|
||||
/** If this flag is enabled, then any sub-components of the OpenGLComponent
|
||||
will be correctly overlaid on top of the GL content, and its paint() method will
|
||||
be able to render over it. If you're not using sub-components, you can disable
|
||||
this flag, which will eliminate some overhead.
|
||||
*/
|
||||
allowSubComponents = 8
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an OpenGLComponent.
|
||||
If useBackgroundThread is true, the component will launch a background thread
|
||||
to do the rendering. If false, then renderOpenGL() will be called as part of the
|
||||
normal paint() method.
|
||||
The flags parameter should be a combination of the values in the
|
||||
OpenGLFlags enum.
|
||||
*/
|
||||
OpenGLComponent (OpenGLType type = openGLDefault,
|
||||
bool useBackgroundThread = false);
|
||||
OpenGLComponent (int flags = openGLDefault);
|
||||
|
||||
/** Destructor. */
|
||||
~OpenGLComponent();
|
||||
|
|
@ -101,7 +117,7 @@ public:
|
|||
/** Returns true if the component is performing the rendering on a background thread.
|
||||
This property is specified in the constructor.
|
||||
*/
|
||||
bool isUsingDedicatedThread() const noexcept { return useThread; }
|
||||
inline bool isUsingDedicatedThread() const noexcept { return (flags & useBackgroundThread) != 0; }
|
||||
|
||||
/** This replaces the normal paint() callback - use it to draw your openGL stuff.
|
||||
|
||||
|
|
@ -177,6 +193,14 @@ public:
|
|||
int getRenderingTargetWidth() const { return getWidth(); }
|
||||
int getRenderingTargetHeight() const { return getHeight(); }
|
||||
|
||||
/** Causes a repaint to be invoked asynchronously.
|
||||
This has a similar effect to calling repaint(), and triggers a callback to
|
||||
renderOpenGL(), but unlike repaint(), it does not mark any of the component's
|
||||
children as needing a redraw, which means that their cached state can be re-used
|
||||
if possible.
|
||||
*/
|
||||
void triggerRepaint();
|
||||
|
||||
//==============================================================================
|
||||
/** Calls the rendering callback, and swaps the buffers afterwards.
|
||||
This is called automatically by paint() when the component needs to be rendered.
|
||||
|
|
@ -225,12 +249,11 @@ protected:
|
|||
*/
|
||||
virtual void stopRenderThread();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g);
|
||||
void paint (Graphics&);
|
||||
|
||||
private:
|
||||
const OpenGLType type;
|
||||
const int flags;
|
||||
|
||||
class OpenGLComponentRenderThread;
|
||||
friend class OpenGLComponentRenderThread;
|
||||
|
|
@ -247,14 +270,15 @@ private:
|
|||
CriticalSection contextLock;
|
||||
OpenGLPixelFormat preferredPixelFormat;
|
||||
bool needToUpdateViewport, needToDeleteContext, threadStarted;
|
||||
const bool useThread;
|
||||
|
||||
class OpenGLCachedComponentImage;
|
||||
friend class OpenGLCachedComponentImage;
|
||||
|
||||
OpenGLContext* createContext();
|
||||
void updateContext();
|
||||
void updateContextPosition();
|
||||
void stopBackgroundThread();
|
||||
void recreateContextAsync();
|
||||
void internalRepaint (int x, int y, int w, int h);
|
||||
void updateEmbeddedPosition (const Rectangle<int>&);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLComponent);
|
||||
|
|
|
|||
|
|
@ -250,13 +250,6 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
GLShaderProgram (const GLchar* fragment) noexcept
|
||||
: program (glCreateProgram())
|
||||
{
|
||||
addShader (fragment, GL_FRAGMENT_SHADER);
|
||||
link();
|
||||
}
|
||||
|
||||
~GLShaderProgram() noexcept
|
||||
{
|
||||
glDeleteProgram (program);
|
||||
|
|
@ -297,9 +290,9 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
struct UniformLocation
|
||||
struct Uniform
|
||||
{
|
||||
UniformLocation (const GLShaderProgram& program, const GLchar* name)
|
||||
Uniform (const GLShaderProgram& program, const GLchar* name)
|
||||
: uniformID (glGetUniformLocation (program.program, name))
|
||||
{
|
||||
jassert (uniformID >= 0);
|
||||
|
|
@ -335,76 +328,85 @@ struct ShaderPrograms
|
|||
|
||||
bool areShadersSupported;
|
||||
|
||||
struct SolidColourMaskedProgram
|
||||
struct ShaderBase
|
||||
{
|
||||
ShaderBase (const char* fragmentShader)
|
||||
{
|
||||
addShader (fragmentShader, GL_FRAGMENT_SHADER);
|
||||
link();
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
};
|
||||
|
||||
struct SolidColourMaskedProgram : public ShaderBase
|
||||
{
|
||||
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;"
|
||||
"}"),
|
||||
: ShaderBase ("#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")
|
||||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation maskTexture, maskBounds;
|
||||
GLShaderProgram::Uniform maskTexture, maskBounds;
|
||||
};
|
||||
|
||||
struct RadialGradientProgram
|
||||
struct RadialGradientProgram : public ShaderBase
|
||||
{
|
||||
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));"
|
||||
"}"),
|
||||
: ShaderBase ("#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")
|
||||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, matrix;
|
||||
GLShaderProgram::Uniform gradientTexture, matrix;
|
||||
};
|
||||
|
||||
struct RadialGradientMaskedProgram
|
||||
struct RadialGradientMaskedProgram : public ShaderBase
|
||||
{
|
||||
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;"
|
||||
"}"),
|
||||
: ShaderBase ("#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"),
|
||||
|
|
@ -412,54 +414,52 @@ struct ShaderPrograms
|
|||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, matrix;
|
||||
GLShaderProgram::UniformLocation maskTexture, maskBounds;
|
||||
GLShaderProgram::Uniform gradientTexture, matrix;
|
||||
GLShaderProgram::Uniform maskTexture, maskBounds;
|
||||
};
|
||||
|
||||
struct LinearGradient1Program
|
||||
struct LinearGradient1Program : public ShaderBase
|
||||
{
|
||||
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));"
|
||||
"}"),
|
||||
: ShaderBase ("#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")
|
||||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, gradientInfo;
|
||||
GLShaderProgram::Uniform gradientTexture, gradientInfo;
|
||||
};
|
||||
|
||||
struct LinearGradient1MaskedProgram
|
||||
struct LinearGradient1MaskedProgram : public ShaderBase
|
||||
{
|
||||
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;"
|
||||
"}"),
|
||||
: ShaderBase ("#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"),
|
||||
|
|
@ -467,54 +467,52 @@ struct ShaderPrograms
|
|||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, gradientInfo;
|
||||
GLShaderProgram::UniformLocation maskTexture, maskBounds;
|
||||
GLShaderProgram::Uniform gradientTexture, gradientInfo;
|
||||
GLShaderProgram::Uniform maskTexture, maskBounds;
|
||||
};
|
||||
|
||||
struct LinearGradient2Program
|
||||
struct LinearGradient2Program : public ShaderBase
|
||||
{
|
||||
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));"
|
||||
"}"),
|
||||
: ShaderBase ("#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")
|
||||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, gradientInfo;
|
||||
GLShaderProgram::Uniform gradientTexture, gradientInfo;
|
||||
};
|
||||
|
||||
struct LinearGradient2MaskedProgram
|
||||
struct LinearGradient2MaskedProgram : public ShaderBase
|
||||
{
|
||||
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;"
|
||||
"}"),
|
||||
: ShaderBase ("#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"),
|
||||
|
|
@ -522,9 +520,8 @@ struct ShaderPrograms
|
|||
{
|
||||
}
|
||||
|
||||
GLShaderProgram program;
|
||||
GLShaderProgram::UniformLocation gradientTexture, gradientInfo;
|
||||
GLShaderProgram::UniformLocation maskTexture, maskBounds;
|
||||
GLShaderProgram::Uniform gradientTexture, gradientInfo;
|
||||
GLShaderProgram::Uniform maskTexture, maskBounds;
|
||||
};
|
||||
|
||||
SolidColourMaskedProgram solidColourMasked;
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ public:
|
|||
OpenGLPixelFormat (int bitsPerRGBComponent = 8,
|
||||
int alphaBits = 8,
|
||||
int depthBufferBits = 16,
|
||||
int stencilBufferBits = 0);
|
||||
int stencilBufferBits = 0) noexcept;
|
||||
|
||||
OpenGLPixelFormat (const OpenGLPixelFormat&);
|
||||
OpenGLPixelFormat& operator= (const OpenGLPixelFormat&);
|
||||
bool operator== (const OpenGLPixelFormat&) const;
|
||||
OpenGLPixelFormat (const OpenGLPixelFormat&) noexcept;
|
||||
OpenGLPixelFormat& operator= (const OpenGLPixelFormat&) noexcept;
|
||||
bool operator== (const OpenGLPixelFormat&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
int redBits; /**< The number of bits per pixel to use for the red channel. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue