/* * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Gfx { class PaintStyle : public RefCounted { public: virtual ~PaintStyle() = default; using SamplerFunction = Function; using PaintFunction = Function; friend Painter; friend AntiAliasingPainter; private: // Simple paint styles can simply override sample_color() if they can easily generate a color from a coordinate. virtual Color sample_color(IntPoint) const { return Color(); }; // Paint styles that have paint time dependent state (e.g. based on the paint size) may find it easier to override paint(). // If paint() is overridden sample_color() is unused. virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const { (void)physical_bounding_box; paint([this](IntPoint point) { return sample_color(point); }); } }; class SolidColorPaintStyle final : public PaintStyle { public: static ErrorOr> create(Color color) { return adopt_nonnull_ref_or_enomem(new (nothrow) SolidColorPaintStyle(color)); } virtual Color sample_color(IntPoint) const override { return m_color; } private: SolidColorPaintStyle(Color color) : m_color(color) { } Color m_color; }; class BitmapPaintStyle : public PaintStyle { public: static ErrorOr> create(Bitmap const& bitmap, IntPoint offset = {}) { return adopt_nonnull_ref_or_enomem(new (nothrow) BitmapPaintStyle(bitmap, offset)); } virtual Color sample_color(IntPoint point) const override { point += m_offset; if (m_bitmap->rect().contains(point)) return m_bitmap->get_pixel(point); return Color(); } private: BitmapPaintStyle(Bitmap const& bitmap, IntPoint offset) : m_bitmap(bitmap) , m_offset(offset) { } NonnullRefPtr m_bitmap; IntPoint m_offset; }; class GradientPaintStyle : public PaintStyle { public: ErrorOr add_color_stop(float position, Color color, Optional transition_hint = {}) { return add_color_stop(ColorStop { color, position, transition_hint }); } ErrorOr add_color_stop(ColorStop stop, bool sort = true) { TRY(m_color_stops.try_append(stop)); if (sort) quick_sort(m_color_stops, [](auto& a, auto& b) { return a.position < b.position; }); return {}; } void set_repeat_length(float repeat_length) { m_repeat_length = repeat_length; } ReadonlySpan color_stops() const { return m_color_stops; } Optional repeat_length() const { return m_repeat_length; } private: Vector m_color_stops; Optional m_repeat_length; }; // These paint styles are based on the CSS gradients. They are relative to the painted // shape and support premultiplied alpha. class LinearGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr>> create(float angle = 0.0f) { return adopt_nonnull_ref_or_enomem(new (nothrow) LinearGradientPaintStyle(angle)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; LinearGradientPaintStyle(float angle) : m_angle(angle) { } float m_angle { 0.0f }; }; class ConicGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr> create(IntPoint center, float start_angle = 0.0f) { return adopt_nonnull_ref_or_enomem(new (nothrow) ConicGradientPaintStyle(center, start_angle)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; ConicGradientPaintStyle(IntPoint center, float start_angle) : m_center(center) , m_start_angle(start_angle) { } IntPoint m_center; float m_start_angle { 0.0f }; }; class RadialGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr> create(IntPoint center, IntSize size) { return adopt_nonnull_ref_or_enomem(new (nothrow) RadialGradientPaintStyle(center, size)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; RadialGradientPaintStyle(IntPoint center, IntSize size) : m_center(center) , m_size(size) { } IntPoint m_center; IntSize m_size; }; // The following paint styles implement the gradients required for the HTML canvas. // These gradients are (unlike CSS ones) not relative to the painted shape, and do not // support premultiplied alpha. class CanvasLinearGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr> create(FloatPoint p0, FloatPoint p1) { return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasLinearGradientPaintStyle(p0, p1)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; CanvasLinearGradientPaintStyle(FloatPoint p0, FloatPoint p1) : m_p0(p0) , m_p1(p1) { } FloatPoint m_p0; FloatPoint m_p1; }; class CanvasConicGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr> create(FloatPoint center, float start_angle = 0.0f) { return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasConicGradientPaintStyle(center, start_angle)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; CanvasConicGradientPaintStyle(FloatPoint center, float start_angle) : m_center(center) , m_start_angle(start_angle) { } FloatPoint m_center; float m_start_angle { 0.0f }; }; class CanvasRadialGradientPaintStyle final : public GradientPaintStyle { public: static ErrorOr> create(FloatPoint start_center, float start_radius, FloatPoint end_center, float end_radius) { return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasRadialGradientPaintStyle(start_center, start_radius, end_center, end_radius)); } private: virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; CanvasRadialGradientPaintStyle(FloatPoint start_center, float start_radius, FloatPoint end_center, float end_radius) : m_start_center(start_center) , m_start_radius(start_radius) , m_end_center(end_center) , m_end_radius(end_radius) { } FloatPoint m_start_center; float m_start_radius { 0.0f }; FloatPoint m_end_center; float m_end_radius { 0.0f }; }; }