mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
LibGfx: Add support for SVG repeat/reflect gradients
This commit is contained in:
parent
a48b2c1c66
commit
b9294e5fdf
Notes:
sideshowbarker
2024-07-19 01:59:31 +09:00
Author: https://github.com/MacDue Commit: https://github.com/SerenityOS/serenity/commit/b9294e5fdf Pull-request: https://github.com/SerenityOS/serenity/pull/20682
2 changed files with 69 additions and 5 deletions
|
@ -53,8 +53,8 @@ enum class UsePremultipliedAlpha {
|
||||||
class GradientLine {
|
class GradientLine {
|
||||||
public:
|
public:
|
||||||
GradientLine(int gradient_length, ReadonlySpan<ColorStop> color_stops, Optional<float> repeat_length, UsePremultipliedAlpha use_premultiplied_alpha = UsePremultipliedAlpha::Yes)
|
GradientLine(int gradient_length, ReadonlySpan<ColorStop> color_stops, Optional<float> repeat_length, UsePremultipliedAlpha use_premultiplied_alpha = UsePremultipliedAlpha::Yes)
|
||||||
: m_repeating(repeat_length.has_value())
|
: m_repeat_mode(repeat_length.has_value() ? RepeatMode::Repeat : RepeatMode::None)
|
||||||
, m_start_offset(round_to<int>((m_repeating ? color_stops.first().position : 0.0f) * gradient_length))
|
, m_start_offset(round_to<int>((repeating() ? color_stops.first().position : 0.0f) * gradient_length))
|
||||||
, m_color_stops(color_stops)
|
, m_color_stops(color_stops)
|
||||||
, m_use_premultiplied_alpha(use_premultiplied_alpha)
|
, m_use_premultiplied_alpha(use_premultiplied_alpha)
|
||||||
{
|
{
|
||||||
|
@ -104,8 +104,18 @@ public:
|
||||||
if (m_sample_scale != 1.0f)
|
if (m_sample_scale != 1.0f)
|
||||||
loc *= m_sample_scale;
|
loc *= m_sample_scale;
|
||||||
auto repeat_wrap_if_required = [&](i64 loc) {
|
auto repeat_wrap_if_required = [&](i64 loc) {
|
||||||
if (m_repeating)
|
if (m_repeat_mode != RepeatMode::None) {
|
||||||
return (loc + m_start_offset) % static_cast<i64>(m_gradient_line_colors.size());
|
auto current_loc = loc + m_start_offset;
|
||||||
|
auto gradient_len = static_cast<i64>(m_gradient_line_colors.size());
|
||||||
|
if (m_repeat_mode == RepeatMode::Repeat) {
|
||||||
|
auto color_loc = current_loc % gradient_len;
|
||||||
|
return color_loc < 0 ? gradient_len + color_loc : color_loc;
|
||||||
|
} else if (m_repeat_mode == RepeatMode::Reflect) {
|
||||||
|
auto color_loc = AK::abs(current_loc % gradient_len);
|
||||||
|
auto repeats = current_loc / gradient_len;
|
||||||
|
return (repeats & 1) ? gradient_len - color_loc : color_loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
return loc;
|
return loc;
|
||||||
};
|
};
|
||||||
auto int_loc = static_cast<i64>(floor(loc));
|
auto int_loc = static_cast<i64>(floor(loc));
|
||||||
|
@ -129,8 +139,26 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool repeating() const
|
||||||
|
{
|
||||||
|
return m_repeat_mode != RepeatMode::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RepeatMode {
|
||||||
|
None,
|
||||||
|
Repeat,
|
||||||
|
Reflect
|
||||||
|
};
|
||||||
|
|
||||||
|
void set_repeat_mode(RepeatMode repeat_mode)
|
||||||
|
{
|
||||||
|
// Note: A gradient can be set to repeating without a repeat length.
|
||||||
|
// The repeat length is used for CSS gradients but not for SVG gradients.
|
||||||
|
m_repeat_mode = repeat_mode;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_repeating { false };
|
RepeatMode m_repeat_mode { RepeatMode::None };
|
||||||
int m_start_offset { 0 };
|
int m_start_offset { 0 };
|
||||||
float m_sample_scale { 1 };
|
float m_sample_scale { 1 };
|
||||||
ReadonlySpan<ColorStop> m_color_stops {};
|
ReadonlySpan<ColorStop> m_color_stops {};
|
||||||
|
@ -161,6 +189,11 @@ struct Gradient {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GradientLine& gradient_line()
|
||||||
|
{
|
||||||
|
return m_gradient_line;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GradientLine m_gradient_line;
|
GradientLine m_gradient_line;
|
||||||
TransformFunction m_transform_function;
|
TransformFunction m_transform_function;
|
||||||
|
@ -340,6 +373,20 @@ void CanvasLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintF
|
||||||
paint(make_sample_non_relative(physical_bounding_box.location(), linear_gradient.sample_function()));
|
paint(make_sample_non_relative(physical_bounding_box.location(), linear_gradient.sample_function()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GradientLine::RepeatMode svg_spread_method_to_repeat_mode(SVGGradientPaintStyle::SpreadMethod spread_method)
|
||||||
|
{
|
||||||
|
switch (spread_method) {
|
||||||
|
case SVGGradientPaintStyle::SpreadMethod::Pad:
|
||||||
|
return GradientLine::RepeatMode::None;
|
||||||
|
case SVGGradientPaintStyle::SpreadMethod::Reflect:
|
||||||
|
return GradientLine::RepeatMode::Reflect;
|
||||||
|
case SVGGradientPaintStyle::SpreadMethod::Repeat:
|
||||||
|
return GradientLine::RepeatMode::Repeat;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SVGGradientPaintStyle::set_gradient_transform(AffineTransform transform)
|
void SVGGradientPaintStyle::set_gradient_transform(AffineTransform transform)
|
||||||
{
|
{
|
||||||
// Note: The scaling is removed so enough points on the gradient line are generated.
|
// Note: The scaling is removed so enough points on the gradient line are generated.
|
||||||
|
@ -369,6 +416,8 @@ void SVGLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunc
|
||||||
auto linear_gradient = make_linear_gradient_between_two_points(
|
auto linear_gradient = make_linear_gradient_between_two_points(
|
||||||
m_p0.scaled(scale, scale), m_p1.scaled(scale, scale),
|
m_p0.scaled(scale, scale), m_p1.scaled(scale, scale),
|
||||||
color_stops(), repeat_length());
|
color_stops(), repeat_length());
|
||||||
|
linear_gradient.gradient_line().set_repeat_mode(
|
||||||
|
svg_spread_method_to_repeat_mode(spread_method()));
|
||||||
|
|
||||||
paint([&, sampler = linear_gradient.sample_function<float>()](IntPoint target_point) {
|
paint([&, sampler = linear_gradient.sample_function<float>()](IntPoint target_point) {
|
||||||
auto point = target_point.translated(physical_bounding_box.location()).to_type<float>();
|
auto point = target_point.translated(physical_bounding_box.location()).to_type<float>();
|
||||||
|
@ -511,6 +560,8 @@ void SVGRadialGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunc
|
||||||
auto radial_gradient = create_radial_gradient_between_two_circles(
|
auto radial_gradient = create_radial_gradient_between_two_circles(
|
||||||
m_start_center.scaled(scale, scale), m_start_radius * scale, m_end_center.scaled(scale, scale), m_end_radius * scale,
|
m_start_center.scaled(scale, scale), m_start_radius * scale, m_end_center.scaled(scale, scale), m_end_radius * scale,
|
||||||
color_stops(), repeat_length());
|
color_stops(), repeat_length());
|
||||||
|
radial_gradient.gradient_line().set_repeat_mode(
|
||||||
|
svg_spread_method_to_repeat_mode(spread_method()));
|
||||||
|
|
||||||
paint([&, sampler = radial_gradient.sample_function<float>()](IntPoint target_point) {
|
paint([&, sampler = radial_gradient.sample_function<float>()](IntPoint target_point) {
|
||||||
auto point = target_point.translated(physical_bounding_box.location()).to_type<float>();
|
auto point = target_point.translated(physical_bounding_box.location()).to_type<float>();
|
||||||
|
|
|
@ -246,13 +246,26 @@ class SVGGradientPaintStyle : public GradientPaintStyle {
|
||||||
public:
|
public:
|
||||||
void set_gradient_transform(Gfx::AffineTransform transform);
|
void set_gradient_transform(Gfx::AffineTransform transform);
|
||||||
|
|
||||||
|
enum class SpreadMethod {
|
||||||
|
Pad,
|
||||||
|
Repeat,
|
||||||
|
Reflect
|
||||||
|
};
|
||||||
|
|
||||||
|
void set_spread_method(SpreadMethod spread_method)
|
||||||
|
{
|
||||||
|
m_spread_method = spread_method;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Optional<AffineTransform> const& scale_adjusted_inverse_gradient_transform() const { return m_inverse_transform; }
|
Optional<AffineTransform> const& scale_adjusted_inverse_gradient_transform() const { return m_inverse_transform; }
|
||||||
float gradient_transform_scale() const { return m_scale; }
|
float gradient_transform_scale() const { return m_scale; }
|
||||||
|
SpreadMethod spread_method() const { return m_spread_method; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Optional<AffineTransform> m_inverse_transform {};
|
Optional<AffineTransform> m_inverse_transform {};
|
||||||
float m_scale = 1.0f;
|
float m_scale = 1.0f;
|
||||||
|
SpreadMethod m_spread_method { SpreadMethod::Pad };
|
||||||
};
|
};
|
||||||
|
|
||||||
class SVGLinearGradientPaintStyle final : public SVGGradientPaintStyle {
|
class SVGLinearGradientPaintStyle final : public SVGGradientPaintStyle {
|
||||||
|
|
Loading…
Reference in a new issue