diff --git a/Tests/LibGfx/TestBilevelImage.cpp b/Tests/LibGfx/TestBilevelImage.cpp index f7dce5fa0f5f23..69c82b90b1a744 100644 --- a/Tests/LibGfx/TestBilevelImage.cpp +++ b/Tests/LibGfx/TestBilevelImage.cpp @@ -4,6 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include @@ -60,3 +63,45 @@ TEST_CASE(get_bits_over_8bits) EXPECT_EQ(bilevel->get_bits(8, 0, 8), 0xFE); EXPECT_EQ(bilevel->get_bits(12, 0, 4), 0xE); } + +static ErrorOr test_bayer_dither(Gfx::DitheringAlgorithm algorithm, u32 size) +{ + auto srgb_curve = TRY(Gfx::ICC::sRGB_curve()); + auto bitmap = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { size, size })); + auto number_of_states = size * size + 1; + + auto test_luminosity = [&](f32 input_luminosity, f32 output_luminosity) -> ErrorOr { + auto uncompressed = round_to(srgb_curve->evaluate_inverse(input_luminosity) * 255); + bitmap->fill(Gfx::Color(uncompressed, uncompressed, uncompressed)); + auto bilevel = TRY(Gfx::BilevelImage::create_from_bitmap(bitmap, algorithm)); + double average = 0; + for (u32 y = 0; y < bilevel->height(); ++y) { + for (u32 x = 0; x < bilevel->width(); ++x) + average += bilevel->get_bit(x, y) ? 0 : 1; + } + + EXPECT_APPROXIMATE(average / (size * size), output_luminosity); + return {}; + }; + + // Test full black and full white. + TRY(test_luminosity(0, 0)); + TRY(test_luminosity(1, 1)); + + // Test all states at half the range. + for (u32 s = 0; s < number_of_states; ++s) { + // We test all states in the middle of there range. + auto value = (static_cast(s) + 0.5f) / number_of_states; + // There are only (number_of_states - 1) states of luminosity. + TRY(test_luminosity(value, static_cast(s) / (number_of_states - 1))); + } + return {}; +} + +TEST_CASE(bayer_dither) +{ + + TRY_OR_FAIL(test_bayer_dither(Gfx::DitheringAlgorithm::Bayer2x2, 2)); + TRY_OR_FAIL(test_bayer_dither(Gfx::DitheringAlgorithm::Bayer4x4, 4)); + TRY_OR_FAIL(test_bayer_dither(Gfx::DitheringAlgorithm::Bayer8x8, 8)); +} diff --git a/Userland/Libraries/LibGfx/ImageFormats/BilevelImage.cpp b/Userland/Libraries/LibGfx/ImageFormats/BilevelImage.cpp index 4884c34c1fe594..b23c605f920eff 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/BilevelImage.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/BilevelImage.cpp @@ -312,9 +312,13 @@ ErrorOr> BilevelImage::create_from_bitmap(Gfx::Bitma VERIFY(is_power_of_two(n)); auto mask = n - 1; + // A bayer matrix of dimension N has N x N +1 different states. First one + // is an all black matrix, and then one more for each element turning white. + u32 number_of_states = n * n + 1; + for (int y = 0, i = 0; y < bitmap.height(); ++y) { for (int x = 0; x < bitmap.width(); ++x, ++i) { - u8 threshold = (bayer_matrix[(y & mask) * n + (x & mask)] * 255) / ((n * n) - 1); + u8 threshold = round_to((1 + bayer_matrix[(y & mask) * n + (x & mask)]) * 255.f / number_of_states); bilevel_image->set_bit(x, y, gray_bitmap[i] > threshold ? 0 : 1); } }