Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions Libraries/LibWeb/SVG/SVGEllipseElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,29 @@ SVGEllipseElement::SVGEllipseElement(DOM::Document& document, DOM::QualifiedName
{
}

void SVGEllipseElement::attribute_changed(Utf16FlyString const& name, Optional<Utf16String> const& old_value, Optional<Utf16String> const& value, Optional<Utf16FlyString> const& namespace_)
{
Base::attribute_changed(name, old_value, value, namespace_);

if (name == SVG::AttributeNames::cx) {
m_center_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::cy) {
m_center_y = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::rx) {
m_radius_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::ry) {
m_radius_y = AttributeParser::parse_number_percentage(value.value_or({}));
}
}

Gfx::Path SVGEllipseElement::get_path(CSSPixelSize viewport_size)
{
float rx = m_radius_x.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float());
float ry = m_radius_y.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float());
float cx = m_center_x.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float());
float cy = m_center_y.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float());
auto const& computed_values = this->computed_values();

auto computed_rx = computed_values->rx();
auto computed_ry = computed_values->ry();

float rx = computed_rx.to_px_or_zero(viewport_size.width()).to_float();
float ry = computed_ry.to_px_or_zero(viewport_size.height()).to_float();

// https://svgwg.org/svg2-draft/geometry.html#RxProperty
// When the computed value of ‘rx’ is auto, the used radius is equal to the absolute length used for ry, creating a
// circular arc. If both ‘rx’ and ‘ry’ have a computed value of auto, the used value is 0.
if (computed_rx.is_auto())
rx = computed_ry.to_px_or_zero(viewport_size.height()).to_float();

// When the computed value of ‘ry’ is auto, the used radius is equal to the absolute length used for rx, creating a
// circular arc. If both ‘rx’ and ‘ry’ have a computed value of auto, the used value is 0.
if (computed_ry.is_auto())
ry = computed_rx.to_px_or_zero(viewport_size.width()).to_float();

float cx = computed_values->cx().to_px(viewport_size.width()).to_float();
float cy = computed_values->cy().to_px(viewport_size.height()).to_float();
Gfx::Path path;

// A negative radius is invalid. If only one radius is invalid, SVG uses
Expand Down
7 changes: 0 additions & 7 deletions Libraries/LibWeb/SVG/SVGEllipseElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class SVGEllipseElement final : public SVGGeometryElement {
public:
virtual ~SVGEllipseElement() override = default;

virtual void attribute_changed(Utf16FlyString const& name, Optional<Utf16String> const& old_value, Optional<Utf16String> const& value, Optional<Utf16FlyString> const& namespace_) override;

virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;

// AD-HOC: The spec states that the cx, cy, rx and ry IDL attributes reflect the respective computed values and their
Expand All @@ -41,11 +39,6 @@ class SVGEllipseElement final : public SVGGeometryElement {

private:
SVGEllipseElement(DOM::Document&, DOM::QualifiedName);

Optional<NumberPercentage> m_center_x;
Optional<NumberPercentage> m_center_y;
Optional<NumberPercentage> m_radius_x;
Optional<NumberPercentage> m_radius_y;
};

}
45 changes: 18 additions & 27 deletions Libraries/LibWeb/SVG/SVGImageElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "SVGImageElement.h"
#include <LibGC/Heap.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibWeb/CSS/Sizing.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/DocumentObserver.h>
#include <LibWeb/DOM/Event.h>
Expand Down Expand Up @@ -55,15 +56,7 @@ void SVGImageElement::attribute_changed(Utf16FlyString const& name, Optional<Utf
{
Base::attribute_changed(name, old_value, value, namespace_);

if (name == SVG::AttributeNames::x) {
m_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::y) {
m_y = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::width) {
m_width = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::height) {
m_height = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::href) {
if (name == SVG::AttributeNames::href) {
// https://svgwg.org/svg2-draft/linking.html#XLinkRefAttrs
// For backwards compatibility, elements with an ‘href’ attribute also recognize an ‘href’ attribute in the
// XLink namespace. If the element is in the XLink namespace, it does not recognize an ‘href’ attribute in the
Expand All @@ -82,31 +75,29 @@ void SVGImageElement::attribute_changed(Utf16FlyString const& name, Optional<Utf

Gfx::FloatRect SVGImageElement::bounding_box(CSSPixelSize viewport_size) const
{
Optional<float> width;
if (m_width.has_value())
width = m_width->resolve_relative_to(viewport_size.width().to_float());
auto computed_values = this->computed_values();

Optional<float> height;
if (m_height.has_value())
height = m_height->resolve_relative_to(viewport_size.height().to_float());
// https://w3c.github.io/svgwg/svg2-draft/embedded.html#Placement
// Computation of automatically-sized values follows the Default Sizing Algorithm defined for replaced elements in
// CSS layout [css-images-3]. In particular, when the referenced resource does not have an intrinsic size (such as
// image types with no defined dimensions), it is assumed to have a width of 300px and a height of 150px.
auto specified_width = computed_values->width().is_length_percentage() ? computed_values->width().to_px(viewport_size.width()) : Optional<CSSPixels> {};
auto specified_height = computed_values->height().is_length_percentage() ? computed_values->height().to_px(viewport_size.height()) : Optional<CSSPixels> {};

if (!height.has_value() && width.has_value() && intrinsic_aspect_ratio().has_value())
height = width.value() / intrinsic_aspect_ratio().value().to_float();
CSS::SizeWithAspectRatio intrinsic_size_with_aspect_ratio { this->intrinsic_width(), this->intrinsic_height(), this->intrinsic_aspect_ratio() };

if (!width.has_value() && height.has_value() && intrinsic_aspect_ratio().has_value())
width = height.value() * intrinsic_aspect_ratio().value().to_float();
CSSPixelSize default_size {};

if (!width.has_value() && intrinsic_width().has_value())
width = intrinsic_width()->to_float();
if (decoded_image_data())
default_size = CSSPixelSize { 300, 150 };

if (!height.has_value() && intrinsic_height().has_value())
height = intrinsic_height()->to_float();
auto sizing = CSS::run_default_sizing_algorithm(specified_width, specified_height, intrinsic_size_with_aspect_ratio, default_size);

return {
m_x.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float()),
m_y.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float()),
width.value_or(0.0f),
height.value_or(0.0f),
computed_values->x().to_px(viewport_size.width()).to_float(),
computed_values->y().to_px(viewport_size.height()).to_float(),
sizing.width().to_float(),
sizing.height().to_float()
};
}

Expand Down
5 changes: 0 additions & 5 deletions Libraries/LibWeb/SVG/SVGImageElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ class SVGImageElement final
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::ComputedValues const>) override;
virtual void decoded_image_data_did_update() override { set_needs_repaint(); }

Optional<NumberPercentage> m_x;
Optional<NumberPercentage> m_y;
Optional<NumberPercentage> m_width;
Optional<NumberPercentage> m_height;

Optional<URL::URL> m_href;

GC::Ptr<HTML::SharedResourceRequest> m_resource_request;
Expand Down
130 changes: 68 additions & 62 deletions Libraries/LibWeb/SVG/SVGRectElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
*/

#include <LibGfx/Path.h>
#include <LibWeb/SVG/AttributeNames.h>
#include <LibWeb/SVG/AttributeParser.h>
#include <LibWeb/SVG/SVGAnimatedLength.h>
#include <LibWeb/SVG/SVGLength.h>
#include <LibWeb/SVG/SVGRectElement.h>

namespace Web::SVG {
Expand All @@ -20,31 +16,20 @@ SVGRectElement::SVGRectElement(DOM::Document& document, DOM::QualifiedName quali
{
}

void SVGRectElement::attribute_changed(Utf16FlyString const& name, Optional<Utf16String> const& old_value, Optional<Utf16String> const& value, Optional<Utf16FlyString> const& namespace_)
{
Base::attribute_changed(name, old_value, value, namespace_);

if (name == SVG::AttributeNames::x) {
m_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::y) {
m_y = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::width) {
m_width = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::height) {
m_height = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::rx) {
m_radius_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::ry) {
m_radius_y = AttributeParser::parse_number_percentage(value.value_or({}));
}
}

Gfx::Path SVGRectElement::get_path(CSSPixelSize viewport_size)
{
float width = m_width.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float());
float height = m_height.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float());
float x = m_x.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float());
float y = m_y.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float());
auto computed_values = this->computed_values();

auto computed_width = computed_values->width();
auto computed_height = computed_values->height();

// FIXME: to_px rounds prematurely here - we shouldn't round to fixed point CSSPixels until converting to CSS pixel
// space from SVG user space - this likely extends to other SVG geometry elements as well.
auto width = computed_width.is_length_percentage() ? computed_width.length_percentage().to_px(viewport_size.width()).to_double() : 0.0;
auto height = computed_height.is_length_percentage() ? computed_height.length_percentage().to_px(viewport_size.height()).to_double() : 0.0;

auto x = computed_values->x().to_px(viewport_size.width()).to_double();
auto y = computed_values->y().to_px(viewport_size.height()).to_double();

Gfx::Path path;
// Non-positive dimensions disable rendering. In particular, a negative
Expand All @@ -53,7 +38,7 @@ Gfx::Path SVGRectElement::get_path(CSSPixelSize viewport_size)
if (width <= 0 || height <= 0)
return path;

auto corner_radii = calculate_used_corner_radius_values(viewport_size);
auto corner_radii = calculate_used_corner_radius_values(width, height);
float rx = corner_radii.width();
float ry = corner_radii.height();

Expand Down Expand Up @@ -106,45 +91,66 @@ Gfx::Path SVGRectElement::get_path(CSSPixelSize viewport_size)
return path;
}

Gfx::FloatSize SVGRectElement::calculate_used_corner_radius_values(CSSPixelSize viewport_size) const
Gfx::FloatSize SVGRectElement::calculate_used_corner_radius_values(float used_width, float used_height) const
{
// 1. Let rx and ry be length values.
float rx = 0;
float ry = 0;

// 2. If neither ‘rx’ nor ‘ry’ are properly specified, then set both rx and ry to 0. (This will result in square corners.)
if (!m_radius_x.has_value() && !m_radius_y.has_value()) {
rx = 0;
ry = 0;
}
// 3. Otherwise, if a properly specified value is provided for ‘rx’, but not for ‘ry’, then set both rx and ry to the value of ‘rx’.
else if (m_radius_x.has_value() && !m_radius_y.has_value()) {
rx = m_radius_x.value().resolve_relative_to(viewport_size.width().to_float());
ry = m_radius_x.value().resolve_relative_to(viewport_size.width().to_float());
}
// 4. Otherwise, if a properly specified value is provided for ‘ry’, but not for ‘rx’, then set both rx and ry to the value of ‘ry’.
else if (m_radius_y.has_value() && !m_radius_x.has_value()) {
rx = m_radius_y.value().resolve_relative_to(viewport_size.height().to_float());
ry = m_radius_y.value().resolve_relative_to(viewport_size.height().to_float());
}
// 5. Otherwise, both ‘rx’ and ‘ry’ were specified properly. Set rx to the value of ‘rx’ and ry to the value of ‘ry’.
else {
rx = m_radius_x.value().resolve_relative_to(viewport_size.width().to_float());
ry = m_radius_y.value().resolve_relative_to(viewport_size.height().to_float());
// The used values for rx and ry are determined from the computed values by following these steps in order:

auto const& computed_values = this->computed_values();

auto computed_rx = computed_values->rx();
auto computed_ry = computed_values->ry();

// 1. If both rx and ry have a computed value of auto (since auto is the initial value for both properties, this
// will also occur if neither are specified by the author or if all author-supplied values are invalid), then
// the used value of both rx and ry is 0. (This will result in square corners.)
if (computed_rx.is_auto() && computed_ry.is_auto())
return { 0, 0 };

// 2. Otherwise, convert specified values to absolute values as follows:
float used_rx;
float used_ry;

{
// 1. If rx is set to a length value or a percentage, but ry is auto, calculate an absolute length equivalent
// for rx, resolving percentages against the used width of the rectangle; the absolute value for ry is the
// same.
if (!computed_rx.is_auto() && computed_ry.is_auto()) {
used_rx = computed_rx.to_px_or_zero(CSSPixels { used_width }).to_float();
used_ry = used_rx;
}

// 2. If ry is set to a length value or a percentage, but rx is auto, calculate the absolute length equivalent
// for ry, resolving percentages against the used height of the rectangle; the absolute value for rx is the
// same.
if (!computed_ry.is_auto() && computed_rx.is_auto()) {
used_ry = computed_ry.to_px_or_zero(CSSPixels { used_height }).to_float();
used_rx = used_ry;
}

// 3. If both rx and ry were set to lengths or percentages, absolute values are generated individually,
// resolving rx percentages against the used width, and resolving ry percentages against the used height.
if (!computed_rx.is_auto() && !computed_ry.is_auto()) {
used_rx = computed_rx.to_px_or_zero(CSSPixels { used_width }).to_float();
used_ry = computed_ry.to_px_or_zero(CSSPixels { used_height }).to_float();
}
}

// 6. If rx is greater than half of ‘width’, then set rx to half of ‘width’.
auto half_width = m_width.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float()) / 2;
if (rx > half_width)
rx = half_width;
// 3. Finally, apply clamping to generate the used values:
{
// 1. If the absolute rx (after the above steps) is greater than half of the used width, then the used value of
// rx is half of the used width.
if (used_rx > used_width / 2)
used_rx = used_width / 2;

// 2. If the absolute ry (after the above steps) is greater than half of the used height, then the used value of
// ry is half of the used height.
if (used_ry > used_height / 2)
used_ry = used_height / 2;

// 7. If ry is greater than half of ‘height’, then set ry to half of ‘height’.
auto half_height = m_height.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float()) / 2;
if (ry > half_height)
ry = half_height;
// 3. Otherwise, the used values of rx and ry are the absolute values computed previously.
}

// 8. The effective values of ‘rx’ and ‘ry’ are rx and ry, respectively.
return { rx, ry };
return { used_rx, used_ry };
}

// Reflected length accessors are generated by SVGElement's reflection macro.
Expand Down
11 changes: 1 addition & 10 deletions Libraries/LibWeb/SVG/SVGRectElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class SVGRectElement final : public SVGGeometryElement {
public:
virtual ~SVGRectElement() override = default;

virtual void attribute_changed(Utf16FlyString const& name, Optional<Utf16String> const& old_value, Optional<Utf16String> const& value, Optional<Utf16FlyString> const& namespace_) override;

virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;

// AD-HOC: The spec states that the x, y, width, height, rx and ry IDL attributes reflect the respective computed values
Expand Down Expand Up @@ -48,14 +46,7 @@ class SVGRectElement final : public SVGGeometryElement {
private:
SVGRectElement(DOM::Document&, DOM::QualifiedName);

Gfx::FloatSize calculate_used_corner_radius_values(CSSPixelSize viewport_size) const;

Optional<NumberPercentage> m_x;
Optional<NumberPercentage> m_y;
Optional<NumberPercentage> m_width;
Optional<NumberPercentage> m_height;
Optional<NumberPercentage> m_radius_x;
Optional<NumberPercentage> m_radius_y;
Gfx::FloatSize calculate_used_corner_radius_values(float used_width, float used_height) const;
};

}
13 changes: 5 additions & 8 deletions Libraries/LibWeb/SVG/SVGUseElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,7 @@ void SVGUseElement::attribute_changed(Utf16FlyString const& name, Optional<Utf16
{
Base::attribute_changed(name, old_value, value, namespace_);

// https://svgwg.org/svg2-draft/struct.html#UseLayout
if (name == SVG::AttributeNames::x) {
m_x = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::y) {
m_y = AttributeParser::parse_number_percentage(value.value_or({}));
} else if (name == SVG::AttributeNames::href || name == SVG::AttributeNames::xlink_href) {
if (name == SVG::AttributeNames::href || name == SVG::AttributeNames::xlink_href) {
// When the ‘href’ attribute is set (or, in the absence of an ‘href’ attribute, an ‘xlink:href’ attribute), the user agent must process the URL.
process_the_url(value);
}
Expand Down Expand Up @@ -222,8 +217,10 @@ Gfx::AffineTransform SVGUseElement::element_transform() const
viewport_size = { svg_svg_layout_node->computed_values().width().to_px(0), svg_svg_layout_node->computed_values().height().to_px(0) };
}

auto x = m_x.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.width().to_float());
auto y = m_y.value_or(NumberPercentage::create_number(0)).resolve_relative_to(viewport_size.height().to_float());
auto computed_values = this->computed_values();

auto x = computed_values->x().to_px(viewport_size.width()).to_float();
auto y = computed_values->y().to_px(viewport_size.height()).to_float();

// The x and y properties define an additional transformation (translate(x,y), where x and y represent the computed value of the corresponding property)
// to be applied to the ‘use’ element, after any transformations specified with other properties
Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/SVG/SVGUseElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ class SVGUseElement final
void register_for_referenced_element_changes();
void unregister_for_referenced_element_changes();

Optional<NumberPercentage> m_x;
Optional<NumberPercentage> m_y;
bool m_needs_document_complete_reclone { false };

Optional<URL::URL> m_href;
Expand Down
Loading