mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 14:57:34 +00:00
LibGfx: Implement PaintStyle for SVG linear gradients
This commit is contained in:
parent
71938550fa
commit
89d3c6d718
2 changed files with 96 additions and 14 deletions
|
@ -297,6 +297,23 @@ static auto make_sample_non_relative(IntPoint draw_location, auto sample)
|
||||||
return [=, sample = move(sample)](IntPoint point) { return sample(point.translated(draw_location)); };
|
return [=, sample = move(sample)](IntPoint point) { return sample(point.translated(draw_location)); };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto make_linear_gradient_between_two_points(FloatPoint p0, FloatPoint p1, ReadonlySpan<ColorStop> color_stops, Optional<float> repeat_length)
|
||||||
|
{
|
||||||
|
auto delta = p1 - p0;
|
||||||
|
auto angle = AK::atan2(delta.y(), delta.x());
|
||||||
|
float sin_angle, cos_angle;
|
||||||
|
AK::sincos(angle, sin_angle, cos_angle);
|
||||||
|
int gradient_length = ceilf(p1.distance_from(p0));
|
||||||
|
auto rotated_start_point_x = p0.x() * cos_angle - p0.y() * -sin_angle;
|
||||||
|
|
||||||
|
return Gradient {
|
||||||
|
GradientLine(gradient_length, color_stops, repeat_length, UsePremultipliedAlpha::No),
|
||||||
|
[=](int x, int y) {
|
||||||
|
return (x * cos_angle - y * -sin_angle) - rotated_start_point_x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const
|
void CanvasLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const
|
||||||
{
|
{
|
||||||
// If x0 = x1 and y0 = y1, then the linear gradient must paint nothing.
|
// If x0 = x1 and y0 = y1, then the linear gradient must paint nothing.
|
||||||
|
@ -307,23 +324,43 @@ void CanvasLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintF
|
||||||
if (color_stops().size() < 2)
|
if (color_stops().size() < 2)
|
||||||
return paint([this](IntPoint) { return color_stops().first().color; });
|
return paint([this](IntPoint) { return color_stops().first().color; });
|
||||||
|
|
||||||
auto delta = m_p1 - m_p0;
|
auto linear_gradient = make_linear_gradient_between_two_points(m_p0, m_p1, color_stops(), repeat_length());
|
||||||
auto angle = AK::atan2(delta.y(), delta.x());
|
|
||||||
float sin_angle, cos_angle;
|
|
||||||
AK::sincos(angle, sin_angle, cos_angle);
|
|
||||||
int gradient_length = ceilf(m_p1.distance_from(m_p0));
|
|
||||||
auto rotated_start_point_x = m_p0.x() * cos_angle - m_p0.y() * -sin_angle;
|
|
||||||
|
|
||||||
Gradient linear_gradient {
|
|
||||||
GradientLine(gradient_length, color_stops(), repeat_length(), UsePremultipliedAlpha::No),
|
|
||||||
[=](int x, int y) {
|
|
||||||
return (x * cos_angle - y * -sin_angle) - rotated_start_point_x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SVGLinearGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const
|
||||||
|
{
|
||||||
|
if (color_stops().is_empty())
|
||||||
|
return;
|
||||||
|
// If ‘x1’ = ‘x2’ and ‘y1’ = ‘y2’, then the area to be painted will be painted as
|
||||||
|
// a single color using the color and opacity of the last gradient stop.
|
||||||
|
if (m_p0 == m_p1)
|
||||||
|
return paint([this](IntPoint) { return color_stops().last().color; });
|
||||||
|
if (color_stops().size() < 2)
|
||||||
|
return paint([this](IntPoint) { return color_stops().first().color; });
|
||||||
|
|
||||||
|
// Note: The scaling is removed so enough points on the gradient line are generated.
|
||||||
|
// Otherwise, if you scale a tiny path the gradient looks pixelated.
|
||||||
|
FloatPoint scale { 1, 1 };
|
||||||
|
auto sample_transform = gradient_transform().map([&](auto& transform) {
|
||||||
|
if (auto inverse = transform.inverse(); inverse.has_value()) {
|
||||||
|
scale = transform.scale();
|
||||||
|
return Gfx::AffineTransform {}.scale(scale).multiply(*inverse);
|
||||||
|
}
|
||||||
|
return Gfx::AffineTransform {};
|
||||||
|
});
|
||||||
|
|
||||||
|
auto linear_gradient = make_linear_gradient_between_two_points(m_p0.scaled(scale), m_p1.scaled(scale), color_stops(), repeat_length());
|
||||||
|
|
||||||
|
paint([&, sampler = linear_gradient.sample_function()](auto point) {
|
||||||
|
point.translate_by(physical_bounding_box.location());
|
||||||
|
if (sample_transform.has_value())
|
||||||
|
point = sample_transform->map(point);
|
||||||
|
|
||||||
|
return sampler(point);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasConicGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const
|
void CanvasConicGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const
|
||||||
{
|
{
|
||||||
if (color_stops().is_empty())
|
if (color_stops().is_empty())
|
||||||
|
|
|
@ -243,4 +243,49 @@ private:
|
||||||
float m_end_radius { 0.0f };
|
float m_end_radius { 0.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The following paint styles implement the gradients required for SVGs
|
||||||
|
|
||||||
|
class SVGGradientPaintStyle : public GradientPaintStyle {
|
||||||
|
public:
|
||||||
|
void set_gradient_transform(Gfx::AffineTransform transform)
|
||||||
|
{
|
||||||
|
m_gradient_transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Gfx::AffineTransform> const& gradient_transform() const { return m_gradient_transform; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Optional<Gfx::AffineTransform> m_gradient_transform {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGLinearGradientPaintStyle final : public SVGGradientPaintStyle {
|
||||||
|
public:
|
||||||
|
static ErrorOr<NonnullRefPtr<SVGLinearGradientPaintStyle>> create(FloatPoint p0, FloatPoint p1)
|
||||||
|
{
|
||||||
|
return adopt_nonnull_ref_or_enomem(new (nothrow) SVGLinearGradientPaintStyle(p0, p1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_start_point(FloatPoint start_point)
|
||||||
|
{
|
||||||
|
m_p0 = start_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_end_point(FloatPoint end_point)
|
||||||
|
{
|
||||||
|
m_p1 = end_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override;
|
||||||
|
|
||||||
|
SVGLinearGradientPaintStyle(FloatPoint p0, FloatPoint p1)
|
||||||
|
: m_p0(p0)
|
||||||
|
, m_p1(p1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatPoint m_p0;
|
||||||
|
FloatPoint m_p1;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue