mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibGfx/TIFF: Add support for images with UnassociatedAlpha
UnassociatedAlpha is the one used by GIMP when generating TIFF images with transparency. Support is added for Grayscale and RGB images as it's the two that we support right now but managing transparency should be really straightforward for other types as well.
This commit is contained in:
parent
796ece7b35
commit
2cfca633ca
Notes:
sideshowbarker
2024-07-17 03:35:16 +09:00
Author: https://github.com/LucasChollet Commit: https://github.com/SerenityOS/serenity/commit/2cfca633ca Pull-request: https://github.com/SerenityOS/serenity/pull/22375 Reviewed-by: https://github.com/nico ✅
4 changed files with 58 additions and 9 deletions
|
@ -465,6 +465,30 @@ TEST_CASE(test_tiff_grayscale)
|
|||
EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color(130, 130, 130));
|
||||
}
|
||||
|
||||
TEST_CASE(test_tiff_grayscale_alpha)
|
||||
{
|
||||
auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/grayscale_alpha.tiff"sv)));
|
||||
EXPECT(Gfx::TIFFImageDecoderPlugin::sniff(file->bytes()));
|
||||
auto plugin_decoder = TRY_OR_FAIL(Gfx::TIFFImageDecoderPlugin::create(file->bytes()));
|
||||
|
||||
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 400, 300 }));
|
||||
|
||||
EXPECT_EQ(frame.image->get_pixel(0, 0).alpha(), 0);
|
||||
EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color(130, 130, 130));
|
||||
}
|
||||
|
||||
TEST_CASE(test_tiff_rgb_alpha)
|
||||
{
|
||||
auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/rgb_alpha.tiff"sv)));
|
||||
EXPECT(Gfx::TIFFImageDecoderPlugin::sniff(file->bytes()));
|
||||
auto plugin_decoder = TRY_OR_FAIL(Gfx::TIFFImageDecoderPlugin::create(file->bytes()));
|
||||
|
||||
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 400, 300 }));
|
||||
|
||||
EXPECT_EQ(frame.image->get_pixel(0, 0).alpha(), 0);
|
||||
EXPECT_EQ(frame.image->get_pixel(60, 75), Gfx::Color::NamedColor::Red);
|
||||
}
|
||||
|
||||
TEST_CASE(test_tiff_16_bits)
|
||||
{
|
||||
auto file = MUST(Core::MappedFile::map(TEST_INPUT("tiff/16_bits.tiff"sv)));
|
||||
|
|
BIN
Tests/LibGfx/test-inputs/tiff/grayscale_alpha.tiff
Normal file
BIN
Tests/LibGfx/test-inputs/tiff/grayscale_alpha.tiff
Normal file
Binary file not shown.
BIN
Tests/LibGfx/test-inputs/tiff/rgb_alpha.tiff
Normal file
BIN
Tests/LibGfx/test-inputs/tiff/rgb_alpha.tiff
Normal file
Binary file not shown.
|
@ -100,6 +100,18 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
Optional<u8> alpha_channel_index() const
|
||||
{
|
||||
if (m_metadata.extra_samples().has_value()) {
|
||||
auto const extra_samples = m_metadata.extra_samples().value();
|
||||
for (u8 i = 0; i < extra_samples.size(); ++i) {
|
||||
if (extra_samples[i] == ExtraSample::UnassociatedAlpha)
|
||||
return i + samples_for_photometric_interpretation();
|
||||
}
|
||||
}
|
||||
return OptionalNone {};
|
||||
}
|
||||
|
||||
ErrorOr<Color> read_color(BigEndianInputBitStream& stream)
|
||||
{
|
||||
auto bits_per_sample = *m_metadata.bits_per_sample();
|
||||
|
@ -107,19 +119,32 @@ private:
|
|||
// Section 7: Additional Baseline TIFF Requirements
|
||||
// Some TIFF files may have more components per pixel than you think. A Baseline TIFF reader must skip over
|
||||
// them gracefully, using the values of the SamplesPerPixel and BitsPerSample fields.
|
||||
auto discard_unknown_channels = [&]() -> ErrorOr<void> {
|
||||
auto const unknown_channels = *m_metadata.samples_per_pixel() - samples_for_photometric_interpretation();
|
||||
for (u8 i = bits_per_sample.size() - unknown_channels; i < bits_per_sample.size(); ++i)
|
||||
TRY(read_component(stream, bits_per_sample[i]));
|
||||
return {};
|
||||
auto manage_extra_channels = [&]() -> ErrorOr<u8> {
|
||||
// Both unknown and alpha channels are considered as extra channels, so let's iterate over
|
||||
// them, conserve the alpha value (if any) and discard everything else.
|
||||
|
||||
auto const number_base_channels = samples_for_photometric_interpretation();
|
||||
auto const alpha_index = alpha_channel_index();
|
||||
|
||||
Optional<u8> alpha {};
|
||||
|
||||
for (u8 i = number_base_channels; i < bits_per_sample.size(); ++i) {
|
||||
if (alpha_index == i)
|
||||
alpha = TRY(read_component(stream, bits_per_sample[i]));
|
||||
else
|
||||
TRY(read_component(stream, bits_per_sample[i]));
|
||||
}
|
||||
|
||||
return alpha.value_or(NumericLimits<u8>::max());
|
||||
};
|
||||
|
||||
if (m_metadata.photometric_interpretation() == PhotometricInterpretation::RGB) {
|
||||
auto const first_component = TRY(read_component(stream, bits_per_sample[0]));
|
||||
auto const second_component = TRY(read_component(stream, bits_per_sample[1]));
|
||||
auto const third_component = TRY(read_component(stream, bits_per_sample[2]));
|
||||
TRY(discard_unknown_channels());
|
||||
return Color(first_component, second_component, third_component);
|
||||
|
||||
auto const alpha = TRY(manage_extra_channels());
|
||||
return Color(first_component, second_component, third_component, alpha);
|
||||
}
|
||||
|
||||
if (*m_metadata.photometric_interpretation() == PhotometricInterpretation::WhiteIsZero
|
||||
|
@ -129,8 +154,8 @@ private:
|
|||
if (m_metadata.photometric_interpretation() == PhotometricInterpretation::WhiteIsZero)
|
||||
luminosity = ~luminosity;
|
||||
|
||||
TRY(discard_unknown_channels());
|
||||
return Color(luminosity, luminosity, luminosity);
|
||||
auto const alpha = TRY(manage_extra_channels());
|
||||
return Color(luminosity, luminosity, luminosity, alpha);
|
||||
}
|
||||
|
||||
return Error::from_string_literal("Unsupported value for PhotometricInterpretation");
|
||||
|
|
Loading…
Reference in a new issue