From 5c0cd0f48457d046bc4ee235cb5fcff63836da17 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sun, 3 Dec 2023 18:05:21 +0100 Subject: [PATCH] LibAccelGfx+LibWeb: Add text shadow support in GPU painter --- Userland/Libraries/LibAccelGfx/GL.cpp | 6 ++ Userland/Libraries/LibAccelGfx/GL.h | 1 + Userland/Libraries/LibAccelGfx/Painter.cpp | 95 ++++++++++++++++++- Userland/Libraries/LibAccelGfx/Painter.h | 10 +- Userland/Libraries/LibAccelGfx/Program.h | 2 + .../Painting/PaintingCommandExecutorGPU.cpp | 23 ++++- 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibAccelGfx/GL.cpp b/Userland/Libraries/LibAccelGfx/GL.cpp index 09f343ae0f..e7601cd5bf 100644 --- a/Userland/Libraries/LibAccelGfx/GL.cpp +++ b/Userland/Libraries/LibAccelGfx/GL.cpp @@ -151,6 +151,12 @@ void delete_texture(Texture const& texture) verify_no_error(); } +void set_uniform(Uniform const& uniform, int value) +{ + glUniform1i(uniform.id, value); + verify_no_error(); +} + void set_uniform(Uniform const& uniform, float value1, float value2) { glUniform2f(uniform.id, value1, value2); diff --git a/Userland/Libraries/LibAccelGfx/GL.h b/Userland/Libraries/LibAccelGfx/GL.h index 81a7634e49..9727a7508c 100644 --- a/Userland/Libraries/LibAccelGfx/GL.h +++ b/Userland/Libraries/LibAccelGfx/GL.h @@ -81,6 +81,7 @@ void bind_texture(Texture const&); void upload_texture_data(Texture& texture, Gfx::Bitmap const& bitmap); void delete_texture(Texture const&); +void set_uniform(Uniform const& uniform, int); 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 814f966d29..79decc4138 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -135,6 +135,39 @@ void main() { } )"; +char const* blur_fragment_shader_source = R"( +#version 330 core + +uniform vec2 uResolution; +uniform int uRadius; +uniform int uHorizontal; +uniform sampler2D uSampler; + +in vec2 vTextureCoord; +out vec4 fragColor; + +#define pow2(x) (x * x) +const float pi = atan(1.0) * 4.0; +float gaussian(vec2 i, float sigma) { + return 1.0 / (2.0 * pi * pow2(sigma)) * exp(-((pow2(i.x) + pow2(i.y)) / (2.0 * pow2(sigma)))); +} + +void main() { + vec2 scale = vec2(1.0) / uResolution.xy; + float sigma = float(uRadius); + vec4 col = vec4(0.0); + float accum = 0.0; + float weight = 0.0; + for (int i = -uRadius; i <= uRadius; i++) { + vec2 offset = vec2(i * uHorizontal, i * (1 - uHorizontal)); + weight = gaussian(offset, sigma); + col += texture(uSampler, vTextureCoord + scale * offset) * weight; + accum += weight; + } + fragColor = col / accum; +} +)"; + HashMap s_immutable_bitmap_texture_cache; NonnullOwnPtr Painter::create() @@ -149,6 +182,7 @@ Painter::Painter(Context& context) , m_rounded_rectangle_program(Program::create(Program::Name::RoundedRectangleProgram, vertex_shader_source, rect_with_rounded_corners_fragment_shader_source)) , m_blit_program(Program::create(Program::Name::BlitProgram, blit_vertex_shader_source, blit_fragment_shader_source)) , m_linear_gradient_program(Program::create(Program::Name::LinearGradientProgram, linear_gradient_vertex_shader_source, linear_gradient_fragment_shader_source)) + , m_blur_program(Program::create(Program::Name::BlurProgram, blit_vertex_shader_source, blur_fragment_shader_source)) { m_state_stack.empend(State()); } @@ -352,7 +386,7 @@ void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap con GL::delete_texture(texture); } -void Painter::draw_glyph_run(Vector const& glyph_run, Color const& color) +void Painter::draw_glyph_run(Span glyph_run, Color const& color) { bind_target_canvas(); @@ -667,6 +701,65 @@ void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture co GL::delete_vertex_array(vao); } +void Painter::blit_blurred_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, int radius, BlurDirection direction, ScalingMode scaling_mode) +{ + bind_target_canvas(); + + m_blur_program.use(); + + auto dst_rect_in_clip_space = to_clip_space(transform().map(dst_rect)); + auto src_rect_in_texture_space = to_texture_space(src_rect, *texture.size); + + Vector vertices; + vertices.ensure_capacity(16); + + auto add_vertex = [&](auto const& p, auto const& s) { + vertices.append(p.x()); + vertices.append(p.y()); + vertices.append(s.x()); + vertices.append(s.y()); + }; + + add_vertex(dst_rect_in_clip_space.top_left(), src_rect_in_texture_space.top_left()); + add_vertex(dst_rect_in_clip_space.bottom_left(), src_rect_in_texture_space.bottom_left()); + add_vertex(dst_rect_in_clip_space.bottom_right(), src_rect_in_texture_space.bottom_right()); + add_vertex(dst_rect_in_clip_space.top_right(), src_rect_in_texture_space.top_right()); + + 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 vertex_position_attribute = m_blur_program.get_attribute_location("aVertexPosition"); + GL::set_vertex_attribute(vertex_position_attribute, 0, 4); + + auto resolution_uniform = m_blur_program.get_uniform_location("uResolution"); + GL::set_uniform(resolution_uniform, dst_rect.width(), dst_rect.height()); + + auto radius_uniform = m_blur_program.get_uniform_location("uRadius"); + GL::set_uniform(radius_uniform, radius); + + auto direction_uniform = m_blur_program.get_uniform_location("uHorizontal"); + GL::set_uniform(direction_uniform, direction == BlurDirection::Horizontal ? 1 : 0); + + GL::bind_texture(texture); + + auto scaling_mode_gl = to_gl_scaling_mode(scaling_mode); + GL::set_texture_scale_mode(scaling_mode_gl); + GL::enable_blending(GL::BlendFactor::SrcAlpha, GL::BlendFactor::OneMinusSrcAlpha, GL::BlendFactor::One, GL::BlendFactor::One); + GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); + + GL::delete_buffer(vbo); + GL::delete_vertex_array(vao); +} + +void Painter::blit_blurred_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, int radius, BlurDirection direction, ScalingMode scaling_mode) +{ + blit_blurred_texture(dst_rect, canvas.framebuffer().texture, { { 0, 0 }, canvas.size() }, radius, direction, scaling_mode); +} + void Painter::update_immutable_bitmap_texture_cache(HashMap& immutable_bitmaps) { for (auto immutable_bitmap_id : s_immutable_bitmap_texture_cache.keys()) { diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index a2fc83b8da..53ff704e98 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -62,7 +62,7 @@ public: void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); void draw_scaled_immutable_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::FloatRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); - void draw_glyph_run(Vector const& glyph_run, Color const& color); + void draw_glyph_run(Span glyph_run, Color const& color); void set_clip_rect(Gfx::IntRect); void clear_clip_rect(); @@ -83,6 +83,12 @@ public: void blit_canvas(Gfx::IntRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional affine_transform = {}); void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional affine_transform = {}); + enum class BlurDirection { + Horizontal, + Vertical, + }; + void blit_blurred_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, int radius, BlurDirection direction, ScalingMode = ScalingMode::NearestNeighbor); + void update_immutable_bitmap_texture_cache(HashMap&); private: @@ -97,6 +103,7 @@ private: [[nodiscard]] State const& state() const { return m_state_stack.last(); } void blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, ScalingMode, float opacity = 1.0f, Optional affine_transform = {}); + void blit_blurred_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, int radius, BlurDirection direction, ScalingMode = ScalingMode::NearestNeighbor); void bind_target_canvas(); [[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const; @@ -109,6 +116,7 @@ private: Program m_rounded_rectangle_program; Program m_blit_program; Program m_linear_gradient_program; + Program m_blur_program; }; } diff --git a/Userland/Libraries/LibAccelGfx/Program.h b/Userland/Libraries/LibAccelGfx/Program.h index a68f291b13..406669a64e 100644 --- a/Userland/Libraries/LibAccelGfx/Program.h +++ b/Userland/Libraries/LibAccelGfx/Program.h @@ -20,6 +20,8 @@ public: RoundedRectangleProgram, BlitProgram, LinearGradientProgram, + BlurProgram, + ProgramCount, }; diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index eaf435ff58..a3540edab0 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -165,9 +165,28 @@ CommandResult PaintingCommandExecutorGPU::paint_inner_box_shadow(PaintOuterBoxSh return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::paint_text_shadow(int, Gfx::IntRect const&, Gfx::IntRect const&, Span, Color const&, int, Gfx::IntPoint const&) +CommandResult PaintingCommandExecutorGPU::paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span glyph_run, Color const& color, int fragment_baseline, Gfx::IntPoint const& draw_location) { - // FIXME + auto text_shadow_canvas = AccelGfx::Canvas::create(shadow_bounding_rect.size()); + auto text_shadow_painter = AccelGfx::Painter::create(); + text_shadow_painter->set_target_canvas(text_shadow_canvas); + text_shadow_painter->clear(color.with_alpha(0)); + + Gfx::FloatRect const shadow_location { draw_location, shadow_bounding_rect.size() }; + Gfx::IntPoint const baseline_start(text_rect.x(), text_rect.y() + fragment_baseline); + text_shadow_painter->translate(baseline_start.to_type()); + text_shadow_painter->draw_glyph_run(glyph_run, color); + if (blur_radius == 0) { + painter().blit_canvas(shadow_location, *text_shadow_canvas); + return CommandResult::Continue; + } + + auto horizontal_blur_canvas = AccelGfx::Canvas::create(shadow_bounding_rect.size()); + auto horizontal_blur_painter = AccelGfx::Painter::create(); + horizontal_blur_painter->set_target_canvas(horizontal_blur_canvas); + horizontal_blur_painter->clear(color.with_alpha(0)); + horizontal_blur_painter->blit_blurred_canvas(shadow_bounding_rect.to_type(), *text_shadow_canvas, blur_radius, AccelGfx::Painter::BlurDirection::Horizontal); + painter().blit_blurred_canvas(shadow_location, *horizontal_blur_canvas, blur_radius, AccelGfx::Painter::BlurDirection::Vertical); return CommandResult::Continue; }