blob: f302470dafb69e7558ac3d36c16c6d95000262a2 [file] [log] [blame]
/*
* Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
* Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
* Copyright (C) 2009 Google, Inc. All rights reserved.
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
* Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/paint/scoped_svg_paint_state.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.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/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_controller.h"
namespace blink {
SVGFilterRecordingContext::SVGFilterRecordingContext(
const PaintInfo& initial_paint_info)
// Create a new controller and context so the contents of the filter can be
// drawn and cached.
: paint_controller_(std::make_unique<PaintController>()),
context_(std::make_unique<GraphicsContext>(*paint_controller_)),
paint_info_(*context_, initial_paint_info) {
// Use initial_paint_info's current paint chunk properties so that any new
// chunk created during painting the content will be in the correct state.
paint_controller_->UpdateCurrentPaintChunkProperties(
nullptr, initial_paint_info.context.GetPaintController()
.CurrentPaintChunkProperties());
// Because we cache the filter contents and do not invalidate on paint
// invalidation rect changes, we need to paint the entire filter region so
// elements outside the initial paint (due to scrolling, etc) paint.
paint_info_.ApplyInfiniteCullRect();
}
SVGFilterRecordingContext::~SVGFilterRecordingContext() = default;
sk_sp<PaintRecord> SVGFilterRecordingContext::GetPaintRecord(
const PaintInfo& initial_paint_info) {
paint_controller_->CommitNewDisplayItems();
return paint_controller_->GetPaintArtifact().GetPaintRecord(
initial_paint_info.context.GetPaintController()
.CurrentPaintChunkProperties());
}
static void PaintFilteredContent(GraphicsContext& context,
const LayoutObject& object,
const DisplayItemClient& display_item_client,
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 = 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 = filter_data->MapRect(object_bounds);
context.ClipRect(paint_rect);
// Use the union of the pre-image and the post-image as the layer bounds.
const FloatRect layer_bounds = UnionRect(object_bounds, paint_rect);
context.BeginLayer(1, SkBlendMode::kSrcOver, &layer_bounds, kColorFilterNone,
std::move(image_filter));
context.EndLayer();
context.Restore();
}
ScopedSVGPaintState::~ScopedSVGPaintState() {
if (filter_data_ && filter_data_->UpdateStateOnFinish()) {
DCHECK(SVGResourcesCache::CachedResourcesForLayoutObject(object_));
DCHECK(
SVGResourcesCache::CachedResourcesForLayoutObject(object_)->Filter());
if (filter_recording_context_) {
filter_data_->UpdateContent(
filter_recording_context_->GetPaintRecord(paint_info_));
filter_recording_context_ = nullptr;
}
PaintFilteredContent(paint_info_.context, object_, display_item_client_,
filter_data_);
filter_data_ = nullptr;
}
}
bool ScopedSVGPaintState::ApplyEffects() {
#if DCHECK_IS_ON()
DCHECK(!apply_clip_mask_and_filter_if_necessary_called_);
apply_clip_mask_and_filter_if_necessary_called_ = true;
#endif
ApplyPaintPropertyState();
// When rendering clip paths as masks, only geometric operations should be
// included so skip non-geometric operations such as compositing, masking, and
// filtering.
if (GetPaintInfo().IsRenderingClipPathAsMaskImage()) {
DCHECK(!object_.IsSVGRoot());
ApplyClipIfNecessary();
return true;
}
// LayoutSVGRoot and LayoutSVGForeignObject always have a self-painting
// PaintLayer (hence comments below about PaintLayerPainter).
bool is_svg_root_or_foreign_object =
object_.IsSVGRoot() || object_.IsSVGForeignObject();
if (is_svg_root_or_foreign_object) {
// PaintLayerPainter takes care of opacity and blend mode.
DCHECK(object_.HasLayer() || !(object_.StyleRef().HasOpacity() ||
object_.StyleRef().HasBlendMode() ||
object_.StyleRef().ClipPath()));
} else {
ApplyClipIfNecessary();
}
SVGResources* resources =
SVGResourcesCache::CachedResourcesForLayoutObject(object_);
ApplyMaskIfNecessary(resources);
if (is_svg_root_or_foreign_object) {
// PaintLayerPainter takes care of filter.
DCHECK(object_.HasLayer() || !object_.StyleRef().HasFilter());
} else if (!ApplyFilterIfNecessary(resources)) {
return false;
}
return true;
}
void ScopedSVGPaintState::ApplyPaintPropertyState() {
// SVGRoot works like normal CSS replaced element and its effects are
// applied as stacking context effect by PaintLayerPainter.
if (object_.IsSVGRoot())
return;
const auto* fragment = GetPaintInfo().FragmentToPaint(object_);
if (!fragment)
return;
const auto* properties = fragment->PaintProperties();
// MaskClip() implies Effect(), thus we don't need to check MaskClip().
if (!properties || (!properties->Effect() && !properties->ClipPathClip()))
return;
auto& paint_controller = GetPaintInfo().context.GetPaintController();
PropertyTreeState state = paint_controller.CurrentPaintChunkProperties();
if (const auto* effect = properties->Effect())
state.SetEffect(*effect);
if (const auto* mask_clip = properties->MaskClip())
state.SetClip(*mask_clip);
else if (const auto* clip_path_clip = properties->ClipPathClip())
state.SetClip(*clip_path_clip);
scoped_paint_chunk_properties_.emplace(
paint_controller, state, display_item_client_,
DisplayItem::PaintPhaseToSVGEffectType(GetPaintInfo().phase));
}
void ScopedSVGPaintState::ApplyClipIfNecessary() {
if (object_.StyleRef().ClipPath()) {
clip_path_clipper_.emplace(GetPaintInfo().context, object_,
display_item_client_, PhysicalOffset());
}
}
void ScopedSVGPaintState::ApplyMaskIfNecessary(SVGResources* resources) {
if (resources && resources->Masker())
mask_painter_.emplace(paint_info_.context, object_, display_item_client_);
}
static bool HasReferenceFilterOnly(const ComputedStyle& style) {
if (!style.HasFilter())
return false;
const FilterOperations& operations = style.Filter();
if (operations.size() != 1)
return false;
return operations.at(0)->GetType() == FilterOperation::REFERENCE;
}
bool ScopedSVGPaintState::ApplyFilterIfNecessary(SVGResources* resources) {
if (!resources)
return !HasReferenceFilterOnly(object_.StyleRef());
LayoutSVGResourceFilter* filter = resources->Filter();
if (!filter)
return true;
filter->ClearInvalidationMask();
filter_data_ = SVGResources::GetClient(object_)->UpdateFilterData();
// If we have no filter data (== the filter was invalid) or if we
// don't need to update the source graphics, we can short-circuit
// here.
if (!filter_data_ || !filter_data_->ContentNeedsUpdate())
return false;
// Because the filter needs to cache its contents we replace the context
// during filtering with the filter's context.
filter_recording_context_ =
std::make_unique<SVGFilterRecordingContext>(paint_info_);
return true;
}
} // namespace blink