From 790daa475478aaf77bd064ddfa23f61529d1ef29 Mon Sep 17 00:00:00 2001 From: MacDue Date: Mon, 10 Apr 2023 22:02:16 +0100 Subject: [PATCH] LibGfx: Optimize Painter::draw_scaled_bitmap_with_transform() This now applies clipping to the destination bounding box before painting, which cuts out a load of clipped computation and allows using the faster set_physical_pixel() method. Alongside this it also combines the source_transform and inverse_transform outside the hot loop (which might cut things down a little). The `destination_quad.contains()` check is also removed in favour of just checking if the mapped point is inside the source rect, which takes less work to compute than checking the bounding box. This takes this method down from 98% of the time to 10% of the time when painting Google Street View (with no obvious issues). --- Userland/Libraries/LibGfx/Painter.cpp | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index c0f5302e26..b5be252aa0 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -2519,11 +2519,11 @@ void Painter::draw_scaled_bitmap_with_transform(IntRect const& dst_rect, Bitmap } else { // The painter has an affine transform, we have to draw through it! - // FIXME: This is *super* inefficient. + // FIXME: This is kinda inefficient. // What we currently do, roughly: // - Map the destination rect through the context's transform. // - Compute the bounding rect of the destination quad. - // - For each point in the computed bounding rect, reverse-map it to a point in the source image. + // - For each point in the clipped bounding rect, reverse-map it to a point in the source image. // - Sample the source image at the computed point. // - Set or blend (depending on alpha values) one pixel in the canvas. // - Loop. @@ -2536,31 +2536,31 @@ void Painter::draw_scaled_bitmap_with_transform(IntRect const& dst_rect, Bitmap auto destination_quad = transform.map_to_quad(dst_rect.to_type()); auto destination_bounding_rect = destination_quad.bounding_rect().to_rounded(); + auto source_rect = enclosing_int_rect(src_rect).intersected(bitmap.rect()); Gfx::AffineTransform source_transform; source_transform.translate(src_rect.x(), src_rect.y()); source_transform.scale(src_rect.width() / dst_rect.width(), src_rect.height() / dst_rect.height()); source_transform.translate(-dst_rect.x(), -dst_rect.y()); - for (int y = destination_bounding_rect.y(); y <= destination_bounding_rect.bottom(); ++y) { - for (int x = destination_bounding_rect.x(); x <= destination_bounding_rect.right(); ++x) { - auto destination_point = Gfx::IntPoint { x, y }; - if (!clip_rect().contains(destination_point)) - continue; - if (!destination_quad.contains(destination_point.to_type())) - continue; - auto source_point = source_transform.map(inverse_transform->map(destination_point)).to_rounded(); - if (!bitmap.rect().contains(source_point)) + auto translated_dest_rect = destination_bounding_rect.translated(translation()); + auto clipped_bounding_rect = translated_dest_rect.intersected(clip_rect()); + if (clipped_bounding_rect.is_empty()) + return; + + auto sample_transform = source_transform.multiply(*inverse_transform); + auto start_offset = destination_bounding_rect.location() + (clipped_bounding_rect.location() - translated_dest_rect.location()); + for (int y = 0; y < clipped_bounding_rect.height(); ++y) { + for (int x = 0; x < clipped_bounding_rect.width(); ++x) { + auto point = Gfx::IntPoint { x, y }; + auto sample_point = point + start_offset; + auto source_point = sample_transform.map(sample_point).to_rounded(); + if (!source_rect.contains(source_point)) continue; auto source_color = bitmap.get_pixel(source_point); if (source_color.alpha() == 0) continue; - if (source_color.alpha() == 255) { - set_pixel(destination_point, source_color); - continue; - } - auto dst_color = target()->get_pixel(destination_point); - set_pixel(destination_point, dst_color.blend(source_color)); + set_physical_pixel(point + clipped_bounding_rect.location(), source_color, true); } } }