mirror of
https://github.com/RGBCube/serenity
synced 2025-05-30 22:18:11 +00:00

BorderRadiusCornerClipper usage to clip border radius is specific to CPU painter so it should not be stored in painting commands. Also with this change bitmaps for corner sampling are allocated during painting commands replaying instead of commands recording.
372 lines
14 KiB
C++
372 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibAccelGfx/GlyphAtlas.h>
|
|
#include <LibWeb/Painting/PaintingCommandExecutorGPU.h>
|
|
|
|
namespace Web::Painting {
|
|
|
|
PaintingCommandExecutorGPU::PaintingCommandExecutorGPU(Gfx::Bitmap& bitmap)
|
|
: m_target_bitmap(bitmap)
|
|
{
|
|
auto painter = AccelGfx::Painter::create();
|
|
auto canvas = AccelGfx::Canvas::create(bitmap.size());
|
|
painter->set_target_canvas(canvas);
|
|
m_stacking_contexts.append({ .canvas = canvas,
|
|
.painter = move(painter),
|
|
.opacity = 1.0f,
|
|
.destination = {},
|
|
.transform = {} });
|
|
}
|
|
|
|
PaintingCommandExecutorGPU::~PaintingCommandExecutorGPU()
|
|
{
|
|
VERIFY(m_stacking_contexts.size() == 1);
|
|
painter().flush(m_target_bitmap);
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color)
|
|
{
|
|
painter().draw_glyph_run(glyph_run, color);
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Color const& color)
|
|
{
|
|
painter().fill_rect(rect, color);
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
static AccelGfx::Painter::ScalingMode to_accelgfx_scaling_mode(Gfx::Painter::ScalingMode scaling_mode)
|
|
{
|
|
switch (scaling_mode) {
|
|
case Gfx::Painter::ScalingMode::NearestNeighbor:
|
|
case Gfx::Painter::ScalingMode::BoxSampling:
|
|
case Gfx::Painter::ScalingMode::SmoothPixels:
|
|
case Gfx::Painter::ScalingMode::None:
|
|
return AccelGfx::Painter::ScalingMode::NearestNeighbor;
|
|
case Gfx::Painter::ScalingMode::BilinearBlend:
|
|
return AccelGfx::Painter::ScalingMode::Bilinear;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
|
|
{
|
|
painter().draw_scaled_bitmap(dst_rect, bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode));
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
|
|
{
|
|
painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode));
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::set_clip_rect(Gfx::IntRect const& rect)
|
|
{
|
|
painter().set_clip_rect(rect);
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::clear_clip_rect()
|
|
{
|
|
painter().clear_clip_rect();
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::set_font(Gfx::Font const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering, StackingContextTransform transform, Optional<StackingContextMask>)
|
|
{
|
|
if (source_paintable_rect.is_empty())
|
|
return CommandResult::SkipStackingContext;
|
|
|
|
m_stacking_contexts.last().stacking_context_depth++;
|
|
painter().save();
|
|
if (is_fixed_position) {
|
|
auto const& translation = painter().transform().translation();
|
|
painter().translate(-translation);
|
|
}
|
|
|
|
auto stacking_context_transform = Gfx::extract_2d_affine_transform(transform.matrix);
|
|
|
|
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 canvas = AccelGfx::Canvas::create(source_paintable_rect.size());
|
|
painter->set_target_canvas(canvas);
|
|
painter->translate(-source_paintable_rect.location().to_type<float>());
|
|
painter->clear(Color::Transparent);
|
|
m_stacking_contexts.append({ .canvas = canvas,
|
|
.painter = move(painter),
|
|
.opacity = opacity,
|
|
.destination = source_paintable_rect,
|
|
.transform = final_transform });
|
|
} else {
|
|
painter().translate(stacking_context_transform.translation() + post_transform_translation.to_type<float>());
|
|
m_stacking_contexts.append({ .canvas = {},
|
|
.painter = MaybeOwned(painter()),
|
|
.opacity = opacity,
|
|
.destination = {},
|
|
.transform = final_transform });
|
|
}
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::pop_stacking_context()
|
|
{
|
|
auto stacking_context = m_stacking_contexts.take_last();
|
|
VERIFY(stacking_context.stacking_context_depth == 0);
|
|
if (stacking_context.painter.is_owned()) {
|
|
painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity, stacking_context.transform);
|
|
}
|
|
painter().restore();
|
|
m_stacking_contexts.last().stacking_context_depth--;
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data)
|
|
{
|
|
painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length);
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_outer_box_shadow(PaintOuterBoxShadowParams const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_inner_box_shadow(PaintOuterBoxShadowParams const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color const& color, int fragment_baseline, Gfx::IntPoint const& draw_location)
|
|
{
|
|
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<float>());
|
|
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<float>(), *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;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Optional<Gfx::FloatPoint> const&)
|
|
{
|
|
painter().fill_rect_with_rounded_corners(
|
|
rect, color,
|
|
{ static_cast<float>(top_left_radius.horizontal_radius), static_cast<float>(top_left_radius.vertical_radius) },
|
|
{ static_cast<float>(top_right_radius.horizontal_radius), static_cast<float>(top_right_radius.vertical_radius) },
|
|
{ static_cast<float>(bottom_left_radius.horizontal_radius), static_cast<float>(bottom_left_radius.vertical_radius) },
|
|
{ static_cast<float>(bottom_right_radius.horizontal_radius), static_cast<float>(bottom_right_radius.vertical_radius) });
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule, Optional<Gfx::FloatPoint> const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const&, Gfx::Painter::WindingRule, float, Optional<Gfx::FloatPoint> const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::stroke_path_using_color(Gfx::Path const&, Color const&, float, Optional<Gfx::FloatPoint> const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::stroke_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const&, float, float, Optional<Gfx::FloatPoint> const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_ellipse(Gfx::IntRect const&, Color const&, int)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::fill_ellipse(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::BlendMode)
|
|
{
|
|
auto horizontal_radius = static_cast<float>(rect.width() / 2);
|
|
auto vertical_radius = static_cast<float>(rect.height() / 2);
|
|
painter().fill_rect_with_rounded_corners(
|
|
rect, color,
|
|
{ horizontal_radius, vertical_radius },
|
|
{ horizontal_radius, vertical_radius },
|
|
{ horizontal_radius, vertical_radius },
|
|
{ horizontal_radius, vertical_radius });
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_line(Color const& color, Gfx::IntPoint const& a, Gfx::IntPoint const& b, int thickness, Gfx::Painter::LineStyle, Color const&)
|
|
{
|
|
// FIXME: Pass line style and alternate color once AccelGfx::Painter supports it
|
|
painter().draw_line(a, b, thickness, color);
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_signed_distance_field(Gfx::IntRect const&, Color const&, Gfx::GrayscaleBitmap const&, float)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_progressbar(Gfx::IntRect const&, Gfx::IntRect const&, Palette const&, int, int, int, StringView const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_frame(Gfx::IntRect const&, Palette const&, Gfx::FrameStyle)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::apply_backdrop_filter(Gfx::IntRect const&, Web::CSS::ResolvedBackdropFilter const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_rect(Gfx::IntRect const&, Color const&, bool)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_radial_gradient(Gfx::IntRect const&, Web::Painting::RadialGradientData const&, Gfx::IntPoint const&, Gfx::IntSize const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_conic_gradient(Gfx::IntRect const&, Web::Painting::ConicGradientData const&, Gfx::IntPoint const&)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::draw_triangle_wave(Gfx::IntPoint const&, Gfx::IntPoint const&, Color const&, int, int)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::sample_under_corners(u32, CornerRadii const&, Gfx::IntRect const&, CornerClip)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::blit_corner_clipping(u32)
|
|
{
|
|
// FIXME
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
CommandResult PaintingCommandExecutorGPU::paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
|
|
{
|
|
// FIXME: Add support for corner radiuses
|
|
(void)corner_radii;
|
|
|
|
Gfx::IntRect top_border_rect = {
|
|
border_rect.x(),
|
|
border_rect.y(),
|
|
border_rect.width(),
|
|
borders_data.top.width
|
|
};
|
|
Gfx::IntRect right_border_rect = {
|
|
border_rect.x() + (border_rect.width() - borders_data.right.width),
|
|
border_rect.y(),
|
|
borders_data.right.width,
|
|
border_rect.height()
|
|
};
|
|
Gfx::IntRect bottom_border_rect = {
|
|
border_rect.x(),
|
|
border_rect.y() + (border_rect.height() - borders_data.bottom.width),
|
|
border_rect.width(),
|
|
borders_data.bottom.width
|
|
};
|
|
Gfx::IntRect left_border_rect = {
|
|
border_rect.x(),
|
|
border_rect.y(),
|
|
borders_data.left.width,
|
|
border_rect.height()
|
|
};
|
|
|
|
if (borders_data.top.width > 0)
|
|
painter().fill_rect(top_border_rect, borders_data.top.color);
|
|
if (borders_data.right.width > 0)
|
|
painter().fill_rect(right_border_rect, borders_data.right.color);
|
|
if (borders_data.bottom.width > 0)
|
|
painter().fill_rect(bottom_border_rect, borders_data.bottom.color);
|
|
if (borders_data.left.width > 0)
|
|
painter().fill_rect(left_border_rect, borders_data.left.color);
|
|
|
|
return CommandResult::Continue;
|
|
}
|
|
|
|
bool PaintingCommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
|
|
{
|
|
auto translation = painter().transform().translation().to_type<int>();
|
|
return !painter().clip_rect().intersects(rect.translated(translation));
|
|
}
|
|
|
|
void PaintingCommandExecutorGPU::prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
|
|
{
|
|
AccelGfx::GlyphAtlas::the().update(unique_glyphs);
|
|
}
|
|
|
|
void PaintingCommandExecutorGPU::update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>& immutable_bitmaps)
|
|
{
|
|
painter().update_immutable_bitmap_texture_cache(immutable_bitmaps);
|
|
}
|
|
|
|
}
|