diff --git a/Libraries/LibGfx/GradientPainting.cpp b/Libraries/LibGfx/GradientPainting.cpp index f38226e9a9..c099e85941 100644 --- a/Libraries/LibGfx/GradientPainting.cpp +++ b/Libraries/LibGfx/GradientPainting.cpp @@ -8,10 +8,6 @@ #include #include -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - namespace Gfx { // Note: This file implements the CSS/Canvas gradients for LibWeb according to the spec. @@ -44,215 +40,6 @@ float color_stop_step(ColorStop const& previous_stop, ColorStop const& next_stop return c; } -class GradientLine { -public: - GradientLine(int gradient_length, ReadonlySpan color_stops, Optional repeat_length, AlphaType alpha_type = AlphaType::Premultiplied) - : m_repeat_mode(repeat_length.has_value() ? RepeatMode::Repeat : RepeatMode::None) - , m_start_offset(round_to((repeating() ? color_stops.first().position : 0.0f) * gradient_length)) - , m_color_stops(color_stops) - , m_alpha_type(alpha_type) - { - // Avoid generating excessive amounts of colors when the not enough shades to fill that length. - auto necessary_length = min((color_stops.size() - 1) * 255, gradient_length); - m_sample_scale = float(necessary_length) / gradient_length; - // Note: color_count will be < gradient_length for repeating gradients. - auto color_count = round_to(repeat_length.value_or(1.0f) * necessary_length); - m_gradient_line_colors.resize(color_count); - - for (int loc = 0; loc < color_count; loc++) { - auto relative_loc = float(loc + m_start_offset) / necessary_length; - Color gradient_color = color_blend(color_stops[0].color, color_stops[1].color, - color_stop_step(color_stops[0], color_stops[1], relative_loc)); - for (size_t i = 1; i < color_stops.size() - 1; i++) { - gradient_color = color_blend(gradient_color, color_stops[i + 1].color, - color_stop_step(color_stops[i], color_stops[i + 1], relative_loc)); - } - m_gradient_line_colors[loc] = gradient_color; - if (gradient_color.alpha() < 255) - m_requires_blending = true; - } - } - - Color color_blend(Color a, Color b, float amount) const - { - // Note: color.mixed_with() performs premultiplied alpha mixing when necessary as defined in: - // https://drafts.csswg.org/css-images/#coloring-gradient-line - if (m_alpha_type == AlphaType::Premultiplied) - return a.mixed_with(b, amount); - return a.interpolate(b, amount); - } - - Color get_color(i64 index) const - { - if (index < 0) - return m_color_stops.first().color; - if (index >= static_cast(m_gradient_line_colors.size())) - return m_color_stops.last().color; - return m_gradient_line_colors[index]; - } - - Color sample_color(float loc) const - { - if (!isfinite(loc)) - return Color(); - if (m_sample_scale != 1.0f) - loc *= m_sample_scale; - auto repeat_wrap_if_required = [&](i64 loc) { - if (m_repeat_mode != RepeatMode::None) { - auto current_loc = loc + m_start_offset; - auto gradient_len = static_cast(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; - }; - auto int_loc = static_cast(floor(loc)); - auto blend = loc - int_loc; - auto color = get_color(repeat_wrap_if_required(int_loc)); - // Blend between the two neighboring colors (this fixes some nasty aliasing issues at small angles) - if (blend >= 0.004f) - color = color_blend(color, get_color(repeat_wrap_if_required(int_loc + 1)), blend); - return color; - } - - 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: - RepeatMode m_repeat_mode { RepeatMode::None }; - int m_start_offset { 0 }; - float m_sample_scale { 1 }; - ReadonlySpan m_color_stops {}; - AlphaType m_alpha_type { AlphaType::Premultiplied }; - - Vector m_gradient_line_colors; - bool m_requires_blending = false; -}; - -template -struct Gradient { - Gradient(GradientLine gradient_line, TransformFunction transform_function) - : m_gradient_line(move(gradient_line)) - , m_transform_function(move(transform_function)) - { - } - - template - auto sample_function() - { - return [this](Point point) { - return m_gradient_line.sample_color(m_transform_function(point.x(), point.y())); - }; - } - - GradientLine& gradient_line() - { - return m_gradient_line; - } - -private: - GradientLine m_gradient_line; - TransformFunction m_transform_function; -}; - -static auto create_conic_gradient(ReadonlySpan color_stops, FloatPoint center_point, float start_angle, Optional repeat_length, AlphaType alpha_type = AlphaType::Premultiplied) -{ - // FIXME: Do we need/want sub-degree accuracy for the gradient line? - GradientLine gradient_line(360, color_stops, repeat_length, alpha_type); - float normalized_start_angle = (360.0f - start_angle) + 90.0f; - // The flooring can make gradients that want soft edges look worse, so only floor if we have hard edges. - // Which makes sure the hard edge stay hard edges :^) - bool should_floor_angles = false; - for (size_t i = 0; i < color_stops.size() - 1; i++) { - if (color_stops[i + 1].position - color_stops[i].position <= 0.01f) { - should_floor_angles = true; - break; - } - } - return Gradient { - move(gradient_line), - [=](int x, int y) { - auto point = FloatPoint { x, y } - center_point; - // FIXME: We could probably get away with some approximation here: - auto loc = fmod((AK::to_degrees(AK::atan2(point.y(), point.x())) + 360.0f + normalized_start_angle), 360.0f); - return should_floor_angles ? floor(loc) : loc; - } - }; -} - -// The following implements the gradient fill/stoke styles for the HTML canvas: https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles - -static auto make_sample_non_relative(IntPoint draw_location, auto sample) -{ - 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 color_stops, Optional 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, AlphaType::Unpremultiplied), - [=](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 -{ - // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing. - if (m_p0 == m_p1) - return; - if (color_stops().is_empty()) - return; - if (color_stops().size() < 2) - return paint([this](IntPoint) { return color_stops().first().color; }); - - auto linear_gradient = make_linear_gradient_between_two_points(m_p0, m_p1, color_stops(), repeat_length()); - 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) { // Note: The scaling is removed so enough points on the gradient line are generated. @@ -267,187 +54,4 @@ void SVGGradientPaintStyle::set_gradient_transform(AffineTransform transform) } } -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; }); - - float scale = gradient_transform_scale(); - auto linear_gradient = make_linear_gradient_between_two_points( - m_p0.scaled(scale, scale), m_p1.scaled(scale, scale), - 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()](IntPoint target_point) { - auto point = target_point.translated(physical_bounding_box.location()).to_type(); - if (auto inverse_transform = scale_adjusted_inverse_gradient_transform(); inverse_transform.has_value()) - point = inverse_transform->map(point); - - return sampler(point); - }); -} - -void CanvasConicGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const -{ - if (color_stops().is_empty()) - return; - if (color_stops().size() < 2) - return paint([this](IntPoint) { return color_stops().first().color; }); - - // Follows the same rendering rule as CSS 'conic-gradient' and it is equivalent to CSS - // 'conic-gradient(from adjustedStartAnglerad at xpx ypx, angularColorStopList)'. - // Here: - // adjustedStartAngle is given by startAngle + π/2; - auto conic_gradient = create_conic_gradient(color_stops(), m_center, m_start_angle + 90.0f, repeat_length(), AlphaType::Unpremultiplied); - paint(make_sample_non_relative(physical_bounding_box.location(), conic_gradient.sample_function())); -} - -static auto create_radial_gradient_between_two_circles(Gfx::FloatPoint start_center, float start_radius, Gfx::FloatPoint end_center, float end_radius, ReadonlySpan color_stops, Optional repeat_length) -{ - bool reverse_gradient = end_radius < start_radius; - if (reverse_gradient) { - swap(end_radius, start_radius); - swap(end_center, start_center); - } - - // FIXME: Handle the start_radius == end_radius special case separately. - // This hack is not quite correct. - if (end_radius - start_radius < 1) - end_radius += 1; - - // Spec steps: Useless for writing an actual implementation (give it a go :P): - // - // 2. Let x(ω) = (x1-x0)ω + x0. - // Let y(ω) = (y1-y0)ω + y0. - // Let r(ω) = (r1-r0)ω + r0. - // Let the color at ω be the color at that position on the gradient - // (with the colors coming from the interpolation and extrapolation described above). - // - // 3. For all values of ω where r(ω) > 0, starting with the value of ω nearest to positive infinity and - // ending with the value of ω nearest to negative infinity, draw the circumference of the circle with - // radius r(ω) at position (x(ω), y(ω)), with the color at ω, but only painting on the parts of the - // bitmap that have not yet been painted on by earlier circles in this step for this rendering of the gradient. - - auto center_dist = end_center.distance_from(start_center); - bool inner_contained = ((center_dist + start_radius) < end_radius); - - auto start_point = start_center; - if (start_radius != 0) { - // Set the start point to the focal point. - auto f = end_radius / (end_radius - start_radius); - auto one_minus_f = 1 - f; - start_point = start_center.scaled(f) + end_center.scaled(one_minus_f); - } - - // This is just an approximate upperbound (the gradient line class will shorten this if necessary). - int gradient_length = AK::ceil(center_dist + end_radius + start_radius); - GradientLine gradient_line(gradient_length, color_stops, repeat_length, AlphaType::Unpremultiplied); - - // If you can simplify this please do, this is "best guess" implementation due to lack of specification. - // It was implemented to visually match chrome/firefox in all cases: - // - Start circle inside end circle - // - Start circle outside end circle - // - Start circle radius == end circle radius - // - Start circle larger than end circle (inside end circle) - // - Start circle larger than end circle (outside end circle) - // - Start circle or end circle radius == 0 - - auto circle_distance_finder = [=](auto radius, auto center) { - auto radius2 = radius * radius; - auto delta = center - start_point; - auto delta_xy = delta.x() * delta.y(); - auto dx2_factor = radius2 - delta.y() * delta.y(); - auto dy2_factor = radius2 - delta.x() * delta.x(); - return [=](bool positive_root, auto vec) { - // This works out the distance to the nearest point on the circle - // in the direction of the "vec" vector. - auto dx2 = vec.x() * vec.x(); - auto dy2 = vec.y() * vec.y(); - auto root = sqrtf(dx2 * dx2_factor + dy2 * dy2_factor - + 2 * vec.x() * vec.y() * delta_xy); - auto dot = vec.x() * delta.x() + vec.y() * delta.y(); - return ((positive_root ? root : -root) + dot) / (dx2 + dy2); - }; - }; - - auto end_circle_dist = circle_distance_finder(end_radius, end_center); - auto start_circle_dist = [=, dist = circle_distance_finder(start_radius, start_center)](bool positive_root, auto vec) { - if (start_center == start_point) - return start_radius; - return dist(positive_root, vec); - }; - - return Gradient { - move(gradient_line), - [=](float x, float y) { - auto loc = [&] { - FloatPoint point { x, y }; - // Add a little to avoid division by zero at the focal point. - if (point == start_point) - point += FloatPoint { 0.001f, 0.001f }; - // The "vec" (unit) vector points from the focal point to the current point. - auto dist = point.distance_from(start_point); - auto vec = (point - start_point) / dist; - bool use_positive_root = inner_contained || reverse_gradient; - auto dist_end = end_circle_dist(use_positive_root, vec); - auto dist_start = start_circle_dist(use_positive_root, vec); - // FIXME: Returning nan is a hack for "Don't paint me!" - if (dist_end < 0) - return AK::NaN; - if (dist_end - dist_start < 0) - return float(gradient_length); - return (dist - dist_start) / (dist_end - dist_start); - }(); - if (reverse_gradient) - loc = 1.0f - loc; - return loc * gradient_length; - } - }; -} - -void CanvasRadialGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const -{ - // 1. If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing. Return. - if (m_start_center == m_end_center && m_start_radius == m_end_radius) - return; - if (color_stops().is_empty()) - return; - if (color_stops().size() < 2) - return paint([this](IntPoint) { return color_stops().first().color; }); - if (m_end_radius == 0 && m_start_radius == 0) - return; - auto radial_gradient = create_radial_gradient_between_two_circles(m_start_center, m_start_radius, m_end_center, m_end_radius, color_stops(), repeat_length()); - paint(make_sample_non_relative(physical_bounding_box.location(), radial_gradient.sample_function())); -} - -void SVGRadialGradientPaintStyle::paint(IntRect physical_bounding_box, PaintFunction paint) const -{ - // FIXME: Ensure this handles all the edge cases of SVG gradients. - if (color_stops().is_empty()) - return; - if (color_stops().size() < 2 || (m_end_radius == 0 && m_start_radius == 0)) - return paint([this](IntPoint) { return color_stops().last().color; }); - - float scale = gradient_transform_scale(); - 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, - 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()](IntPoint target_point) { - auto point = target_point.translated(physical_bounding_box.location()).to_type(); - if (auto inverse_transform = scale_adjusted_inverse_gradient_transform(); inverse_transform.has_value()) - point = inverse_transform->map(point); - return sampler(point); - }); -} - } diff --git a/Libraries/LibGfx/Gradients.h b/Libraries/LibGfx/Gradients.h index b66015a39f..e647cce648 100644 --- a/Libraries/LibGfx/Gradients.h +++ b/Libraries/LibGfx/Gradients.h @@ -22,8 +22,6 @@ struct ColorStop { float color_stop_step(ColorStop const& previous_stop, ColorStop const& next_stop, float position); -class GradientLine; - inline float normalized_gradient_angle_radians(float gradient_angle) { // Adjust angle so 0 degrees is bottom diff --git a/Libraries/LibGfx/PaintStyle.h b/Libraries/LibGfx/PaintStyle.h index 9ebf66736e..7c6aaebc2f 100644 --- a/Libraries/LibGfx/PaintStyle.h +++ b/Libraries/LibGfx/PaintStyle.h @@ -6,11 +6,9 @@ #pragma once -#include #include #include #include -#include #include #include #include @@ -22,20 +20,6 @@ namespace Gfx { class PaintStyle : public RefCounted { public: virtual ~PaintStyle() = default; - using SamplerFunction = Function; - using PaintFunction = Function; - - // 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); }); - } - -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(); } }; class SolidColorPaintStyle : public PaintStyle { @@ -45,7 +29,7 @@ public: return adopt_nonnull_ref_or_enomem(new (nothrow) SolidColorPaintStyle(color)); } - virtual Color sample_color(IntPoint) const override { return m_color; } + Color const& color() const { return m_color; } private: SolidColorPaintStyle(Color color) @@ -101,8 +85,6 @@ public: FloatPoint end_point() const { return m_p1; } private: - virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; - CanvasLinearGradientPaintStyle(FloatPoint p0, FloatPoint p1) : m_p0(p0) , m_p1(p1) @@ -120,9 +102,10 @@ public: return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasConicGradientPaintStyle(center, start_angle)); } -private: - virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; + FloatPoint center() const { return m_center; } + float start_angle() const { return m_start_angle; } +private: CanvasConicGradientPaintStyle(FloatPoint center, float start_angle) : m_center(center) , m_start_angle(start_angle) @@ -146,8 +129,6 @@ public: float end_radius() const { return m_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) @@ -214,8 +195,6 @@ public: } private: - virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; - FloatPoint m_p0; FloatPoint m_p1; }; @@ -256,8 +235,6 @@ public: } private: - virtual void paint(IntRect physical_bounding_box, PaintFunction paint) const override; - FloatPoint m_start_center; float m_start_radius { 0.0f }; FloatPoint m_end_center; diff --git a/Libraries/LibGfx/PainterSkia.cpp b/Libraries/LibGfx/PainterSkia.cpp index 8f62d0a964..1f8a39d085 100644 --- a/Libraries/LibGfx/PainterSkia.cpp +++ b/Libraries/LibGfx/PainterSkia.cpp @@ -47,9 +47,7 @@ static void apply_paint_style(SkPaint& paint, Gfx::PaintStyle const& style) { if (is(style)) { auto const& solid_color = static_cast(style); - auto color = solid_color.sample_color(Gfx::IntPoint(0, 0)); - - paint.setColor(to_skia_color(color)); + paint.setColor(to_skia_color(solid_color.color())); } else if (is(style)) { auto const& linear_gradient = static_cast(style); auto const& color_stops = linear_gradient.color_stops(); diff --git a/Libraries/LibWeb/HTML/CanvasPattern.cpp b/Libraries/LibWeb/HTML/CanvasPattern.cpp index 439d5d1372..1c29ee0f45 100644 --- a/Libraries/LibWeb/HTML/CanvasPattern.cpp +++ b/Libraries/LibWeb/HTML/CanvasPattern.cpp @@ -10,91 +10,12 @@ #include #include #include -#include #include namespace Web::HTML { GC_DEFINE_ALLOCATOR(CanvasPattern); -// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvaspattern-settransform -void CanvasPatternPaintStyle::paint(Gfx::IntRect physical_bounding_box, PaintFunction paint) const -{ - // 1. Create an infinite transparent black bitmap. - // *waves magic wand 🪄* - // Done! - - // 2. Place a copy of the image on the bitmap, anchored such that its top left corner - // is at the origin of the coordinate space, with one coordinate space unit per CSS pixel of the image, - // then place repeated copies of this image horizontally to the left and right, if the repetition behavior - // is "repeat-x", or vertically up and down, if the repetition behavior is "repeat-y", or in all four directions - // all over the bitmap, if the repetition behavior is "repeat". - - // FIXME: If the original image data is a bitmap image, then the value painted at a point in the area of - // the repetitions is computed by filtering the original image data. When scaling up, if the imageSmoothingEnabled - // attribute is set to false, then the image must be rendered using nearest-neighbor interpolation. - // Otherwise, the user agent may use any filtering algorithm (for example bilinear interpolation or nearest-neighbor). - // User agents which support multiple filtering algorithms may use the value of the imageSmoothingQuality attribute - // to guide the choice of filtering algorithm. When such a filtering algorithm requires a pixel value from outside - // the original image data, it must instead use the value from wrapping the pixel's coordinates to the original - // image's dimensions. (That is, the filter uses 'repeat' behavior, regardless of the value of the pattern's repetition behavior.) - - // FIXME: 3. Transform the resulting bitmap according to the pattern's transformation matrix. - - // FIXME: 4. Transform the resulting bitmap again, this time according to the current transformation matrix. - - // 5. Replace any part of the image outside the area in which the pattern is to be rendered with transparent black. - - // 6. The resulting bitmap is what is to be rendered, with the same origin and same scale. - - // FIXME: This doesn't handle a 'none' canvas context mode. - auto bitmap = m_image.visit( - [](GC::Root const& source) -> RefPtr { return source->immutable_bitmap(); }, - [](GC::Root const& source) -> RefPtr { return source->current_image_bitmap(); }, - [](GC::Root const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }, - [](GC::Root const& source) -> RefPtr { return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*source->surface()); }, - [](GC::Root const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }, - [](GC::Root const& source) -> RefPtr { return Gfx::ImmutableBitmap::create(*source->bitmap()); }); - VERIFY(bitmap); - - auto const bitmap_width = bitmap->width(); - auto const bitmap_height = bitmap->height(); - - paint([=, this](auto point) { - point.translate_by(physical_bounding_box.location()); - point = [&]() -> Gfx::IntPoint { - switch (m_repetition) { - case Repetition::NoRepeat: { - return point; - } - case Repetition::Repeat: { - return { - point.x() % bitmap_width, - point.y() % bitmap_height - }; - } - case Repetition::RepeatX: { - return { - point.x() % bitmap_width, - point.y() - }; - } - case Repetition::RepeatY: { - return { - point.x(), - point.y() % bitmap_height - }; - } - default: - VERIFY_NOT_REACHED(); - } - }(); - if (bitmap->rect().contains(point)) - return bitmap->get_pixel(point.x(), point.y()); - return Gfx::Color(); - }); -} - CanvasPattern::CanvasPattern(JS::Realm& realm, CanvasPatternPaintStyle& pattern) : PlatformObject(realm) , m_pattern(pattern) diff --git a/Libraries/LibWeb/HTML/CanvasPattern.h b/Libraries/LibWeb/HTML/CanvasPattern.h index 4abf33b387..5fb1b27808 100644 --- a/Libraries/LibWeb/HTML/CanvasPattern.h +++ b/Libraries/LibWeb/HTML/CanvasPattern.h @@ -27,7 +27,7 @@ public: return adopt_nonnull_ref_or_enomem(new (nothrow) CanvasPatternPaintStyle(move(image), repetition)); } - virtual void paint(Gfx::IntRect physical_bounding_box, PaintFunction paint) const override; + Repetition repetition() const { return m_repetition; } private: CanvasPatternPaintStyle(CanvasImageSource image, Repetition repetition)