Quellcode durchsuchen

LibGfx/PNG: Read metadata from the eXIf chunk

The test image comes from this WPT test:
http://wpt.live/png/exif-chunk.html
Lucas CHOLLET vor 1 Jahr
Ursprung
Commit
09f4032eeb

+ 13 - 0
Tests/LibGfx/TestImageDecoder.cpp

@@ -25,6 +25,7 @@
 #include <LibGfx/ImageFormats/QMArithmeticDecoder.h>
 #include <LibGfx/ImageFormats/TGALoader.h>
 #include <LibGfx/ImageFormats/TIFFLoader.h>
+#include <LibGfx/ImageFormats/TIFFMetadata.h>
 #include <LibGfx/ImageFormats/TinyVGLoader.h>
 #include <LibGfx/ImageFormats/WebPLoader.h>
 #include <LibTest/TestCase.h>
@@ -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<Gfx::ExifMetadata const&>(plugin_decoder->metadata().value());
+    EXPECT_EQ(*exif_metadata.orientation(), Gfx::TIFF::Orientation::Rotate90Clockwise);
+}
+
 TEST_CASE(test_png_malformed_frame)
 {
     Array test_inputs = {

BIN
Tests/LibGfx/test-inputs/png/exif.png


+ 19 - 0
Userland/Libraries/LibGfx/ImageFormats/PNGLoader.cpp

@@ -11,6 +11,8 @@
 #include <AK/Vector.h>
 #include <LibCompress/Zlib.h>
 #include <LibGfx/ImageFormats/PNGLoader.h>
+#include <LibGfx/ImageFormats/TIFFLoader.h>
+#include <LibGfx/ImageFormats/TIFFMetadata.h>
 #include <LibGfx/Painter.h>
 
 namespace Gfx {
@@ -198,6 +200,8 @@ struct PNGLoadingContext {
     Optional<ByteBuffer> decompressed_icc_profile;
     Optional<RenderingIntent> sRGB_rendering_intent;
 
+    OwnPtr<ExifMetadata> exif_metadata;
+
     Checked<int> compute_row_size_for_width(int width)
     {
         Checked<int> row_size = width;
@@ -1168,6 +1172,12 @@ static ErrorOr<void> process_fdAT(ReadonlyBytes data, PNGLoadingContext& context
     return {};
 }
 
+static ErrorOr<void> 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<void> 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<ImageFrameDescriptor> PNGImageDecoderPlugin::frame(size_t index, Optiona
     return descriptor;
 }
 
+Optional<Metadata const&> PNGImageDecoderPlugin::metadata()
+{
+    if (m_context->exif_metadata)
+        return *m_context->exif_metadata;
+    return OptionalNone {};
+}
+
 ErrorOr<Optional<ReadonlyBytes>> PNGImageDecoderPlugin::icc_data()
 {
     if (!decode_png_chunks(*m_context))

+ 1 - 0
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<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
+    virtual Optional<Metadata const&> metadata() override;
     virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
 
     static void unfilter_scanline(PNG::FilterType filter, Bytes scanline_data, ReadonlyBytes previous_scanlines_data, u8 bytes_per_complete_pixel);