| /* |
| * 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/graphics/filters/svg_filter_builder.h" |
| |
| #include "third_party/blink/renderer/core/css/css_primitive_value.h" |
| #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h" |
| #include "third_party/blink/renderer/core/css/css_property_value_set.h" |
| #include "third_party/blink/renderer/core/dom/element_traversal.h" |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/svg/svg_filter_element.h" |
| #include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" |
| #include "third_party/blink/renderer/platform/graphics/filters/filter.h" |
| #include "third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h" |
| #include "third_party/blink/renderer/platform/graphics/filters/source_alpha.h" |
| #include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| class FilterInputKeywords { |
| public: |
| static const AtomicString& GetSourceGraphic() { |
| DEFINE_STATIC_LOCAL(const AtomicString, source_graphic_name, |
| ("SourceGraphic")); |
| return source_graphic_name; |
| } |
| |
| static const AtomicString& SourceAlpha() { |
| DEFINE_STATIC_LOCAL(const AtomicString, source_alpha_name, ("SourceAlpha")); |
| return source_alpha_name; |
| } |
| |
| static const AtomicString& FillPaint() { |
| DEFINE_STATIC_LOCAL(const AtomicString, fill_paint_name, ("FillPaint")); |
| return fill_paint_name; |
| } |
| |
| static const AtomicString& StrokePaint() { |
| DEFINE_STATIC_LOCAL(const AtomicString, stroke_paint_name, ("StrokePaint")); |
| return stroke_paint_name; |
| } |
| }; |
| |
| } // namespace |
| |
| SVGFilterGraphNodeMap::SVGFilterGraphNodeMap() = default; |
| |
| void SVGFilterGraphNodeMap::AddBuiltinEffect(FilterEffect* effect) { |
| effect_references_.insert(effect, FilterEffectSet()); |
| } |
| |
| void SVGFilterGraphNodeMap::AddPrimitive(LayoutObject* object, |
| FilterEffect* effect) { |
| // The effect must be a newly created filter effect. |
| DCHECK(!effect_references_.Contains(effect)); |
| DCHECK(!object || !effect_renderer_.Contains(object)); |
| effect_references_.insert(effect, FilterEffectSet()); |
| |
| unsigned number_of_input_effects = effect->InputEffects().size(); |
| |
| // Add references from the inputs of this effect to the effect itself, to |
| // allow determining what effects needs to be invalidated when a certain |
| // effect changes. |
| for (unsigned i = 0; i < number_of_input_effects; ++i) |
| EffectReferences(effect->InputEffect(i)).insert(effect); |
| |
| // If object is null, that means the element isn't attached for some |
| // reason, which in turn mean that certain types of invalidation will not |
| // work (the LayoutObject -> FilterEffect mapping will not be defined). |
| if (object) |
| effect_renderer_.insert(object, effect); |
| } |
| |
| void SVGFilterGraphNodeMap::InvalidateDependentEffects(FilterEffect* effect) { |
| if (!effect->HasImageFilter()) |
| return; |
| |
| effect->DisposeImageFilters(); |
| |
| FilterEffectSet& effect_references = this->EffectReferences(effect); |
| for (FilterEffect* effect_reference : effect_references) |
| InvalidateDependentEffects(effect_reference); |
| } |
| |
| void SVGFilterGraphNodeMap::Trace(blink::Visitor* visitor) { |
| visitor->Trace(effect_renderer_); |
| visitor->Trace(effect_references_); |
| } |
| |
| SVGFilterBuilder::SVGFilterBuilder(FilterEffect* source_graphic, |
| SVGFilterGraphNodeMap* node_map, |
| const PaintFlags* fill_flags, |
| const PaintFlags* stroke_flags) |
| : node_map_(node_map) { |
| FilterEffect* source_graphic_ref = source_graphic; |
| builtin_effects_.insert(FilterInputKeywords::GetSourceGraphic(), |
| source_graphic_ref); |
| builtin_effects_.insert(FilterInputKeywords::SourceAlpha(), |
| SourceAlpha::Create(source_graphic_ref)); |
| if (fill_flags) { |
| builtin_effects_.insert(FilterInputKeywords::FillPaint(), |
| PaintFilterEffect::Create( |
| source_graphic_ref->GetFilter(), *fill_flags)); |
| } |
| if (stroke_flags) { |
| builtin_effects_.insert( |
| FilterInputKeywords::StrokePaint(), |
| PaintFilterEffect::Create(source_graphic_ref->GetFilter(), |
| *stroke_flags)); |
| } |
| AddBuiltinEffects(); |
| } |
| |
| void SVGFilterBuilder::AddBuiltinEffects() { |
| if (!node_map_) |
| return; |
| for (const auto& entry : builtin_effects_) |
| node_map_->AddBuiltinEffect(entry.value.Get()); |
| } |
| |
| // Returns the color-interpolation-filters property of the element. |
| static EColorInterpolation ColorInterpolationForElement( |
| SVGElement& element, |
| EColorInterpolation parent_color_interpolation) { |
| if (const LayoutObject* layout_object = element.GetLayoutObject()) |
| return layout_object->StyleRef().SvgStyle().ColorInterpolationFilters(); |
| |
| // No layout has been performed, try to determine the property value |
| // "manually" (used by external SVG files.) |
| if (const CSSPropertyValueSet* property_set = |
| element.PresentationAttributeStyle()) { |
| const CSSValue* css_value = |
| property_set->GetPropertyCSSValue(CSSPropertyColorInterpolationFilters); |
| if (css_value && css_value->IsIdentifierValue()) { |
| return ToCSSIdentifierValue(*css_value).ConvertTo<EColorInterpolation>(); |
| } |
| } |
| // 'auto' is the default (per Filter Effects), but since the property is |
| // inherited, propagate the parent's value. |
| return parent_color_interpolation; |
| } |
| |
| InterpolationSpace SVGFilterBuilder::ResolveInterpolationSpace( |
| EColorInterpolation color_interpolation) { |
| return color_interpolation == CI_LINEARRGB ? kInterpolationSpaceLinear |
| : kInterpolationSpaceSRGB; |
| } |
| |
| void SVGFilterBuilder::BuildGraph(Filter* filter, |
| SVGFilterElement& filter_element, |
| const FloatRect& reference_box) { |
| EColorInterpolation filter_color_interpolation = |
| ColorInterpolationForElement(filter_element, CI_AUTO); |
| SVGUnitTypes::SVGUnitType primitive_units = |
| filter_element.primitiveUnits()->CurrentValue()->EnumValue(); |
| |
| for (SVGElement* element = Traversal<SVGElement>::FirstChild(filter_element); |
| element; element = Traversal<SVGElement>::NextSibling(*element)) { |
| if (!element->IsFilterEffect()) |
| continue; |
| |
| SVGFilterPrimitiveStandardAttributes& effect_element = |
| ToSVGFilterPrimitiveStandardAttributes(*element); |
| FilterEffect* effect = effect_element.Build(this, filter); |
| if (!effect) |
| continue; |
| |
| if (node_map_) |
| node_map_->AddPrimitive(effect_element.GetLayoutObject(), effect); |
| |
| effect_element.SetStandardAttributes(effect, primitive_units, |
| reference_box); |
| EColorInterpolation color_interpolation = ColorInterpolationForElement( |
| effect_element, filter_color_interpolation); |
| effect->SetOperatingInterpolationSpace( |
| ResolveInterpolationSpace(color_interpolation)); |
| if (effect->InputsTaintOrigin() || effect_element.TaintsOrigin()) |
| effect->SetOriginTainted(); |
| |
| Add(AtomicString(effect_element.result()->CurrentValue()->Value()), effect); |
| } |
| } |
| |
| void SVGFilterBuilder::Add(const AtomicString& id, FilterEffect* effect) { |
| if (id.IsEmpty()) { |
| last_effect_ = effect; |
| return; |
| } |
| |
| if (builtin_effects_.Contains(id)) |
| return; |
| |
| last_effect_ = effect; |
| named_effects_.Set(id, last_effect_); |
| } |
| |
| FilterEffect* SVGFilterBuilder::GetEffectById(const AtomicString& id) const { |
| if (!id.IsEmpty()) { |
| if (FilterEffect* builtin_effect = builtin_effects_.at(id)) |
| return builtin_effect; |
| |
| if (FilterEffect* named_effect = named_effects_.at(id)) |
| return named_effect; |
| } |
| |
| if (last_effect_) |
| return last_effect_.Get(); |
| |
| return builtin_effects_.at(FilterInputKeywords::GetSourceGraphic()); |
| } |
| |
| } // namespace blink |