mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:37:43 +00:00
LibAccelGfx+LibWeb: Support non-translation transforms in GPU painter
This change introduces GPU painter support for rotate(), skew(), scale(), and other transformations that could be applied to stacking context.
This commit is contained in:
parent
45d81dceed
commit
c28f6828c9
4 changed files with 52 additions and 25 deletions
|
@ -586,24 +586,44 @@ void Painter::flush(Gfx::Bitmap& bitmap)
|
||||||
GL::read_pixels({ 0, 0, bitmap.width(), bitmap.height() }, bitmap);
|
GL::read_pixels({ 0, 0, bitmap.width(), bitmap.height() }, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Painter::blit_canvas(Gfx::IntRect const& dst_rect, Canvas const& canvas, float opacity)
|
void Painter::blit_canvas(Gfx::IntRect const& dst_rect, Canvas const& canvas, float opacity, Optional<Gfx::AffineTransform> affine_transform)
|
||||||
{
|
{
|
||||||
blit_canvas(dst_rect.to_type<float>(), canvas, opacity);
|
blit_canvas(dst_rect.to_type<float>(), canvas, opacity, move(affine_transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, float opacity)
|
void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, float opacity, Optional<Gfx::AffineTransform> affine_transform)
|
||||||
{
|
{
|
||||||
auto texture = GL::Texture(canvas.framebuffer().texture);
|
auto texture = GL::Texture(canvas.framebuffer().texture);
|
||||||
blit_scaled_texture(dst_rect, texture, { { 0, 0 }, canvas.size() }, Painter::ScalingMode::NearestNeighbor, opacity);
|
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)
|
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)
|
||||||
{
|
{
|
||||||
bind_target_canvas();
|
bind_target_canvas();
|
||||||
|
|
||||||
m_blit_program.use();
|
m_blit_program.use();
|
||||||
|
|
||||||
auto dst_rect_in_clip_space = to_clip_space(transform().map(dst_rect));
|
auto dst_rect_rotated = dst_rect;
|
||||||
|
Array<Gfx::FloatPoint, 4> dst_rect_vertices = {
|
||||||
|
dst_rect_rotated.top_left(),
|
||||||
|
dst_rect_rotated.bottom_left(),
|
||||||
|
dst_rect_rotated.bottom_right(),
|
||||||
|
dst_rect_rotated.top_right(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (affine_transform.has_value()) {
|
||||||
|
for (auto& point : dst_rect_vertices)
|
||||||
|
point = affine_transform->map(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const viewport_width = static_cast<float>(m_target_canvas->size().width());
|
||||||
|
auto const viewport_height = static_cast<float>(m_target_canvas->size().height());
|
||||||
|
for (auto& point : dst_rect_vertices) {
|
||||||
|
point = transform().map(point);
|
||||||
|
point.set_x(2.0f * point.x() / viewport_width - 1.0f);
|
||||||
|
point.set_y(-1.0f + 2.0f * point.y() / viewport_height);
|
||||||
|
}
|
||||||
|
|
||||||
auto src_rect_in_texture_space = to_texture_space(src_rect, *texture.size);
|
auto src_rect_in_texture_space = to_texture_space(src_rect, *texture.size);
|
||||||
|
|
||||||
Vector<GLfloat> vertices;
|
Vector<GLfloat> vertices;
|
||||||
|
@ -616,10 +636,10 @@ void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture co
|
||||||
vertices.append(s.y());
|
vertices.append(s.y());
|
||||||
};
|
};
|
||||||
|
|
||||||
add_vertex(dst_rect_in_clip_space.top_left(), src_rect_in_texture_space.top_left());
|
add_vertex(dst_rect_vertices[0], 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_vertices[1], 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_vertices[2], src_rect_in_texture_space.bottom_right());
|
||||||
add_vertex(dst_rect_in_clip_space.top_right(), src_rect_in_texture_space.top_right());
|
add_vertex(dst_rect_vertices[3], src_rect_in_texture_space.top_right());
|
||||||
|
|
||||||
auto vbo = GL::create_buffer();
|
auto vbo = GL::create_buffer();
|
||||||
GL::upload_to_buffer(vbo, vertices);
|
GL::upload_to_buffer(vbo, vertices);
|
||||||
|
|
|
@ -80,8 +80,8 @@ public:
|
||||||
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::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::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 blit_canvas(Gfx::IntRect const& dst_rect, Canvas const&, float opacity = 1.0f);
|
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);
|
void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f, Optional<Gfx::AffineTransform> affine_transform = {});
|
||||||
|
|
||||||
void update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>&);
|
void update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>&);
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ private:
|
||||||
[[nodiscard]] State& state() { return m_state_stack.last(); }
|
[[nodiscard]] State& state() { return m_state_stack.last(); }
|
||||||
[[nodiscard]] State const& state() const { 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);
|
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 bind_target_canvas();
|
void bind_target_canvas();
|
||||||
|
|
||||||
[[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const;
|
[[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const;
|
||||||
|
|
|
@ -18,7 +18,8 @@ PaintingCommandExecutorGPU::PaintingCommandExecutorGPU(Gfx::Bitmap& bitmap)
|
||||||
m_stacking_contexts.append({ .canvas = canvas,
|
m_stacking_contexts.append({ .canvas = canvas,
|
||||||
.painter = move(painter),
|
.painter = move(painter),
|
||||||
.opacity = 1.0f,
|
.opacity = 1.0f,
|
||||||
.destination = {} });
|
.destination = {},
|
||||||
|
.transform = {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
PaintingCommandExecutorGPU::~PaintingCommandExecutorGPU()
|
PaintingCommandExecutorGPU::~PaintingCommandExecutorGPU()
|
||||||
|
@ -99,29 +100,34 @@ CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, b
|
||||||
painter().translate(-translation);
|
painter().translate(-translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto affine_transform = Gfx::extract_2d_affine_transform(transform.matrix);
|
auto stacking_context_transform = Gfx::extract_2d_affine_transform(transform.matrix);
|
||||||
|
|
||||||
if (opacity < 1) {
|
Gfx::AffineTransform inverse_origin_translation;
|
||||||
|
inverse_origin_translation.translate(-transform.origin);
|
||||||
|
Gfx::AffineTransform origin_translation;
|
||||||
|
origin_translation.translate(transform.origin);
|
||||||
|
|
||||||
|
Gfx::AffineTransform final_transform = origin_translation;
|
||||||
|
final_transform.multiply(stacking_context_transform);
|
||||||
|
final_transform.multiply(inverse_origin_translation);
|
||||||
|
if (opacity < 1 || !stacking_context_transform.is_identity_or_translation()) {
|
||||||
auto painter = AccelGfx::Painter::create();
|
auto painter = AccelGfx::Painter::create();
|
||||||
auto canvas = AccelGfx::Canvas::create(source_paintable_rect.size());
|
auto canvas = AccelGfx::Canvas::create(source_paintable_rect.size());
|
||||||
painter->set_target_canvas(canvas);
|
painter->set_target_canvas(canvas);
|
||||||
painter->translate(-source_paintable_rect.location().to_type<float>());
|
painter->translate(-source_paintable_rect.location().to_type<float>());
|
||||||
painter->clear(Color::Transparent);
|
painter->clear(Color::Transparent);
|
||||||
|
|
||||||
auto source_rect = source_paintable_rect.to_type<float>().translated(-transform.origin);
|
|
||||||
auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform.origin);
|
|
||||||
auto destination_rect = transformed_destination_rect.to_rounded<int>();
|
|
||||||
|
|
||||||
m_stacking_contexts.append({ .canvas = canvas,
|
m_stacking_contexts.append({ .canvas = canvas,
|
||||||
.painter = move(painter),
|
.painter = move(painter),
|
||||||
.opacity = opacity,
|
.opacity = opacity,
|
||||||
.destination = destination_rect });
|
.destination = source_paintable_rect,
|
||||||
|
.transform = final_transform });
|
||||||
} else {
|
} else {
|
||||||
painter().translate(affine_transform.translation() + post_transform_translation.to_type<float>());
|
painter().translate(stacking_context_transform.translation() + post_transform_translation.to_type<float>());
|
||||||
m_stacking_contexts.append({ .canvas = {},
|
m_stacking_contexts.append({ .canvas = {},
|
||||||
.painter = MaybeOwned(painter()),
|
.painter = MaybeOwned(painter()),
|
||||||
.opacity = opacity,
|
.opacity = opacity,
|
||||||
.destination = {} });
|
.destination = {},
|
||||||
|
.transform = final_transform });
|
||||||
}
|
}
|
||||||
return CommandResult::Continue;
|
return CommandResult::Continue;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +137,7 @@ CommandResult PaintingCommandExecutorGPU::pop_stacking_context()
|
||||||
auto stacking_context = m_stacking_contexts.take_last();
|
auto stacking_context = m_stacking_contexts.take_last();
|
||||||
VERIFY(stacking_context.stacking_context_depth == 0);
|
VERIFY(stacking_context.stacking_context_depth == 0);
|
||||||
if (stacking_context.painter.is_owned()) {
|
if (stacking_context.painter.is_owned()) {
|
||||||
painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity);
|
painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity, stacking_context.transform);
|
||||||
}
|
}
|
||||||
painter().restore();
|
painter().restore();
|
||||||
m_stacking_contexts.last().stacking_context_depth--;
|
m_stacking_contexts.last().stacking_context_depth--;
|
||||||
|
|
|
@ -67,6 +67,7 @@ private:
|
||||||
MaybeOwned<AccelGfx::Painter> painter;
|
MaybeOwned<AccelGfx::Painter> painter;
|
||||||
float opacity;
|
float opacity;
|
||||||
Gfx::IntRect destination;
|
Gfx::IntRect destination;
|
||||||
|
Gfx::AffineTransform transform;
|
||||||
int stacking_context_depth { 0 };
|
int stacking_context_depth { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue