1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 06:48:12 +00:00
serenity/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
Andreas Kling 2e0297d703 LibWeb: Handle reference cycles in SVG gradient linking
Since SVG gradients can reference each other, we have to keep track of
visited gradients when traversing the link chain, or we will recurse
infinitely when there's a reference cycle.
2024-03-11 18:29:10 +01:00

95 lines
3.1 KiB
C++

/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IterationDecision.h>
#include <LibGfx/PaintStyle.h>
#include <LibWeb/SVG/AttributeParser.h>
#include <LibWeb/SVG/SVGElement.h>
#include <LibWeb/SVG/SVGStopElement.h>
namespace Web::SVG {
struct SVGPaintContext {
Gfx::FloatRect viewport;
Gfx::FloatRect path_bounding_box;
Gfx::AffineTransform transform;
};
inline Gfx::SVGGradientPaintStyle::SpreadMethod to_gfx_spread_method(SpreadMethod spread_method)
{
switch (spread_method) {
case SpreadMethod::Pad:
return Gfx::SVGGradientPaintStyle::SpreadMethod::Pad;
case SpreadMethod::Reflect:
return Gfx::SVGGradientPaintStyle::SpreadMethod::Reflect;
case SpreadMethod::Repeat:
return Gfx::SVGGradientPaintStyle::SpreadMethod::Repeat;
default:
VERIFY_NOT_REACHED();
}
}
class SVGGradientElement : public SVGElement {
WEB_PLATFORM_OBJECT(SVGGradientElement, SVGElement);
public:
virtual ~SVGGradientElement() override = default;
virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
virtual Optional<Gfx::PaintStyle const&> to_gfx_paint_style(SVGPaintContext const&) const = 0;
GradientUnits gradient_units() const;
SpreadMethod spread_method() const;
Optional<Gfx::AffineTransform> gradient_transform() const;
protected:
SVGGradientElement(DOM::Document&, DOM::QualifiedName);
virtual void initialize(JS::Realm&) override;
JS::GCPtr<SVGGradientElement const> linked_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const;
Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const;
template<VoidFunction<SVGStopElement> Callback>
void for_each_color_stop(Callback const& callback) const
{
HashTable<SVGGradientElement const*> seen_gradients;
return for_each_color_stop_impl(callback, seen_gradients);
}
void add_color_stops(Gfx::SVGGradientPaintStyle&) const;
private:
template<VoidFunction<SVGStopElement> Callback>
void for_each_color_stop_impl(Callback const& callback, HashTable<SVGGradientElement const*>& seen_gradients) const
{
bool color_stops_found = false;
for_each_child_of_type<SVG::SVGStopElement>([&](auto& stop) {
color_stops_found = true;
callback(stop);
});
if (!color_stops_found) {
if (auto gradient = linked_gradient(seen_gradients))
gradient->for_each_color_stop_impl(callback, seen_gradients);
}
}
GradientUnits gradient_units_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
SpreadMethod spread_method_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
Optional<Gfx::AffineTransform> gradient_transform_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
Optional<GradientUnits> m_gradient_units = {};
Optional<SpreadMethod> m_spread_method = {};
Optional<Gfx::AffineTransform> m_gradient_transform = {};
};
}