diff --git a/Userland/Libraries/LibAccelGfx/GL.cpp b/Userland/Libraries/LibAccelGfx/GL.cpp index e7601cd5bf..cf10beec46 100644 --- a/Userland/Libraries/LibAccelGfx/GL.cpp +++ b/Userland/Libraries/LibAccelGfx/GL.cpp @@ -30,6 +30,8 @@ static GLenum to_gl_enum(BlendFactor factor) return GL_SRC_ALPHA; case BlendFactor::One: return GL_ONE; + case BlendFactor::Zero: + return GL_ZERO; case BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; } diff --git a/Userland/Libraries/LibAccelGfx/GL.h b/Userland/Libraries/LibAccelGfx/GL.h index 9727a7508c..d82da15664 100644 --- a/Userland/Libraries/LibAccelGfx/GL.h +++ b/Userland/Libraries/LibAccelGfx/GL.h @@ -61,6 +61,7 @@ struct Framebuffer { void set_viewport(Gfx::IntRect); enum class BlendFactor { + Zero, One, OneMinusSrcAlpha, SrcAlpha, diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index 69745f1979..5225832735 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -168,6 +168,23 @@ void main() { } )"; +static void set_blending_mode(Painter::BlendingMode blending_mode) +{ + switch (blending_mode) { + case Painter::BlendingMode::AlphaAdd: + GL::enable_blending(GL::BlendFactor::SrcAlpha, GL::BlendFactor::OneMinusSrcAlpha, GL::BlendFactor::One, GL::BlendFactor::One); + break; + case Painter::BlendingMode::AlphaOverride: + GL::enable_blending(GL::BlendFactor::SrcAlpha, GL::BlendFactor::OneMinusSrcAlpha, GL::BlendFactor::One, GL::BlendFactor::Zero); + break; + case Painter::BlendingMode::AlphaPreserve: + GL::enable_blending(GL::BlendFactor::SrcAlpha, GL::BlendFactor::OneMinusSrcAlpha, GL::BlendFactor::Zero, GL::BlendFactor::One); + break; + default: + VERIFY_NOT_REACHED(); + } +} + HashMap s_immutable_bitmap_texture_cache; NonnullOwnPtr Painter::create(Context& context, NonnullRefPtr canvas) @@ -249,12 +266,12 @@ 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) +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, BlendingMode blending_mode) { - fill_rect_with_rounded_corners(rect.to_type(), color, top_left_radius, top_right_radius, bottom_left_radius, bottom_right_radius); + fill_rect_with_rounded_corners(rect.to_type(), color, top_left_radius, top_right_radius, bottom_left_radius, bottom_right_radius, blending_mode); } -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) +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, BlendingMode blending_mode) { bind_target_canvas(); @@ -291,7 +308,8 @@ void Painter::fill_rect_with_rounded_corners(Gfx::FloatRect const& rect, Color c 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::BlendFactor::SrcAlpha, GL::BlendFactor::OneMinusSrcAlpha, GL::BlendFactor::One, GL::BlendFactor::One); + set_blending_mode(blending_mode); + GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); GL::delete_buffer(vbo); @@ -627,7 +645,13 @@ void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, blit_scaled_texture(dst_rect, texture, { { 0, 0 }, canvas.size() }, Painter::ScalingMode::NearestNeighbor, opacity, move(affine_transform)); } -void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode, float opacity, Optional affine_transform) +void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, Gfx::FloatRect const& src_rect, float opacity, Optional affine_transform, BlendingMode blending_mode) +{ + auto texture = GL::Texture(canvas.framebuffer().texture); + blit_scaled_texture(dst_rect, texture, src_rect, Painter::ScalingMode::NearestNeighbor, opacity, move(affine_transform), blending_mode); +} + +void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode, float opacity, Optional affine_transform, BlendingMode blending_mode) { bind_target_canvas(); @@ -689,7 +713,7 @@ void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture co 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); + set_blending_mode(blending_mode); GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index 6c99937186..0cc6768561 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -34,6 +34,8 @@ public: Painter(Context&, NonnullRefPtr); ~Painter(); + Canvas const& canvas() { return *m_target_canvas; } + void clear(Gfx::Color); void save(); @@ -53,6 +55,12 @@ public: Bilinear, }; + enum class BlendingMode { + AlphaAdd, + AlphaOverride, + AlphaPreserve, + }; + void draw_line(Gfx::IntPoint a, Gfx::IntPoint b, float thickness, Gfx::Color color); void draw_line(Gfx::FloatPoint a, Gfx::FloatPoint b, float thickness, Gfx::Color color); @@ -76,11 +84,12 @@ public: 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); + 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, BlendingMode = BlendingMode::AlphaAdd); + 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, BlendingMode = BlendingMode::AlphaAdd); 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 = {}); + void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, Gfx::FloatRect const& src_rect, float opacity = 1.0f, Optional affine_transform = {}, BlendingMode = BlendingMode::AlphaAdd); enum class BlurDirection { Horizontal, @@ -101,7 +110,7 @@ private: [[nodiscard]] State& state() { return m_state_stack.last(); } [[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_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, ScalingMode, float opacity = 1.0f, Optional affine_transform = {}, BlendingMode = BlendingMode::AlphaAdd); 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(); diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index e31465cc27..1d61c79297 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -5,6 +5,7 @@ */ #include +#include #include namespace Web::Painting { @@ -290,15 +291,77 @@ CommandResult PaintingCommandExecutorGPU::draw_triangle_wave(Gfx::IntPoint const return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::sample_under_corners(u32, CornerRadii const&, Gfx::IntRect const&, CornerClip) +CommandResult PaintingCommandExecutorGPU::sample_under_corners(u32 id, CornerRadii const& corner_radii, Gfx::IntRect const& border_rect, CornerClip) { - // FIXME + m_corner_clippers.resize(id + 1); + m_corner_clippers[id] = make(); + auto& corner_clipper = *m_corner_clippers[id]; + + auto const& top_left = corner_radii.top_left; + auto const& top_right = corner_radii.top_right; + auto const& bottom_right = corner_radii.bottom_right; + auto const& bottom_left = corner_radii.bottom_left; + + auto sampling_config = calculate_border_radius_sampling_config(corner_radii, border_rect); + auto const& page_locations = sampling_config.page_locations; + auto const& bitmap_locations = sampling_config.bitmap_locations; + + auto top_left_corner_size = Gfx::IntSize { top_left.horizontal_radius, top_left.vertical_radius }; + auto top_right_corner_size = Gfx::IntSize { top_right.horizontal_radius, top_right.vertical_radius }; + auto bottom_right_corner_size = Gfx::IntSize { bottom_right.horizontal_radius, bottom_right.vertical_radius }; + auto bottom_left_corner_size = Gfx::IntSize { bottom_left.horizontal_radius, bottom_left.vertical_radius }; + + corner_clipper.page_top_left_rect = { page_locations.top_left, top_left_corner_size }; + corner_clipper.page_top_right_rect = { page_locations.top_right, top_right_corner_size }; + corner_clipper.page_bottom_right_rect = { page_locations.bottom_right, bottom_right_corner_size }; + corner_clipper.page_bottom_left_rect = { page_locations.bottom_left, bottom_left_corner_size }; + + corner_clipper.sample_canvas_top_left_rect = { bitmap_locations.top_left, top_left_corner_size }; + corner_clipper.sample_canvas_top_right_rect = { bitmap_locations.top_right, top_right_corner_size }; + corner_clipper.sample_canvas_bottom_right_rect = { bitmap_locations.bottom_right, bottom_right_corner_size }; + corner_clipper.sample_canvas_bottom_left_rect = { bitmap_locations.bottom_left, bottom_left_corner_size }; + + corner_clipper.corners_sample_canvas = AccelGfx::Canvas::create(sampling_config.corners_bitmap_size); + auto corner_painter = AccelGfx::Painter::create(m_context, *corner_clipper.corners_sample_canvas); + corner_painter->clear(Color::White); + + corner_painter->fill_rect_with_rounded_corners( + Gfx::IntRect { { 0, 0 }, sampling_config.corners_bitmap_size }, + Color::Transparent, + { static_cast(top_left.horizontal_radius), static_cast(top_left.vertical_radius) }, + { static_cast(top_right.horizontal_radius), static_cast(top_right.vertical_radius) }, + { static_cast(bottom_right.horizontal_radius), static_cast(bottom_right.vertical_radius) }, + { static_cast(bottom_left.horizontal_radius), static_cast(bottom_left.vertical_radius) }, + AccelGfx::Painter::BlendingMode::AlphaOverride); + + auto const& target_canvas = painter().canvas(); + if (!corner_clipper.sample_canvas_top_left_rect.is_empty()) + corner_painter->blit_canvas(corner_clipper.sample_canvas_top_left_rect, target_canvas, painter().transform().map(corner_clipper.page_top_left_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve); + if (!corner_clipper.sample_canvas_top_right_rect.is_empty()) + corner_painter->blit_canvas(corner_clipper.sample_canvas_top_right_rect, target_canvas, painter().transform().map(corner_clipper.page_top_right_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve); + if (!corner_clipper.sample_canvas_bottom_right_rect.is_empty()) + corner_painter->blit_canvas(corner_clipper.sample_canvas_bottom_right_rect, target_canvas, painter().transform().map(corner_clipper.page_bottom_right_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve); + if (!corner_clipper.sample_canvas_bottom_left_rect.is_empty()) + corner_painter->blit_canvas(corner_clipper.sample_canvas_bottom_left_rect, target_canvas, painter().transform().map(corner_clipper.page_bottom_left_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve); + return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::blit_corner_clipping(u32) +CommandResult PaintingCommandExecutorGPU::blit_corner_clipping(u32 id) { - // FIXME + auto const& corner_clipper = *m_corner_clippers[id]; + auto const& corner_sample_canvas = *corner_clipper.corners_sample_canvas; + if (!corner_clipper.sample_canvas_top_left_rect.is_empty()) + painter().blit_canvas(corner_clipper.page_top_left_rect, corner_sample_canvas, corner_clipper.sample_canvas_top_left_rect); + if (!corner_clipper.sample_canvas_top_right_rect.is_empty()) + painter().blit_canvas(corner_clipper.page_top_right_rect, corner_sample_canvas, corner_clipper.sample_canvas_top_right_rect); + if (!corner_clipper.sample_canvas_bottom_right_rect.is_empty()) + painter().blit_canvas(corner_clipper.page_bottom_right_rect, corner_sample_canvas, corner_clipper.sample_canvas_bottom_right_rect); + if (!corner_clipper.sample_canvas_bottom_left_rect.is_empty()) + painter().blit_canvas(corner_clipper.page_bottom_left_rect, corner_sample_canvas, corner_clipper.sample_canvas_bottom_left_rect); + + m_corner_clippers[id].clear(); + return CommandResult::Continue; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h index 05c4926079..f7bc2663ac 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h @@ -71,10 +71,25 @@ private: int stacking_context_depth { 0 }; }; + struct BorderRadiusCornerClipper { + RefPtr corners_sample_canvas; + + Gfx::FloatRect page_top_left_rect; + Gfx::FloatRect page_top_right_rect; + Gfx::FloatRect page_bottom_right_rect; + Gfx::FloatRect page_bottom_left_rect; + + Gfx::FloatRect sample_canvas_top_left_rect; + Gfx::FloatRect sample_canvas_top_right_rect; + Gfx::FloatRect sample_canvas_bottom_right_rect; + Gfx::FloatRect sample_canvas_bottom_left_rect; + }; + [[nodiscard]] AccelGfx::Painter const& painter() const { return *m_stacking_contexts.last().painter; } [[nodiscard]] AccelGfx::Painter& painter() { return *m_stacking_contexts.last().painter; } Vector m_stacking_contexts; + Vector> m_corner_clippers; }; }