mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-08 23:24:19 +00:00
Component: Revert improvements to opaque component checks
Reverts commitscea19a9d,961ff32b, and partially reverts5ec4d85dSome issues have appeared on the forums so these commits have been reverted while the issue is investigated.
This commit is contained in:
parent
fc29df83b5
commit
10ec977d05
3 changed files with 92 additions and 280 deletions
|
|
@ -209,7 +209,7 @@ public:
|
|||
return std::exchange (effect, &i) != &i;
|
||||
}
|
||||
|
||||
void paint (Graphics& g, Component& c, bool ignoreAlphaLevel, OpaqueLayer& opaqueLayer)
|
||||
void paint (Graphics& g, Component& c, bool ignoreAlphaLevel)
|
||||
{
|
||||
auto scale = g.getInternalContext().getPhysicalPixelScaleFactor();
|
||||
auto scaledBounds = c.getLocalBounds() * scale;
|
||||
|
|
@ -238,7 +238,7 @@ public:
|
|||
Graphics g2 (effectImage);
|
||||
g2.addTransform (AffineTransform::scale ((float) scaledBounds.getWidth() / (float) c.getWidth(),
|
||||
(float) scaledBounds.getHeight() / (float) c.getHeight()));
|
||||
c.paintComponentAndChildren (g2, opaqueLayer);
|
||||
c.paintComponentAndChildren (g2);
|
||||
}
|
||||
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
|
@ -257,133 +257,6 @@ private:
|
|||
ImageEffectFilter* effect;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Component::OpaqueLayer
|
||||
{
|
||||
public:
|
||||
explicit OpaqueLayer (const Component&& c) = delete;
|
||||
explicit OpaqueLayer (const Component& c)
|
||||
{
|
||||
appendOpaqueChildren (c, c.getPosition());
|
||||
}
|
||||
|
||||
enum class ObscuredByKind
|
||||
{
|
||||
children,
|
||||
siblings
|
||||
};
|
||||
|
||||
void popComponent (Component& c)
|
||||
{
|
||||
// The most likely scenario is that a component isn't opaque
|
||||
if (! c.isOpaque())
|
||||
return;
|
||||
|
||||
// As the component is opaque chances are it's the next item in the list
|
||||
// of opaque components.
|
||||
// If not, it has been skipped, probably because it's covered by another opaque component.
|
||||
// Chances are that the component is only one or two steps away in the list.
|
||||
for (auto i = currentPosition; i < opaqueComponents.size(); ++i)
|
||||
{
|
||||
if (opaqueComponents[i] == &c)
|
||||
{
|
||||
currentPosition = i + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This suggests we've encountered an opaque component that should be
|
||||
// in the list but isn't! Please contact the JUCE team if you encounter
|
||||
// this assertion.
|
||||
jassert (c.getAlpha() < 1.0f);
|
||||
}
|
||||
|
||||
Rectangle<int> getNonObscuredBoundsFor (const Component& component,
|
||||
Rectangle<int> clipBounds,
|
||||
ObscuredByKind obscuredBy) const
|
||||
{
|
||||
auto visibleBounds = component.getBounds().getIntersection (clipBounds);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
|
||||
RectangleList visibleRegions { visibleBounds };
|
||||
|
||||
const auto obscureWith = [&] (const Component& opaqueComponent)
|
||||
{
|
||||
const auto offset = positionOffsets[&opaqueComponent] - positionOffsets[&component];
|
||||
const auto opaqueBounds = opaqueComponent.getBounds() + offset;
|
||||
|
||||
if (! opaqueBounds.intersects (visibleBounds))
|
||||
return;
|
||||
|
||||
if (opaqueBounds.contains (visibleBounds))
|
||||
visibleRegions.clear();
|
||||
else
|
||||
visibleRegions.subtract (opaqueBounds);
|
||||
|
||||
visibleBounds = visibleRegions.getBounds();
|
||||
};
|
||||
|
||||
if (obscuredBy == ObscuredByKind::children)
|
||||
{
|
||||
for (auto i = currentPosition; i < opaqueComponents.size(); ++i)
|
||||
{
|
||||
const auto* opaqueComponent = opaqueComponents.getUnchecked (i);
|
||||
|
||||
if (! component.isParentOf (opaqueComponent))
|
||||
break;
|
||||
|
||||
obscureWith (*opaqueComponent);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = opaqueComponents.size(); --i >= currentPosition;)
|
||||
{
|
||||
const auto* opaqueComponent = opaqueComponents.getUnchecked (i);
|
||||
|
||||
if (component.isParentOf (opaqueComponent))
|
||||
break;
|
||||
|
||||
obscureWith (*opaqueComponent);
|
||||
|
||||
if (visibleBounds.isEmpty())
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return visibleBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
void appendOpaqueChildren (const Component& parent, Point<int> offset)
|
||||
{
|
||||
for (auto* child : parent.getChildren())
|
||||
{
|
||||
positionOffsets.set (child, offset);
|
||||
|
||||
if (! detail::ComponentHelpers::isVisible (*child, false)
|
||||
|| child->isTransformed())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child->isOpaque())
|
||||
opaqueComponents.add (child);
|
||||
|
||||
appendOpaqueChildren (*child, child->getPosition() + offset);
|
||||
}
|
||||
}
|
||||
|
||||
Array<Component*> opaqueComponents;
|
||||
int currentPosition = 0;
|
||||
HashMap<const Component*, Point<int>> positionOffsets;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Component::Component() noexcept
|
||||
: componentFlags (0)
|
||||
|
|
@ -1828,17 +1701,17 @@ void Component::paintOverChildren (Graphics&)
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void Component::paintWithinParentContext (Graphics& g, OpaqueLayer& opaqueLayer)
|
||||
void Component::paintWithinParentContext (Graphics& g)
|
||||
{
|
||||
g.setOrigin (getPosition());
|
||||
|
||||
if (cachedImage != nullptr)
|
||||
cachedImage->paint (g);
|
||||
else
|
||||
paintEntireComponent (g, false, opaqueLayer);
|
||||
paintEntireComponent (g, false);
|
||||
}
|
||||
|
||||
void Component::paintComponentAndChildren (Graphics& g, OpaqueLayer& opaqueLayer)
|
||||
void Component::paintComponentAndChildren (Graphics& g)
|
||||
{
|
||||
#if JUCE_ETW_TRACELOGGING
|
||||
{
|
||||
|
|
@ -1854,69 +1727,70 @@ void Component::paintComponentAndChildren (Graphics& g, OpaqueLayer& opaqueLayer
|
|||
}
|
||||
#endif
|
||||
|
||||
using ObscuredByKind = OpaqueLayer::ObscuredByKind;
|
||||
auto clipBounds = g.getClipBounds();
|
||||
|
||||
const auto parentClipBounds = opaqueLayer.getNonObscuredBoundsFor (*this, g.getClipBounds() + getPosition(), ObscuredByKind::children);
|
||||
|
||||
if (! parentClipBounds.isEmpty())
|
||||
if (flags.dontClipGraphicsFlag && getNumChildComponents() == 0)
|
||||
{
|
||||
paint (g);
|
||||
}
|
||||
else
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (! isPaintingUnclipped())
|
||||
g.reduceClipRegion (parentClipBounds - getPosition());
|
||||
|
||||
paint (g);
|
||||
if (! (detail::ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
|
||||
paint (g);
|
||||
}
|
||||
|
||||
for (auto* child : getChildren())
|
||||
for (int i = 0; i < childComponentList.size(); ++i)
|
||||
{
|
||||
if (! detail::ComponentHelpers::isVisible (*child))
|
||||
continue;
|
||||
auto& child = *childComponentList.getUnchecked (i);
|
||||
|
||||
if (child->isTransformed())
|
||||
if (child.isVisible())
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
if (child.affineTransform != nullptr)
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (auto& transform = child->affineTransform)
|
||||
g.addTransform (*transform);
|
||||
g.addTransform (*child.affineTransform);
|
||||
|
||||
child->paintWithinParentContext (g, opaqueLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
opaqueLayer.popComponent (*child);
|
||||
if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
else if (clipBounds.intersects (child.getBounds()))
|
||||
{
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
const auto clipBounds = opaqueLayer.getNonObscuredBoundsFor (*child,
|
||||
g.getClipBounds(),
|
||||
ObscuredByKind::siblings);
|
||||
if (child.flags.dontClipGraphicsFlag)
|
||||
{
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
else if (g.reduceClipRegion (child.getBounds()))
|
||||
{
|
||||
bool nothingClipped = true;
|
||||
|
||||
if (clipBounds.isEmpty())
|
||||
continue;
|
||||
for (int j = i + 1; j < childComponentList.size(); ++j)
|
||||
{
|
||||
auto& sibling = *childComponentList.getUnchecked (j);
|
||||
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
|
||||
{
|
||||
nothingClipped = false;
|
||||
g.excludeClipRegion (sibling.getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
if (! child->isPaintingUnclipped())
|
||||
g.reduceClipRegion (clipBounds);
|
||||
|
||||
child->paintWithinParentContext (g, opaqueLayer);
|
||||
if (nothingClipped || ! g.isClipEmpty())
|
||||
child.paintWithinParentContext (g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::ScopedSaveState ss (g);
|
||||
|
||||
if (! isPaintingUnclipped())
|
||||
g.reduceClipRegion (getLocalBounds());
|
||||
|
||||
paintOverChildren (g);
|
||||
}
|
||||
|
||||
void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
|
||||
{
|
||||
OpaqueLayer opaqueLayer { *this };
|
||||
paintEntireComponent (g, ignoreAlphaLevel, opaqueLayer);
|
||||
}
|
||||
|
||||
void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel, OpaqueLayer& opaqueLayer)
|
||||
{
|
||||
// If sizing a top-level-window and the OS paint message is delivered synchronously
|
||||
// before resized() is called, then we'll invoke the callback here, to make sure
|
||||
|
|
@ -1932,26 +1806,20 @@ void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel, Opaque
|
|||
|
||||
if (effectState != nullptr)
|
||||
{
|
||||
effectState->paint (g, *this, ignoreAlphaLevel, opaqueLayer);
|
||||
effectState->paint (g, *this, ignoreAlphaLevel);
|
||||
}
|
||||
else if (componentTransparency > 0 && ! ignoreAlphaLevel)
|
||||
{
|
||||
if (componentTransparency < 255)
|
||||
{
|
||||
OpaqueLayer transparentOpaqueLayer { *this };
|
||||
g.beginTransparencyLayer (getAlpha());
|
||||
paintComponentAndChildren (g, transparentOpaqueLayer);
|
||||
paintComponentAndChildren (g);
|
||||
g.endTransparencyLayer();
|
||||
}
|
||||
}
|
||||
else if (isTransformed())
|
||||
{
|
||||
OpaqueLayer transformedOpaqueLayer { *this };
|
||||
paintComponentAndChildren (g, transformedOpaqueLayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
paintComponentAndChildren (g, opaqueLayer);
|
||||
paintComponentAndChildren (g);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
|
|
@ -3382,34 +3250,6 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child3->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("An opaque component will hide parent-sibling components behind it");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
const auto child3 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
parent->addAndMakeVisible (*child2);
|
||||
|
||||
child3->setBounds (bounds);
|
||||
child3->setOpaque (true);
|
||||
child2->addAndMakeVisible (*child3);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 0);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 0);
|
||||
expectEquals (child3->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("An opaque component will reduce the clip bounds of sibling components behind it");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
@ -3640,30 +3480,6 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Opaque components will only be considered opaque up to a transparent parent");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
child1->setAlpha (0.5f);
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
child2->setOpaque (true);
|
||||
child1->addAndMakeVisible (*child2);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Transformed components will not be considered opaque");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
@ -3683,30 +3499,6 @@ struct ComponentTests : public UnitTest
|
|||
expectEquals (child->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Opaque components will only be considered opaque up to a transformed parent");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
const auto child1 = std::make_unique<TestComponent>();
|
||||
const auto child2 = std::make_unique<TestComponent>();
|
||||
|
||||
Rectangle<int> bounds { 0, 0, 100, 100 };
|
||||
parent->setBounds (bounds);
|
||||
|
||||
child1->setBounds (bounds);
|
||||
child1->setTransform (AffineTransform::rotation (degreesToRadians (45.0f)));
|
||||
parent->addAndMakeVisible (*child1);
|
||||
|
||||
child2->setBounds (bounds);
|
||||
child2->setOpaque (true);
|
||||
child1->addAndMakeVisible (*child2);
|
||||
|
||||
paintComponentBounds (*parent);
|
||||
|
||||
expectEquals (parent->numPaintCalls, 1);
|
||||
expectEquals (child1->numPaintCalls, 0);
|
||||
expectEquals (child2->numPaintCalls, 1);
|
||||
}
|
||||
|
||||
beginTest ("Nested opaque components prevent parents from being painted");
|
||||
{
|
||||
const auto parent = std::make_unique<TestComponent>();
|
||||
|
|
|
|||
|
|
@ -1194,7 +1194,7 @@ public:
|
|||
|
||||
If you enable this mode, you'll need to make sure your paint method doesn't call anything like
|
||||
Graphics::fillAll(), and doesn't draw beyond the component's bounds, because that'll produce
|
||||
artifacts.
|
||||
artifacts. This option will have no effect on components that contain any child components.
|
||||
*/
|
||||
void setPaintingIsUnclipped (bool shouldPaintWithoutClipping) noexcept;
|
||||
|
||||
|
|
@ -1276,19 +1276,14 @@ public:
|
|||
/** Indicates whether any parts of the component might be transparent.
|
||||
|
||||
Components that always paint all of their contents with solid colour and
|
||||
thus completely cover any components behind them, can use this method to
|
||||
thus completely cover any components behind them should use this method
|
||||
to tell the repaint system that they are opaque.
|
||||
|
||||
This information is used to optimise drawing, because it means that
|
||||
objects underneath opaque components or windows don't need to be painted
|
||||
or can have their clip bounds reduced to a smaller size.
|
||||
objects underneath opaque windows don't need to be painted.
|
||||
|
||||
Note however that there is a cost for every other component to check if
|
||||
it is being obscured by opaque components. This cost should be carefully
|
||||
weighed up against the benefits before deciding to enable this.
|
||||
|
||||
By default, components are considered transparent, unless this is used
|
||||
to make it otherwise.
|
||||
By default, components are considered transparent, unless this is used to
|
||||
make it otherwise.
|
||||
|
||||
@see isOpaque
|
||||
*/
|
||||
|
|
@ -2717,8 +2712,6 @@ private:
|
|||
uint8 componentTransparency = 0;
|
||||
|
||||
//==============================================================================
|
||||
class OpaqueLayer;
|
||||
|
||||
static void internalMouseEnter (SafePointer<Component>, MouseInputSource, Point<float>, Time);
|
||||
static void internalMouseExit (SafePointer<Component>, MouseInputSource, Point<float>, Time);
|
||||
static void internalMouseDown (SafePointer<Component>, MouseInputSource, const detail::PointerState&, Time);
|
||||
|
|
@ -2740,9 +2733,8 @@ private:
|
|||
void internalRepaintUnchecked (Rectangle<int>, bool);
|
||||
Component* removeChildComponent (int index, bool sendParentEvents, bool sendChildEvents);
|
||||
void reorderChildInternal (int sourceIndex, int destIndex);
|
||||
void paintEntireComponent (Graphics&, bool, OpaqueLayer&);
|
||||
void paintComponentAndChildren (Graphics&, OpaqueLayer&);
|
||||
void paintWithinParentContext (Graphics&, OpaqueLayer&);
|
||||
void paintComponentAndChildren (Graphics&);
|
||||
void paintWithinParentContext (Graphics&);
|
||||
void sendMovedResizedMessages (bool wasMoved, bool wasResized);
|
||||
void sendMovedResizedMessagesIfPending();
|
||||
void repaintParent();
|
||||
|
|
|
|||
|
|
@ -193,6 +193,42 @@ struct ComponentHelpers
|
|||
return convertFromDistantParentSpace (topLevelComp, *target, p);
|
||||
}
|
||||
|
||||
static bool clipChildComponent (const Component& child,
|
||||
Graphics& g,
|
||||
const Rectangle<int> clipRect,
|
||||
Point<int> delta)
|
||||
{
|
||||
if (! child.isVisible() || child.isTransformed())
|
||||
return false;
|
||||
|
||||
const auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
|
||||
|
||||
if (newClip.isEmpty())
|
||||
return false;
|
||||
|
||||
if (child.isOpaque() && child.componentTransparency == 0)
|
||||
{
|
||||
g.excludeClipRegion (newClip + delta);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto childPos = child.getPosition();
|
||||
return clipObscuredRegions (child, g, newClip - childPos, childPos + delta);
|
||||
}
|
||||
|
||||
static bool clipObscuredRegions (const Component& comp,
|
||||
Graphics& g,
|
||||
const Rectangle<int> clipRect,
|
||||
Point<int> delta)
|
||||
{
|
||||
auto wasClipped = false;
|
||||
|
||||
for (int i = comp.childComponentList.size(); --i >= 0;)
|
||||
wasClipped |= clipChildComponent (*comp.childComponentList.getUnchecked (i), g, clipRect, delta);
|
||||
|
||||
return wasClipped;
|
||||
}
|
||||
|
||||
static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
|
||||
{
|
||||
if (auto* p = comp.getParentComponent())
|
||||
|
|
@ -227,14 +263,6 @@ struct ComponentHelpers
|
|||
function (c, ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
|
||||
}
|
||||
|
||||
static bool isVisible (const Component& component, bool allowTransparency = true)
|
||||
{
|
||||
return component.isVisible()
|
||||
&& component.getWidth() > 0
|
||||
&& component.getHeight() > 0
|
||||
&& component.componentTransparency < (allowTransparency ? 255 : 1);
|
||||
}
|
||||
|
||||
class ModalComponentManagerChangeNotifier
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue