diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc | 319 |
1 files changed, 189 insertions, 130 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc index 1ff19413ab7..4d1cc23961b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc @@ -27,15 +27,22 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h" #include "third_party/blink/renderer/core/style/style_svg_resource.h" +#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" +#include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" #include "third_party/blink/renderer/core/svg/svg_pattern_element.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" #include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h" #include "third_party/blink/renderer/core/svg/svg_uri_reference.h" #include "third_party/blink/renderer/core/svg_names.h" +#include "third_party/blink/renderer/platform/graphics/filters/filter.h" +#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h" +#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h" +#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h" #if DCHECK_IS_ON() #include <stdio.h> @@ -45,10 +52,32 @@ namespace blink { SVGResources::SVGResources() : linked_resource_(nullptr) {} -SVGResourceClient* SVGResources::GetClient(const LayoutObject& object) { +SVGElementResourceClient* SVGResources::GetClient(const LayoutObject& object) { return To<SVGElement>(object.GetNode())->GetSVGResourceClient(); } +FloatRect SVGResources::ReferenceBoxForEffects( + const LayoutObject& layout_object) { + // For SVG foreign objects, remove the position part of the bounding box. The + // position is already baked into the transform, and we don't want to re-apply + // the offset when, e.g., using "objectBoundingBox" for clipPathUnits. + if (layout_object.IsSVGForeignObject()) { + FloatRect rect = layout_object.ObjectBoundingBox(); + return FloatRect(FloatPoint::Zero(), rect.Size()); + } + + // Text "sub-elements" (<tspan>, <textpath>, <a>) should use the entire + // <text>s object bounding box rather then their own. + // https://svgwg.org/svg2-draft/text.html#ObjectBoundingBoxUnitsTextObjects + const LayoutObject* obb_layout_object = &layout_object; + if (layout_object.IsSVGInline()) { + obb_layout_object = + LayoutSVGText::LocateLayoutSVGTextAncestor(&layout_object); + } + DCHECK(obb_layout_object); + return obb_layout_object->ObjectBoundingBox(); +} + static HashSet<AtomicString>& ClipperFilterMaskerTags() { DEFINE_STATIC_LOCAL( HashSet<AtomicString>, tag_list, @@ -115,37 +144,6 @@ static HashSet<AtomicString>& FillAndStrokeTags() { return tag_list; } -namespace { - -template <typename ContainerType> -bool IsResourceOfType(LayoutSVGResourceContainer* container) { - return container->ResourceType() == ContainerType::kResourceType; -} - -template <> -bool IsResourceOfType<LayoutSVGResourcePaintServer>( - LayoutSVGResourceContainer* container) { - return container->IsSVGPaintServer(); -} - -template <typename ContainerType> -ContainerType* CastResource(SVGResource* resource) { - if (!resource) - return nullptr; - if (LayoutSVGResourceContainer* container = resource->ResourceContainer()) { - if (IsResourceOfType<ContainerType>(container)) - return static_cast<ContainerType*>(container); - } - return nullptr; -} - -template <typename ContainerType> -ContainerType* CastResource(StyleSVGResource& style_resource) { - return CastResource<ContainerType>(style_resource.Resource()); -} - -} // namespace - bool SVGResources::HasResourceData() const { return clipper_filter_masker_data_ || marker_data_ || fill_stroke_data_ || linked_resource_; @@ -176,59 +174,51 @@ std::unique_ptr<SVGResources> SVGResources::BuildResources( std::unique_ptr<SVGResources> resources; if (ClipperFilterMaskerTags().Contains(tag_name)) { if (computed_style.ClipPath() && !object.IsSVGRoot()) { - ClipPathOperation* clip_path_operation = computed_style.ClipPath(); - if (clip_path_operation->GetType() == ClipPathOperation::REFERENCE) { - const ReferenceClipPathOperation& clip_path_reference = - To<ReferenceClipPathOperation>(*clip_path_operation); - EnsureResources(resources).SetClipper( - CastResource<LayoutSVGResourceClipper>( - clip_path_reference.Resource())); + if (LayoutSVGResourceClipper* clipper = + GetSVGResourceAsType(computed_style.ClipPath())) { + EnsureResources(resources).SetClipper(clipper); } } if (computed_style.HasFilter() && !object.IsSVGRoot()) { - const FilterOperations& filter_operations = computed_style.Filter(); - if (filter_operations.size() == 1) { - const FilterOperation& filter_operation = *filter_operations.at(0); - if (const auto* reference_filter_operation = - DynamicTo<ReferenceFilterOperation>(filter_operation)) { - EnsureResources(resources).SetFilter( - CastResource<LayoutSVGResourceFilter>( - reference_filter_operation->Resource())); - } + if (LayoutSVGResourceFilter* filter = + GetFilterResourceForSVG(computed_style)) { + EnsureResources(resources).SetFilter(filter); } } - if (StyleSVGResource* masker_resource = style.MaskerResource()) { - EnsureResources(resources).SetMasker( - CastResource<LayoutSVGResourceMasker>(*masker_resource)); + if (auto* masker = GetSVGResourceAsType<LayoutSVGResourceMasker>( + style.MaskerResource())) { + EnsureResources(resources).SetMasker(masker); } } if (style.HasMarkers() && SupportsMarkers(element)) { - if (StyleSVGResource* marker_start_resource = style.MarkerStartResource()) { - EnsureResources(resources).SetMarkerStart( - CastResource<LayoutSVGResourceMarker>(*marker_start_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerStartResource())) { + EnsureResources(resources).SetMarkerStart(marker); } - if (StyleSVGResource* marker_mid_resource = style.MarkerMidResource()) { - EnsureResources(resources).SetMarkerMid( - CastResource<LayoutSVGResourceMarker>(*marker_mid_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerMidResource())) { + EnsureResources(resources).SetMarkerMid(marker); } - if (StyleSVGResource* marker_end_resource = style.MarkerEndResource()) { - EnsureResources(resources).SetMarkerEnd( - CastResource<LayoutSVGResourceMarker>(*marker_end_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerEndResource())) { + EnsureResources(resources).SetMarkerEnd(marker); } } if (FillAndStrokeTags().Contains(tag_name)) { - if (StyleSVGResource* fill_resource = style.FillPaint().Resource()) { - EnsureResources(resources).SetFill( - CastResource<LayoutSVGResourcePaintServer>(*fill_resource)); + if (auto* paint_resource = + GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + style.FillPaint().Resource())) { + EnsureResources(resources).SetFill(paint_resource); } - if (StyleSVGResource* stroke_resource = style.StrokePaint().Resource()) { - EnsureResources(resources).SetStroke( - CastResource<LayoutSVGResourcePaintServer>(*stroke_resource)); + if (auto* paint_resource = + GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + style.StrokePaint().Resource())) { + EnsureResources(resources).SetStroke(paint_resource); } } @@ -277,41 +267,28 @@ void SVGResources::LayoutIfNeeded() { linked_resource_->LayoutIfNeeded(); } -InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( - SVGResourceClient& client) const { - if (!clipper_filter_masker_data_) - return 0; - InvalidationModeMask invalidation_flags = 0; - if (LayoutSVGResourceClipper* clipper = clipper_filter_masker_data_->clipper) - clipper->RemoveClientFromCache(client); - if (LayoutSVGResourceFilter* filter = clipper_filter_masker_data_->filter) { - if (filter->RemoveClientFromCache(client)) - invalidation_flags |= SVGResourceClient::kPaintInvalidation; - } - if (LayoutSVGResourceMasker* masker = clipper_filter_masker_data_->masker) - masker->RemoveClientFromCache(client); - return invalidation_flags | SVGResourceClient::kBoundariesInvalidation; +bool SVGResources::DifferenceNeedsLayout(const SVGResources* a, + const SVGResources* b) { + bool a_has_bounds_affecting_resource = a && a->clipper_filter_masker_data_; + bool b_has_bounds_affecting_resource = b && b->clipper_filter_masker_data_; + if (a_has_bounds_affecting_resource != b_has_bounds_affecting_resource) + return true; + if (!a_has_bounds_affecting_resource) + return false; + return a->Clipper() != b->Clipper() || a->Filter() != b->Filter() || + a->Masker() != b->Masker(); } -InvalidationModeMask SVGResources::RemoveClientFromCache( - SVGResourceClient& client) const { - if (!HasResourceData()) +InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( + SVGElementResourceClient& client) const { + if (!clipper_filter_masker_data_) return 0; - - // We never call this method for the elements where this would be non-null. - DCHECK(!linked_resource_); - InvalidationModeMask invalidation_flags = - RemoveClientFromCacheAffectingObjectBounds(client); - - if (fill_stroke_data_) { - if (LayoutSVGResourcePaintServer* fill = fill_stroke_data_->fill) - fill->RemoveClientFromCache(client); - if (LayoutSVGResourcePaintServer* stroke = fill_stroke_data_->stroke) - stroke->RemoveClientFromCache(client); - invalidation_flags |= SVGResourceClient::kPaintInvalidation; + SVGResourceClient::kBoundariesInvalidation; + if (clipper_filter_masker_data_->filter) { + if (client.ClearFilterData()) + invalidation_flags |= SVGResourceClient::kPaintInvalidation; } - return invalidation_flags; } @@ -465,9 +442,7 @@ void SVGResources::BuildSetOfResources( } void SVGResources::SetClipper(LayoutSVGResourceClipper* clipper) { - if (!clipper) - return; - + DCHECK(clipper); DCHECK_EQ(clipper->ResourceType(), kClipperResourceType); if (!clipper_filter_masker_data_) @@ -477,9 +452,7 @@ void SVGResources::SetClipper(LayoutSVGResourceClipper* clipper) { } void SVGResources::SetFilter(LayoutSVGResourceFilter* filter) { - if (!filter) - return; - + DCHECK(filter); DCHECK_EQ(filter->ResourceType(), kFilterResourceType); if (!clipper_filter_masker_data_) @@ -489,9 +462,7 @@ void SVGResources::SetFilter(LayoutSVGResourceFilter* filter) { } void SVGResources::SetMarkerStart(LayoutSVGResourceMarker* marker_start) { - if (!marker_start) - return; - + DCHECK(marker_start); DCHECK_EQ(marker_start->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -501,9 +472,7 @@ void SVGResources::SetMarkerStart(LayoutSVGResourceMarker* marker_start) { } void SVGResources::SetMarkerMid(LayoutSVGResourceMarker* marker_mid) { - if (!marker_mid) - return; - + DCHECK(marker_mid); DCHECK_EQ(marker_mid->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -513,9 +482,7 @@ void SVGResources::SetMarkerMid(LayoutSVGResourceMarker* marker_mid) { } void SVGResources::SetMarkerEnd(LayoutSVGResourceMarker* marker_end) { - if (!marker_end) - return; - + DCHECK(marker_end); DCHECK_EQ(marker_end->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -525,9 +492,7 @@ void SVGResources::SetMarkerEnd(LayoutSVGResourceMarker* marker_end) { } void SVGResources::SetMasker(LayoutSVGResourceMasker* masker) { - if (!masker) - return; - + DCHECK(masker); DCHECK_EQ(masker->ResourceType(), kMaskerResourceType); if (!clipper_filter_masker_data_) @@ -537,8 +502,7 @@ void SVGResources::SetMasker(LayoutSVGResourceMasker* masker) { } void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) { - if (!fill) - return; + DCHECK(fill); if (!fill_stroke_data_) fill_stroke_data_ = std::make_unique<FillStrokeData>(); @@ -547,8 +511,7 @@ void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) { } void SVGResources::SetStroke(LayoutSVGResourcePaintServer* stroke) { - if (!stroke) - return; + DCHECK(stroke); if (!fill_stroke_data_) fill_stroke_data_ = std::make_unique<FillStrokeData>(); @@ -705,6 +668,83 @@ void SVGResources::ClearMarkers(SVGElement& element, marker_resource->RemoveClient(*client); } +bool FilterData::UpdateStateOnFinish() { + switch (state_) { + // A cycle can occur when an FeImage references a source that + // makes use of the FeImage itself. This is the first place we + // would hit the cycle so we reset the state and continue. + case kGeneratingFilterCycleDetected: + state_ = kGeneratingFilter; + return false; + case kRecordingContentCycleDetected: + state_ = kRecordingContent; + return false; + default: + return true; + } +} + +void FilterData::UpdateStateOnPrepare() { + switch (state_) { + case kGeneratingFilter: + state_ = kGeneratingFilterCycleDetected; + break; + case kRecordingContent: + state_ = kRecordingContentCycleDetected; + break; + default: + break; + } +} + +void FilterData::UpdateContent(sk_sp<PaintRecord> content) { + DCHECK_EQ(state_, kRecordingContent); + Filter* filter = last_effect_->GetFilter(); + FloatRect bounds = filter->FilterRegion(); + DCHECK(filter->GetSourceGraphic()); + paint_filter_builder::BuildSourceGraphic(filter->GetSourceGraphic(), + std::move(content), bounds); + state_ = kReadyToPaint; +} + +sk_sp<PaintFilter> FilterData::CreateFilter() { + DCHECK_EQ(state_, kReadyToPaint); + state_ = kGeneratingFilter; + sk_sp<PaintFilter> image_filter = + paint_filter_builder::Build(last_effect_, kInterpolationSpaceSRGB); + state_ = kReadyToPaint; + return image_filter; +} + +FloatRect FilterData::MapRect(const FloatRect& input_rect) const { + DCHECK_EQ(state_, kReadyToPaint); + return last_effect_->MapRect(input_rect); +} + +bool FilterData::Invalidate(SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) { + if (state_ != kReadyToPaint) + return true; + if (FilterEffect* effect = node_map_->EffectForElement(primitive)) { + if (!primitive.SetFilterEffectAttribute(effect, attribute)) + return false; // No change + node_map_->InvalidateDependentEffects(effect); + } + return true; +} + +void FilterData::Trace(Visitor* visitor) { + visitor->Trace(last_effect_); + visitor->Trace(node_map_); +} + +void FilterData::Dispose() { + node_map_ = nullptr; + if (last_effect_) + last_effect_->DisposeImageFiltersRecursive(); + last_effect_ = nullptr; +} + SVGElementResourceClient::SVGElementResourceClient(SVGElement* element) : element_(element) {} @@ -713,22 +753,15 @@ void SVGElementResourceClient::ResourceContentChanged( LayoutObject* layout_object = element_->GetLayoutObject(); if (!layout_object) return; - bool mark_for_invalidation = - invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation; if (layout_object->IsSVGResourceContainer()) { - ToLayoutSVGResourceContainer(layout_object) - ->RemoveAllClientsFromCache(mark_for_invalidation); + ToLayoutSVGResourceContainer(layout_object)->RemoveAllClientsFromCache(); return; } - if (mark_for_invalidation) { - LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object, - invalidation_mask); - } + LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object, + invalidation_mask); - // Special case for filter invalidation. - if (invalidation_mask & SVGResourceClient::kSkipAncestorInvalidation) - return; + ClearFilterData(); bool needs_layout = invalidation_mask & SVGResourceClient::kLayoutInvalidation; @@ -737,8 +770,10 @@ void SVGElementResourceClient::ResourceContentChanged( } void SVGElementResourceClient::ResourceElementChanged() { - if (LayoutObject* layout_object = element_->GetLayoutObject()) + if (LayoutObject* layout_object = element_->GetLayoutObject()) { + ClearFilterData(); SVGResourcesCache::ResourceReferenceChanged(*layout_object); + } } void SVGElementResourceClient::ResourceDestroyed( @@ -752,8 +787,32 @@ void SVGElementResourceClient::ResourceDestroyed( resources->ResourceDestroyed(resource); } +void SVGElementResourceClient::FilterPrimitiveChanged( + SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) { + if (filter_data_ && !filter_data_->Invalidate(primitive, attribute)) + return; // No change + LayoutObject* layout_object = element_->GetLayoutObject(); + if (!layout_object) + return; + LayoutSVGResourceContainer::MarkClientForInvalidation( + *layout_object, SVGResourceClient::kPaintInvalidation); +} + +void SVGElementResourceClient::SetFilterData(FilterData* filter_data) { + filter_data_ = filter_data; +} + +bool SVGElementResourceClient::ClearFilterData() { + FilterData* filter_data = filter_data_.Release(); + if (filter_data) + filter_data->Dispose(); + return !!filter_data; +} + void SVGElementResourceClient::Trace(Visitor* visitor) { visitor->Trace(element_); + visitor->Trace(filter_data_); SVGResourceClient::Trace(visitor); } |