blob: b758a352bf4fda846947e864ad3e6a50cccf0368 [file] [log] [blame] [edit]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "platform/graphics/paint/DisplayItemPropertyTreeBuilder.h"
#include "platform/graphics/paint/DisplayItem.h"
#include "platform/graphics/paint/DisplayItemClipTree.h"
#include "platform/graphics/paint/DisplayItemTransformTree.h"
#include "platform/graphics/paint/ScrollDisplayItem.h"
#include "platform/graphics/paint/Transform3DDisplayItem.h"
#include "platform/graphics/paint/TransformDisplayItem.h"
namespace blink {
DisplayItemPropertyTreeBuilder::DisplayItemPropertyTreeBuilder()
: m_transformTree(adoptPtr(new DisplayItemTransformTree))
, m_clipTree(adoptPtr(new DisplayItemClipTree))
, m_rangeBeginIndex(0)
, m_currentIndex(0)
{
m_stateStack.append(BuilderState());
}
DisplayItemPropertyTreeBuilder::~DisplayItemPropertyTreeBuilder()
{
}
PassOwnPtr<DisplayItemTransformTree> DisplayItemPropertyTreeBuilder::releaseTransformTree()
{
ASSERT(m_stateStack.size() == 1);
ASSERT(currentState().ignoredBeginCount == 0);
finishRange();
return m_transformTree.release();
}
PassOwnPtr<DisplayItemClipTree> DisplayItemPropertyTreeBuilder::releaseClipTree()
{
ASSERT(m_stateStack.size() == 1);
ASSERT(currentState().ignoredBeginCount == 0);
finishRange();
return m_clipTree.release();
}
Vector<DisplayItemPropertyTreeBuilder::RangeRecord> DisplayItemPropertyTreeBuilder::releaseRangeRecords()
{
Vector<RangeRecord> output;
output.swap(m_rangeRecords);
return output;
}
void DisplayItemPropertyTreeBuilder::processDisplayItem(const DisplayItem& displayItem)
{
if (displayItem.isBegin())
processBeginItem(displayItem);
else if (displayItem.isEnd())
processEndItem(displayItem);
m_currentIndex++;
}
namespace {
enum TransformKind { NotATransform = 0, Only2DTranslation, RequiresTransformNode };
enum ClipKind { NotAClip = 0, RequiresClipNode };
struct BeginDisplayItemClassification {
// |transformOrigin| is irrelevant unless a non-translation matrix is
// provided.
TransformKind transformKind = NotATransform;
TransformationMatrix matrix;
FloatPoint3D transformOrigin;
ClipKind clipKind = NotAClip;
FloatRect clipRect;
};
// Classifies a begin display item based on how it affects the property trees.
static BeginDisplayItemClassification classifyBeginItem(const DisplayItem& beginDisplayItem)
{
ASSERT(beginDisplayItem.isBegin());
BeginDisplayItemClassification result;
DisplayItem::Type type = beginDisplayItem.type();
if (DisplayItem::isTransform3DType(type)) {
const auto& begin3D = static_cast<const BeginTransform3DDisplayItem&>(beginDisplayItem);
result.matrix = begin3D.transform();
result.transformOrigin = begin3D.transformOrigin();
result.transformKind = result.matrix.isIdentityOr2DTranslation() ? Only2DTranslation : RequiresTransformNode;
} else if (type == DisplayItem::BeginTransform) {
const auto& begin2D = static_cast<const BeginTransformDisplayItem&>(beginDisplayItem);
result.matrix = begin2D.transform();
result.transformKind = begin2D.transform().isIdentityOrTranslation() ? Only2DTranslation : RequiresTransformNode;
} else if (DisplayItem::isScrollType(type)) {
const auto& beginScroll = static_cast<const BeginScrollDisplayItem&>(beginDisplayItem);
const IntSize& offset = beginScroll.currentOffset();
result.matrix.translate(-offset.width(), -offset.height());
result.transformKind = Only2DTranslation;
}
return result;
}
} // namespace
void DisplayItemPropertyTreeBuilder::processBeginItem(const DisplayItem& displayItem)
{
BeginDisplayItemClassification classification = classifyBeginItem(displayItem);
if (!classification.transformKind && !classification.clipKind) {
currentState().ignoredBeginCount++;
return;
}
finishRange();
BuilderState newState(currentState());
newState.ignoredBeginCount = 0;
switch (classification.transformKind) {
case NotATransform:
break;
case Only2DTranslation:
// Adjust the offset associated with the current transform node.
newState.offset += classification.matrix.to2DTranslation();
break;
case RequiresTransformNode:
// Emit a transform node.
newState.transformNode = m_transformTree->createNewNode(
newState.transformNode, classification.matrix, classification.transformOrigin);
newState.offset = FloatSize();
break;
}
switch (classification.clipKind) {
case NotAClip:
break;
case RequiresClipNode:
// Emit a clip node.
FloatRect adjustedClipRect = classification.clipRect;
adjustedClipRect.move(newState.offset);
newState.clipNode = m_clipTree->createNewNode(
newState.clipNode, newState.transformNode, adjustedClipRect);
break;
}
m_stateStack.append(newState);
}
void DisplayItemPropertyTreeBuilder::processEndItem(const DisplayItem& displayItem)
{
if (currentState().ignoredBeginCount) {
// Ignored this end display item.
currentState().ignoredBeginCount--;
} else {
// We've closed the scope of some property.
finishRange();
m_stateStack.removeLast();
ASSERT(!m_stateStack.isEmpty());
}
}
void DisplayItemPropertyTreeBuilder::finishRange()
{
// Don't emit an empty range record.
if (m_rangeBeginIndex < m_currentIndex) {
const auto& current = currentState();
RangeRecord rangeRecord;
rangeRecord.displayListBeginIndex = m_rangeBeginIndex;
rangeRecord.displayListEndIndex = m_currentIndex;
rangeRecord.transformNodeIndex = current.transformNode;
rangeRecord.offset = current.offset;
rangeRecord.clipNodeIndex = current.clipNode;
m_rangeRecords.append(rangeRecord);
}
// The current display item is a boundary.
// The earliest the next range could begin is the next one.
m_rangeBeginIndex = m_currentIndex + 1;
}
} // namespace blink