diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index f51e7e9ca13..8b8379e6f80 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES Font/ScaledFont.cpp Font/Typeface.cpp Font/WOFF/Font.cpp + GradientPainting.cpp GIFLoader.cpp ICCProfile.cpp ICOLoader.cpp diff --git a/Userland/Libraries/LibGfx/GradientPainting.cpp b/Userland/Libraries/LibGfx/GradientPainting.cpp new file mode 100644 index 00000000000..3dd12f9a3ff --- /dev/null +++ b/Userland/Libraries/LibGfx/GradientPainting.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2022-2023, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#if defined(AK_COMPILER_GCC) +# pragma GCC optimize("O3") +#endif +namespace Gfx { + +// Note: This file implements the CSS gradients for LibWeb according to the spec. +// Please do not make ad-hoc changes that may break spec compliance! + +static float color_stop_step(ColorStop const& previous_stop, ColorStop const& next_stop, float position) +{ + if (position < previous_stop.position) + return 0; + if (position > next_stop.position) + return 1; + // For any given point between the two color stops, + // determine the point’s location as a percentage of the distance between the two color stops. + // Let this percentage be P. + auto stop_length = next_stop.position - previous_stop.position; + // FIXME: Avoids NaNs... Still not quite correct? + if (stop_length <= 0) + return 1; + auto p = (position - previous_stop.position) / stop_length; + if (!next_stop.transition_hint.has_value()) + return p; + if (*next_stop.transition_hint >= 1) + return 0; + if (*next_stop.transition_hint <= 0) + return 1; + // Let C, the color weighting at that point, be equal to P^(logH(.5)). + auto c = AK::pow(p, AK::log(0.5) / AK::log(*next_stop.transition_hint)); + // The color at that point is then a linear blend between the colors of the two color stops, + // blending (1 - C) of the first stop and C of the second stop. + return c; +} + +class GradientLine { +public: + GradientLine(int gradient_length, Span color_stops, Optional repeat_length) + : m_repeating { repeat_length.has_value() } + , m_start_offset { round_to((m_repeating ? color_stops.first().position : 0.0f) * gradient_length) } + { + // 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); + // Note: color.mixed_with() performs premultiplied alpha mixing when necessary as defined in: + // https://drafts.csswg.org/css-images/#coloring-gradient-line + for (int loc = 0; loc < color_count; loc++) { + auto relative_loc = float(loc + m_start_offset) / necessary_length; + Color gradient_color = color_stops[0].color.mixed_with( + 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 = gradient_color.mixed_with( + 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 get_color(i64 index) const + { + return m_gradient_line_colors[clamp(index, 0, m_gradient_line_colors.size() - 1)]; + } + + Color sample_color(float loc) const + { + if (m_sample_scale != 1.0f) + loc *= m_sample_scale; + auto repeat_wrap_if_required = [&](i64 loc) { + if (m_repeating) + return (loc + m_start_offset) % static_cast(m_gradient_line_colors.size()); + 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 neighbouring colors (this fixes some nasty aliasing issues at small angles) + if (blend >= 0.004f) + color = color.mixed_with(get_color(repeat_wrap_if_required(int_loc + 1)), blend); + return color; + } + + void paint_into_physical_rect(Painter& painter, IntRect rect, auto location_transform) + { + auto clipped_rect = rect.intersected(painter.clip_rect() * painter.scale()); + auto start_offset = clipped_rect.location() - rect.location(); + for (int y = 0; y < clipped_rect.height(); y++) { + for (int x = 0; x < clipped_rect.width(); x++) { + auto pixel = sample_color(location_transform(x + start_offset.x(), y + start_offset.y())); + painter.set_physical_pixel(clipped_rect.location().translated(x, y), pixel, m_requires_blending); + } + } + } + +private: + bool m_repeating; + int m_start_offset; + float m_sample_scale { 1 }; + Vector m_gradient_line_colors; + + bool m_requires_blending = false; +}; + +void Painter::fill_rect_with_linear_gradient(IntRect const& rect, Span const& color_stops, float angle, Optional repeat_length) +{ + auto a_rect = to_physical(rect); + if (a_rect.intersected(clip_rect() * scale()).is_empty()) + return; + + float normalized_angle = normalized_gradient_angle_radians(angle); + float sin_angle, cos_angle; + AK::sincos(normalized_angle, sin_angle, cos_angle); + + // Full length of the gradient + auto gradient_length = calculate_gradient_length(a_rect.size(), sin_angle, cos_angle); + IntPoint offset { cos_angle * (gradient_length / 2), sin_angle * (gradient_length / 2) }; + auto center = a_rect.translated(-a_rect.location()).center(); + auto start_point = center - offset; + // Rotate gradient line to be horizontal + auto rotated_start_point_x = start_point.x() * cos_angle - start_point.y() * -sin_angle; + + GradientLine gradient_line(gradient_length, color_stops, repeat_length); + gradient_line.paint_into_physical_rect(*this, a_rect, [&](int x, int y) { + return (x * cos_angle - (a_rect.height() - y) * -sin_angle) - rotated_start_point_x; + }); +} + +void Painter::fill_rect_with_conic_gradient(IntRect const& rect, Span const& color_stops, IntPoint center, float start_angle, Optional repeat_length) +{ + auto a_rect = to_physical(rect); + if (a_rect.intersected(clip_rect() * scale()).is_empty()) + return; + + // FIXME: Do we need/want sub-degree accuracy for the gradient line? + GradientLine gradient_line(360, color_stops, repeat_length); + float normalized_start_angle = (360.0f - start_angle) + 90.0f; + // Translate position/center to the center of the pixel (avoids some funky painting) + auto center_point = FloatPoint { center * scale() }.translated(0.5, 0.5); + // 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; + } + } + gradient_line.paint_into_physical_rect(*this, a_rect, [&](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::atan2(point.y(), point.x()) * 180.0f / AK::Pi + 360.0f + normalized_start_angle), 360.0f); + return should_floor_angles ? floor(loc) : loc; + }); +} + +void Painter::fill_rect_with_radial_gradient(IntRect const& rect, Span const& color_stops, IntPoint center, IntSize size, Optional repeat_length) +{ + auto a_rect = to_physical(rect); + if (a_rect.intersected(clip_rect() * scale()).is_empty()) + return; + + // A conservative guesstimate on how many colors we need to generate: + auto max_dimension = max(a_rect.width(), a_rect.height()); + auto max_visible_gradient = max(max_dimension / 2, min(size.width(), max_dimension)); + GradientLine gradient_line(max_visible_gradient, color_stops, repeat_length); + auto center_point = FloatPoint { center * scale() }.translated(0.5, 0.5); + gradient_line.paint_into_physical_rect(*this, a_rect, [&](int x, int y) { + // FIXME: See if there's a more efficient calculation we do there :^) + auto point = FloatPoint(x, y) - center_point; + auto gradient_x = point.x() / size.width(); + auto gradient_y = point.y() / size.height(); + return AK::sqrt(gradient_x * gradient_x + gradient_y * gradient_y) * max_visible_gradient; + }); +} + +} diff --git a/Userland/Libraries/LibGfx/Gradients.h b/Userland/Libraries/LibGfx/Gradients.h new file mode 100644 index 00000000000..62b848712e2 --- /dev/null +++ b/Userland/Libraries/LibGfx/Gradients.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Gfx { + +struct ColorStop { + Color color; + float position = AK::NaN; + Optional transition_hint = {}; +}; + +class GradientLine; + +inline float normalized_gradient_angle_radians(float gradient_angle) +{ + // Adjust angle so 0 degrees is bottom + float real_angle = 90 - gradient_angle; + return real_angle * (AK::Pi / 180); +} + +template +inline float calculate_gradient_length(Size gradient_size, float sin_angle, float cos_angle) +{ + return AK::fabs(static_cast(gradient_size.height()) * sin_angle) + AK::fabs(static_cast(gradient_size.width()) * cos_angle); +} + +template +inline float calculate_gradient_length(Size gradient_size, float gradient_angle) +{ + float angle = normalized_gradient_angle_radians(gradient_angle); + float sin_angle, cos_angle; + AK::sincos(angle, sin_angle, cos_angle); + return calculate_gradient_length(gradient_size, sin_angle, cos_angle); +} + +} diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 06fd77fb026..9ee41f91ced 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -235,47 +235,7 @@ void Painter::fill_rect_with_gradient(Orientation orientation, IntRect const& a_ fill_rect(a_rect, gradient_start); return; } - - auto rect = to_physical(a_rect); - auto clipped_rect = IntRect::intersection(rect, clip_rect() * scale()); - if (clipped_rect.is_empty()) - return; - - int offset = clipped_rect.primary_offset_for_orientation(orientation) - rect.primary_offset_for_orientation(orientation); - - ARGB32* dst = m_target->scanline(clipped_rect.top()) + clipped_rect.left(); - size_t const dst_skip = m_target->pitch() / sizeof(ARGB32); - - float increment = (1.0 / ((rect.primary_size_for_orientation(orientation)))); - float alpha_increment = increment * ((float)gradient_end.alpha() - (float)gradient_start.alpha()); - - if (orientation == Orientation::Horizontal) { - for (int i = clipped_rect.height() - 1; i >= 0; --i) { - float c = offset * increment; - float c_alpha = gradient_start.alpha() + offset * alpha_increment; - for (int j = 0; j < clipped_rect.width(); ++j) { - auto color = gamma_accurate_blend(gradient_start, gradient_end, c); - color.set_alpha(c_alpha); - dst[j] = Color::from_argb(dst[j]).blend(color).value(); - c_alpha += alpha_increment; - c += increment; - } - dst += dst_skip; - } - } else { - float c = offset * increment; - float c_alpha = gradient_start.alpha() + offset * alpha_increment; - for (int i = clipped_rect.height() - 1; i >= 0; --i) { - auto color = gamma_accurate_blend(gradient_start, gradient_end, c); - color.set_alpha(c_alpha); - for (int j = 0; j < clipped_rect.width(); ++j) { - dst[j] = Color::from_argb(dst[j]).blend(color).value(); - } - c_alpha += alpha_increment; - c += increment; - dst += dst_skip; - } - } + return fill_rect_with_linear_gradient(a_rect, Array { ColorStop { gradient_start, 0 }, ColorStop { gradient_end, 1 } }, orientation == Orientation::Horizontal ? 90.0f : 0.0f); } void Painter::fill_rect_with_gradient(IntRect const& a_rect, Color gradient_start, Color gradient_end) diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 86d4e54fbe0..1b5d54e64e7 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include - namespace Gfx { class Painter { @@ -50,6 +50,9 @@ public: void fill_rect_with_checkerboard(IntRect const&, IntSize, Color color_dark, Color color_light); void fill_rect_with_gradient(Orientation, IntRect const&, Color gradient_start, Color gradient_end); void fill_rect_with_gradient(IntRect const&, Color gradient_start, Color gradient_end); + void fill_rect_with_linear_gradient(IntRect const&, Span const&, float angle, Optional repeat_length = {}); + void fill_rect_with_conic_gradient(IntRect const&, Span const&, IntPoint center, float start_angle, Optional repeat_length = {}); + void fill_rect_with_radial_gradient(IntRect const&, Span const&, IntPoint center, IntSize size, Optional repeat_length = {}); void fill_rect_with_rounded_corners(IntRect const&, Color, int radius); void fill_rect_with_rounded_corners(IntRect const&, Color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius); void fill_ellipse(IntRect const&, Color); @@ -173,28 +176,9 @@ public: int scale() const { return state().scale; } - template - void fill_pixels(Gfx::IntRect const& region, TGetPixelCallback callback, bool blend = false) - { - // Note: This function paints physical pixels and therefore does not make sense when - // called on a scaled painter. Scaling the region painted may break the caller (this - // would be the case for gradients in LibWeb), and if you scale the pixels (to squares) - // then this is no longer filling pixels. - VERIFY(scale() == 1); - auto paint_region = region.translated(translation()); - auto clipped_region = paint_region.intersected(clip_rect()); - if (clipped_region.is_empty()) - return; - auto start_offset = clipped_region.location() - paint_region.location(); - for (int y = 0; y < clipped_region.height(); y++) { - for (int x = 0; x < clipped_region.width(); x++) { - auto pixel = callback(IntPoint(x, y).translated(start_offset)); - set_physical_pixel(clipped_region.location().translated(x, y), pixel, blend); - } - } - } - protected: + friend GradientLine; + IntRect to_physical(IntRect const& r) const { return r.translated(translation()) * scale(); } IntPoint to_physical(IntPoint p) const { return p.translated(translation()) * scale(); } void set_physical_pixel_with_draw_op(u32& pixel, Color); diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp index 6f0407f84d7..128f1417ad7 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp @@ -1,40 +1,16 @@ /* - * Copyright (c) 2022, MacDue + * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ -#include #include -#include -#include +#include #include #include namespace Web::Painting { -static float normalized_gradient_angle_radians(float gradient_angle) -{ - // Adjust angle so 0 degrees is bottom - float real_angle = 90 - gradient_angle; - return real_angle * (AK::Pi / 180); -} - -template -static float calculate_gradient_length(Gfx::Size gradient_size, float sin_angle, float cos_angle) -{ - return AK::fabs(gradient_size.height().value() * sin_angle) + AK::fabs(gradient_size.width().value() * cos_angle); -} - -template -static float calculate_gradient_length(Gfx::Size gradient_size, float gradient_angle) -{ - float angle = normalized_gradient_angle_radians(gradient_angle); - float sin_angle, cos_angle; - AK::sincos(angle, sin_angle, cos_angle); - return calculate_gradient_length(gradient_size, sin_angle, cos_angle); -} - static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, auto resolve_position_to_float, bool repeating) { VERIFY(color_stop_list.size() >= 2); @@ -50,7 +26,7 @@ static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, a resolved_color_stops.ensure_capacity(expanded_size); for (auto& stop : color_stop_list) { - auto resolved_stop = ColorStop { .color = stop.color_stop.color }; + auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color }; for (int i = 0; i < color_stop_length(stop); i++) resolved_color_stops.append(resolved_stop); } @@ -133,7 +109,7 @@ static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, a LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, CSSPixelSize gradient_size, CSS::LinearGradientStyleValue const& linear_gradient) { auto gradient_angle = linear_gradient.angle_degrees(gradient_size); - auto gradient_length_px = calculate_gradient_length(gradient_size, gradient_angle); + auto gradient_length_px = Gfx::calculate_gradient_length(gradient_size, gradient_angle); auto gradient_length = CSS::Length::make_px(gradient_length_px); auto resolved_color_stops = resolve_color_stop_positions( @@ -168,158 +144,19 @@ RadialGradientData resolve_radial_gradient_data(Layout::Node const& node, CSSPix return { resolved_color_stops }; } -static float color_stop_step(ColorStop const& previous_stop, ColorStop const& next_stop, float position) -{ - if (position < previous_stop.position) - return 0; - if (position > next_stop.position) - return 1; - // For any given point between the two color stops, - // determine the point’s location as a percentage of the distance between the two color stops. - // Let this percentage be P. - auto stop_length = next_stop.position - previous_stop.position; - // FIXME: Avoids NaNs... Still not quite correct? - if (stop_length <= 0) - return 1; - auto p = (position - previous_stop.position) / stop_length; - if (!next_stop.transition_hint.has_value()) - return p; - if (*next_stop.transition_hint >= 1) - return 0; - if (*next_stop.transition_hint <= 0) - return 1; - // Let C, the color weighting at that point, be equal to P^(logH(.5)). - auto c = AK::pow(p, AK::log(0.5) / AK::log(*next_stop.transition_hint)); - // The color at that point is then a linear blend between the colors of the two color stops, - // blending (1 - C) of the first stop and C of the second stop. - return c; -} - -class GradientLine { -public: - GradientLine(int gradient_length, ColorStopData const& color_stops) - : m_repeating { color_stops.repeat_length.has_value() } - , m_start_offset { round_to((m_repeating ? color_stops.list.first().position : 0.0f) * gradient_length) } - { - // Note: color_count will be < gradient_length for repeating gradients. - auto color_count = round_to(color_stops.repeat_length.value_or(1.0f) * gradient_length); - m_gradient_line_colors.resize(color_count); - // Note: color.mixed_with() performs premultiplied alpha mixing when necessary as defined in: - // https://drafts.csswg.org/css-images/#coloring-gradient-line - auto& stop_list = color_stops.list; - for (int loc = 0; loc < color_count; loc++) { - auto relative_loc = float(loc + m_start_offset) / gradient_length; - Gfx::Color gradient_color = stop_list[0].color.mixed_with( - stop_list[1].color, - color_stop_step(stop_list[0], stop_list[1], relative_loc)); - for (size_t i = 1; i < stop_list.size() - 1; i++) { - gradient_color = gradient_color.mixed_with( - stop_list[i + 1].color, - color_stop_step(stop_list[i], stop_list[i + 1], relative_loc)); - } - m_gradient_line_colors[loc] = gradient_color; - if (gradient_color.alpha() < 255) - m_requires_blending = true; - } - } - - Gfx::Color get_color(i64 index) const - { - return m_gradient_line_colors[clamp(index, 0, m_gradient_line_colors.size() - 1)]; - } - - Gfx::Color sample_color(float loc) const - { - auto repeat_wrap_if_required = [&](i64 loc) { - if (m_repeating) - return (loc + m_start_offset) % static_cast(m_gradient_line_colors.size()); - 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 neighbouring colors (this fixes some nasty aliasing issues at small angles) - if (blend >= 0.004f) - color = color.mixed_with(get_color(repeat_wrap_if_required(int_loc + 1)), blend); - return color; - } - - void paint_into_rect(Gfx::Painter& painter, DevicePixelRect rect, auto location_transform) - { - painter.fill_pixels( - rect.to_type(), [&](auto point) { - return sample_color(location_transform(point.x(), point.y())); - }, - m_requires_blending); - } - -private: - bool m_repeating; - int m_start_offset; - Vector m_gradient_line_colors; - - bool m_requires_blending = false; -}; - void paint_linear_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, LinearGradientData const& data) { - float angle = normalized_gradient_angle_radians(data.gradient_angle); - float sin_angle, cos_angle; - AK::sincos(angle, sin_angle, cos_angle); - - // Full length of the gradient - auto gradient_length = calculate_gradient_length(gradient_rect.size(), sin_angle, cos_angle); - DevicePixelPoint offset { cos_angle * (gradient_length / 2), sin_angle * (gradient_length / 2) }; - auto center = gradient_rect.translated(-gradient_rect.location()).center(); - auto start_point = center.to_type() - offset.to_type(); - // Rotate gradient line to be horizontal - auto rotated_start_point_x = start_point.x() * cos_angle - start_point.y() * -sin_angle; - - GradientLine gradient_line(gradient_length, data.color_stops); - gradient_line.paint_into_rect(context.painter(), gradient_rect, [&](DevicePixels x, DevicePixels y) { - return (x.value() * cos_angle - (gradient_rect.height() - y).value() * -sin_angle) - rotated_start_point_x; - }); + context.painter().fill_rect_with_linear_gradient(gradient_rect.to_type(), data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length); } void paint_conic_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, ConicGradientData const& data, DevicePixelPoint position) { - // FIXME: Do we need/want sub-degree accuracy for the gradient line? - GradientLine gradient_line(360, data.color_stops); - float start_angle = (360.0f - data.start_angle) + 90.0f; - // Translate position/center to the center of the pixel (avoids some funky painting) - auto center_point = Gfx::FloatPoint { position.to_type() }.translated(0.5, 0.5); - // 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; - auto& color_stops = data.color_stops.list; - 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; - } - } - gradient_line.paint_into_rect(context.painter(), gradient_rect, [&](DevicePixels x, DevicePixels y) { - auto point = Gfx::FloatPoint { x.value(), y.value() } - center_point; - // FIXME: We could probably get away with some approximation here: - auto loc = fmod((AK::atan2(point.y(), point.x()) * 180.0f / AK::Pi + 360.0f + start_angle), 360.0f); - return should_floor_angles ? floor(loc) : loc; - }); + context.painter().fill_rect_with_conic_gradient(gradient_rect.to_type(), data.color_stops.list, position.to_type(), data.start_angle, data.color_stops.repeat_length); } void paint_radial_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, RadialGradientData const& data, DevicePixelPoint center, DevicePixelSize size) { - // A conservative guesstimate on how many colors we need to generate: - auto max_dimension = max(gradient_rect.width(), gradient_rect.height()); - auto max_visible_gradient = max(max_dimension / 2, min(size.width(), max_dimension.value())).value(); - GradientLine gradient_line(max_visible_gradient, data.color_stops); - auto center_point = Gfx::FloatPoint { center.to_type() }.translated(0.5, 0.5); - gradient_line.paint_into_rect(context.painter(), gradient_rect, [&](DevicePixels x, DevicePixels y) { - // FIXME: See if there's a more efficient calculation we do there :^) - auto point = Gfx::FloatPoint(x.value(), y.value()) - center_point; - auto gradient_x = point.x() / size.width().value(); - auto gradient_y = point.y() / size.height().value(); - return AK::sqrt(gradient_x * gradient_x + gradient_y * gradient_y) * max_visible_gradient; - }); + context.painter().fill_rect_with_radial_gradient(gradient_rect.to_type(), data.color_stops.list, center.to_type(), size.to_type(), data.color_stops.repeat_length); } } diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.h b/Userland/Libraries/LibWeb/Painting/GradientPainting.h index 74085051361..18940a741a0 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.h +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, MacDue + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -14,13 +14,7 @@ namespace Web::Painting { -struct ColorStop { - Gfx::Color color; - float position = AK::NaN; - Optional transition_hint = {}; -}; - -using ColorStopList = Vector; +using ColorStopList = Vector; struct ColorStopData { ColorStopList list;