Browse Source

LibAccelGfx+LibWeb: Add basic support for linear gradients painting

Linear gradient painting is implemented in the following way:
1. The rectangle is divided into segments where each segment represents
   a simple linear gradient between an adjacent pair of stops.
2. Each quad is filled separately using a fragment shader that
   interpolates between two colors.

For now `angle` and `repeat_length` parameters are ignored.
Aliaksandr Kalenik 1 năm trước cách đây
mục cha
commit
f6a9f613c7

+ 112 - 0
Userland/Libraries/LibAccelGfx/Painter.cpp

@@ -79,6 +79,26 @@ void main() {
 }
 )";
 
+char const* linear_gradient_vertex_shader_source = R"(
+#version 330 core
+layout (location = 0) in vec2 aVertexPosition;
+layout (location = 1) in vec4 aColor;
+out vec4 vColor;
+void main() {
+    gl_Position = vec4(aVertexPosition, 0.0, 1.0);
+    vColor = aColor;
+}
+)";
+
+char const* linear_gradient_fragment_shader_source = R"(
+#version 330 core
+out vec4 FragColor;
+in vec4 vColor;
+void main() {
+    FragColor = vec4(vColor);
+}
+)";
+
 OwnPtr<Painter> Painter::create()
 {
     auto& context = Context::the();
@@ -89,6 +109,7 @@ Painter::Painter(Context& context)
     : m_context(context)
     , m_rectangle_program(Program::create(vertex_shader_source, solid_color_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())
 {
     m_state_stack.empend(State());
@@ -406,6 +427,97 @@ void Painter::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Col
     GL::delete_vertex_array(vao);
 }
 
+void Painter::fill_rect_with_linear_gradient(Gfx::IntRect const& rect, ReadonlySpan<Gfx::ColorStop> stops, float angle, Optional<float> repeat_length)
+{
+    fill_rect_with_linear_gradient(rect.to_type<float>(), stops, angle, repeat_length);
+}
+
+void Painter::fill_rect_with_linear_gradient(Gfx::FloatRect const& rect, ReadonlySpan<Gfx::ColorStop> stops, float angle, Optional<float> repeat_length)
+{
+    // FIXME: Implement support for angle and repeat_length
+    (void)angle;
+    (void)repeat_length;
+
+    Vector<GLfloat> vertices;
+    Vector<GLfloat> colors;
+    for (size_t stop_index = 0; stop_index < stops.size() - 1; stop_index++) {
+        auto const& stop_start = stops[stop_index];
+        auto const& stop_end = stops[stop_index + 1];
+
+        // The gradient is divided into segments that represent linear gradients between adjacent pairs of stops.
+        auto segment_rect_location = rect.location();
+        segment_rect_location.set_x(segment_rect_location.x() + stop_start.position * rect.width());
+        auto segment_rect_width = (stop_end.position - stop_start.position) * rect.width();
+        auto segment_rect_height = rect.height();
+        auto segment_rect = Gfx::FloatRect { segment_rect_location.x(), segment_rect_location.y(), segment_rect_width, segment_rect_height };
+
+        auto rect_in_clip_space = to_clip_space(segment_rect);
+
+        // p0 --- p1
+        // | \     |
+        // |   \   |
+        // |     \ |
+        // p2 --- p3
+
+        auto p0 = rect_in_clip_space.top_left();
+        auto p1 = rect_in_clip_space.top_right();
+        auto p2 = rect_in_clip_space.bottom_left();
+        auto p3 = rect_in_clip_space.bottom_right();
+
+        auto c0 = gfx_color_to_opengl_color(stop_start.color);
+        auto c1 = gfx_color_to_opengl_color(stop_end.color);
+        auto c2 = gfx_color_to_opengl_color(stop_start.color);
+        auto c3 = gfx_color_to_opengl_color(stop_end.color);
+
+        auto add_triangle = [&](auto& p1, auto& p2, auto& p3, auto& c1, auto& c2, auto& c3) {
+            vertices.append(p1.x());
+            vertices.append(p1.y());
+            colors.append(c1.red);
+            colors.append(c1.green);
+            colors.append(c1.blue);
+            colors.append(c1.alpha);
+
+            vertices.append(p2.x());
+            vertices.append(p2.y());
+            colors.append(c2.red);
+            colors.append(c2.green);
+            colors.append(c2.blue);
+            colors.append(c2.alpha);
+
+            vertices.append(p3.x());
+            vertices.append(p3.y());
+            colors.append(c3.red);
+            colors.append(c3.green);
+            colors.append(c3.blue);
+            colors.append(c3.alpha);
+        };
+
+        add_triangle(p0, p1, p3, c0, c1, c3);
+        add_triangle(p0, p3, p2, c0, c3, c2);
+    }
+
+    auto vao = GL::create_vertex_array();
+    GL::bind_vertex_array(vao);
+
+    auto vbo_vertices = GL::create_buffer();
+    GL::upload_to_buffer(vbo_vertices, vertices);
+
+    auto vbo_colors = GL::create_buffer();
+    GL::upload_to_buffer(vbo_colors, colors);
+
+    m_linear_gradient_program.use();
+    auto position_attribute = m_linear_gradient_program.get_attribute_location("aVertexPosition");
+    auto color_attribute = m_linear_gradient_program.get_attribute_location("aColor");
+
+    GL::bind_buffer(vbo_vertices);
+    GL::set_vertex_attribute(position_attribute, 0, 2);
+
+    GL::bind_buffer(vbo_colors);
+    GL::set_vertex_attribute(color_attribute, 0, 4);
+
+    GL::draw_arrays(GL::DrawPrimitive::Triangles, vertices.size() / 2);
+}
+
 void Painter::save()
 {
     m_state_stack.append(state());

+ 5 - 0
Userland/Libraries/LibAccelGfx/Painter.h

@@ -17,6 +17,7 @@
 #include <LibGfx/AffineTransform.h>
 #include <LibGfx/Font/Font.h>
 #include <LibGfx/Forward.h>
+#include <LibGfx/Gradients.h>
 #include <LibGfx/TextLayout.h>
 
 namespace AccelGfx {
@@ -73,6 +74,9 @@ public:
     void set_target_bitmap(Gfx::Bitmap&);
     void flush();
 
+    void fill_rect_with_linear_gradient(Gfx::IntRect const&, ReadonlySpan<Gfx::ColorStop>, float angle, Optional<float> repeat_length = {});
+    void fill_rect_with_linear_gradient(Gfx::FloatRect const&, ReadonlySpan<Gfx::ColorStop>, float angle, Optional<float> repeat_length = {});
+
 private:
     Context& m_context;
 
@@ -89,6 +93,7 @@ private:
 
     Program m_rectangle_program;
     Program m_blit_program;
+    Program m_linear_gradient_program;
 
     HashMap<GlyphsTextureKey, Gfx::IntRect> m_glyphs_texture_map;
     Gfx::IntSize m_glyphs_texture_size;

+ 2 - 2
Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp

@@ -99,9 +99,9 @@ CommandResult PaintingCommandExecutorGPU::pop_stacking_context_with_mask(Gfx::In
     return CommandResult::Continue;
 }
 
-CommandResult PaintingCommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&)
+CommandResult PaintingCommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data)
 {
-    // FIXME
+    painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length);
     return CommandResult::Continue;
 }