blob: 44f0dd870538faefb695c96ff8f6983e9bd0e0d0 [file] [log] [blame]
// Copyright 2014 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/platform/graphics/paint/cull_rect.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
namespace blink {
bool CullRect::Intersects(const IntRect& rect) const {
return IsInfinite() || rect.Intersects(rect_);
}
bool CullRect::Intersects(const LayoutRect& rect) const {
return IsInfinite() || rect_.Intersects(EnclosingIntRect(rect));
}
bool CullRect::IntersectsTransformed(const AffineTransform& transform,
const FloatRect& rect) const {
return IsInfinite() || transform.MapRect(rect).Intersects(rect_);
}
bool CullRect::IntersectsHorizontalRange(LayoutUnit lo, LayoutUnit hi) const {
return !(lo >= rect_.MaxX() || hi <= rect_.X());
}
bool CullRect::IntersectsVerticalRange(LayoutUnit lo, LayoutUnit hi) const {
return !(lo >= rect_.MaxY() || hi <= rect_.Y());
}
void CullRect::MoveBy(const IntPoint& offset) {
if (!IsInfinite())
rect_.MoveBy(offset);
}
void CullRect::Move(const IntSize& offset) {
if (!IsInfinite())
rect_.Move(offset);
}
CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
const TransformPaintPropertyNode* transform) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
if (const auto* scroll = transform->ScrollNode()) {
rect_.Intersect(scroll->ContainerRect());
if (rect_.IsEmpty())
return kNotExpanded;
rect_ = transform->Matrix().Inverse().MapRect(rect_);
// Expand the cull rect for scrolling contents in case of composited
// scrolling.
// TODO(wangxianzhu): options for non-composited-scrolling contents:
// 1. to use non-composted-scrolling heuristics to avoid expansion;
// 2. to reduce the 4000px distance, no matter if the contents with be
// composited scrolling.
// 3. mixed method of 1 and 2, e.g. the distance could be a function of
// confidence that the contents will be composited scrolling.
static const int kPixelDistanceToExpand = 4000;
rect_.Inflate(kPixelDistanceToExpand);
// Don't clip the cull rect by contents size to let ChangedEnough() work
// even if the new cull rect exceeds the bounds of contents rect.
return rect_.Contains(IntRect(IntPoint(), scroll->ContentsSize()))
? kExpandedForWholeScrollingContents
: kExpandedForPartialScrollingContents;
}
}
if (!IsInfinite())
rect_ = transform->Matrix().Inverse().MapRect(rect_);
return kNotExpanded;
}
void CullRect::ApplyTransforms(const TransformPaintPropertyNode* source,
const TransformPaintPropertyNode* destination,
const base::Optional<CullRect>& old_cull_rect) {
DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
Vector<const TransformPaintPropertyNode*> scroll_translations;
for (const auto* t = destination; t != source; t = t->Parent()) {
if (!t) {
// |source| is not an ancestor of |destination|. Simply map.
GeometryMapper::SourceToDestinationRect(source, destination, rect_);
return;
}
if (t->ScrollNode())
scroll_translations.push_back(t);
}
const auto* last_transform = source;
ApplyTransformResult last_scroll_translation_result = kNotExpanded;
for (auto it = scroll_translations.rbegin(); it != scroll_translations.rend();
++it) {
const auto* scroll_translation = *it;
if (!IsInfinite()) {
GeometryMapper::SourceToDestinationRect(
last_transform, scroll_translation->Parent(), rect_);
}
last_scroll_translation_result = ApplyTransformInternal(scroll_translation);
last_transform = scroll_translation;
}
if (!IsInfinite())
GeometryMapper::SourceToDestinationRect(last_transform, destination, rect_);
if (last_scroll_translation_result == kExpandedForPartialScrollingContents &&
old_cull_rect && !ChangedEnough(*old_cull_rect))
rect_ = old_cull_rect->Rect();
}
bool CullRect::ChangedEnough(const CullRect& old_cull_rect) const {
DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
const auto& new_rect = Rect();
const auto& old_rect = old_cull_rect.Rect();
if (old_rect == new_rect || (old_rect.IsEmpty() && new_rect.IsEmpty()))
return false;
if (old_rect.IsEmpty())
return true;
auto expanded_old_rect = old_rect;
static const int kChangedEnoughMinimumDistance = 512;
expanded_old_rect.Inflate(kChangedEnoughMinimumDistance);
return !expanded_old_rect.Contains(new_rect);
}
} // namespace blink