0

SVG Text NG: Fix text layout after <svg> transform change

If 'transform' property of the owner <svg> is changed, and <text> is
painted without laying out, the content was painted at a wrong position
because the <text> kept positions computed with the old scaling factor.

This CL fixes it by invalidating <text> layout on 'transform' changes
on the owner <svg>.  This CL is similar to crrev.com/942976 but for
<svg>, not for containing blocks.

Bug: 1271931
Change-Id: Iefc7d9b50a8474ed536be2267ad7e5e237ed2546
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3293550
Commit-Queue: Kent Tamura <tkent@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Auto-Submit: Kent Tamura <tkent@chromium.org>
Reviewed-by: Koji Ishii <kojii@chromium.org>
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#943987}
This commit is contained in:
Kent Tamura
2021-11-22 09:06:42 +00:00
committed by Chromium LUCI CQ
parent 8610693146
commit 00e19d90d3
5 changed files with 54 additions and 0 deletions

@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
@@ -88,12 +89,25 @@ void LayoutNGSVGText::InsertedIntoTree() {
LayoutNGBlockFlowMixin<LayoutSVGBlock>::InsertedIntoTree();
for (LayoutBlock* cb = ContainingBlock(); cb; cb = cb->ContainingBlock())
cb->AddSvgTextDescendant(*this);
for (auto* ancestor = Parent(); ancestor; ancestor = ancestor->Parent()) {
if (auto* root = DynamicTo<LayoutSVGRoot>(ancestor)) {
root->AddSvgTextDescendant(*this);
break;
}
}
}
void LayoutNGSVGText::WillBeRemovedFromTree() {
NOT_DESTROYED();
for (LayoutBlock* cb = ContainingBlock(); cb; cb = cb->ContainingBlock())
cb->RemoveSvgTextDescendant(*this);
for (auto* ancestor = Parent(); ancestor; ancestor = ancestor->Parent()) {
if (auto* root = DynamicTo<LayoutSVGRoot>(ancestor)) {
root->RemoveSvgTextDescendant(*this);
break;
}
}
LayoutNGBlockFlowMixin<LayoutSVGBlock>::WillBeRemovedFromTree();
}

@@ -31,6 +31,7 @@
#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
@@ -70,6 +71,7 @@ LayoutSVGRoot::~LayoutSVGRoot() = default;
void LayoutSVGRoot::Trace(Visitor* visitor) const {
visitor->Trace(content_);
visitor->Trace(text_set_);
LayoutReplaced::Trace(visitor);
}
@@ -376,6 +378,15 @@ void LayoutSVGRoot::StyleDidChange(StyleDifference diff,
SVGResources::UpdateClipPathFilterMask(To<SVGSVGElement>(*GetNode()),
old_style, StyleRef());
if (diff.TransformChanged()) {
for (auto& svg_text : text_set_) {
svg_text->SetNeedsLayout(layout_invalidation_reason::kStyleChange,
kMarkContainerChain);
svg_text->SetNeedsTextMetricsUpdate();
}
}
if (!Parent())
return;
if (diff.HasDifference())
@@ -588,6 +599,18 @@ void LayoutSVGRoot::NotifyDescendantCompositingReasonsChanged() {
SetNeedsLayout(layout_invalidation_reason::kSvgChanged);
}
void LayoutSVGRoot::AddSvgTextDescendant(LayoutNGSVGText& svg_text) {
NOT_DESTROYED();
DCHECK(!text_set_.Contains(&svg_text));
text_set_.insert(&svg_text);
}
void LayoutSVGRoot::RemoveSvgTextDescendant(LayoutNGSVGText& svg_text) {
NOT_DESTROYED();
DCHECK(text_set_.Contains(&svg_text));
text_set_.erase(&svg_text);
}
PaintLayerType LayoutSVGRoot::LayerTypeRequired() const {
NOT_DESTROYED();
auto layer_type_required = LayoutReplaced::LayerTypeRequired();

@@ -28,6 +28,7 @@
namespace blink {
class LayoutNGSVGText;
class SVGElement;
enum class SVGTransformChange;
@@ -106,6 +107,9 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced {
}
void NotifyDescendantCompositingReasonsChanged();
void AddSvgTextDescendant(LayoutNGSVGText& svg_text);
void RemoveSvgTextDescendant(LayoutNGSVGText& svg_text);
const char* GetName() const override {
NOT_DESTROYED();
return "LayoutSVGRoot";
@@ -208,6 +212,7 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced {
SVGContentContainer content_;
LayoutSize container_size_;
AffineTransform local_to_border_box_transform_;
HeapHashSet<Member<LayoutNGSVGText>> text_set_;
bool is_layout_size_changed_ : 1;
bool did_screen_scale_factor_change_ : 1;
bool needs_boundaries_or_transform_update_ : 1;

@@ -7,5 +7,9 @@
<text fill="red" style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)">A</text>
</svg>
</div>
<svg width="500" height="400" style="transform-origin: 0px 0px; transform: scale(2) translate(-300px, -300px);">
<text x="300" y="350" font-size="50" fill="green">PASS</text>
</svg>
</body>
</html>

@@ -1,6 +1,7 @@
<!DOCTYPE html>
<html class="reftest-wait">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1270713">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1271931">
<link rel="match" href="transform-dynamic-change-ref.html">
<body>
@@ -11,11 +12,18 @@
</svg>
</div>
<svg id="svg2" width="500" height="400" style="transform-origin: 0px 0px; transform: translate(-300px, -300px);">
<text x="300" y="350" id="text2" font-size="50">PASS</text>
</svg>
<script>
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.getElementById('moveme').style.transform = 'matrix(0.9, 0, 0, 0.9, -210, -777)';
document.getElementById('txt').style.fill = 'red';
document.getElementById('svg2').style.transform = 'scale(2) translate(-300px, -300px)';
document.getElementById('text2').style.fill = 'green';
document.documentElement.classList.remove('reftest-wait');
});
});