mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
LibWeb+LibGfx: Move the blit image through 2D transfrom to Gfx::Painter
Even though this code is obnoxiously slow, it still belongs in LibGfx and should not be hidden away in LibWeb's CanvasRenderingContext2D.
This commit is contained in:
parent
37ea6de772
commit
b52165c5d7
Notes:
sideshowbarker
2024-07-17 06:41:05 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/b52165c5d7
@ -31,6 +31,7 @@
|
||||
#include <LibGfx/FillPathImplementation.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/TextDirection.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <stdio.h>
|
||||
@ -2423,4 +2424,60 @@ void Painter::draw_text_run(FloatPoint const& baseline_start, Utf8View const& st
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::draw_scaled_bitmap_with_transform(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::FloatRect const& src_rect, Gfx::AffineTransform const& transform, float opacity, Gfx::Painter::ScalingMode scaling_mode)
|
||||
{
|
||||
if (transform.is_identity_or_translation()) {
|
||||
translate(transform.e(), transform.f());
|
||||
draw_scaled_bitmap(dst_rect, bitmap, src_rect, opacity, scaling_mode);
|
||||
translate(-transform.e(), -transform.f());
|
||||
} else {
|
||||
// The painter has an affine transform, we have to draw through it!
|
||||
|
||||
// FIXME: This is *super* 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.
|
||||
// - Sample the source image at the computed point.
|
||||
// - Set or blend (depending on alpha values) one pixel in the canvas.
|
||||
// - Loop.
|
||||
|
||||
// FIXME: Painter should have an affine transform as part of its state and handle all of this instead.
|
||||
|
||||
auto inverse_transform = transform.inverse();
|
||||
if (!inverse_transform.has_value())
|
||||
return;
|
||||
|
||||
auto destination_quad = transform.map_to_quad(dst_rect.to_type<float>());
|
||||
auto destination_bounding_rect = destination_quad.bounding_rect().to_rounded<int>();
|
||||
|
||||
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<float>()))
|
||||
continue;
|
||||
auto source_point = source_transform.map(inverse_transform->map(destination_point)).to_rounded<int>();
|
||||
if (!bitmap.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
void draw_bitmap(IntPoint const&, GlyphBitmap const&, Color = Color());
|
||||
void draw_scaled_bitmap(IntRect const& dst_rect, Gfx::Bitmap const&, IntRect const& src_rect, float opacity = 1.0f, ScalingMode = ScalingMode::NearestNeighbor);
|
||||
void draw_scaled_bitmap(IntRect const& dst_rect, Gfx::Bitmap const&, FloatRect const& src_rect, float opacity = 1.0f, ScalingMode = ScalingMode::NearestNeighbor);
|
||||
void draw_scaled_bitmap_with_transform(IntRect const& dst_rect, Gfx::Bitmap const&, FloatRect const& src_rect, Gfx::AffineTransform const&, float opacity = 1.0f, ScalingMode = ScalingMode::NearestNeighbor);
|
||||
void draw_triangle(IntPoint const&, IntPoint const&, IntPoint const&, Color);
|
||||
void draw_triangle(IntPoint const& offset, Span<IntPoint const>, Color);
|
||||
void draw_ellipse_intersecting(IntRect const&, Color, int thickness = 1);
|
||||
|
@ -155,60 +155,7 @@ DOM::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasImage
|
||||
if (!painter)
|
||||
return {};
|
||||
|
||||
auto& drawing_state = this->drawing_state();
|
||||
|
||||
if (drawing_state.transform.is_identity_or_translation()) {
|
||||
painter->translate(drawing_state.transform.e(), drawing_state.transform.f());
|
||||
painter->draw_scaled_bitmap(destination_rect.to_rounded<int>(), *bitmap, source_rect, 1.0f, Gfx::Painter::ScalingMode::BilinearBlend);
|
||||
painter->translate(-drawing_state.transform.e(), -drawing_state.transform.f());
|
||||
} else {
|
||||
// The context has an affine transform, we have to draw through it!
|
||||
|
||||
// FIXME: This is *super* 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.
|
||||
// - Sample the source image at the computed point.
|
||||
// - Set or blend (depending on alpha values) one pixel in the canvas.
|
||||
// - Loop.
|
||||
|
||||
// FIXME: Gfx::Painter should have an affine transform as part of its state and handle all of this instead.
|
||||
|
||||
auto inverse_transform = drawing_state.transform.inverse();
|
||||
if (!inverse_transform.has_value())
|
||||
return {};
|
||||
|
||||
auto destination_quad = drawing_state.transform.map_to_quad(destination_rect);
|
||||
auto destination_bounding_rect = destination_quad.bounding_rect().to_rounded<int>();
|
||||
|
||||
Gfx::AffineTransform source_transform;
|
||||
source_transform.translate(source_x, source_y);
|
||||
source_transform.scale(source_width / destination_width, source_height / destination_height);
|
||||
source_transform.translate(-destination_x, -destination_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 (!painter->clip_rect().contains(destination_point))
|
||||
continue;
|
||||
if (!destination_quad.contains(destination_point.to_type<float>()))
|
||||
continue;
|
||||
auto source_point = source_transform.map(inverse_transform->map(destination_point)).to_rounded<int>();
|
||||
if (!bitmap->rect().contains(source_point))
|
||||
continue;
|
||||
auto source_color = bitmap->get_pixel(source_point);
|
||||
if (source_color.alpha() == 0)
|
||||
continue;
|
||||
if (source_color.alpha() == 255) {
|
||||
painter->set_pixel(destination_point, source_color);
|
||||
continue;
|
||||
}
|
||||
auto dst_color = painter->target()->get_pixel(destination_point);
|
||||
painter->set_pixel(destination_point, dst_color.blend(source_color));
|
||||
}
|
||||
}
|
||||
}
|
||||
painter->draw_scaled_bitmap_with_transform(destination_rect.to_rounded<int>(), *bitmap, source_rect, drawing_state().transform, 1.0f, Gfx::Painter::ScalingMode::BilinearBlend);
|
||||
|
||||
// 7. If image is not origin-clean, then set the CanvasRenderingContext2D's origin-clean flag to false.
|
||||
if (image_is_not_origin_clean(image))
|
||||
|
Loading…
Reference in New Issue
Block a user