Commit 109f9eed authored by Philip Rogers's avatar Philip Rogers Committed by Commit Bot

Do not cull svg image or shape painting when there are transform styles

This patch stops culling out painting of svg images and svg shapes in the
presence of transform styles. This was done for containers in crrev.com/575855
but needs to apply to all svg painting.

SVGModelObjectPainter has been introduced so the cull rect optimization can be
shared by shapes, images, and containers. A lengthy comment has been added in
SVGModelObjectPainter::CullRectSkipsPainting describing why. While we're in
the area and thinking about sharing code, the common outline painting code for
has also been unified (SVGModelObjectPainter::PaintOutline).

Bug: 865965, 862318
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I5bb3d43ae089d4bd6c617957abf9c401dfb89e0c
Reviewed-on: https://chromium-review.googlesource.com/1146147
Commit-Queue: Philip Rogers <pdr@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577593}
parent 75bb0d02
<!DOCTYPE html>
<script src="../../resources/run-after-layout-and-paint.js"></script>
<svg style="width: 400px; height: 400px">
<image xlink:href="../custom/resources/green-rect.svg" width="100" height="100" style="transform: translate(1px, 1px);"></image>
</svg>
<!DOCTYPE html>
<script src="../../resources/run-after-layout-and-paint.js"></script>
<svg style="width: 400px; height: 400px">
<image id="image" xlink:href="../custom/resources/green-rect.svg" width="100" height="100"></image>
</svg>
<script>
image.style.transform = "translate(-300px, -300px)";
if (window.testRunner)
testRunner.waitUntilDone();
onload = function() {
runAfterLayoutAndPaint(() => {
image.style.transform = "translate(1px, 1px)";
window.testRunner.notifyDone();
});
}
</script>
<!DOCTYPE html>
<script src="../../resources/run-after-layout-and-paint.js"></script>
<svg style="width: 400px; height: 400px">
<rect width="100" height="100" fill="green" style="transform: translate(1px, 1px);"></rect>
</svg>
<!DOCTYPE html>
<script src="../../resources/run-after-layout-and-paint.js"></script>
<svg style="width: 400px; height: 400px">
<rect id="rect" width="100" height="100" fill="green"></rect>
</svg>
<script>
rect.style.transform = "translate(-300px, -300px)";
if (window.testRunner)
testRunner.waitUntilDone();
runAfterLayoutAndPaint(() => {
rect.style.transform = "translate(1px, 1px)";
window.testRunner.notifyDone();
});
</script>
......@@ -216,6 +216,8 @@ blink_core_sources("paint") {
"svg_inline_text_box_painter.h",
"svg_mask_painter.cc",
"svg_mask_painter.h",
"svg_model_object_painter.cc",
"svg_model_object_painter.h",
"svg_paint_context.cc",
"svg_paint_context.h",
"svg_root_inline_box_painter.cc",
......
......@@ -817,9 +817,12 @@ PaintResult PaintLayerPainter::PaintSingleFragment(
PaintLayerFlags paint_flags,
const PaintLayerFragment& fragment,
const LayoutSize& subpixel_accumulation) {
// Now do a paint with the root layer shifted to be us. The cull rect
// is infinite to simplify invalidation of clip property node changes across
// transform boundaries.
// Now do a paint with the root layer shifted to be us.
// We do not apply cull rect optimizations across transforms for two reasons:
// 1) Performance: We can optimize transform changes by not repainting.
// 2) Complexity: Difficulty updating clips when ancestor transforms change.
// For these reasons, we use an infinite dirty rect here.
PaintLayerPaintingInfo new_paint_info(
&paint_layer_, LayoutRect(LayoutRect::InfiniteIntRect()),
painting_info.GetGlobalPaintFlags(), subpixel_accumulation);
......
......@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/paint/object_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/svg_foreign_object_painter.h"
#include "third_party/blink/renderer/core/paint/svg_model_object_painter.h"
#include "third_party/blink/renderer/core/paint/svg_paint_context.h"
#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
......@@ -24,11 +25,6 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) {
!layout_svg_container_.SelfWillPaint())
return;
FloatRect bounding_box =
layout_svg_container_.VisualRectInLocalSVGCoordinates();
// LayoutSVGHiddenContainer's visual rect is always empty but we need to
// paint its descendants.
// Spec: An empty viewBox on the <svg> element disables rendering.
DCHECK(layout_svg_container_.GetElement());
if (IsSVGSVGElement(*layout_svg_container_.GetElement()) &&
......@@ -37,17 +33,19 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) {
PaintInfo paint_info_before_filtering(paint_info);
// Content underneath transforms applies an infinite cull rect. This is
// to simplify invalidation of clip property node changes across transform
// boundaries.
if (SVGModelObjectPainter(layout_svg_container_)
.CullRectSkipsPainting(paint_info_before_filtering)) {
return;
}
// We do not apply cull rect optimizations across transforms for two reasons:
// 1) Performance: We can optimize transform changes by not repainting.
// 2) Complexity: Difficulty updating clips when ancestor transforms change.
// This is why we use an infinite cull rect if there is a transform. Non-svg
// content, does this in PaintLayerPainter::PaintSingleFragment.
if (layout_svg_container_.StyleRef().HasTransform()) {
paint_info_before_filtering.ApplyInfiniteCullRect();
} else {
if (!layout_svg_container_.IsSVGHiddenContainer() &&
!paint_info.GetCullRect().IntersectsCullRect(
layout_svg_container_.LocalToSVGParentTransform(), bounding_box))
return;
paint_info_before_filtering.UpdateCullRect(
layout_svg_container_.LocalToSVGParentTransform());
}
......@@ -94,20 +92,14 @@ void SVGContainerPainter::Paint(const PaintInfo& paint_info) {
}
}
if (paint_info_before_filtering.phase != PaintPhase::kForeground)
return;
SVGModelObjectPainter(layout_svg_container_)
.PaintOutline(paint_info_before_filtering);
if (layout_svg_container_.Style()->OutlineWidth() &&
layout_svg_container_.Style()->Visibility() == EVisibility::kVisible) {
PaintInfo outline_paint_info(paint_info_before_filtering);
outline_paint_info.phase = PaintPhase::kSelfOutlineOnly;
ObjectPainter(layout_svg_container_)
.PaintOutline(outline_paint_info, LayoutPoint(bounding_box.Location()));
}
if (paint_info_before_filtering.IsPrinting())
if (paint_info_before_filtering.IsPrinting() &&
paint_info_before_filtering.phase == PaintPhase::kForeground) {
ObjectPainter(layout_svg_container_)
.AddPDFURLRectIfNeeded(paint_info_before_filtering, LayoutPoint());
}
}
} // namespace blink
......@@ -4,20 +4,15 @@
#include "third_party/blink/renderer/core/paint/svg_image_painter.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_image_resource.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/object_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/svg_model_object_painter.h"
#include "third_party/blink/renderer/core/paint/svg_paint_context.h"
#include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
#include "third_party/blink/renderer/core/svg/svg_image_element.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
#include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h"
namespace blink {
......@@ -28,13 +23,14 @@ void SVGImagePainter::Paint(const PaintInfo& paint_info) {
!layout_svg_image_.ImageResource()->HasImage())
return;
FloatRect bounding_box = layout_svg_image_.VisualRectInLocalSVGCoordinates();
if (!paint_info.GetCullRect().IntersectsCullRect(
layout_svg_image_.LocalToSVGParentTransform(), bounding_box))
PaintInfo paint_info_before_filtering(paint_info);
if (SVGModelObjectPainter(layout_svg_image_)
.CullRectSkipsPainting(paint_info_before_filtering)) {
return;
}
// Images cannot have children so do not call UpdateCullRect.
PaintInfo paint_info_before_filtering(paint_info);
// Images cannot have children so do not call updateCullRect.
SVGTransformContext transform_context(
paint_info_before_filtering, layout_svg_image_,
layout_svg_image_.LocalToSVGParentTransform());
......@@ -52,12 +48,8 @@ void SVGImagePainter::Paint(const PaintInfo& paint_info) {
}
}
if (layout_svg_image_.Style()->OutlineWidth()) {
PaintInfo outline_paint_info(paint_info_before_filtering);
outline_paint_info.phase = PaintPhase::kSelfOutlineOnly;
ObjectPainter(layout_svg_image_)
.PaintOutline(outline_paint_info, LayoutPoint(bounding_box.Location()));
}
SVGModelObjectPainter(layout_svg_image_)
.PaintOutline(paint_info_before_filtering);
}
void SVGImagePainter::PaintForeground(const PaintInfo& paint_info) {
......
// Copyright 2018 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 "third_party/blink/renderer/core/paint/svg_model_object_painter.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h"
#include "third_party/blink/renderer/core/paint/object_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
namespace blink {
bool SVGModelObjectPainter::CullRectSkipsPainting(const PaintInfo& paint_info) {
// We do not apply cull rect optimizations across transforms for two reasons:
// 1) Performance: We can optimize transform changes by not repainting.
// 2) Complexity: Difficulty updating clips when ancestor transforms change.
// For these reasons, we do not cull painting if there is a transform.
if (layout_svg_model_object_.StyleRef().HasTransform())
return false;
// LayoutSVGHiddenContainer's visual rect is always empty but we need to
// paint its descendants so we cannot skip painting.
if (layout_svg_model_object_.IsSVGHiddenContainer())
return false;
return !paint_info.GetCullRect().IntersectsCullRect(
layout_svg_model_object_.LocalToSVGParentTransform(),
layout_svg_model_object_.VisualRectInLocalSVGCoordinates());
}
void SVGModelObjectPainter::PaintOutline(const PaintInfo& paint_info) {
if (paint_info.phase != PaintPhase::kForeground)
return;
if (layout_svg_model_object_.Style()->Visibility() != EVisibility::kVisible)
return;
if (!layout_svg_model_object_.Style()->OutlineWidth())
return;
PaintInfo outline_paint_info(paint_info);
outline_paint_info.phase = PaintPhase::kSelfOutlineOnly;
auto visual_rect = layout_svg_model_object_.VisualRectInLocalSVGCoordinates();
ObjectPainter(layout_svg_model_object_)
.PaintOutline(outline_paint_info, LayoutPoint(visual_rect.Location()));
}
} // namespace blink
// Copyright 2018 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
#include "third_party/blink/renderer/platform/wtf/allocator.h"
namespace blink {
struct PaintInfo;
class LayoutSVGModelObject;
class SVGModelObjectPainter {
STACK_ALLOCATED();
public:
SVGModelObjectPainter(const LayoutSVGModelObject& layout_svg_model_object)
: layout_svg_model_object_(layout_svg_model_object) {}
// If the object is outside the cull rect, painting can be skipped in most
// cases. An important exception is when there is a transform style: see the
// comment in the implementation.
bool CullRectSkipsPainting(const PaintInfo&);
void PaintOutline(const PaintInfo&);
private:
const LayoutSVGModelObject& layout_svg_model_object_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_MODEL_OBJECT_PAINTER_H_
......@@ -11,9 +11,9 @@
#include "third_party/blink/renderer/core/layout/svg/svg_marker_data.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
#include "third_party/blink/renderer/core/paint/object_painter.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/svg_container_painter.h"
#include "third_party/blink/renderer/core/paint/svg_model_object_painter.h"
#include "third_party/blink/renderer/core/paint/svg_paint_context.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
......@@ -46,13 +46,14 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) {
layout_svg_shape_.IsShapeEmpty())
return;
FloatRect bounding_box = layout_svg_shape_.VisualRectInLocalSVGCoordinates();
if (!paint_info.GetCullRect().IntersectsCullRect(
layout_svg_shape_.LocalSVGTransform(), bounding_box))
PaintInfo paint_info_before_filtering(paint_info);
if (SVGModelObjectPainter(layout_svg_shape_)
.CullRectSkipsPainting(paint_info_before_filtering)) {
return;
}
// Shapes cannot have children so do not call UpdateCullRect.
PaintInfo paint_info_before_filtering(paint_info);
// Shapes cannot have children so do not call updateCullRect.
SVGTransformContext transform_context(paint_info_before_filtering,
layout_svg_shape_,
layout_svg_shape_.LocalSVGTransform());
......@@ -124,7 +125,8 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) {
}
break;
case PT_MARKERS:
PaintMarkers(paint_context.GetPaintInfo(), bounding_box);
PaintMarkers(paint_context.GetPaintInfo(),
layout_svg_shape_.VisualRectInLocalSVGCoordinates());
break;
default:
NOTREACHED();
......@@ -134,12 +136,8 @@ void SVGShapePainter::Paint(const PaintInfo& paint_info) {
}
}
if (layout_svg_shape_.Style()->OutlineWidth()) {
PaintInfo outline_paint_info(paint_info_before_filtering);
outline_paint_info.phase = PaintPhase::kSelfOutlineOnly;
ObjectPainter(layout_svg_shape_)
.PaintOutline(outline_paint_info, LayoutPoint(bounding_box.Location()));
}
SVGModelObjectPainter(layout_svg_shape_)
.PaintOutline(paint_info_before_filtering);
}
class PathWithTemporaryWindingRule {
......
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