Commit 1de8a82b authored by pdr's avatar pdr Committed by Commit bot

Implement SVG's transform and effect paint property nodes

This patch starts generating transform and effect paint property nodes
for SVG. PaintPropertyTreeBuilder has been changed to walk both
LayoutBoxModelObjects and SVG nodes. The effect node generation is
straightforward, but SVG transform nodes need some svg-specific logic:
  1) SVG does not use paint offset because everything is effectively
     absolutely positioned. The SVG root element now always creates a
     paint offset transform so all children do not need to.
  2) SVG bakes the transform origin into all matrices. We may want to
     refactor this later to support compositor animations.
  3) At the SVG->HTML boundary, out-of-flow and fixed positioning
     data is kept up to date by checking for both xforms and isSVGRoot.

BUG=537409

Review URL: https://codereview.chromium.org/1461223002

Cr-Commit-Position: refs/heads/master@{#361183}
parent 796348f8
...@@ -79,21 +79,26 @@ void PaintPropertyTreeBuilder::walk(FrameView& frameView, const PaintPropertyTre ...@@ -79,21 +79,26 @@ void PaintPropertyTreeBuilder::walk(FrameView& frameView, const PaintPropertyTre
walk(*layoutView, localContext); walk(*layoutView, localContext);
} }
static void deriveBorderBoxFromContainerContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
if (!object.isBoxModelObject())
return;
const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);
// TODO(trchen): There is some insanity going on with tables. Double check results. // TODO(trchen): There is some insanity going on with tables. Double check results.
switch (object.styleRef().position()) { switch (object.styleRef().position()) {
case StaticPosition: case StaticPosition:
break; break;
case RelativePosition: case RelativePosition:
context.paintOffset += object.offsetForInFlowPosition(); context.paintOffset += boxModelObject.offsetForInFlowPosition();
break; break;
case AbsolutePosition: case AbsolutePosition:
context.currentTransform = context.transformForOutOfFlowPositioned; context.currentTransform = context.transformForOutOfFlowPositioned;
context.paintOffset = context.paintOffsetForOutOfFlowPositioned; context.paintOffset = context.paintOffsetForOutOfFlowPositioned;
break; break;
case StickyPosition: case StickyPosition:
context.paintOffset += object.offsetForInFlowPosition(); context.paintOffset += boxModelObject.offsetForInFlowPosition();
break; break;
case FixedPosition: case FixedPosition:
context.currentTransform = context.transformForFixedPositioned; context.currentTransform = context.transformForFixedPositioned;
...@@ -102,14 +107,21 @@ static void deriveBorderBoxFromContainerContext(const LayoutBoxModelObject& obje ...@@ -102,14 +107,21 @@ static void deriveBorderBoxFromContainerContext(const LayoutBoxModelObject& obje
default: default:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
if (object.isBox()) if (boxModelObject.isBox())
context.paintOffset += toLayoutBox(object).locationOffset(); context.paintOffset += toLayoutBox(boxModelObject).locationOffset();
} }
static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
// TODO(trchen): Eliminate PaintLayer dependency. bool shouldCreatePaintOffsetTranslationNode = false;
bool shouldCreatePaintOffsetTranslationNode = object.layer() && object.layer()->paintsWithTransform(GlobalPaintNormalPhase); if (object.isSVGRoot()) {
// SVG doesn't use paint offset internally so emit a paint offset at the html->svg boundary.
shouldCreatePaintOffsetTranslationNode = true;
} else if (object.isBoxModelObject()) {
// TODO(trchen): Eliminate PaintLayer dependency.
PaintLayer* layer = toLayoutBoxModelObject(object).layer();
shouldCreatePaintOffsetTranslationNode = layer && layer->paintsWithTransform(GlobalPaintNormalPhase);
}
if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslationNode) if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslationNode)
return nullptr; return nullptr;
...@@ -132,8 +144,20 @@ static FloatPoint3D transformOrigin(const LayoutBox& box) ...@@ -132,8 +144,20 @@ static FloatPoint3D transformOrigin(const LayoutBox& box)
style.transformOriginZ()); style.transformOriginZ());
} }
static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
if (object.isSVG() && !object.isSVGRoot()) {
const AffineTransform& transform = object.localToParentTransform();
if (transform.isIdentity())
return nullptr;
// SVG's transform origin is baked into the localToParentTransform.
RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create(
transform, FloatPoint3D(0, 0, 0), context.currentTransform);
context.currentTransform = newTransformNodeForTransform.get();
return newTransformNodeForTransform.release();
}
const ComputedStyle& style = object.styleRef(); const ComputedStyle& style = object.styleRef();
if (!object.isBox() || !style.hasTransform()) if (!object.isBox() || !style.hasTransform())
return nullptr; return nullptr;
...@@ -149,10 +173,10 @@ static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const Layo ...@@ -149,10 +173,10 @@ static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const Layo
return newTransformNodeForTransform.release(); return newTransformNodeForTransform.release();
} }
static PassRefPtr<EffectPaintPropertyNode> createEffectIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static PassRefPtr<EffectPaintPropertyNode> createEffectIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
const ComputedStyle& style = object.styleRef(); const ComputedStyle& style = object.styleRef();
if (!object.isBox() || !style.hasOpacity()) if (!style.hasOpacity())
return nullptr; return nullptr;
RefPtr<EffectPaintPropertyNode> newEffectNode = EffectPaintPropertyNode::create(style.opacity(), context.currentEffect); RefPtr<EffectPaintPropertyNode> newEffectNode = EffectPaintPropertyNode::create(style.opacity(), context.currentEffect);
context.currentEffect = newEffectNode.get(); context.currentEffect = newEffectNode.get();
...@@ -168,7 +192,7 @@ static FloatPoint perspectiveOrigin(const LayoutBox& box) ...@@ -168,7 +192,7 @@ static FloatPoint perspectiveOrigin(const LayoutBox& box)
floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height())); floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height()));
} }
static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
const ComputedStyle& style = object.styleRef(); const ComputedStyle& style = object.styleRef();
if (!object.isBox() || !style.hasPerspective()) if (!object.isBox() || !style.hasPerspective())
...@@ -181,12 +205,12 @@ static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const La ...@@ -181,12 +205,12 @@ static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const La
return newTransformNodeForPerspective.release(); return newTransformNodeForPerspective.release();
} }
static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{ {
if (!object.hasOverflowClip()) if (!object.isBoxModelObject() || !object.hasOverflowClip())
return nullptr; return nullptr;
PaintLayer* layer = object.layer(); PaintLayer* layer = toLayoutBoxModelObject(object).layer();
ASSERT(layer); ASSERT(layer);
DoubleSize scrollOffset = layer->scrollableArea()->scrollOffset(); DoubleSize scrollOffset = layer->scrollableArea()->scrollOffset();
if (scrollOffset.isZero() && !layer->scrollsOverflow()) if (scrollOffset.isZero() && !layer->scrollsOverflow())
...@@ -199,24 +223,26 @@ static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(co ...@@ -199,24 +223,26 @@ static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(co
return newTransformNodeForScrollTranslation.release(); return newTransformNodeForScrollTranslation.release();
} }
static void updateOutOfFlowContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) static void updateOutOfFlowContext(const LayoutObject& object, bool createdNewTransform, PaintPropertyTreeBuilderContext& context)
{ {
const ComputedStyle& style = object.styleRef(); // At the html->svg boundary (see: createPaintOffsetTranslationIfNeeded) the currentTransform is
bool hasTransform = object.isBox() && style.hasTransform(); // up-to-date for all children of the svg root element. Additionally, inside SVG, all positioning
if (style.position() != StaticPosition || hasTransform) { // uses transforms. Therefore, we only need to check createdNewTransform and isSVGRoot() to
// ensure out-of-flow and fixed positioning is correct at the svg->html boundary.
if (object.styleRef().position() != StaticPosition || createdNewTransform || object.isSVGRoot()) {
context.transformForOutOfFlowPositioned = context.currentTransform; context.transformForOutOfFlowPositioned = context.currentTransform;
context.paintOffsetForOutOfFlowPositioned = context.paintOffset; context.paintOffsetForOutOfFlowPositioned = context.paintOffset;
} }
if (hasTransform) {
if (createdNewTransform || object.isSVGRoot()) {
context.transformForFixedPositioned = context.currentTransform; context.transformForFixedPositioned = context.currentTransform;
context.paintOffsetForFixedPositioned = context.paintOffset; context.paintOffsetForFixedPositioned = context.paintOffset;
} }
} }
void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPropertyTreeBuilderContext& context) void PaintPropertyTreeBuilder::walk(LayoutObject& object, const PaintPropertyTreeBuilderContext& context)
{ {
ASSERT(object.isBox() != object.isLayoutInline()); // Either or.
PaintPropertyTreeBuilderContext localContext(context); PaintPropertyTreeBuilderContext localContext(context);
deriveBorderBoxFromContainerContext(object, localContext); deriveBorderBoxFromContainerContext(object, localContext);
...@@ -225,7 +251,7 @@ void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPro ...@@ -225,7 +251,7 @@ void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPro
RefPtr<EffectPaintPropertyNode> newEffectNode = createEffectIfNeeded(object, localContext); RefPtr<EffectPaintPropertyNode> newEffectNode = createEffectIfNeeded(object, localContext);
RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPerspectiveIfNeeded(object, localContext); RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPerspectiveIfNeeded(object, localContext);
RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = createScrollTranslationIfNeeded(object, localContext); RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = createScrollTranslationIfNeeded(object, localContext);
updateOutOfFlowContext(object, localContext); updateOutOfFlowContext(object, newTransformNodeForTransform, localContext);
if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransform || newEffectNode || newTransformNodeForPerspective || newTransformNodeForScrollTranslation) { if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransform || newEffectNode || newTransformNodeForPerspective || newTransformNodeForScrollTranslation) {
OwnPtr<ObjectPaintProperties> updatedPaintProperties = ObjectPaintProperties::create( OwnPtr<ObjectPaintProperties> updatedPaintProperties = ObjectPaintProperties::create(
...@@ -241,15 +267,9 @@ void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPro ...@@ -241,15 +267,9 @@ void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPro
// TODO(trchen): Walk subframes for LayoutFrame. // TODO(trchen): Walk subframes for LayoutFrame.
// TODO(trchen): Implement SVG walk.
if (object.isSVGRoot()) {
return;
}
for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) { for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) {
if (child->isText()) if (child->isBoxModelObject() || child->isSVG())
continue; walk(*child, localContext);
walk(toLayoutBoxModelObject(*child), localContext);
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
namespace blink { namespace blink {
class FrameView; class FrameView;
class LayoutBoxModelObject; class LayoutObject;
struct PaintPropertyTreeBuilderContext; struct PaintPropertyTreeBuilderContext;
// This class walks the whole layout tree, beginning from the root FrameView, across // This class walks the whole layout tree, beginning from the root FrameView, across
...@@ -23,7 +23,7 @@ public: ...@@ -23,7 +23,7 @@ public:
private: private:
void walk(FrameView&, const PaintPropertyTreeBuilderContext&); void walk(FrameView&, const PaintPropertyTreeBuilderContext&);
void walk(LayoutBoxModelObject&, const PaintPropertyTreeBuilderContext&); void walk(LayoutObject&, const PaintPropertyTreeBuilderContext&);
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment