diff --git a/Tests/LibGfx/CMakeLists.txt b/Tests/LibGfx/CMakeLists.txt index b66e75844cf..8afe85836c6 100644 --- a/Tests/LibGfx/CMakeLists.txt +++ b/Tests/LibGfx/CMakeLists.txt @@ -6,6 +6,7 @@ set(TEST_SOURCES TestGfxBitmap.cpp TestICCProfile.cpp TestImageDecoder.cpp + TestPainter.cpp TestParseISOBMFF.cpp TestRect.cpp TestScalingFunctions.cpp diff --git a/Tests/LibGfx/TestPainter.cpp b/Tests/LibGfx/TestPainter.cpp new file mode 100644 index 00000000000..ba3aa28c4de --- /dev/null +++ b/Tests/LibGfx/TestPainter.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include + +TEST_CASE(draw_scaled_bitmap_with_transform) +{ + auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { 40, 30 })); + bitmap->fill(Gfx::Color::White); + Gfx::Painter painter(bitmap); + + auto source_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { 1, 1 })); + source_bitmap->fill(Gfx::Color::Black); + + auto dest_rect = source_bitmap->rect(); + auto source_rect = source_bitmap->rect().to_rounded(); + + // Identity transform: Lower left pixel is black, rest stays white. + Gfx::AffineTransform transform; + painter.draw_scaled_bitmap_with_transform(dest_rect, source_bitmap, source_rect, transform); + for (int y = 0; y < bitmap->height(); ++y) { + for (int x = 0; x < bitmap->width(); ++x) { + if (x == 0 && y == 0) + EXPECT_EQ(bitmap->get_pixel(x, y), Color::Black); + else + EXPECT_EQ(bitmap->get_pixel(x, y), Color::White); + } + } + + // Scale up 1x1 source bitmap 10x in x and 5x in y and paint at 10, 20. Should fill that rect: + bitmap->fill(Gfx::Color::White); + transform = transform.translate(10, 20).scale(10, 5); + painter.draw_scaled_bitmap_with_transform(dest_rect, source_bitmap, source_rect, transform); + for (int y = 0; y < bitmap->height(); ++y) { + for (int x = 0; x < bitmap->width(); ++x) { + dbgln("{} {}: {}", x, y, bitmap->get_pixel(x, y)); + if (x >= 10 && x < 10 + 10 && y >= 20 && y < 20 + 5) + EXPECT_EQ(bitmap->get_pixel(x, y), Color::Black); + else + EXPECT_EQ(bitmap->get_pixel(x, y), Color::White); + } + } +} diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index da1caf9139e..62044519cca 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -2489,7 +2489,10 @@ void Painter::draw_scaled_bitmap_with_transform(IntRect const& dst_rect, Bitmap 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(); + + // AffineTransform::map(IntPoint) rounds internally, which is wrong here. So explicitly call the FloatPoint version, and then truncate the result. + auto source_point = Gfx::IntPoint { sample_transform.map(Gfx::FloatPoint { sample_point }) }; + if (!source_rect.contains(source_point)) continue; auto source_color = bitmap.get_pixel(source_point);