blob: 6bf7616c4cb3a94368599eb40fe1e51e72da8f3f [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
* Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
* Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
*
* 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/svg/svg_filter_primitive_standard_attributes.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.h"
#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h"
#include "third_party/blink/renderer/core/svg/svg_animated_length.h"
#include "third_party/blink/renderer/core/svg/svg_animated_string.h"
#include "third_party/blink/renderer/core/svg/svg_filter_element.h"
#include "third_party/blink/renderer/core/svg/svg_length.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/heap/heap.h"
namespace blink {
SVGFilterPrimitiveStandardAttributes::SVGFilterPrimitiveStandardAttributes(
const QualifiedName& tag_name,
Document& document)
: SVGElement(tag_name, document),
// Spec: If the x/y attribute is not specified, the effect is as if a
// value of "0%" were specified.
x_(MakeGarbageCollected<SVGAnimatedLength>(
this,
svg_names::kXAttr,
SVGLengthMode::kWidth,
SVGLength::Initial::kPercent0)),
y_(MakeGarbageCollected<SVGAnimatedLength>(
this,
svg_names::kYAttr,
SVGLengthMode::kHeight,
SVGLength::Initial::kPercent0)),
// Spec: If the width/height attribute is not specified, the effect is as
// if a value of "100%" were specified.
width_(MakeGarbageCollected<SVGAnimatedLength>(
this,
svg_names::kWidthAttr,
SVGLengthMode::kWidth,
SVGLength::Initial::kPercent100)),
height_(MakeGarbageCollected<SVGAnimatedLength>(
this,
svg_names::kHeightAttr,
SVGLengthMode::kHeight,
SVGLength::Initial::kPercent100)),
result_(MakeGarbageCollected<SVGAnimatedString>(this,
svg_names::kResultAttr)) {
AddToPropertyMap(x_);
AddToPropertyMap(y_);
AddToPropertyMap(width_);
AddToPropertyMap(height_);
AddToPropertyMap(result_);
}
void SVGFilterPrimitiveStandardAttributes::Trace(Visitor* visitor) const {
visitor->Trace(x_);
visitor->Trace(y_);
visitor->Trace(width_);
visitor->Trace(height_);
visitor->Trace(result_);
SVGElement::Trace(visitor);
}
bool SVGFilterPrimitiveStandardAttributes::SetFilterEffectAttribute(
FilterEffect* effect,
const QualifiedName& attr_name) {
DCHECK(attr_name == svg_names::kColorInterpolationFiltersAttr);
DCHECK(GetLayoutObject());
EColorInterpolation color_interpolation =
GetLayoutObject()->StyleRef().ColorInterpolationFilters();
InterpolationSpace resolved_interpolation_space =
SVGFilterBuilder::ResolveInterpolationSpace(color_interpolation);
if (resolved_interpolation_space == effect->OperatingInterpolationSpace())
return false;
effect->SetOperatingInterpolationSpace(resolved_interpolation_space);
return true;
}
void SVGFilterPrimitiveStandardAttributes::SvgAttributeChanged(
const SvgAttributeChangedParams& params) {
const QualifiedName& attr_name = params.name;
if (attr_name == svg_names::kXAttr || attr_name == svg_names::kYAttr ||
attr_name == svg_names::kWidthAttr ||
attr_name == svg_names::kHeightAttr ||
attr_name == svg_names::kResultAttr) {
SVGElement::InvalidationGuard invalidation_guard(this);
Invalidate();
return;
}
SVGElement::SvgAttributeChanged(params);
}
void SVGFilterPrimitiveStandardAttributes::ChildrenChanged(
const ChildrenChange& change) {
SVGElement::ChildrenChanged(change);
if (!change.ByParser())
Invalidate();
}
static FloatRect DefaultFilterPrimitiveSubregion(FilterEffect* filter_effect) {
// https://drafts.fxtf.org/filter-effects/#FilterPrimitiveSubRegion
DCHECK(filter_effect->GetFilter());
// <feTurbulence>, <feFlood> and <feImage> don't have input effects, so use
// the filter region as default subregion. <feTile> does have an input
// reference, but due to its function (and special-cases) its default
// resolves to the filter region.
if (filter_effect->GetFilterEffectType() == kFilterEffectTypeTile ||
!filter_effect->NumberOfEffectInputs())
return filter_effect->GetFilter()->FilterRegion();
// "x, y, width and height default to the union (i.e., tightest fitting
// bounding box) of the subregions defined for all referenced nodes."
FloatRect subregion_union;
for (const auto& input_effect : filter_effect->InputEffects()) {
// "If ... one or more of the referenced nodes is a standard input
// ... the default subregion is 0%, 0%, 100%, 100%, where as a
// special-case the percentages are relative to the dimensions of the
// filter region..."
if (input_effect->GetFilterEffectType() == kFilterEffectTypeSourceInput)
return filter_effect->GetFilter()->FilterRegion();
subregion_union.Unite(input_effect->FilterPrimitiveSubregion());
}
return subregion_union;
}
void SVGFilterPrimitiveStandardAttributes::SetStandardAttributes(
FilterEffect* filter_effect,
SVGUnitTypes::SVGUnitType primitive_units,
const FloatRect& reference_box) const {
DCHECK(filter_effect);
FloatRect subregion = DefaultFilterPrimitiveSubregion(filter_effect);
FloatRect primitive_boundaries =
SVGLengthContext::ResolveRectangle(this, primitive_units, reference_box);
if (x()->IsSpecified())
subregion.SetX(primitive_boundaries.X());
if (y()->IsSpecified())
subregion.SetY(primitive_boundaries.Y());
if (width()->IsSpecified())
subregion.SetWidth(primitive_boundaries.Width());
if (height()->IsSpecified())
subregion.SetHeight(primitive_boundaries.Height());
filter_effect->SetFilterPrimitiveSubregion(subregion);
}
LayoutObject* SVGFilterPrimitiveStandardAttributes::CreateLayoutObject(
const ComputedStyle&,
LegacyLayout) {
return new LayoutSVGFilterPrimitive(this);
}
bool SVGFilterPrimitiveStandardAttributes::LayoutObjectIsNeeded(
const ComputedStyle& style) const {
if (IsA<SVGFilterElement>(parentNode()))
return SVGElement::LayoutObjectIsNeeded(style);
return false;
}
void SVGFilterPrimitiveStandardAttributes::Invalidate() {
if (auto* filter = DynamicTo<SVGFilterElement>(parentElement()))
filter->InvalidateFilterChain();
}
void SVGFilterPrimitiveStandardAttributes::PrimitiveAttributeChanged(
const QualifiedName& attribute) {
if (auto* filter = DynamicTo<SVGFilterElement>(parentElement()))
filter->PrimitiveAttributeChanged(*this, attribute);
}
void InvalidateFilterPrimitiveParent(SVGElement& element) {
auto* svg_parent =
DynamicTo<SVGFilterPrimitiveStandardAttributes>(element.parentElement());
if (!svg_parent)
return;
svg_parent->Invalidate();
}
} // namespace blink