| /* |
| * Copyright (C) 2009 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/trace_event/trace_event_argument.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/picture_image_layer.h" |
| #include "cc/layers/picture_layer.h" |
| #include "cc/paint/display_item_list.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_float_point.h" |
| #include "third_party/blink/public/platform/web_float_rect.h" |
| #include "third_party/blink/public/platform/web_point.h" |
| #include "third_party/blink/public/platform/web_size.h" |
| #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" |
| #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" |
| #include "third_party/blink/renderer/platform/drag_image.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/geometry/region.h" |
| #include "third_party/blink/renderer/platform/graphics/bitmap_image.h" |
| #include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h" |
| #include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_context.h" |
| #include "third_party/blink/renderer/platform/graphics/image.h" |
| #include "third_party/blink/renderer/platform/graphics/link_highlight.h" |
| #include "third_party/blink/renderer/platform/graphics/logging_canvas.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h" |
| #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/scroll/scroll_snap_data.h" |
| #include "third_party/blink/renderer/platform/scroll/scrollable_area.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_map.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_set.h" |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" |
| #include "third_party/blink/renderer/platform/wtf/text/text_stream.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| #include "third_party/blink/renderer/platform/wtf/time.h" |
| #include "third_party/skia/include/core/SkMatrix44.h" |
| |
| namespace blink { |
| |
| std::unique_ptr<GraphicsLayer> GraphicsLayer::Create( |
| GraphicsLayerClient& client) { |
| return base::WrapUnique(new GraphicsLayer(client)); |
| } |
| |
| GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client) |
| : client_(client), |
| background_color_(Color::kTransparent), |
| opacity_(1), |
| blend_mode_(BlendMode::kNormal), |
| has_transform_origin_(false), |
| contents_opaque_(false), |
| prevent_contents_opaque_changes_(false), |
| should_flatten_transform_(true), |
| backface_visibility_(true), |
| draws_content_(false), |
| contents_visible_(true), |
| is_root_for_isolated_group_(false), |
| hit_testable_without_draws_content_(false), |
| needs_check_raster_invalidation_(false), |
| has_scroll_parent_(false), |
| has_clip_parent_(false), |
| painted_(false), |
| painting_phase_(kGraphicsLayerPaintAllWithOverflowClip), |
| parent_(nullptr), |
| mask_layer_(nullptr), |
| contents_clipping_mask_layer_(nullptr), |
| paint_count_(0), |
| contents_layer_(nullptr), |
| contents_layer_id_(0), |
| scrollable_area_(nullptr), |
| rendering_context3d_(0), |
| weak_ptr_factory_(this) { |
| #if DCHECK_IS_ON() |
| client.VerifyNotPainting(); |
| #endif |
| layer_ = cc::PictureLayer::Create(this); |
| layer_->SetIsDrawable(draws_content_ && contents_visible_); |
| layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr()); |
| |
| UpdateTrackingRasterInvalidations(); |
| } |
| |
| GraphicsLayer::~GraphicsLayer() { |
| layer_->SetLayerClient(nullptr); |
| SetContentsLayer(nullptr); |
| for (size_t i = 0; i < link_highlights_.size(); ++i) |
| link_highlights_[i]->ClearCurrentGraphicsLayer(); |
| link_highlights_.clear(); |
| |
| #if DCHECK_IS_ON() |
| client_.VerifyNotPainting(); |
| #endif |
| |
| RemoveAllChildren(); |
| RemoveFromParent(); |
| DCHECK(!parent_); |
| } |
| |
| LayoutRect GraphicsLayer::VisualRect() const { |
| LayoutRect bounds = LayoutRect(LayoutPoint(), LayoutSize(Size())); |
| DCHECK(layer_state_); |
| bounds.MoveBy(layer_state_->offset); |
| return bounds; |
| } |
| |
| void GraphicsLayer::SetHasWillChangeTransformHint( |
| bool has_will_change_transform) { |
| layer_->SetHasWillChangeTransformHint(has_will_change_transform); |
| } |
| |
| void GraphicsLayer::SetOverscrollBehavior( |
| const cc::OverscrollBehavior& behavior) { |
| layer_->SetOverscrollBehavior(behavior); |
| } |
| |
| void GraphicsLayer::SetSnapContainerData( |
| base::Optional<SnapContainerData> data) { |
| layer_->SetSnapContainerData(std::move(data)); |
| } |
| |
| void GraphicsLayer::SetIsResizedByBrowserControls( |
| bool is_resized_by_browser_controls) { |
| CcLayer()->SetIsResizedByBrowserControls(is_resized_by_browser_controls); |
| } |
| |
| void GraphicsLayer::SetIsContainerForFixedPositionLayers(bool is_container) { |
| CcLayer()->SetIsContainerForFixedPositionLayers(is_container); |
| } |
| |
| void GraphicsLayer::SetParent(GraphicsLayer* layer) { |
| #if DCHECK_IS_ON() |
| DCHECK(!layer || !layer->HasAncestor(this)); |
| #endif |
| parent_ = layer; |
| } |
| |
| #if DCHECK_IS_ON() |
| |
| bool GraphicsLayer::HasAncestor(GraphicsLayer* ancestor) const { |
| for (GraphicsLayer* curr = Parent(); curr; curr = curr->Parent()) { |
| if (curr == ancestor) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #endif |
| |
| bool GraphicsLayer::SetChildren(const GraphicsLayerVector& new_children) { |
| // If the contents of the arrays are the same, nothing to do. |
| if (new_children == children_) |
| return false; |
| |
| RemoveAllChildren(); |
| |
| size_t list_size = new_children.size(); |
| for (size_t i = 0; i < list_size; ++i) |
| AddChildInternal(new_children[i]); |
| |
| UpdateChildList(); |
| |
| return true; |
| } |
| |
| void GraphicsLayer::AddChildInternal(GraphicsLayer* child_layer) { |
| DCHECK_NE(child_layer, this); |
| |
| if (child_layer->Parent()) |
| child_layer->RemoveFromParent(); |
| |
| child_layer->SetParent(this); |
| children_.push_back(child_layer); |
| |
| // Don't call updateChildList here, this function is used in cases where it |
| // should not be called until all children are processed. |
| } |
| |
| void GraphicsLayer::AddChild(GraphicsLayer* child_layer) { |
| AddChildInternal(child_layer); |
| UpdateChildList(); |
| } |
| |
| void GraphicsLayer::AddChildBelow(GraphicsLayer* child_layer, |
| GraphicsLayer* sibling) { |
| DCHECK_NE(child_layer, this); |
| child_layer->RemoveFromParent(); |
| |
| bool found = false; |
| for (unsigned i = 0; i < children_.size(); i++) { |
| if (sibling == children_[i]) { |
| children_.insert(i, child_layer); |
| found = true; |
| break; |
| } |
| } |
| |
| child_layer->SetParent(this); |
| |
| if (!found) |
| children_.push_back(child_layer); |
| |
| UpdateChildList(); |
| } |
| |
| void GraphicsLayer::RemoveAllChildren() { |
| while (!children_.IsEmpty()) { |
| GraphicsLayer* cur_layer = children_.back(); |
| DCHECK(cur_layer->Parent()); |
| cur_layer->RemoveFromParent(); |
| } |
| } |
| |
| void GraphicsLayer::RemoveFromParent() { |
| if (parent_) { |
| // We use reverseFind so that removeAllChildren() isn't n^2. |
| parent_->children_.EraseAt(parent_->children_.ReverseFind(this)); |
| SetParent(nullptr); |
| } |
| |
| CcLayer()->RemoveFromParent(); |
| } |
| |
| void GraphicsLayer::SetOffsetFromLayoutObject(const IntSize& offset) { |
| if (offset == offset_from_layout_object_) |
| return; |
| |
| offset_from_layout_object_ = offset; |
| CcLayer()->SetFiltersOrigin(FloatPoint() - |
| FloatSize(offset_from_layout_object_)); |
| |
| // If the compositing layer offset changes, we need to repaint. |
| SetNeedsDisplay(); |
| } |
| |
| LayoutSize GraphicsLayer::OffsetFromLayoutObjectWithSubpixelAccumulation() |
| const { |
| return LayoutSize(OffsetFromLayoutObject()) + client_.SubpixelAccumulation(); |
| } |
| |
| IntRect GraphicsLayer::InterestRect() { |
| return previous_interest_rect_; |
| } |
| |
| void GraphicsLayer::PaintRecursively() { |
| Vector<GraphicsLayer*> repainted_layers; |
| PaintRecursivelyInternal(repainted_layers); |
| |
| // Notify the controllers that the artifact has been pushed and some |
| // lifecycle state can be freed (such as raster invalidations). |
| for (auto* layer : repainted_layers) { |
| #if DCHECK_IS_ON() |
| if (VLOG_IS_ON(2)) |
| LOG(ERROR) << "FinishCycle for GraphicsLayer: " << layer->DebugName(); |
| #endif |
| layer->GetPaintController().FinishCycle(); |
| } |
| } |
| |
| void GraphicsLayer::PaintRecursivelyInternal( |
| Vector<GraphicsLayer*>& repainted_layers) { |
| if (DrawsContent()) { |
| if (Paint(nullptr)) |
| repainted_layers.push_back(this); |
| } |
| |
| if (MaskLayer()) |
| MaskLayer()->PaintRecursivelyInternal(repainted_layers); |
| if (ContentsClippingMaskLayer()) |
| ContentsClippingMaskLayer()->PaintRecursivelyInternal(repainted_layers); |
| |
| for (auto* child : Children()) |
| child->PaintRecursivelyInternal(repainted_layers); |
| } |
| |
| bool GraphicsLayer::Paint(const IntRect* interest_rect, |
| GraphicsContext::DisabledMode disabled_mode) { |
| #if !DCHECK_IS_ON() |
| // TODO(crbug.com/853096): Investigate why we can ever reach here without |
| // a valid layer state. Seems to only happen on Android builds. |
| if (!layer_state_) |
| return false; |
| #endif |
| |
| if (PaintWithoutCommit(interest_rect, disabled_mode)) |
| GetPaintController().CommitNewDisplayItems(); |
| else if (!needs_check_raster_invalidation_) |
| return false; |
| |
| #if DCHECK_IS_ON() |
| if (VLOG_IS_ON(2)) { |
| LOG(ERROR) << "Painted GraphicsLayer: " << DebugName() |
| << " interest_rect=" << InterestRect().ToString(); |
| } |
| #endif |
| |
| DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); |
| // Generate raster invalidations for SPv1. |
| IntRect layer_bounds(layer_state_->offset, Size()); |
| EnsureRasterInvalidator().Generate( |
| GetPaintController().GetPaintArtifactShared(), layer_bounds, |
| layer_state_->state, VisualRectSubpixelOffset(), this); |
| |
| if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && |
| DrawsContent()) { |
| auto& tracking = EnsureRasterInvalidator().EnsureTracking(); |
| tracking.CheckUnderInvalidations(DebugName(), CapturePaintRecord(), |
| InterestRect()); |
| if (auto record = tracking.UnderInvalidationRecord()) { |
| // Add the under-invalidation overlay onto the painted result. |
| GetPaintController().AppendDebugDrawingAfterCommit(std::move(record), |
| layer_state_->state); |
| // Ensure the compositor will raster the under-invalidation overlay. |
| layer_->SetNeedsDisplay(); |
| } |
| } |
| |
| needs_check_raster_invalidation_ = false; |
| return true; |
| } |
| |
| bool GraphicsLayer::PaintWithoutCommit( |
| const IntRect* interest_rect, |
| GraphicsContext::DisabledMode disabled_mode) { |
| DCHECK(DrawsContent()); |
| |
| if (client_.ShouldThrottleRendering()) |
| return false; |
| |
| IncrementPaintCount(); |
| |
| IntRect new_interest_rect; |
| if (!interest_rect) { |
| new_interest_rect = |
| client_.ComputeInterestRect(this, previous_interest_rect_); |
| interest_rect = &new_interest_rect; |
| } |
| |
| if (!GetPaintController().SubsequenceCachingIsDisabled() && |
| !client_.NeedsRepaint(*this) && |
| !GetPaintController().CacheIsAllInvalid() && |
| previous_interest_rect_ == *interest_rect) { |
| return false; |
| } |
| |
| GraphicsContext context(GetPaintController(), disabled_mode, nullptr); |
| DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); |
| GetPaintController().UpdateCurrentPaintChunkProperties(base::nullopt, |
| layer_state_->state); |
| |
| previous_interest_rect_ = *interest_rect; |
| client_.PaintContents(this, context, painting_phase_, *interest_rect); |
| return true; |
| } |
| |
| void GraphicsLayer::UpdateChildList() { |
| cc::Layer* child_host = layer_.get(); |
| child_host->RemoveAllChildren(); |
| |
| ClearContentsLayerIfUnregistered(); |
| |
| if (contents_layer_) { |
| // FIXME: Add the contents layer in the correct order with negative z-order |
| // children. This does not currently cause visible rendering issues because |
| // contents layers are only used for replaced elements that don't have |
| // children. |
| child_host->AddChild(contents_layer_); |
| } |
| |
| for (size_t i = 0; i < children_.size(); ++i) |
| child_host->AddChild(children_[i]->CcLayer()); |
| |
| for (size_t i = 0; i < link_highlights_.size(); ++i) |
| child_host->AddChild(link_highlights_[i]->Layer()); |
| } |
| |
| void GraphicsLayer::UpdateLayerIsDrawable() { |
| // For the rest of the accelerated compositor code, there is no reason to make |
| // a distinction between drawsContent and contentsVisible. So, for |
| // m_layer->layer(), these two flags are combined here. |m_contentsLayer| |
| // shouldn't receive the drawsContent flag, so it is only given |
| // contentsVisible. |
| |
| layer_->SetIsDrawable(draws_content_ && contents_visible_); |
| if (cc::Layer* contents_layer = ContentsLayerIfRegistered()) |
| contents_layer->SetIsDrawable(contents_visible_); |
| |
| if (draws_content_) { |
| layer_->SetNeedsDisplay(); |
| for (size_t i = 0; i < link_highlights_.size(); ++i) |
| link_highlights_[i]->Invalidate(); |
| } |
| } |
| |
| void GraphicsLayer::UpdateContentsRect() { |
| cc::Layer* contents_layer = ContentsLayerIfRegistered(); |
| if (!contents_layer) |
| return; |
| |
| contents_layer->SetPosition( |
| FloatPoint(contents_rect_.X(), contents_rect_.Y())); |
| if (!image_layer_) { |
| contents_layer->SetBounds(static_cast<gfx::Size>(contents_rect_.Size())); |
| } else { |
| DCHECK_EQ(image_layer_.get(), contents_layer_); |
| // The image_layer_ has fixed bounds, and we apply bounds changes via the |
| // transform instead. Since we never change the transform on the |
| // |image_layer_| otherwise, we can assume it is identity and just apply |
| // the bounds to it directly. Same thing for transform origin. |
| DCHECK(image_layer_->transform_origin() == gfx::Point3F()); |
| |
| if (contents_rect_.Size().IsEmpty() || image_size_.IsEmpty()) { |
| image_layer_->SetTransform(gfx::Transform()); |
| contents_layer->SetBounds(static_cast<gfx::Size>(contents_rect_.Size())); |
| } else { |
| gfx::Transform image_transform; |
| image_transform.Scale( |
| static_cast<float>(contents_rect_.Width()) / image_size_.Width(), |
| static_cast<float>(contents_rect_.Height()) / image_size_.Height()); |
| image_layer_->SetTransform(image_transform); |
| image_layer_->SetBounds(static_cast<gfx::Size>(image_size_)); |
| } |
| } |
| |
| if (contents_clipping_mask_layer_) { |
| if (contents_clipping_mask_layer_->Size() != contents_rect_.Size()) { |
| contents_clipping_mask_layer_->SetSize(contents_rect_.Size()); |
| contents_clipping_mask_layer_->SetNeedsDisplay(); |
| } |
| contents_clipping_mask_layer_->SetPosition(FloatPoint()); |
| contents_clipping_mask_layer_->SetOffsetFromLayoutObject( |
| OffsetFromLayoutObject() + |
| IntSize(contents_rect_.Location().X(), contents_rect_.Location().Y())); |
| } |
| } |
| |
| static HashSet<int>* g_registered_layer_set; |
| |
| void GraphicsLayer::RegisterContentsLayer(cc::Layer* layer) { |
| if (!g_registered_layer_set) |
| g_registered_layer_set = new HashSet<int>; |
| CHECK(!g_registered_layer_set->Contains(layer->id())); |
| g_registered_layer_set->insert(layer->id()); |
| } |
| |
| void GraphicsLayer::UnregisterContentsLayer(cc::Layer* layer) { |
| DCHECK(g_registered_layer_set); |
| CHECK(g_registered_layer_set->Contains(layer->id())); |
| g_registered_layer_set->erase(layer->id()); |
| layer->SetLayerClient(nullptr); |
| } |
| |
| void GraphicsLayer::SetContentsTo(cc::Layer* layer, |
| bool prevent_contents_opaque_changes) { |
| bool children_changed = false; |
| if (layer) { |
| DCHECK(g_registered_layer_set); |
| CHECK(g_registered_layer_set->Contains(layer->id())); |
| if (contents_layer_id_ != layer->id()) { |
| SetupContentsLayer(layer); |
| children_changed = true; |
| } |
| UpdateContentsRect(); |
| prevent_contents_opaque_changes_ = prevent_contents_opaque_changes; |
| } else { |
| if (contents_layer_) { |
| children_changed = true; |
| |
| // The old contents layer will be removed via updateChildList. |
| SetContentsLayer(nullptr); |
| } |
| } |
| |
| if (children_changed) |
| UpdateChildList(); |
| } |
| |
| void GraphicsLayer::SetupContentsLayer(cc::Layer* contents_layer) { |
| DCHECK(contents_layer); |
| SetContentsLayer(contents_layer); |
| |
| contents_layer_->SetTransformOrigin(FloatPoint3D()); |
| contents_layer_->SetUseParentBackfaceVisibility(true); |
| |
| // It is necessary to call SetDrawsContent() as soon as we receive the new |
| // contents_layer, for the correctness of early exit conditions in |
| // SetDrawsContent() and SetContentsVisible(). |
| contents_layer_->SetIsDrawable(contents_visible_); |
| |
| // Insert the content layer first. Video elements require this, because they |
| // have shadow content that must display in front of the video. |
| layer_->InsertChild(contents_layer_, 0); |
| cc::PictureLayer* border_cc_layer = |
| contents_clipping_mask_layer_ ? contents_clipping_mask_layer_->CcLayer() |
| : nullptr; |
| contents_layer_->SetMaskLayer(border_cc_layer); |
| |
| contents_layer_->Set3dSortingContextId(rendering_context3d_); |
| } |
| |
| void GraphicsLayer::ClearContentsLayerIfUnregistered() { |
| if (!contents_layer_id_ || |
| g_registered_layer_set->Contains(contents_layer_id_)) |
| return; |
| |
| SetContentsLayer(nullptr); |
| } |
| |
| void GraphicsLayer::SetContentsLayer(cc::Layer* contents_layer) { |
| // If we have a previous contents layer which is still registered, then unset |
| // this client pointer. If unregistered, it has already nulled out the client |
| // pointer and may have been deleted. |
| if (contents_layer_ && g_registered_layer_set->Contains(contents_layer_id_)) |
| contents_layer_->SetLayerClient(nullptr); |
| contents_layer_ = contents_layer; |
| if (!contents_layer_) { |
| contents_layer_id_ = 0; |
| return; |
| } |
| contents_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr()); |
| contents_layer_id_ = contents_layer_->id(); |
| } |
| |
| cc::Layer* GraphicsLayer::ContentsLayerIfRegistered() { |
| ClearContentsLayerIfUnregistered(); |
| return contents_layer_; |
| } |
| |
| RasterInvalidator& GraphicsLayer::EnsureRasterInvalidator() { |
| if (!raster_invalidator_) { |
| raster_invalidator_ = std::make_unique<RasterInvalidator>( |
| [this](const IntRect& r) { SetNeedsDisplayInRect(r); }); |
| raster_invalidator_->SetTracksRasterInvalidations( |
| client_.IsTrackingRasterInvalidations()); |
| } |
| return *raster_invalidator_; |
| } |
| |
| void GraphicsLayer::UpdateTrackingRasterInvalidations() { |
| bool should_track = client_.IsTrackingRasterInvalidations(); |
| if (should_track) |
| EnsureRasterInvalidator().SetTracksRasterInvalidations(true); |
| else if (raster_invalidator_) |
| raster_invalidator_->SetTracksRasterInvalidations(false); |
| } |
| |
| void GraphicsLayer::ResetTrackedRasterInvalidations() { |
| if (auto* tracking = GetRasterInvalidationTracking()) |
| tracking->ClearInvalidations(); |
| } |
| |
| bool GraphicsLayer::HasTrackedRasterInvalidations() const { |
| if (auto* tracking = GetRasterInvalidationTracking()) |
| return tracking->HasInvalidations(); |
| return false; |
| } |
| |
| RasterInvalidationTracking* GraphicsLayer::GetRasterInvalidationTracking() |
| const { |
| return raster_invalidator_ ? raster_invalidator_->GetTracking() : nullptr; |
| } |
| |
| void GraphicsLayer::TrackRasterInvalidation(const DisplayItemClient& client, |
| const IntRect& rect, |
| PaintInvalidationReason reason) { |
| if (RasterInvalidationTracking::ShouldAlwaysTrack()) |
| EnsureRasterInvalidator().EnsureTracking(); |
| |
| // This only tracks invalidations that the cc::Layer is fully invalidated |
| // directly, e.g. from SetContentsNeedsDisplay(), etc. Other raster |
| // invalidations are tracked in RasterInvalidator. |
| if (auto* tracking = GetRasterInvalidationTracking()) |
| tracking->AddInvalidation(&client, client.DebugName(), rect, reason); |
| } |
| |
| String GraphicsLayer::DebugName(cc::Layer* layer) const { |
| if (layer->id() == contents_layer_id_) |
| return "ContentsLayer for " + client_.DebugName(this); |
| |
| for (size_t i = 0; i < link_highlights_.size(); ++i) { |
| if (layer == link_highlights_[i]->Layer()) { |
| return "LinkHighlight[" + String::Number(i) + "] for " + |
| client_.DebugName(this); |
| } |
| } |
| |
| if (layer == layer_.get()) |
| return client_.DebugName(this); |
| |
| NOTREACHED(); |
| return ""; |
| } |
| |
| void GraphicsLayer::SetPosition(const FloatPoint& point) { |
| position_ = point; |
| CcLayer()->SetPosition(position_); |
| } |
| |
| void GraphicsLayer::SetSize(const IntSize& size) { |
| // We are receiving negative sizes here that cause assertions to fail in the |
| // compositor. Clamp them to 0 to avoid those assertions. |
| // FIXME: This should be an DCHECK instead, as negative sizes should not exist |
| // in WebCore. |
| auto clamped_size = size; |
| clamped_size.ClampNegativeToZero(); |
| |
| if (clamped_size == size_) |
| return; |
| |
| size_ = clamped_size; |
| |
| Invalidate(PaintInvalidationReason::kIncremental); // as DisplayItemClient. |
| |
| layer_->SetBounds(static_cast<gfx::Size>(size_)); |
| // Note that we don't resize m_contentsLayer. It's up the caller to do that. |
| } |
| |
| void GraphicsLayer::SetTransform(const TransformationMatrix& transform) { |
| transform_ = transform; |
| CcLayer()->SetTransform(TransformationMatrix::ToTransform(transform)); |
| } |
| |
| void GraphicsLayer::SetTransformOrigin(const FloatPoint3D& transform_origin) { |
| has_transform_origin_ = true; |
| transform_origin_ = transform_origin; |
| CcLayer()->SetTransformOrigin(transform_origin); |
| } |
| |
| void GraphicsLayer::SetShouldFlattenTransform(bool should_flatten) { |
| if (should_flatten == should_flatten_transform_) |
| return; |
| |
| should_flatten_transform_ = should_flatten; |
| |
| layer_->SetShouldFlattenTransform(should_flatten); |
| } |
| |
| void GraphicsLayer::SetRenderingContext(int context) { |
| if (rendering_context3d_ == context) |
| return; |
| |
| rendering_context3d_ = context; |
| layer_->Set3dSortingContextId(context); |
| |
| if (contents_layer_) |
| contents_layer_->Set3dSortingContextId(rendering_context3d_); |
| } |
| |
| bool GraphicsLayer::MasksToBounds() const { |
| return layer_->masks_to_bounds(); |
| } |
| |
| void GraphicsLayer::SetMasksToBounds(bool masks_to_bounds) { |
| layer_->SetMasksToBounds(masks_to_bounds); |
| } |
| |
| void GraphicsLayer::SetDrawsContent(bool draws_content) { |
| // NOTE: This early-exit is only correct because we also properly call |
| // cc::Layer::SetIsDrawable() whenever |contents_layer_| is set to a new |
| // layer in SetupContentsLayer(). |
| if (draws_content == draws_content_) |
| return; |
| |
| draws_content_ = draws_content; |
| UpdateLayerIsDrawable(); |
| |
| if (!draws_content) { |
| paint_controller_.reset(); |
| raster_invalidator_.reset(); |
| } |
| } |
| |
| void GraphicsLayer::SetContentsVisible(bool contents_visible) { |
| // NOTE: This early-exit is only correct because we also properly call |
| // cc::Layer::SetIsDrawable() whenever |contents_layer_| is set to a new |
| // layer in SetupContentsLayer(). |
| if (contents_visible == contents_visible_) |
| return; |
| |
| contents_visible_ = contents_visible; |
| UpdateLayerIsDrawable(); |
| } |
| |
| void GraphicsLayer::SetClipParent(cc::Layer* parent) { |
| has_clip_parent_ = !!parent; |
| layer_->SetClipParent(parent); |
| } |
| |
| void GraphicsLayer::SetScrollParent(cc::Layer* parent) { |
| has_scroll_parent_ = !!parent; |
| layer_->SetScrollParent(parent); |
| } |
| |
| void GraphicsLayer::SetBackgroundColor(const Color& color) { |
| if (color == background_color_) |
| return; |
| |
| background_color_ = color; |
| layer_->SetBackgroundColor(background_color_.Rgb()); |
| } |
| |
| void GraphicsLayer::SetContentsOpaque(bool opaque) { |
| contents_opaque_ = opaque; |
| layer_->SetContentsOpaque(contents_opaque_); |
| ClearContentsLayerIfUnregistered(); |
| if (contents_layer_ && !prevent_contents_opaque_changes_) |
| contents_layer_->SetContentsOpaque(opaque); |
| } |
| |
| void GraphicsLayer::SetMaskLayer(GraphicsLayer* mask_layer) { |
| if (mask_layer == mask_layer_) |
| return; |
| |
| mask_layer_ = mask_layer; |
| layer_->SetMaskLayer(mask_layer_ ? mask_layer_->CcLayer() : nullptr); |
| } |
| |
| void GraphicsLayer::SetContentsClippingMaskLayer( |
| GraphicsLayer* contents_clipping_mask_layer) { |
| if (contents_clipping_mask_layer == contents_clipping_mask_layer_) |
| return; |
| |
| contents_clipping_mask_layer_ = contents_clipping_mask_layer; |
| cc::Layer* contents_layer = ContentsLayerIfRegistered(); |
| if (!contents_layer) |
| return; |
| cc::PictureLayer* contents_clipping_mask_cc_layer = |
| contents_clipping_mask_layer_ ? contents_clipping_mask_layer_->CcLayer() |
| : nullptr; |
| contents_layer->SetMaskLayer(contents_clipping_mask_cc_layer); |
| UpdateContentsRect(); |
| } |
| |
| void GraphicsLayer::SetBackfaceVisibility(bool visible) { |
| backface_visibility_ = visible; |
| CcLayer()->SetDoubleSided(backface_visibility_); |
| } |
| |
| void GraphicsLayer::SetOpacity(float opacity) { |
| float clamped_opacity = clampTo(opacity, 0.0f, 1.0f); |
| opacity_ = clamped_opacity; |
| CcLayer()->SetOpacity(opacity); |
| } |
| |
| void GraphicsLayer::SetBlendMode(BlendMode blend_mode) { |
| if (blend_mode_ == blend_mode) |
| return; |
| blend_mode_ = blend_mode; |
| CcLayer()->SetBlendMode(WebCoreBlendModeToSkBlendMode(blend_mode)); |
| } |
| |
| void GraphicsLayer::SetIsRootForIsolatedGroup(bool isolated) { |
| if (is_root_for_isolated_group_ == isolated) |
| return; |
| is_root_for_isolated_group_ = isolated; |
| CcLayer()->SetIsRootForIsolatedGroup(isolated); |
| } |
| |
| void GraphicsLayer::SetHitTestableWithoutDrawsContent(bool should_hit_test) { |
| if (hit_testable_without_draws_content_ == should_hit_test) |
| return; |
| hit_testable_without_draws_content_ = should_hit_test; |
| CcLayer()->SetHitTestableWithoutDrawsContent(should_hit_test); |
| } |
| |
| void GraphicsLayer::SetContentsNeedsDisplay() { |
| if (cc::Layer* contents_layer = ContentsLayerIfRegistered()) { |
| contents_layer->SetNeedsDisplay(); |
| TrackRasterInvalidation(*this, contents_rect_, |
| PaintInvalidationReason::kFullLayer); |
| } |
| } |
| |
| void GraphicsLayer::SetNeedsDisplay() { |
| if (!DrawsContent()) |
| return; |
| |
| layer_->SetNeedsDisplay(); |
| for (size_t i = 0; i < link_highlights_.size(); ++i) |
| link_highlights_[i]->Invalidate(); |
| |
| GetPaintController().InvalidateAll(); |
| |
| if (raster_invalidator_) |
| raster_invalidator_->ClearOldStates(); |
| |
| TrackRasterInvalidation(*this, IntRect(IntPoint(), size_), |
| PaintInvalidationReason::kFullLayer); |
| } |
| |
| void GraphicsLayer::SetNeedsDisplayInRect(const IntRect& rect) { |
| DCHECK(DrawsContent()); |
| |
| layer_->SetNeedsDisplayRect(rect); |
| for (auto* link_highlight : link_highlights_) |
| link_highlight->Invalidate(); |
| } |
| |
| void GraphicsLayer::SetContentsRect(const IntRect& rect) { |
| if (rect == contents_rect_) |
| return; |
| |
| contents_rect_ = rect; |
| UpdateContentsRect(); |
| } |
| |
| void GraphicsLayer::SetContentsToImage( |
| Image* image, |
| Image::ImageDecodingMode decode_mode, |
| RespectImageOrientationEnum respect_image_orientation) { |
| PaintImage paint_image; |
| if (image) |
| paint_image = image->PaintImageForCurrentFrame(); |
| |
| ImageOrientation image_orientation = kOriginTopLeft; |
| SkMatrix matrix; |
| if (paint_image && image->IsBitmapImage() && |
| respect_image_orientation == kRespectImageOrientation) { |
| image_orientation = ToBitmapImage(image)->CurrentFrameOrientation(); |
| image_size_ = IntSize(paint_image.width(), paint_image.height()); |
| if (image_orientation.UsesWidthAsHeight()) |
| image_size_ = image_size_.TransposedSize(); |
| auto affine = |
| image_orientation.TransformFromDefault(FloatSize(image_size_)); |
| auto transform = affine.ToTransformationMatrix(); |
| matrix = TransformationMatrix::ToSkMatrix44(transform); |
| } else if (paint_image) { |
| matrix = SkMatrix::I(); |
| image_size_ = IntSize(paint_image.width(), paint_image.height()); |
| } else { |
| matrix = SkMatrix::I(); |
| image_size_ = IntSize(); |
| } |
| |
| if (paint_image) { |
| paint_image = |
| PaintImageBuilder::WithCopy(std::move(paint_image)) |
| .set_decoding_mode(Image::ToPaintImageDecodingMode(decode_mode)) |
| .TakePaintImage(); |
| if (!image_layer_) { |
| image_layer_ = cc::PictureImageLayer::Create(); |
| RegisterContentsLayer(image_layer_.get()); |
| } |
| image_layer_->SetImage(std::move(paint_image), matrix, |
| image_orientation.UsesWidthAsHeight()); |
| image_layer_->SetContentsOpaque(image->CurrentFrameKnownToBeOpaque()); |
| UpdateContentsRect(); |
| } else if (image_layer_) { |
| UnregisterContentsLayer(image_layer_.get()); |
| image_layer_ = nullptr; |
| } |
| |
| SetContentsTo(image_layer_.get(), |
| /*prevent_contents_opaque_changes=*/true); |
| } |
| |
| cc::PictureLayer* GraphicsLayer::CcLayer() const { |
| return layer_.get(); |
| } |
| |
| void GraphicsLayer::SetFilters(CompositorFilterOperations filters) { |
| CcLayer()->SetFilters(filters.ReleaseCcFilterOperations()); |
| } |
| |
| void GraphicsLayer::SetBackdropFilters(CompositorFilterOperations filters) { |
| CcLayer()->SetBackgroundFilters(filters.ReleaseCcFilterOperations()); |
| } |
| |
| void GraphicsLayer::SetStickyPositionConstraint( |
| const cc::LayerStickyPositionConstraint& sticky_constraint) { |
| layer_->SetStickyPositionConstraint(sticky_constraint); |
| } |
| |
| void GraphicsLayer::SetFilterQuality(SkFilterQuality filter_quality) { |
| if (image_layer_) |
| image_layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality); |
| } |
| |
| void GraphicsLayer::SetPaintingPhase(GraphicsLayerPaintingPhase phase) { |
| if (painting_phase_ == phase) |
| return; |
| painting_phase_ = phase; |
| SetNeedsDisplay(); |
| } |
| |
| void GraphicsLayer::AddLinkHighlight(LinkHighlight* link_highlight) { |
| DCHECK(link_highlight && !link_highlights_.Contains(link_highlight)); |
| link_highlights_.push_back(link_highlight); |
| link_highlight->Layer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr()); |
| UpdateChildList(); |
| } |
| |
| void GraphicsLayer::RemoveLinkHighlight(LinkHighlight* link_highlight) { |
| link_highlights_.EraseAt(link_highlights_.Find(link_highlight)); |
| UpdateChildList(); |
| } |
| |
| void GraphicsLayer::SetScrollableArea(ScrollableArea* scrollable_area) { |
| if (scrollable_area_ == scrollable_area) |
| return; |
| scrollable_area_ = scrollable_area; |
| } |
| |
| void GraphicsLayer::ScrollableAreaDisposed() { |
| scrollable_area_.Clear(); |
| } |
| |
| std::unique_ptr<base::trace_event::TracedValue> GraphicsLayer::TakeDebugInfo( |
| cc::Layer* layer) { |
| auto traced_value = std::make_unique<base::trace_event::TracedValue>(); |
| |
| traced_value->SetString( |
| "layer_name", WTF::StringUTF8Adaptor(DebugName(layer)).AsStringPiece()); |
| |
| traced_value->BeginArray("compositing_reasons"); |
| for (const char* description : |
| CompositingReason::Descriptions(compositing_reasons_)) |
| traced_value->AppendString(description); |
| traced_value->EndArray(); |
| |
| traced_value->BeginArray("squashing_disallowed_reasons"); |
| for (const char* description : |
| SquashingDisallowedReason::Descriptions(squashing_disallowed_reasons_)) |
| traced_value->AppendString(description); |
| traced_value->EndArray(); |
| |
| if (owner_node_id_) |
| traced_value->SetInteger("owner_node", owner_node_id_); |
| |
| if (auto* tracking = GetRasterInvalidationTracking()) { |
| tracking->AddToTracedValue(*traced_value); |
| tracking->ClearInvalidations(); |
| } |
| |
| return traced_value; |
| } |
| |
| void GraphicsLayer::DidChangeScrollbarsHiddenIfOverlay(bool hidden) { |
| if (scrollable_area_) |
| scrollable_area_->SetScrollbarsHiddenIfOverlay(hidden); |
| } |
| |
| PaintController& GraphicsLayer::GetPaintController() const { |
| CHECK(DrawsContent()); |
| if (!paint_controller_) |
| paint_controller_ = PaintController::Create(); |
| return *paint_controller_; |
| } |
| |
| void GraphicsLayer::SetElementId(const CompositorElementId& id) { |
| if (cc::Layer* layer = CcLayer()) |
| layer->SetElementId(id); |
| } |
| |
| CompositorElementId GraphicsLayer::GetElementId() const { |
| if (cc::Layer* layer = CcLayer()) |
| return layer->element_id(); |
| return CompositorElementId(); |
| } |
| |
| sk_sp<PaintRecord> GraphicsLayer::CapturePaintRecord() const { |
| DCHECK(DrawsContent()); |
| |
| if (client_.ShouldThrottleRendering()) |
| return sk_sp<PaintRecord>(new PaintRecord); |
| |
| FloatRect bounds(IntRect(IntPoint(), Size())); |
| GraphicsContext graphics_context(GetPaintController()); |
| graphics_context.BeginRecording(bounds); |
| DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); |
| GetPaintController().GetPaintArtifact().Replay( |
| graphics_context, layer_state_->state, layer_state_->offset); |
| return graphics_context.EndRecording(); |
| } |
| |
| void GraphicsLayer::SetLayerState(const PropertyTreeState& layer_state, |
| const IntPoint& layer_offset) { |
| if (!layer_state_) { |
| layer_state_ = |
| std::make_unique<LayerState>(LayerState{layer_state, layer_offset}); |
| return; |
| } |
| layer_state_->state = layer_state; |
| layer_state_->offset = layer_offset; |
| } |
| |
| void GraphicsLayer::SetContentsLayerState(const PropertyTreeState& layer_state, |
| const IntPoint& layer_offset) { |
| if (!contents_layer_state_) { |
| contents_layer_state_ = |
| std::make_unique<LayerState>(LayerState{layer_state, layer_offset}); |
| return; |
| } |
| contents_layer_state_->state = layer_state; |
| contents_layer_state_->offset = layer_offset; |
| } |
| |
| scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList( |
| PaintingControlSetting painting_control) { |
| TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents"); |
| |
| PaintController& paint_controller = GetPaintController(); |
| paint_controller.SetDisplayItemConstructionIsDisabled( |
| painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED); |
| paint_controller.SetSubsequenceCachingIsDisabled( |
| painting_control == SUBSEQUENCE_CACHING_DISABLED); |
| |
| if (painting_control == PARTIAL_INVALIDATION) |
| client_.InvalidateTargetElementForTesting(); |
| |
| // We also disable caching when Painting or Construction are disabled. In both |
| // cases we would like to compare assuming the full cost of recording, not the |
| // cost of re-using cached content. |
| if (painting_control == DISPLAY_LIST_CACHING_DISABLED || |
| painting_control == DISPLAY_LIST_PAINTING_DISABLED || |
| painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED) |
| paint_controller.InvalidateAll(); |
| |
| GraphicsContext::DisabledMode disabled_mode = |
| GraphicsContext::kNothingDisabled; |
| if (painting_control == DISPLAY_LIST_PAINTING_DISABLED || |
| painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED) |
| disabled_mode = GraphicsContext::kFullyDisabled; |
| |
| // Anything other than PAINTING_BEHAVIOR_NORMAL is for testing. In non-testing |
| // scenarios, it is an error to call GraphicsLayer::Paint. Actual painting |
| // occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint(); |
| // this method merely copies the painted output to the cc::DisplayItemList. |
| if (painting_control != PAINTING_BEHAVIOR_NORMAL) |
| Paint(nullptr, disabled_mode); |
| |
| auto display_list = base::MakeRefCounted<cc::DisplayItemList>(); |
| |
| DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); |
| PaintChunksToCcLayer::ConvertInto( |
| GetPaintController().PaintChunks(), layer_state_->state, |
| gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()), |
| VisualRectSubpixelOffset(), |
| paint_controller.GetPaintArtifact().GetDisplayItemList(), *display_list); |
| |
| paint_controller.SetDisplayItemConstructionIsDisabled(false); |
| paint_controller.SetSubsequenceCachingIsDisabled(false); |
| |
| display_list->Finalize(); |
| return display_list; |
| } |
| |
| size_t GraphicsLayer::GetApproximateUnsharedMemoryUsage() const { |
| size_t result = sizeof(*this); |
| result += GetPaintController().ApproximateUnsharedMemoryUsage(); |
| if (raster_invalidator_) |
| result += raster_invalidator_->ApproximateUnsharedMemoryUsage(); |
| return result; |
| } |
| |
| // Subpixel offset for visual rects which excluded composited layer's subpixel |
| // accumulation during paint invalidation. |
| // See PaintInvalidator::ExcludeCompositedLayerSubpixelAccumulation(). |
| FloatSize GraphicsLayer::VisualRectSubpixelOffset() const { |
| if (GetCompositingReasons() & CompositingReason::kComboAllDirectReasons) |
| return FloatSize(client_.SubpixelAccumulation()); |
| return FloatSize(); |
| } |
| |
| } // namespace blink |