Commit 7d10b620 authored by Fredrik Söderqvist's avatar Fredrik Söderqvist Committed by Commit Bot

Move FilterData state munging into the class itself

This starts dismantling SVGFilterPainter by moving some of the logic
into the FilterData class. Various methods are added to FilterData to
handle manipulation of its state. Member fields are made private.

The kPaintingFilter* states are renamed to kGeneratingFilter* because
that more closely describes what they are there for (which is made more
obvious by the new structure).

Bug: 109224
Change-Id: Idfa1d584b4d37c8e4d6733f8729e8e942313533c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2102573
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750512}
parent 5e3aa9f7
......@@ -39,7 +39,10 @@
#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>
......@@ -671,16 +674,81 @@ 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);
visitor->Trace(last_effect_);
visitor->Trace(node_map_);
}
void FilterData::Dispose() {
node_map = nullptr;
if (last_effect)
last_effect->DisposeImageFiltersRecursive();
last_effect = nullptr;
node_map_ = nullptr;
if (last_effect_)
last_effect_->DisposeImageFiltersRecursive();
last_effect_ = nullptr;
}
SVGElementResourceClient::SVGElementResourceClient(SVGElement* element)
......@@ -728,14 +796,8 @@ void SVGElementResourceClient::ResourceDestroyed(
void SVGElementResourceClient::FilterPrimitiveChanged(
SVGFilterPrimitiveStandardAttributes& primitive,
const QualifiedName& attribute) {
if (filter_data_ && filter_data_->state_ == FilterData::kReadyToPaint) {
SVGFilterGraphNodeMap* node_map = filter_data_->node_map;
if (FilterEffect* effect = node_map->EffectForElement(primitive)) {
if (!primitive.SetFilterEffectAttribute(effect, attribute))
return; // No change
node_map->InvalidateDependentEffects(effect);
}
}
if (filter_data_ && !filter_data_->Invalidate(primitive, attribute))
return; // No change
LayoutObject* layout_object = element_->GetLayoutObject();
if (!layout_object)
return;
......
......@@ -195,30 +195,46 @@ class SVGResources {
class FilterData final : public GarbageCollected<FilterData> {
public:
FilterData(FilterEffect* last_effect, SVGFilterGraphNodeMap* node_map)
: last_effect_(last_effect),
node_map_(node_map),
state_(kRecordingContent) {}
void UpdateStateOnPrepare();
bool UpdateStateOnFinish();
bool ContentNeedsUpdate() const { return state_ == kRecordingContent; }
void UpdateContent(sk_sp<PaintRecord> content);
sk_sp<PaintFilter> CreateFilter();
FloatRect MapRect(const FloatRect& input_rect) const;
// Perform a finegrained invalidation of the filter chain for the
// specified filter primitive and attribute. Returns false if no
// further invalidation is required, otherwise true.
bool Invalidate(SVGFilterPrimitiveStandardAttributes& primitive,
const QualifiedName& attribute);
void Dispose();
void Trace(Visitor*);
private:
Member<FilterEffect> last_effect_;
Member<SVGFilterGraphNodeMap> node_map_;
/*
* The state transitions should follow the following:
* Initial->RecordingContent->ReadyToPaint->PaintingFilter->ReadyToPaint
* | ^ | ^
* v | v |
* RecordingContentCycleDetected PaintingFilterCycle
*
* RecordingContent->ReadyToPaint->GeneratingFilter->ReadyToPaint
* | ^ | ^
* v | v |
* RecordingContentCycleDetected GeneratingFilterCycleDetected
*/
enum FilterDataState {
kInitial,
kRecordingContent,
kRecordingContentCycleDetected,
kReadyToPaint,
kPaintingFilter,
kPaintingFilterCycleDetected
kGeneratingFilter,
kGeneratingFilterCycleDetected
};
FilterData() : state_(kInitial) {}
void Dispose();
void Trace(Visitor*);
Member<FilterEffect> last_effect;
Member<SVGFilterGraphNodeMap> node_map;
FilterDataState state_;
};
......
......@@ -12,7 +12,6 @@
#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h"
#include "third_party/blink/renderer/core/svg/svg_filter_element.h"
#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
......@@ -56,20 +55,19 @@ void SVGFilterRecordingContext::Abort() {
static void PaintFilteredContent(GraphicsContext& context,
const LayoutObject& object,
const DisplayItemClient& display_item_client,
FilterEffect* effect) {
FilterData* filter_data) {
if (DrawingRecorder::UseCachedDrawingIfPossible(context, display_item_client,
DisplayItem::kSVGFilter))
return;
DrawingRecorder recorder(context, display_item_client,
DisplayItem::kSVGFilter);
sk_sp<PaintFilter> image_filter =
paint_filter_builder::Build(effect, kInterpolationSpaceSRGB);
sk_sp<PaintFilter> image_filter = filter_data->CreateFilter();
context.Save();
// Clip drawing of filtered image to the minimum required paint rect.
const FloatRect object_bounds = object.StrokeBoundingBox();
const FloatRect paint_rect = effect->MapRect(object_bounds);
const FloatRect paint_rect = filter_data->MapRect(object_bounds);
context.ClipRect(paint_rect);
// Use the union of the pre-image and the post-image as the layer bounds.
......@@ -90,12 +88,7 @@ GraphicsContext* SVGFilterPainter::PrepareEffect(
// If the filterData already exists we do not need to record the content
// to be filtered. This can occur if the content was previously recorded
// or we are in a cycle.
if (filter_data->state_ == FilterData::kPaintingFilter)
filter_data->state_ = FilterData::kPaintingFilterCycleDetected;
if (filter_data->state_ == FilterData::kRecordingContent)
filter_data->state_ = FilterData::kRecordingContentCycleDetected;
filter_data->UpdateStateOnPrepare();
return nullptr;
}
......@@ -109,14 +102,10 @@ GraphicsContext* SVGFilterPainter::PrepareEffect(
IntRect source_region = EnclosingIntRect(object.StrokeBoundingBox());
filter->GetSourceGraphic()->SetSourceRect(source_region);
auto* filter_data = MakeGarbageCollected<FilterData>();
filter_data->last_effect = filter->LastEffect();
filter_data->node_map = node_map;
DCHECK_EQ(filter_data->state_, FilterData::kInitial);
auto* filter_data =
MakeGarbageCollected<FilterData>(filter->LastEffect(), node_map);
// TODO(pdr): Can this be moved out of painter?
client->SetFilterData(filter_data);
filter_data->state_ = FilterData::kRecordingContent;
return recording_context.BeginContent();
}
......@@ -135,35 +124,16 @@ void SVGFilterPainter::FinishEffect(
return;
}
// A painting 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.
if (filter_data->state_ == FilterData::kPaintingFilterCycleDetected) {
filter_data->state_ = FilterData::kPaintingFilter;
if (!filter_data->UpdateStateOnFinish())
return;
}
if (filter_data->state_ == FilterData::kRecordingContentCycleDetected) {
filter_data->state_ = FilterData::kRecordingContent;
return;
}
// Check for RecordingContent here because we may can be re-painting
// without re-recording the contents to be filtered.
if (filter_data->state_ == FilterData::kRecordingContent) {
sk_sp<PaintRecord> content = recording_context.EndContent();
Filter* filter = filter_data->last_effect->GetFilter();
FloatRect bounds = filter->FilterRegion();
DCHECK(filter->GetSourceGraphic());
paint_filter_builder::BuildSourceGraphic(filter->GetSourceGraphic(),
std::move(content), bounds);
filter_data->state_ = FilterData::kReadyToPaint;
}
if (filter_data->ContentNeedsUpdate())
filter_data->UpdateContent(recording_context.EndContent());
DCHECK_EQ(filter_data->state_, FilterData::kReadyToPaint);
filter_data->state_ = FilterData::kPaintingFilter;
PaintFilteredContent(recording_context.PaintingContext(), object,
display_item_client, filter_data->last_effect);
filter_data->state_ = FilterData::kReadyToPaint;
display_item_client, filter_data);
}
} // namespace blink
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