diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 14947b39bc2..b29746fe8c0 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -768,6 +769,18 @@ TEST_CASE(test_png) TRY_OR_FAIL(expect_single_frame(*plugin_decoder)); } +TEST_CASE(test_exif) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("png/exif.png"sv))); + EXPECT(Gfx::PNGImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::PNGImageDecoderPlugin::create(file->bytes())); + + TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 100, 200 })); + EXPECT(plugin_decoder->metadata().has_value()); + auto const& exif_metadata = static_cast(plugin_decoder->metadata().value()); + EXPECT_EQ(*exif_metadata.orientation(), Gfx::TIFF::Orientation::Rotate90Clockwise); +} + TEST_CASE(test_png_malformed_frame) { Array test_inputs = { diff --git a/Tests/LibGfx/test-inputs/png/exif.png b/Tests/LibGfx/test-inputs/png/exif.png new file mode 100644 index 00000000000..261d5f4c120 Binary files /dev/null and b/Tests/LibGfx/test-inputs/png/exif.png differ diff --git a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp index cb40ceaba8f..a8c899d811b 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include namespace Gfx { @@ -198,6 +200,8 @@ struct PNGLoadingContext { Optional decompressed_icc_profile; Optional sRGB_rendering_intent; + OwnPtr exif_metadata; + Checked compute_row_size_for_width(int width) { Checked row_size = width; @@ -1168,6 +1172,12 @@ static ErrorOr process_fdAT(ReadonlyBytes data, PNGLoadingContext& context return {}; } +static ErrorOr process_eXIf(ReadonlyBytes bytes, PNGLoadingContext& context) +{ + context.exif_metadata = TRY(TIFFImageDecoderPlugin::read_exif_metadata(bytes)); + return {}; +} + static void process_IEND(ReadonlyBytes, PNGLoadingContext& context) { // https://www.w3.org/TR/png/#11IEND @@ -1235,6 +1245,8 @@ static ErrorOr process_chunk(Streamer& streamer, PNGLoadingContext& contex return process_fcTL(chunk_data, context); if (chunk_type == "fdAT"sv) return process_fdAT(chunk_data, context); + if (chunk_type == "eXIf"sv) + return process_eXIf(chunk_data, context); if (chunk_type == "IEND"sv) process_IEND(chunk_data, context); return {}; @@ -1438,6 +1450,13 @@ ErrorOr PNGImageDecoderPlugin::frame(size_t index, Optiona return descriptor; } +Optional PNGImageDecoderPlugin::metadata() +{ + if (m_context->exif_metadata) + return *m_context->exif_metadata; + return OptionalNone {}; +} + ErrorOr> PNGImageDecoderPlugin::icc_data() { if (!decode_png_chunks(*m_context)) diff --git a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.h b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.h index d052e4344b7..b3f0c0d1aae 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.h +++ b/Userland/Libraries/LibGfx/ImageFormats/PNGLoader.h @@ -27,6 +27,7 @@ public: virtual size_t frame_count() override; virtual size_t first_animated_frame_index() override; virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; + virtual Optional metadata() override; virtual ErrorOr> icc_data() override; static void unfilter_scanline(PNG::FilterType filter, Bytes scanline_data, ReadonlyBytes previous_scanlines_data, u8 bytes_per_complete_pixel);