From 53c015695ea1886b87267ab8fb067f883619bd69 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 20 Nov 2023 17:25:22 +0100 Subject: [PATCH] LibAccelGfx+LibWeb: Implement rounded corners rectangle painting For now corners antialiasing is missing. --- Userland/Libraries/LibAccelGfx/GL.cpp | 6 ++ Userland/Libraries/LibAccelGfx/GL.h | 1 + Userland/Libraries/LibAccelGfx/Painter.cpp | 84 +++++++++++++++++++ Userland/Libraries/LibAccelGfx/Painter.h | 8 ++ .../Painting/PaintingCommandExecutorGPU.cpp | 9 +- 5 files changed, 106 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibAccelGfx/GL.cpp b/Userland/Libraries/LibAccelGfx/GL.cpp index 7c886a0b7e..7efd10ae14 100644 --- a/Userland/Libraries/LibAccelGfx/GL.cpp +++ b/Userland/Libraries/LibAccelGfx/GL.cpp @@ -137,6 +137,12 @@ void delete_texture(Texture const& texture) verify_no_error(); } +void set_uniform(Uniform const& uniform, float value1, float value2) +{ + glUniform2f(uniform.id, value1, value2); + verify_no_error(); +} + void set_uniform(Uniform const& uniform, float value1, float value2, float value3, float value4) { glUniform4f(uniform.id, value1, value2, value3, value4); diff --git a/Userland/Libraries/LibAccelGfx/GL.h b/Userland/Libraries/LibAccelGfx/GL.h index 0d59c701d1..a3cb68f388 100644 --- a/Userland/Libraries/LibAccelGfx/GL.h +++ b/Userland/Libraries/LibAccelGfx/GL.h @@ -73,6 +73,7 @@ void bind_texture(Texture const&); void upload_texture_data(Texture const& texture, Gfx::Bitmap const& bitmap); void delete_texture(Texture const&); +void set_uniform(Uniform const& uniform, float, float); void set_uniform(Uniform const& uniform, float, float, float, float); void set_vertex_attribute(VertexAttribute const& attribute, u32 offset, int number_of_components); diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index 99d106da67..24b446f9dc 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -49,6 +49,43 @@ void main() { } )"; +char const* rect_with_rounded_corners_fragment_shader_source = R"( +#version 330 core +uniform vec2 uRectCenter; +uniform vec2 uRectCorner; +uniform vec2 uTopLeftRadius; +uniform vec2 uTopRightRadius; +uniform vec2 uBottomLeftRadius; +uniform vec2 uBottomRightRadius; +uniform vec4 uColor; +out vec4 fragColor; + +bool isPointWithinEllipse(vec2 point, vec2 radius) { + vec2 normalizedPoint = point / radius; + return dot(normalizedPoint, normalizedPoint) <= 1.0; +} + +void main() { + vec2 p = gl_FragCoord.xy - uRectCenter; + vec2 cornerRadius = vec2(0.0, 0.0); + if (p.x < 0.0 && p.y < 0.0) { + cornerRadius = uTopLeftRadius; + } else if (p.x > 0.0 && p.y < 0.0) { + cornerRadius = uTopRightRadius; + } else if (p.x < 0.0 && p.y > 0.0) { + cornerRadius = uBottomLeftRadius; + } else if (p.x > 0.0 && p.y > 0.0) { + cornerRadius = uBottomRightRadius; + } + vec2 q = abs(p) - (uRectCorner - cornerRadius); + if (q.x < 0 || q.y < 0 || isPointWithinEllipse(q, cornerRadius)) { + fragColor = uColor; + } else { + discard; + } +} +)"; + char const* solid_color_fragment_shader_source = R"( #version 330 core uniform vec4 uColor; @@ -108,6 +145,7 @@ OwnPtr Painter::create() Painter::Painter(Context& context) : m_context(context) , m_rectangle_program(Program::create(vertex_shader_source, solid_color_fragment_shader_source)) + , m_rounded_rectangle_program(Program::create(vertex_shader_source, rect_with_rounded_corners_fragment_shader_source)) , m_blit_program(Program::create(blit_vertex_shader_source, blit_fragment_shader_source)) , m_linear_gradient_program(Program::create(linear_gradient_vertex_shader_source, linear_gradient_fragment_shader_source)) , m_glyphs_texture(GL::create_texture()) @@ -173,6 +211,52 @@ void Painter::fill_rect(Gfx::FloatRect rect, Gfx::Color color) GL::delete_vertex_array(vao); } +void Painter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius) +{ + fill_rect_with_rounded_corners(rect.to_type(), color, top_left_radius, top_right_radius, bottom_left_radius, bottom_right_radius); +} + +void Painter::fill_rect_with_rounded_corners(Gfx::FloatRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius) +{ + auto vertices = rect_to_vertices(to_clip_space(transform().map(rect))); + + auto vbo = GL::create_buffer(); + GL::upload_to_buffer(vbo, vertices); + + auto vao = GL::create_vertex_array(); + GL::bind_vertex_array(vao); + GL::bind_buffer(vbo); + + auto [red, green, blue, alpha] = gfx_color_to_opengl_color(color); + + m_rounded_rectangle_program.use(); + + auto position_attribute = m_rounded_rectangle_program.get_attribute_location("aVertexPosition"); + GL::set_vertex_attribute(position_attribute, 0, 2); + + auto color_uniform = m_rounded_rectangle_program.get_uniform_location("uColor"); + GL::set_uniform(color_uniform, red, green, blue, alpha); + + auto rect_center_uniform = m_rounded_rectangle_program.get_uniform_location("uRectCenter"); + GL::set_uniform(rect_center_uniform, rect.center().x(), rect.center().y()); + auto rect_corner_uniform = m_rounded_rectangle_program.get_uniform_location("uRectCorner"); + GL::set_uniform(rect_corner_uniform, rect.width() / 2, rect.height() / 2); + auto top_left_corner_radius_uniform = m_rounded_rectangle_program.get_uniform_location("uTopLeftRadius"); + GL::set_uniform(top_left_corner_radius_uniform, top_left_radius.horizontal_radius, top_left_radius.vertical_radius); + auto top_right_corner_radius_uniform = m_rounded_rectangle_program.get_uniform_location("uTopRightRadius"); + GL::set_uniform(top_right_corner_radius_uniform, top_right_radius.horizontal_radius, top_right_radius.vertical_radius); + auto bottom_left_corner_radius_uniform = m_rounded_rectangle_program.get_uniform_location("uBottomLeftRadius"); + GL::set_uniform(bottom_left_corner_radius_uniform, bottom_left_radius.horizontal_radius, bottom_left_radius.vertical_radius); + auto bottom_right_corner_radius_uniform = m_rounded_rectangle_program.get_uniform_location("uBottomRightRadius"); + GL::set_uniform(bottom_right_corner_radius_uniform, bottom_right_radius.horizontal_radius, bottom_right_radius.vertical_radius); + + GL::enable_blending(); + GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); + + GL::delete_buffer(vbo); + GL::delete_vertex_array(vao); +} + void Painter::draw_line(Gfx::IntPoint a, Gfx::IntPoint b, float thickness, Gfx::Color color) { draw_line(a.to_type(), b.to_type(), thickness, color); diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index 7816426032..bdad0d9565 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -77,6 +77,13 @@ public: void fill_rect_with_linear_gradient(Gfx::IntRect const&, ReadonlySpan, float angle, Optional repeat_length = {}); void fill_rect_with_linear_gradient(Gfx::FloatRect const&, ReadonlySpan, float angle, Optional repeat_length = {}); + struct CornerRadius { + float horizontal_radius; + float vertical_radius; + }; + void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); + void fill_rect_with_rounded_corners(Gfx::FloatRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); + private: Context& m_context; @@ -92,6 +99,7 @@ private: Vector m_state_stack; Program m_rectangle_program; + Program m_rounded_rectangle_program; Program m_blit_program; Program m_linear_gradient_program; diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index fc718bd9b1..539911433a 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -111,9 +111,14 @@ CommandResult PaintingCommandExecutorGPU::paint_text_shadow(int, Gfx::IntRect co return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const&, Gfx::AntiAliasingPainter::CornerRadius const&, Gfx::AntiAliasingPainter::CornerRadius const&, Gfx::AntiAliasingPainter::CornerRadius const&, Optional const&) +CommandResult PaintingCommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Optional const&) { - // FIXME + painter().fill_rect_with_rounded_corners( + rect, color, + { static_cast(top_left_radius.horizontal_radius), static_cast(top_left_radius.vertical_radius) }, + { static_cast(top_right_radius.horizontal_radius), static_cast(top_right_radius.vertical_radius) }, + { static_cast(bottom_left_radius.horizontal_radius), static_cast(bottom_left_radius.vertical_radius) }, + { static_cast(bottom_right_radius.horizontal_radius), static_cast(bottom_right_radius.vertical_radius) }); return CommandResult::Continue; }