From 09f4032eeb786a391c150f69538773434d49e09a Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Wed, 22 May 2024 22:45:22 -0400 Subject: [PATCH] LibGfx/PNG: Read metadata from the eXIf chunk The test image comes from this WPT test: http://wpt.live/png/exif-chunk.html --- Tests/LibGfx/TestImageDecoder.cpp | 13 ++++++++++++ Tests/LibGfx/test-inputs/png/exif.png | Bin 0 -> 329 bytes .../LibGfx/ImageFormats/PNGLoader.cpp | 19 ++++++++++++++++++ .../Libraries/LibGfx/ImageFormats/PNGLoader.h | 1 + 4 files changed, 33 insertions(+) create mode 100644 Tests/LibGfx/test-inputs/png/exif.png 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 0000000000000000000000000000000000000000..261d5f4c120d5bec05faab969b6476aa6f512989 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^DL{OJgBeI_KHBmbNbv{wgt!9f|3CnuKn%uz_RT;x zYe|q_Fj)NeHojn>0B39ke%u2;uumf=j{bY zUIqgW21A7(*L%1sa%`D>^|Zh3Hc$}YU}0)>z)eN2`^-?8E{ #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);