From 6eb574a2b6910dd36c8d2092384eaf8b4258b192 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sun, 21 Jan 2024 15:25:00 -0500 Subject: [PATCH] LibGfx/JPEG: Expose the Exif metadata The Exif metadata is contained in the APP1 segment. We only need to call the TIFF decoder to get the metadata back :^). --- .../LibGfx/ImageFormats/JPEGLoader.cpp | 33 +++++++++++++++++++ .../LibGfx/ImageFormats/JPEGLoader.h | 3 ++ 2 files changed, 36 insertions(+) diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp index 390609c3a4..cf689edcd1 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include namespace Gfx { @@ -466,6 +468,8 @@ struct JPEGLoadingContext { Optional color_transform {}; + OwnPtr exif_metadata {}; + Optional icc_multi_chunk_state; Optional icc_data; }; @@ -1163,6 +1167,26 @@ static ErrorOr read_colour_encoding(JPEGStream& stream, [[maybe_unused]] J return {}; } +static ErrorOr read_exif(JPEGStream& stream, JPEGLoadingContext& context, int bytes_to_read) +{ + // This refers to Exif's specification, see TIFFLoader for more information. + // 4.7.2.2. - APP1 internal structure + if (bytes_to_read <= 1) { + TRY(stream.discard(bytes_to_read)); + return {}; + } + + // Discard padding byte + TRY(stream.discard(1)); + + auto exif_buffer = TRY(ByteBuffer::create_uninitialized(bytes_to_read - 1)); + TRY(stream.read_until_filled(exif_buffer)); + + context.exif_metadata = TRY(TIFFImageDecoderPlugin::read_exif_metadata(exif_buffer)); + + return {}; +} + static ErrorOr read_app_marker(JPEGStream& stream, JPEGLoadingContext& context, int app_marker_number) { // B.2.4.6 - Application data syntax @@ -1187,6 +1211,8 @@ static ErrorOr read_app_marker(JPEGStream& stream, JPEGLoadingContext& con auto app_id = TRY(builder.to_string()); + if (app_marker_number == 1 && app_id == "Exif"sv) + return read_exif(stream, context, bytes_to_read); if (app_marker_number == 2 && app_id == "ICC_PROFILE"sv) return read_icc_profile(stream, context, bytes_to_read); if (app_marker_number == 14 && app_id == "Adobe"sv) @@ -2006,6 +2032,13 @@ ErrorOr JPEGImageDecoderPlugin::frame(size_t index, Option return ImageFrameDescriptor { m_context->bitmap, 0 }; } +Optional JPEGImageDecoderPlugin::metadata() +{ + if (m_context->exif_metadata) + return *m_context->exif_metadata; + return OptionalNone {}; +} + ErrorOr> JPEGImageDecoderPlugin::icc_data() { if (m_context->icc_data.has_value()) diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.h b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.h index 30468bd3ed..00a33217f0 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.h +++ b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.h @@ -37,6 +37,9 @@ public: virtual IntSize size() override; virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; + + virtual Optional metadata() override; + virtual ErrorOr> icc_data() override; virtual NaturalFrameFormat natural_frame_format() const override;