mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 05:44:58 +00:00
LibWeb: Implement border radius corner clipping in GPU painter
It is implemented in the way identical to how it works in CPU painter: 1. SampleUnderCorners command saves pixels within corners into a texture. 2. BlitCornerClipping command uses the texture prepared earlier to restore pixels within corners.
This commit is contained in:
parent
177e7df1c5
commit
bd08b1815f
6 changed files with 127 additions and 13 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ struct Framebuffer {
|
|||
void set_viewport(Gfx::IntRect);
|
||||
|
||||
enum class BlendFactor {
|
||||
Zero,
|
||||
One,
|
||||
OneMinusSrcAlpha,
|
||||
SrcAlpha,
|
||||
|
|
|
@ -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<u32, GL::Texture> s_immutable_bitmap_texture_cache;
|
||||
|
||||
NonnullOwnPtr<Painter> Painter::create(Context& context, NonnullRefPtr<Canvas> 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<float>(), color, top_left_radius, top_right_radius, bottom_left_radius, bottom_right_radius);
|
||||
fill_rect_with_rounded_corners(rect.to_type<float>(), 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<Gfx::AffineTransform> affine_transform)
|
||||
void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, Gfx::FloatRect const& src_rect, float opacity, Optional<Gfx::AffineTransform> 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<Gfx::AffineTransform> 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);
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
Painter(Context&, NonnullRefPtr<Canvas>);
|
||||
~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<Gfx::AffineTransform> affine_transform = {});
|
||||
void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional<Gfx::AffineTransform> affine_transform = {});
|
||||
void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, Gfx::FloatRect const& src_rect, float opacity = 1.0f, Optional<Gfx::AffineTransform> 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<Gfx::AffineTransform> 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<Gfx::AffineTransform> 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();
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <LibAccelGfx/GlyphAtlas.h>
|
||||
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
|
||||
#include <LibWeb/Painting/PaintingCommandExecutorGPU.h>
|
||||
|
||||
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<BorderRadiusCornerClipper>();
|
||||
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<float>(top_left.horizontal_radius), static_cast<float>(top_left.vertical_radius) },
|
||||
{ static_cast<float>(top_right.horizontal_radius), static_cast<float>(top_right.vertical_radius) },
|
||||
{ static_cast<float>(bottom_right.horizontal_radius), static_cast<float>(bottom_right.vertical_radius) },
|
||||
{ static_cast<float>(bottom_left.horizontal_radius), static_cast<float>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,10 +71,25 @@ private:
|
|||
int stacking_context_depth { 0 };
|
||||
};
|
||||
|
||||
struct BorderRadiusCornerClipper {
|
||||
RefPtr<AccelGfx::Canvas> 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<StackingContext> m_stacking_contexts;
|
||||
Vector<OwnPtr<BorderRadiusCornerClipper>> m_corner_clippers;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue