Skip to content

Commit

Permalink
[LBSE] Activate SVG transform support through layers
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=237711

Reviewed by Rob Buis.

Activate transforms for SVG layers.

Use the flag to keep track of the presence of SVG transformations
on a renderer. CSS/HTML renderers can rely only on the RenderStyle
to determine if transformations are applied, whereas SVG has to
consult additional sources, such as the SVG 'transform' attribute.

A SVG renderer with the 'HasSVGTransform' flag set should behave
like any SVG/HTML renderer that has CSS transformations applied.

With this patch applied SVG transform and CSS transforms can
be applied to <g> / <rect> elements in LBSE. However, the order
of the matrix multiplication is not correct yet for SVG -- that
will be adressed in a follow-up patch.

Covered by existing tests, no change in behaviour.

* platform/graphics/transforms/TransformationMatrix.cpp:
(WebCore::TransformationMatrix::multiplyAffineTransform):
* platform/graphics/transforms/TransformationMatrix.h:
* rendering/RenderBox.cpp:
(WebCore::RenderBox::updateLayerTransform): Deleted.
* rendering/RenderBox.h:
* rendering/RenderLayer.cpp:
(WebCore::canCreateStackingContext):
(WebCore::RenderLayer::currentTransform const):
* rendering/RenderLayerModelObject.cpp:
(WebCore::RenderLayerModelObject::updateLayerTransform):
(WebCore::RenderLayerModelObject::applySVGTransform const):
* rendering/RenderLayerModelObject.h:
* rendering/RenderObject.cpp:
(WebCore::RenderObject::setHasSVGTransform):
* rendering/RenderObject.h:
(WebCore::RenderObject::hasSVGTransform const):
(WebCore::RenderObject::hasTransform const):
* rendering/svg/RenderSVGContainer.cpp:
(WebCore::SVGLayerTransformUpdater::SVGLayerTransformUpdater):
(WebCore::SVGLayerTransformUpdater::~SVGLayerTransformUpdater):
(WebCore::RenderSVGContainer::layout):
* rendering/svg/RenderSVGContainer.h:
(WebCore::RenderSVGContainer::updateLayerInformation): Deleted.
* rendering/svg/RenderSVGModelObject.cpp:
(WebCore::RenderSVGModelObject::updateFromStyle):
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::updateFromStyle):
* rendering/svg/RenderSVGShape.cpp:
(WebCore::RenderSVGShape::layout):
(WebCore::RenderSVGShape::applyTransform const):
* rendering/svg/RenderSVGShape.h:
* rendering/svg/RenderSVGTransformableContainer.cpp:
(WebCore::RenderSVGTransformableContainer::updateFromStyle):
(WebCore::RenderSVGTransformableContainer::applyTransform const):
* svg/SVGGraphicsElement.cpp:
(WebCore::SVGGraphicsElement::animatedLocalTransform const):

Canonical link: https://commits.webkit.org/249482@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@292690 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Nikolas Zimmermann committed Apr 10, 2022
1 parent 4698fe3 commit 29ecc6f
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 41 deletions.
62 changes: 62 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,65 @@
2022-04-10 Nikolas Zimmermann <nzimmermann@igalia.com>

[LBSE] Activate SVG transform support through layers
https://bugs.webkit.org/show_bug.cgi?id=237711

Reviewed by Rob Buis.

Activate transforms for SVG layers.

Use the flag to keep track of the presence of SVG transformations
on a renderer. CSS/HTML renderers can rely only on the RenderStyle
to determine if transformations are applied, whereas SVG has to
consult additional sources, such as the SVG 'transform' attribute.

A SVG renderer with the 'HasSVGTransform' flag set should behave
like any SVG/HTML renderer that has CSS transformations applied.

With this patch applied SVG transform and CSS transforms can
be applied to <g> / <rect> elements in LBSE. However, the order
of the matrix multiplication is not correct yet for SVG -- that
will be adressed in a follow-up patch.

Covered by existing tests, no change in behaviour.

* platform/graphics/transforms/TransformationMatrix.cpp:
(WebCore::TransformationMatrix::multiplyAffineTransform):
* platform/graphics/transforms/TransformationMatrix.h:
* rendering/RenderBox.cpp:
(WebCore::RenderBox::updateLayerTransform): Deleted.
* rendering/RenderBox.h:
* rendering/RenderLayer.cpp:
(WebCore::canCreateStackingContext):
(WebCore::RenderLayer::currentTransform const):
* rendering/RenderLayerModelObject.cpp:
(WebCore::RenderLayerModelObject::updateLayerTransform):
(WebCore::RenderLayerModelObject::applySVGTransform const):
* rendering/RenderLayerModelObject.h:
* rendering/RenderObject.cpp:
(WebCore::RenderObject::setHasSVGTransform):
* rendering/RenderObject.h:
(WebCore::RenderObject::hasSVGTransform const):
(WebCore::RenderObject::hasTransform const):
* rendering/svg/RenderSVGContainer.cpp:
(WebCore::SVGLayerTransformUpdater::SVGLayerTransformUpdater):
(WebCore::SVGLayerTransformUpdater::~SVGLayerTransformUpdater):
(WebCore::RenderSVGContainer::layout):
* rendering/svg/RenderSVGContainer.h:
(WebCore::RenderSVGContainer::updateLayerInformation): Deleted.
* rendering/svg/RenderSVGModelObject.cpp:
(WebCore::RenderSVGModelObject::updateFromStyle):
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::updateFromStyle):
* rendering/svg/RenderSVGShape.cpp:
(WebCore::RenderSVGShape::layout):
(WebCore::RenderSVGShape::applyTransform const):
* rendering/svg/RenderSVGShape.h:
* rendering/svg/RenderSVGTransformableContainer.cpp:
(WebCore::RenderSVGTransformableContainer::updateFromStyle):
(WebCore::RenderSVGTransformableContainer::applyTransform const):
* svg/SVGGraphicsElement.cpp:
(WebCore::SVGGraphicsElement::animatedLocalTransform const):

2022-04-10 Alan Bujtas <zalan@apple.com>

Line clamp specific line-count code should be in RenderDeprecatedFlexibleBox
Expand Down
Expand Up @@ -1533,6 +1533,17 @@ TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix&
return *this;
}

TransformationMatrix& TransformationMatrix::multiplyAffineTransform(const AffineTransform& matrix)
{
if (matrix.isIdentity())
return *this;

if (matrix.isIdentityOrTranslation())
return translate(matrix.e(), matrix.f());

return multiply(matrix.toTransformationMatrix());
}

void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const
{
resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0];
Expand Down
Expand Up @@ -249,6 +249,8 @@ class TransformationMatrix {

// this = mat * this.
WEBCORE_EXPORT TransformationMatrix& multiply(const TransformationMatrix&);
// Identical to multiply(TransformationMatrix&), but saving a AffineTransform -> TransformationMatrix roundtrip for identity or translation matrices.
TransformationMatrix& multiplyAffineTransform(const AffineTransform&);

WEBCORE_EXPORT TransformationMatrix& scale(double);
WEBCORE_EXPORT TransformationMatrix& scaleNonUniform(double sx, double sy);
Expand Down
7 changes: 0 additions & 7 deletions Source/WebCore/rendering/RenderBox.cpp
Expand Up @@ -667,13 +667,6 @@ void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
quads.append(localToAbsoluteQuad(localRect, UseTransforms, wasFixed));
}

void RenderBox::updateLayerTransform()
{
// Transform-origin depends on box size, so we need to update the layer transform after layout.
if (hasLayer())
layer()->updateTransform();
}

void RenderBox::applyTransform(TransformationMatrix& t, const RenderStyle& style, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> options) const
{
style.applyTransform(t, boundingBox, options);
Expand Down
1 change: 0 additions & 1 deletion Source/WebCore/rendering/RenderBox.h
Expand Up @@ -218,7 +218,6 @@ class RenderBox : public RenderBoxModelObject {
void addOverflowFromChild(const RenderBox* child) { addOverflowFromChild(child, child->locationOffset()); }
void addOverflowFromChild(const RenderBox* child, const LayoutSize& delta);

void updateLayerTransform();
void applyTransform(TransformationMatrix&, const RenderStyle&, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> = RenderStyle::allTransformOperations) const override;

LayoutSize contentSize() const { return { contentWidth(), contentHeight() }; }
Expand Down
13 changes: 5 additions & 8 deletions Source/WebCore/rendering/RenderLayer.cpp
Expand Up @@ -552,6 +552,9 @@ static bool canCreateStackingContext(const RenderLayer& layer)
{
auto& renderer = layer.renderer();
return renderer.hasTransformRelatedProperty()
#if ENABLE(LAYER_BASED_SVG_ENGINE)
|| renderer.hasSVGTransform()
#endif
|| renderer.hasClipPath()
|| renderer.hasFilter()
|| renderer.hasMask()
Expand Down Expand Up @@ -1357,19 +1360,13 @@ TransformationMatrix RenderLayer::currentTransform(OptionSet<RenderStyle::Transf
if (!m_transform)
return { };

// FIXME: [LBSE] Upstream transform support for RenderSVGModelObject derived renderers
if (!is<RenderBox>(renderer()))
return { };

auto& renderBox = downcast<RenderBox>(renderer());

// m_transform includes transform-origin and is affected by the choice of the transform-box.
// Therefore we can only use the cached m_transform, if the animation doesn't alter transform-box or excludes transform-origin.

// Query the animatedStyle() to obtain the current transformation, when accelerated transform animations are running.
auto styleable = Styleable::fromRenderer(renderBox);
auto styleable = Styleable::fromRenderer(renderer());
if ((styleable && styleable->isRunningAcceleratedTransformAnimation()) || !options.contains(RenderStyle::TransformOperationOption::TransformOrigin)) {
std::unique_ptr<RenderStyle> animatedStyle = renderBox.animatedStyle();
std::unique_ptr<RenderStyle> animatedStyle = renderer().animatedStyle();

TransformationMatrix transform;
updateTransformFromStyle(transform, *animatedStyle, options);
Expand Down
40 changes: 40 additions & 0 deletions Source/WebCore/rendering/RenderLayerModelObject.cpp
Expand Up @@ -31,6 +31,7 @@
#include "RenderLayerScrollableArea.h"
#include "RenderSVGModelObject.h"
#include "RenderView.h"
#include "SVGGraphicsElement.h"
#include "Settings.h"
#include "StyleScrollSnapPoints.h"
#include "TransformState.h"
Expand Down Expand Up @@ -231,6 +232,13 @@ void RenderLayerModelObject::suspendAnimations(MonotonicTime time)
layer()->backing()->suspendAnimations(time);
}

void RenderLayerModelObject::updateLayerTransform()
{
// Transform-origin depends on box size, so we need to update the layer transform after layout.
if (hasLayer())
layer()->updateTransform();
}

#if ENABLE(LAYER_BASED_SVG_ENGINE)
std::optional<LayoutRect> RenderLayerModelObject::computeVisibleRectInSVGContainer(const LayoutRect& rect, const RenderLayerModelObject* container, RenderObject::VisibleRectContext context) const
{
Expand Down Expand Up @@ -316,6 +324,38 @@ void RenderLayerModelObject::mapLocalToSVGContainer(const RenderLayerModelObject

container->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed);
}

void RenderLayerModelObject::applySVGTransform(TransformationMatrix& transform, SVGGraphicsElement& graphicsElement, const RenderStyle& style, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> options) const
{
// This check does not use style.hasTransformRelatedProperty() on purpose -- we only want to know if either the 'transform' property, an
// offset path, or the individual transform operations are set (perspective / transform-style: preserve-3d are not relevant here).
bool hasCSSTransform = style.hasTransform() || style.rotate() || style.translate() || style.scale();

bool affectedByTransformOrigin = false;
std::optional<AffineTransform> svgTransform;

if (hasCSSTransform)
affectedByTransformOrigin = style.affectedByTransformOrigin();
else if (auto affineTransform = graphicsElement.animatedLocalTransform(); !affineTransform.isIdentity()) {
svgTransform = affineTransform;
affectedByTransformOrigin = affineTransform.a() != 1 || affineTransform.b() || affineTransform.c() || affineTransform.d() != 1;
}

if (!hasCSSTransform && !svgTransform.has_value())
return;

FloatPoint3D originTranslate;
if (options.contains(RenderStyle::TransformOperationOption::TransformOrigin) && affectedByTransformOrigin)
originTranslate = style.applyTransformOrigin(transform, boundingBox);

// CSS transforms take precedence over SVG transforms.
if (hasCSSTransform)
style.applyCSSTransform(transform, boundingBox, options);
else
transform.multiplyAffineTransform(svgTransform.value());

style.unapplyTransformOrigin(transform, originTranslate);
}
#endif

bool rendererNeedsPixelSnapping(const RenderLayerModelObject& renderer)
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/rendering/RenderLayerModelObject.h
Expand Up @@ -28,6 +28,7 @@ namespace WebCore {

class KeyframeList;
class RenderLayer;
class SVGGraphicsElement;

struct LayerRepaintRects {
LayoutRect clippedOverflowRect;
Expand Down Expand Up @@ -75,8 +76,12 @@ class RenderLayerModelObject : public RenderElement {
// Provides the SVG implementation for mapLocalToContainer().
// This lives in RenderLayerModelObject, which is the common base-class for all SVG renderers.
void mapLocalToSVGContainer(const RenderLayerModelObject* ancestorContainer, TransformState&, OptionSet<MapCoordinatesMode>, bool* wasFixed) const;

void applySVGTransform(TransformationMatrix&, SVGGraphicsElement&, const RenderStyle&, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption>) const;
#endif

void updateLayerTransform();

virtual void applyTransform(TransformationMatrix&, const RenderStyle&, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> = RenderStyle::allTransformOperations) const = 0;

protected:
Expand Down
8 changes: 8 additions & 0 deletions Source/WebCore/rendering/RenderObject.cpp
Expand Up @@ -1987,6 +1987,14 @@ void RenderObject::setHasOutlineAutoAncestor(bool hasOutlineAutoAncestor)
ensureRareData().setHasOutlineAutoAncestor(hasOutlineAutoAncestor);
}

#if ENABLE(LAYER_BASED_SVG_ENGINE)
void RenderObject::setHasSVGTransform(bool hasSVGTransform)
{
if (hasSVGTransform || hasRareData())
ensureRareData().setHasSVGTransform(hasSVGTransform);
}
#endif

void RenderObject::setPaintContainmentApplies(bool paintContainmentApplies)
{
if (paintContainmentApplies || hasRareData())
Expand Down
14 changes: 13 additions & 1 deletion Source/WebCore/rendering/RenderObject.h
Expand Up @@ -416,6 +416,12 @@ class RenderObject : public CachedImageClient, public CanMakeWeakPtr<RenderObjec
bool hasOutlineAutoAncestor() const { return m_bitfields.hasRareData() && rareData().hasOutlineAutoAncestor(); }
bool paintContainmentApplies() const { return m_bitfields.hasRareData() && rareData().paintContainmentApplies(); }

#if ENABLE(LAYER_BASED_SVG_ENGINE)
bool hasSVGTransform() const { return m_bitfields.hasRareData() && rareData().hasSVGTransform(); }
#else
bool hasSVGTransform() const { return false; }
#endif

bool isExcludedFromNormalLayout() const { return m_bitfields.isExcludedFromNormalLayout(); }
void setIsExcludedFromNormalLayout(bool excluded) { m_bitfields.setIsExcludedFromNormalLayout(excluded); }
bool isExcludedAndPlacedInBorder() const { return isExcludedFromNormalLayout() && isLegend(); }
Expand Down Expand Up @@ -450,7 +456,7 @@ class RenderObject : public CachedImageClient, public CanMakeWeakPtr<RenderObjec
bool hasPotentiallyScrollableOverflow() const;

bool hasTransformRelatedProperty() const { return m_bitfields.hasTransformRelatedProperty(); } // Transform, perspective or transform-style: preserve-3d.
bool hasTransform() const { return hasTransformRelatedProperty() && (style().hasTransform() || style().translate() || style().scale() || style().rotate()); }
bool hasTransform() const { return hasTransformRelatedProperty() && (style().hasTransform() || style().translate() || style().scale() || style().rotate() || hasSVGTransform()); }
bool hasTransformOrPespective() const { return hasTransformRelatedProperty() && (hasTransform() || style().hasPerspective()); }

inline bool preservesNewline() const;
Expand Down Expand Up @@ -515,6 +521,9 @@ class RenderObject : public CachedImageClient, public CanMakeWeakPtr<RenderObjec
void setIsRenderFragmentedFlow(bool = true);
void setHasOutlineAutoAncestor(bool = true);
void setPaintContainmentApplies(bool = true);
#if ENABLE(LAYER_BASED_SVG_ENGINE)
void setHasSVGTransform(bool = true);
#endif

// Hook so that RenderTextControl can return the line height of its inner renderer.
// For other renderers, the value is the same as lineHeight(false).
Expand Down Expand Up @@ -944,6 +953,9 @@ class RenderObject : public CachedImageClient, public CanMakeWeakPtr<RenderObjec
ADD_BOOLEAN_BITFIELD(isRenderFragmentedFlow, IsRenderFragmentedFlow);
ADD_BOOLEAN_BITFIELD(hasOutlineAutoAncestor, HasOutlineAutoAncestor);
ADD_BOOLEAN_BITFIELD(paintContainmentApplies, PaintContainmentApplies);
#if ENABLE(LAYER_BASED_SVG_ENGINE)
ADD_BOOLEAN_BITFIELD(hasSVGTransform, HasSVGTransform);
#endif

// From RenderElement
std::unique_ptr<ReferencedSVGResources> referencedSVGResources;
Expand Down
35 changes: 31 additions & 4 deletions Source/WebCore/rendering/svg/RenderSVGContainer.cpp
Expand Up @@ -52,6 +52,36 @@ RenderSVGContainer::RenderSVGContainer(SVGElement& element, RenderStyle&& style)

RenderSVGContainer::~RenderSVGContainer() = default;

// Helper class, move to its own file once utilized in more than one place.
class SVGLayerTransformUpdater {
WTF_MAKE_NONCOPYABLE(SVGLayerTransformUpdater);
public:
SVGLayerTransformUpdater(RenderLayerModelObject& renderer)
: m_renderer(renderer)
{
if (!m_renderer.hasLayer())
return;

m_transformReferenceBox = m_renderer.transformReferenceBoxRect();
m_renderer.updateLayerTransform();
}


~SVGLayerTransformUpdater()
{
if (!m_renderer.hasLayer())
return;
if (m_renderer.transformReferenceBoxRect() == m_transformReferenceBox)
return;

m_renderer.updateLayerTransform();
}

private:
RenderLayerModelObject& m_renderer;
FloatRect m_transformReferenceBox;
};

void RenderSVGContainer::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
Expand All @@ -64,13 +94,10 @@ void RenderSVGContainer::layout()
// Update layer transform before laying out children (SVG needs access to the transform matrices during layout for on-screen text font-size calculations).
// Eventually re-update if the transform reference box, relevant for transform-origin, has changed during layout.
{
// FIXME: [LBSE] Upstream SVGLayerTransformUpdater
// SVGLayerTransformUpdater transformUpdater(*this);
SVGLayerTransformUpdater updateTransform(*this);
layoutChildren();
}

updateLayerInformation();

// Invalidate all resources of this client if our layout changed.
if (everHadLayout() && needsLayout())
SVGResourcesCache::clientLayoutChanged(*this);
Expand Down
1 change: 0 additions & 1 deletion Source/WebCore/rendering/svg/RenderSVGContainer.h
Expand Up @@ -55,7 +55,6 @@ class RenderSVGContainer : public RenderSVGModelObject {
virtual void layoutChildren();
bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) override;

virtual void updateLayerInformation() { }
virtual void calculateViewport();
virtual bool pointIsInsideViewportClip(const FloatPoint&) { return true; }

Expand Down
13 changes: 6 additions & 7 deletions Source/WebCore/rendering/svg/RenderSVGModelObject.cpp
Expand Up @@ -36,6 +36,7 @@
#include "RenderGeometryMap.h"
#include "RenderLayer.h"
#include "RenderLayerModelObject.h"
#include "RenderSVGModelObjectInlines.h"
#include "RenderSVGResource.h"
#include "RenderView.h"
#include "SVGElementInlines.h"
Expand All @@ -59,15 +60,13 @@ RenderSVGModelObject::RenderSVGModelObject(SVGElement& element, RenderStyle&& st
void RenderSVGModelObject::updateFromStyle()
{
RenderLayerModelObject::updateFromStyle();
setHasTransformRelatedProperty(style().hasTransformRelatedProperty());

AffineTransform transform;
if (is<SVGGraphicsElement>(nodeForNonAnonymous()))
transform = downcast<SVGGraphicsElement>(nodeForNonAnonymous()).animatedLocalTransform();
bool hasSVGTransform = false;
if (is<SVGGraphicsElement>(element()))
hasSVGTransform = !downcast<SVGGraphicsElement>(element()).animatedLocalTransform().isIdentity();

// FIXME: [LBSE] Upstream RenderObject changes
// if (!transform.isIdentity())
// setHasSVGTransform();
setHasTransformRelatedProperty(style().hasTransformRelatedProperty() || hasSVGTransform);
setHasSVGTransform(hasSVGTransform);
}

FloatRect RenderSVGModelObject::borderBoxRectInFragmentEquivalent(RenderFragmentContainer*, RenderBox::RenderBoxFragmentInfoFlags) const
Expand Down
3 changes: 1 addition & 2 deletions Source/WebCore/rendering/svg/RenderSVGRoot.cpp
Expand Up @@ -396,8 +396,7 @@ void RenderSVGRoot::updateFromStyle()
{
RenderReplaced::updateFromStyle();

// FIXME: [LBSE] Upstream RenderObject changes
// setHasSVGTransform();
setHasSVGTransform();

if (shouldApplyViewportClip())
setHasNonVisibleOverflow();
Expand Down

0 comments on commit 29ecc6f

Please sign in to comment.