Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Tests/LibGfx/TestBilevelImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibGfx/Bitmap.h>
#include <LibGfx/ICC/TagTypes.h>
#include <LibGfx/ICC/WellKnownProfiles.h>
#include <LibGfx/ImageFormats/BilevelImage.h>
#include <LibTest/TestCase.h>

Expand Down Expand Up @@ -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<void> 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<void> {
auto uncompressed = round_to<u8>(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<f32>(s) + 0.5f) / number_of_states;
// There are only (number_of_states - 1) states of luminosity.
TRY(test_luminosity(value, static_cast<f32>(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));
}
6 changes: 5 additions & 1 deletion Userland/Libraries/LibGfx/ImageFormats/BilevelImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,13 @@ ErrorOr<NonnullRefPtr<BilevelImage>> 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<u8>((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);
}
}
Expand Down
Loading