LayerOverlapMap.cpp [plain text]
#include "config.h"
#include "LayerOverlapMap.h"
#include "RenderLayer.h"
#include <wtf/text/TextStream.h>
namespace WebCore {
struct RectList {
Vector<LayoutRect> rects;
LayoutRect boundingRect;
void append(const LayoutRect& rect)
{
rects.append(rect);
boundingRect.unite(rect);
}
void append(const RectList& rectList)
{
rects.appendVector(rectList.rects);
boundingRect.unite(rectList.boundingRect);
}
bool intersects(const LayoutRect& rect) const
{
if (!rects.size() || !rect.intersects(boundingRect))
return false;
for (const auto& currentRect : rects) {
if (currentRect.intersects(rect))
return true;
}
return false;
}
};
static TextStream& operator<<(TextStream& ts, const RectList& rectList)
{
ts << "bounds " << rectList.boundingRect << " (" << rectList.rects << " rects)";
return ts;
}
class OverlapMapContainer {
WTF_MAKE_FAST_ALLOCATED;
public:
OverlapMapContainer(const RenderLayer& rootLayer)
: m_rootScope(rootLayer)
{
}
void add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
bool overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
void append(std::unique_ptr<OverlapMapContainer>&&);
String dump(unsigned) const;
private:
struct ClippingScope {
ClippingScope(const RenderLayer& inLayer)
: layer(inLayer)
{
}
ClippingScope(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
: layer(layerAndBounds.layer)
, bounds(layerAndBounds.bounds)
{
}
ClippingScope* childWithLayer(const RenderLayer& layer) const
{
for (auto& child : children) {
if (&child.layer == &layer)
return const_cast<ClippingScope*>(&child);
}
return nullptr;
}
ClippingScope* addChildWithLayerAndBounds(const LayerOverlapMap::LayerAndBounds& layerAndBounds)
{
children.append({ layerAndBounds });
return &children.last();
}
ClippingScope* addChild(const ClippingScope& child)
{
ASSERT(&layer != &child.layer);
children.append(child);
return &children.last();
}
void appendRect(const LayoutRect& bounds)
{
rectList.append(bounds);
}
const RenderLayer& layer;
LayoutRect bounds; Vector<ClippingScope> children;
RectList rectList;
};
static ClippingScope* clippingScopeContainingLayerChildRecursive(const ClippingScope& currNode, const RenderLayer& layer)
{
for (auto& child : currNode.children) {
if (&layer == &child.layer)
return const_cast<ClippingScope*>(&currNode);
if (auto* foundNode = clippingScopeContainingLayerChildRecursive(child, layer))
return foundNode;
}
return nullptr;
}
ClippingScope* scopeContainingLayer(const RenderLayer& layer) const
{
return clippingScopeContainingLayerChildRecursive(m_rootScope, layer);
}
static void mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope);
ClippingScope* ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers);
ClippingScope* findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const;
void recursiveOutputToStream(TextStream&, const ClippingScope&, unsigned depth) const;
const ClippingScope& rootScope() const { return m_rootScope; }
ClippingScope& rootScope() { return m_rootScope; }
ClippingScope m_rootScope;
};
void OverlapMapContainer::add(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
{
auto* layerScope = ensureClippingScopeForLayers(enclosingClippingLayers);
layerScope->appendRect(bounds);
}
bool OverlapMapContainer::overlapsLayers(const RenderLayer&, const LayoutRect& bounds, const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
{
if (m_rootScope.rectList.intersects(bounds))
return true;
if (m_rootScope.children.isEmpty())
return false;
auto* clippingScope = findClippingScopeForLayers(enclosingClippingLayers);
if (!clippingScope)
return false;
if (clippingScope->rectList.intersects(bounds))
return true;
return false;
}
void OverlapMapContainer::mergeClippingScopesRecursive(const ClippingScope& sourceScope, ClippingScope& destScope)
{
ASSERT(&sourceScope.layer == &destScope.layer);
destScope.rectList.append(sourceScope.rectList);
for (auto& sourceChildScope : sourceScope.children) {
ClippingScope* destChild = destScope.childWithLayer(sourceChildScope.layer);
if (destChild) {
destChild->rectList.append(sourceChildScope.rectList);
mergeClippingScopesRecursive(sourceChildScope, *destChild);
} else {
destScope.addChild(sourceChildScope);
}
}
}
void OverlapMapContainer::append(std::unique_ptr<OverlapMapContainer>&& otherContainer)
{
mergeClippingScopesRecursive(otherContainer->rootScope(), m_rootScope);
}
OverlapMapContainer::ClippingScope* OverlapMapContainer::ensureClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers)
{
ASSERT(enclosingClippingLayers.size());
ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
auto* currScope = &m_rootScope;
for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
auto& scopeLayerAndBounds = enclosingClippingLayers[i];
auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
if (!childScope) {
currScope = currScope->addChildWithLayerAndBounds(scopeLayerAndBounds);
break;
}
currScope = childScope;
}
return const_cast<ClippingScope*>(currScope);
}
OverlapMapContainer::ClippingScope* OverlapMapContainer::findClippingScopeForLayers(const Vector<LayerOverlapMap::LayerAndBounds>& enclosingClippingLayers) const
{
ASSERT(enclosingClippingLayers.size());
ASSERT(enclosingClippingLayers[0].layer.isRenderViewLayer());
const auto* currScope = &m_rootScope;
for (unsigned i = 1; i < enclosingClippingLayers.size(); ++i) {
auto& scopeLayerAndBounds = enclosingClippingLayers[i];
auto* childScope = currScope->childWithLayer(scopeLayerAndBounds.layer);
if (!childScope)
return nullptr;
currScope = childScope;
}
return const_cast<ClippingScope*>(currScope);
}
void OverlapMapContainer::recursiveOutputToStream(TextStream& ts, const ClippingScope& scope, unsigned depth) const
{
ts << "\n" << indent << TextStream::Repeat { 2 * depth, ' ' } << " scope for layer " << &scope.layer << " rects " << scope.rectList;
for (auto& childScope : scope.children)
recursiveOutputToStream(ts, childScope, depth + 1);
}
String OverlapMapContainer::dump(unsigned indent) const
{
TextStream multilineStream;
multilineStream.increaseIndent(indent);
multilineStream << "overlap container - root scope layer " << &m_rootScope.layer << " rects " << m_rootScope.rectList;
for (auto& childScope : m_rootScope.children)
recursiveOutputToStream(multilineStream, childScope, 1);
return multilineStream.release();
}
LayerOverlapMap::LayerOverlapMap(const RenderLayer& rootLayer)
: m_geometryMap(UseTransforms)
, m_rootLayer(rootLayer)
{
pushCompositingContainer();
}
LayerOverlapMap::~LayerOverlapMap() = default;
void LayerOverlapMap::add(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers)
{
ASSERT(m_overlapStack.size() >= 2);
m_overlapStack[m_overlapStack.size() - 2]->add(layer, bounds, enclosingClippingLayers);
m_isEmpty = false;
}
bool LayerOverlapMap::overlapsLayers(const RenderLayer& layer, const LayoutRect& bounds, const Vector<LayerAndBounds>& enclosingClippingLayers) const
{
return m_overlapStack.last()->overlapsLayers(layer, bounds, enclosingClippingLayers);
}
void LayerOverlapMap::pushCompositingContainer()
{
m_overlapStack.append(makeUnique<OverlapMapContainer>(m_rootLayer));
}
void LayerOverlapMap::popCompositingContainer()
{
m_overlapStack[m_overlapStack.size() - 2]->append(WTFMove(m_overlapStack.last()));
m_overlapStack.removeLast();
}
static TextStream& operator<<(TextStream& ts, const OverlapMapContainer& container)
{
ts << container.dump(ts.indent());
return ts;
}
TextStream& operator<<(TextStream& ts, const LayerOverlapMap& overlapMap)
{
TextStream multilineStream;
TextStream::GroupScope scope(ts);
multilineStream << "LayerOverlapMap\n";
multilineStream.increaseIndent(2);
bool needNewline = false;
for (auto& container : overlapMap.overlapStack()) {
if (needNewline)
multilineStream << "\n";
else
needNewline = true;
multilineStream << indent << *container;
}
ts << multilineStream.release();
return ts;
}
}