LibWeb: Implement SVGGradientElement

This is the base class for all SVG gradient types. This supports:

- The `gradientUnits` attribute
- The `gradientTransform` attribute
- And following `xlink:hrefs` for inheriting <stops>/attributes
This commit is contained in:
MacDue 2023-04-21 18:51:00 +01:00 committed by Andreas Kling
parent a5fa5e55ef
commit 71938550fa
Notes: sideshowbarker 2024-07-17 06:35:16 +09:00
7 changed files with 175 additions and 0 deletions

View File

@ -495,6 +495,7 @@ set(SOURCES
SVG/SVGGElement.cpp
SVG/SVGGeometryElement.cpp
SVG/SVGGraphicsElement.cpp
SVG/SVGGradientElement.cpp
SVG/SVGPathElement.cpp
SVG/SVGCircleElement.cpp
SVG/SVGEllipseElement.cpp

View File

@ -494,6 +494,19 @@ Optional<PreserveAspectRatio> AttributeParser::parse_preserve_aspect_ratio(Strin
return PreserveAspectRatio { *align, *meet_or_slice };
}
// https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementGradientUnitsAttribute
Optional<GradientUnits> AttributeParser::parse_gradient_units(StringView input)
{
GenericLexer lexer { input };
lexer.ignore_while(whitespace);
auto gradient_units_string = lexer.consume_until(whitespace);
if (gradient_units_string == "userSpaceOnUse"sv)
return GradientUnits::UserSpaceOnUse;
if (gradient_units_string == "objectBoundingBox"sv)
return GradientUnits::ObjectBoundingBox;
return {};
}
// https://drafts.csswg.org/css-transforms/#svg-syntax
Optional<Vector<Transform>> AttributeParser::parse_transform()
{

View File

@ -88,6 +88,11 @@ struct PreserveAspectRatio {
MeetOrSlice meet_or_slice { MeetOrSlice::Meet };
};
enum class GradientUnits {
ObjectBoundingBox,
UserSpaceOnUse
};
class NumberPercentage {
public:
NumberPercentage(float value, bool is_percentage)
@ -127,6 +132,7 @@ public:
static Vector<PathInstruction> parse_path_data(StringView input);
static Optional<Vector<Transform>> parse_transform(StringView input);
static Optional<PreserveAspectRatio> parse_preserve_aspect_ratio(StringView input);
static Optional<GradientUnits> parse_gradient_units(StringView input);
private:
AttributeParser(StringView source);

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/SVGGradientElement.h>
#include <LibWeb/SVG/SVGGraphicsElement.h>
namespace Web::SVG {
SVGGradientElement::SVGGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: SVGElement(document, move(qualified_name))
{
}
void SVGGradientElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
{
SVGElement::parse_attribute(name, value);
if (name == AttributeNames::gradientUnits) {
m_gradient_units = AttributeParser::parse_gradient_units(value);
} else if (name == AttributeNames::gradientTransform) {
if (auto transform_list = AttributeParser::parse_transform(value); transform_list.has_value()) {
m_gradient_transform = transform_from_transform_list(*transform_list);
} else {
m_gradient_transform = {};
}
}
}
GradientUnits SVGGradientElement::gradient_units() const
{
if (m_gradient_units.has_value())
return *m_gradient_units;
if (auto href = xlink_href())
return href->gradient_units();
return GradientUnits::ObjectBoundingBox;
}
Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform() const
{
if (m_gradient_transform.has_value())
return m_gradient_transform;
if (auto href = xlink_href())
return href->gradient_transform();
return {};
}
JS::GCPtr<SVGGradientElement const> SVGGradientElement::xlink_href() const
{
// FIXME: This entire function is an ad-hoc hack!
// It can only resolve #<ids> in the same document.
if (auto href = get_attribute("href"); !href.is_empty()) {
auto url = document().parse_url(href);
auto id = url.fragment();
if (id.is_empty())
return {};
auto element = document().get_element_by_id(id);
if (!element)
return {};
if (!is<SVGGradientElement>(*element))
return {};
return &verify_cast<SVGGradientElement>(*element);
}
return {};
}
JS::ThrowCompletionOr<void> SVGGradientElement::initialize(JS::Realm& realm)
{
MUST_OR_THROW_OOM(Base::initialize(realm));
set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGGradientElementPrototype>(realm, "SVGGradientElement"));
return {};
}
}

View File

@ -0,0 +1,59 @@
/*
* 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;
};
class SVGGradientElement : public SVGElement {
WEB_PLATFORM_OBJECT(SVGGradientElement, SVGElement);
public:
virtual ~SVGGradientElement() override = default;
virtual void parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) override;
virtual Optional<Gfx::PaintStyle const&> to_gfx_paint_style(SVGPaintContext const&) const = 0;
GradientUnits gradient_units() const;
Optional<Gfx::AffineTransform> gradient_transform() const;
protected:
SVGGradientElement(DOM::Document&, DOM::QualifiedName);
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
JS::GCPtr<SVGGradientElement const> xlink_href() const;
template<VoidFunction<SVGStopElement> Callback>
void for_each_color_stop(Callback const& callback) const
{
for_each_child_of_type<SVG::SVGStopElement>([&](auto& stop) {
callback(stop);
});
if (auto href = xlink_href())
href->for_each_color_stop(callback);
}
private:
Optional<GradientUnits> m_gradient_units = {};
Optional<Gfx::AffineTransform> m_gradient_transform = {};
};
}

View File

@ -0,0 +1,17 @@
#import <SVG/SVGElement.idl>
[Exposed=Window]
interface SVGGradientElement : SVGElement {
// Spread Method Types
const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
const unsigned short SVG_SPREADMETHOD_PAD = 1;
const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
const unsigned short SVG_SPREADMETHOD_REPEAT = 3;
// FIXME: [SameObject] readonly attribute SVGAnimatedEnumeration gradientUnits;
// FIXME: [SameObject] readonly attribute SVGAnimatedTransformList gradientTransform;
// FIXME: [SameObject] readonly attribute SVGAnimatedEnumeration spreadMethod;
};
// FIXME: SVGGradientElement includes SVGURIReference;

View File

@ -196,6 +196,7 @@ libweb_js_bindings(SVG/SVGClipPathElement)
libweb_js_bindings(SVG/SVGDefsElement)
libweb_js_bindings(SVG/SVGElement)
libweb_js_bindings(SVG/SVGGeometryElement)
libweb_js_bindings(SVG/SVGGradientElement)
libweb_js_bindings(SVG/SVGGraphicsElement)
libweb_js_bindings(SVG/SVGCircleElement)
libweb_js_bindings(SVG/SVGEllipseElement)