mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 06:47:40 +00:00
LibWeb: Implement SVGLinearGradientElement (<linearGradient>)
This represents the SVG <linearGradient>. The actual gradient is converted to a Gfx::PaintStyle for use in SVG fills... There is a little guesswork in the implementation, but it seems to match Chrome/Firefox. Note: Still not hooked up to actual painting in this commit.
This commit is contained in:
parent
89d3c6d718
commit
aa3464466e
6 changed files with 243 additions and 0 deletions
|
@ -502,6 +502,7 @@ set(SOURCES
|
||||||
SVG/SVGForeignObjectElement.cpp
|
SVG/SVGForeignObjectElement.cpp
|
||||||
SVG/SVGLength.cpp
|
SVG/SVGLength.cpp
|
||||||
SVG/SVGLineElement.cpp
|
SVG/SVGLineElement.cpp
|
||||||
|
SVG/SVGLinearGradientElement.cpp
|
||||||
SVG/SVGPolygonElement.cpp
|
SVG/SVGPolygonElement.cpp
|
||||||
SVG/SVGPolylineElement.cpp
|
SVG/SVGPolylineElement.cpp
|
||||||
SVG/SVGRectElement.cpp
|
SVG/SVGRectElement.cpp
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
#include <LibWeb/SVG/SVGForeignObjectElement.h>
|
#include <LibWeb/SVG/SVGForeignObjectElement.h>
|
||||||
#include <LibWeb/SVG/SVGGElement.h>
|
#include <LibWeb/SVG/SVGGElement.h>
|
||||||
#include <LibWeb/SVG/SVGLineElement.h>
|
#include <LibWeb/SVG/SVGLineElement.h>
|
||||||
|
#include <LibWeb/SVG/SVGLinearGradientElement.h>
|
||||||
#include <LibWeb/SVG/SVGPathElement.h>
|
#include <LibWeb/SVG/SVGPathElement.h>
|
||||||
#include <LibWeb/SVG/SVGPolygonElement.h>
|
#include <LibWeb/SVG/SVGPolygonElement.h>
|
||||||
#include <LibWeb/SVG/SVGPolylineElement.h>
|
#include <LibWeb/SVG/SVGPolylineElement.h>
|
||||||
|
@ -432,6 +433,8 @@ static WebIDL::ExceptionOr<JS::GCPtr<SVG::SVGElement>> create_svg_element(JS::Re
|
||||||
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGForeignObjectElement>(realm, document, move(qualified_name)));
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGForeignObjectElement>(realm, document, move(qualified_name)));
|
||||||
if (local_name == SVG::TagNames::line)
|
if (local_name == SVG::TagNames::line)
|
||||||
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGLineElement>(realm, document, move(qualified_name)));
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGLineElement>(realm, document, move(qualified_name)));
|
||||||
|
if (local_name == SVG::TagNames::linearGradient)
|
||||||
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGLinearGradientElement>(realm, document, move(qualified_name)));
|
||||||
if (local_name == SVG::TagNames::path)
|
if (local_name == SVG::TagNames::path)
|
||||||
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGPathElement>(realm, document, move(qualified_name)));
|
return MUST_OR_THROW_OOM(realm.heap().allocate<SVG::SVGPathElement>(realm, document, move(qualified_name)));
|
||||||
if (local_name == SVG::TagNames::polygon)
|
if (local_name == SVG::TagNames::polygon)
|
||||||
|
|
173
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
Normal file
173
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* 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/AttributeParser.h>
|
||||||
|
#include <LibWeb/SVG/SVGLinearGradientElement.h>
|
||||||
|
#include <LibWeb/SVG/SVGStopElement.h>
|
||||||
|
|
||||||
|
namespace Web::SVG {
|
||||||
|
|
||||||
|
SVGLinearGradientElement::SVGLinearGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name)
|
||||||
|
: SVGGradientElement(document, qualified_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::ThrowCompletionOr<void> SVGLinearGradientElement::initialize(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
MUST_OR_THROW_OOM(Base::initialize(realm));
|
||||||
|
set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGLinearGradientElementPrototype>(realm, "SVGLinearGradientElement"));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGLinearGradientElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
|
||||||
|
{
|
||||||
|
SVGGradientElement::parse_attribute(name, value);
|
||||||
|
|
||||||
|
// FIXME: Should allow for `<number-percentage> | <length>` for x1, x2, y1, y2
|
||||||
|
if (name == SVG::AttributeNames::x1) {
|
||||||
|
m_x1 = AttributeParser::parse_number_percentage(value);
|
||||||
|
m_paint_style = nullptr;
|
||||||
|
} else if (name == SVG::AttributeNames::y1) {
|
||||||
|
m_y1 = AttributeParser::parse_number_percentage(value);
|
||||||
|
m_paint_style = nullptr;
|
||||||
|
} else if (name == SVG::AttributeNames::x2) {
|
||||||
|
m_x2 = AttributeParser::parse_number_percentage(value);
|
||||||
|
m_paint_style = nullptr;
|
||||||
|
} else if (name == SVG::AttributeNames::y2) {
|
||||||
|
m_y2 = AttributeParser::parse_number_percentage(value);
|
||||||
|
m_paint_style = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute
|
||||||
|
NumberPercentage SVGLinearGradientElement::start_x() const
|
||||||
|
{
|
||||||
|
if (m_x1.has_value())
|
||||||
|
return *m_x1;
|
||||||
|
if (auto href = linear_gradient_xlink_href())
|
||||||
|
return href->start_x();
|
||||||
|
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
|
||||||
|
return NumberPercentage::create_percentage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
|
||||||
|
NumberPercentage SVGLinearGradientElement::start_y() const
|
||||||
|
{
|
||||||
|
if (m_y1.has_value())
|
||||||
|
return *m_y1;
|
||||||
|
if (auto href = linear_gradient_xlink_href())
|
||||||
|
return href->start_x();
|
||||||
|
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
|
||||||
|
return NumberPercentage::create_percentage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
|
||||||
|
NumberPercentage SVGLinearGradientElement::end_x() const
|
||||||
|
{
|
||||||
|
if (m_x2.has_value())
|
||||||
|
return *m_x2;
|
||||||
|
if (auto href = linear_gradient_xlink_href())
|
||||||
|
return href->start_x();
|
||||||
|
// If the attribute is not specified, the effect is as if a value of '100%' were specified.
|
||||||
|
return NumberPercentage::create_percentage(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
|
||||||
|
NumberPercentage SVGLinearGradientElement::end_y() const
|
||||||
|
{
|
||||||
|
if (m_y2.has_value())
|
||||||
|
return *m_y2;
|
||||||
|
if (auto href = linear_gradient_xlink_href())
|
||||||
|
return href->start_x();
|
||||||
|
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
|
||||||
|
return NumberPercentage::create_percentage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Gfx::PaintStyle const&> SVGLinearGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
|
||||||
|
{
|
||||||
|
auto units = gradient_units();
|
||||||
|
// FIXME: Resolve percentages properly
|
||||||
|
Gfx::FloatPoint start_point {};
|
||||||
|
Gfx::FloatPoint end_point {};
|
||||||
|
// https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementGradientUnitsAttribute
|
||||||
|
if (units == GradientUnits::ObjectBoundingBox) {
|
||||||
|
// If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘x1’, ‘y1’, ‘x2’ and ‘y2’
|
||||||
|
// is established using the bounding box of the element to which the gradient is applied (see Object bounding
|
||||||
|
// box units) and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent
|
||||||
|
// values relative to the bounding box for the object.
|
||||||
|
// Note: For gradientUnits="objectBoundingBox" both "100%" and "1" are treated the same.
|
||||||
|
start_point = paint_context.path_bounding_box.location() + Gfx::FloatPoint { start_x().value() * paint_context.path_bounding_box.width(), start_y().value() * paint_context.path_bounding_box.height() };
|
||||||
|
end_point = paint_context.path_bounding_box.location() + Gfx::FloatPoint { end_x().value() * paint_context.path_bounding_box.width(), end_y().value() * paint_context.path_bounding_box.height() };
|
||||||
|
} else {
|
||||||
|
// GradientUnits::UserSpaceOnUse
|
||||||
|
// If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system
|
||||||
|
// that results from taking the current user coordinate system in place at the time when the gradient element
|
||||||
|
// is referenced (i.e., the user coordinate system for the element referencing the gradient element via a
|
||||||
|
// fill or stroke property) and then applying the transform specified by attribute ‘gradientTransform’.
|
||||||
|
// Percentages represent values relative to the current SVG viewport.
|
||||||
|
start_point = Gfx::FloatPoint {
|
||||||
|
start_x().resolve_relative_to(paint_context.viewport.width()),
|
||||||
|
start_y().resolve_relative_to(paint_context.viewport.height()),
|
||||||
|
};
|
||||||
|
end_point = Gfx::FloatPoint {
|
||||||
|
end_x().resolve_relative_to(paint_context.viewport.width()),
|
||||||
|
end_y().resolve_relative_to(paint_context.viewport.height()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_paint_style) {
|
||||||
|
m_paint_style = Gfx::SVGLinearGradientPaintStyle::create(start_point, end_point)
|
||||||
|
.release_value_but_fixme_should_propagate_errors();
|
||||||
|
// FIXME: Update this if DOM changes?
|
||||||
|
for_each_color_stop([&](auto& stop) {
|
||||||
|
m_paint_style->add_color_stop(stop.stop_offset().value(), stop.stop_color()).release_value_but_fixme_should_propagate_errors();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
m_paint_style->set_start_point(start_point);
|
||||||
|
m_paint_style->set_end_point(end_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto gradient_affine_transform = gradient_transform().value_or(Gfx::AffineTransform {});
|
||||||
|
|
||||||
|
if (units == GradientUnits::ObjectBoundingBox) {
|
||||||
|
// Adjust transform to take place in the coordinate system defined by the bounding box:
|
||||||
|
gradient_affine_transform = Gfx::AffineTransform {}
|
||||||
|
.translate(paint_context.path_bounding_box.location())
|
||||||
|
.scale(paint_context.path_bounding_box.width(), paint_context.path_bounding_box.height())
|
||||||
|
.multiply(gradient_affine_transform)
|
||||||
|
.scale(1 / paint_context.path_bounding_box.width(), 1 / paint_context.path_bounding_box.height())
|
||||||
|
.translate(-paint_context.path_bounding_box.location());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_paint_style->set_gradient_transform(Gfx::AffineTransform { paint_context.transform }.multiply(gradient_affine_transform));
|
||||||
|
return *m_paint_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x1() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y1() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x2() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y2() const
|
||||||
|
{
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
Normal file
56
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibWeb/SVG/AttributeParser.h>
|
||||||
|
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||||
|
#include <LibWeb/SVG/SVGGradientElement.h>
|
||||||
|
|
||||||
|
namespace Web::SVG {
|
||||||
|
|
||||||
|
class SVGLinearGradientElement : public SVGGradientElement {
|
||||||
|
WEB_PLATFORM_OBJECT(SVGLinearGradientElement, SVGGradientElement);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~SVGLinearGradientElement() 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 override;
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> x1() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> y1() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> x2() const;
|
||||||
|
JS::NonnullGCPtr<SVGAnimatedLength> y2() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SVGLinearGradientElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
||||||
|
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS::GCPtr<SVGLinearGradientElement const> linear_gradient_xlink_href() const
|
||||||
|
{
|
||||||
|
if (auto href = xlink_href(); href && is<SVGLinearGradientElement>(*href))
|
||||||
|
return &verify_cast<SVGLinearGradientElement>(*href);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberPercentage start_x() const;
|
||||||
|
NumberPercentage start_y() const;
|
||||||
|
NumberPercentage end_x() const;
|
||||||
|
NumberPercentage end_y() const;
|
||||||
|
|
||||||
|
Optional<NumberPercentage> m_x1;
|
||||||
|
Optional<NumberPercentage> m_y1;
|
||||||
|
Optional<NumberPercentage> m_x2;
|
||||||
|
Optional<NumberPercentage> m_y2;
|
||||||
|
|
||||||
|
mutable RefPtr<Gfx::SVGLinearGradientPaintStyle> m_paint_style;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#import <SVG/SVGGradientElement.idl>
|
||||||
|
|
||||||
|
[Exposed=Window]
|
||||||
|
interface SVGLinearGradientElement : SVGGradientElement {
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength x1;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength y1;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength x2;
|
||||||
|
[SameObject] readonly attribute SVGAnimatedLength y2;
|
||||||
|
};
|
|
@ -203,6 +203,7 @@ libweb_js_bindings(SVG/SVGEllipseElement)
|
||||||
libweb_js_bindings(SVG/SVGForeignObjectElement)
|
libweb_js_bindings(SVG/SVGForeignObjectElement)
|
||||||
libweb_js_bindings(SVG/SVGLength)
|
libweb_js_bindings(SVG/SVGLength)
|
||||||
libweb_js_bindings(SVG/SVGLineElement)
|
libweb_js_bindings(SVG/SVGLineElement)
|
||||||
|
libweb_js_bindings(SVG/SVGLinearGradientElement)
|
||||||
libweb_js_bindings(SVG/SVGPathElement)
|
libweb_js_bindings(SVG/SVGPathElement)
|
||||||
libweb_js_bindings(SVG/SVGPolygonElement)
|
libweb_js_bindings(SVG/SVGPolygonElement)
|
||||||
libweb_js_bindings(SVG/SVGPolylineElement)
|
libweb_js_bindings(SVG/SVGPolylineElement)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue