From 8e2102fb7300e0f88f2c695c1a0d091d1477cfd6 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Wed, 14 Feb 2024 01:15:06 -0500 Subject: [PATCH] ImageViewer: Transform the image's dimension accordingly to the metadata Exif metadata have two tags to store the pixel density along each axis. If both values are different and no action is taken, the resulting image will appear deformed. This commit scales the displayed bitmap accordingly to these tags in order to show the image in its intended shape. This unfortunately includes a lot of plumbing to get this information through IPC. --- .../Applications/ImageViewer/ViewWidget.cpp | 2 +- .../Applications/ImageViewer/ViewWidget.h | 8 ++++--- .../LibImageDecoderClient/Client.cpp | 1 + .../Libraries/LibImageDecoderClient/Client.h | 1 + .../ImageDecoder/ConnectionFromClient.cpp | 21 ++++++++++++++++--- .../ImageDecoder/ImageDecoderServer.ipc | 2 +- 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Userland/Applications/ImageViewer/ViewWidget.cpp b/Userland/Applications/ImageViewer/ViewWidget.cpp index ec4cadf384..c65a23518a 100644 --- a/Userland/Applications/ImageViewer/ViewWidget.cpp +++ b/Userland/Applications/ImageViewer/ViewWidget.cpp @@ -246,7 +246,7 @@ ErrorOr ViewWidget::try_open_file(String const& path, Core::File& file) frames.ensure_capacity(decoded_image->frames.size()); for (u32 i = 0; i < decoded_image->frames.size(); i++) { auto& frame_data = decoded_image->frames[i]; - frames.unchecked_append({ BitmapImage::create(frame_data.bitmap), int(frame_data.duration) }); + frames.unchecked_append({ BitmapImage::create(frame_data.bitmap, decoded_image->scale), int(frame_data.duration) }); } } diff --git a/Userland/Applications/ImageViewer/ViewWidget.h b/Userland/Applications/ImageViewer/ViewWidget.h index e509cb8efb..962f0843b4 100644 --- a/Userland/Applications/ImageViewer/ViewWidget.h +++ b/Userland/Applications/ImageViewer/ViewWidget.h @@ -66,9 +66,9 @@ private: class BitmapImage final : public Image { public: - static NonnullRefPtr create(Gfx::Bitmap& bitmap) { return adopt_ref(*new BitmapImage(bitmap)); } + static NonnullRefPtr create(Gfx::Bitmap& bitmap, Gfx::FloatPoint scale) { return adopt_ref(*new BitmapImage(bitmap, scale)); } - virtual Gfx::IntSize size() const override { return m_bitmap->size(); } + virtual Gfx::IntSize size() const override { return { round(m_bitmap->size().width() * m_scale.x()), round(m_bitmap->size().height() * m_scale.y()) }; } virtual void flip(Gfx::Orientation) override; virtual void rotate(Gfx::RotationDirection) override; @@ -81,12 +81,14 @@ public: } private: - BitmapImage(Gfx::Bitmap& bitmap) + BitmapImage(Gfx::Bitmap& bitmap, Gfx::FloatPoint scale) : m_bitmap(bitmap) + , m_scale(scale) { } NonnullRefPtr m_bitmap; + Gfx::FloatPoint m_scale; }; class ViewWidget final : public GUI::AbstractZoomPanWidget { diff --git a/Userland/Libraries/LibImageDecoderClient/Client.cpp b/Userland/Libraries/LibImageDecoderClient/Client.cpp index 0aa014b987..ab27b085bd 100644 --- a/Userland/Libraries/LibImageDecoderClient/Client.cpp +++ b/Userland/Libraries/LibImageDecoderClient/Client.cpp @@ -48,6 +48,7 @@ Optional Client::decode_image(ReadonlyBytes encoded_data, Optional DecodedImage image; image.is_animated = response.is_animated(); image.loop_count = response.loop_count(); + image.scale = response.scale(); image.frames.ensure_capacity(response.bitmaps().size()); auto bitmaps = response.take_bitmaps(); for (size_t i = 0; i < bitmaps.size(); ++i) { diff --git a/Userland/Libraries/LibImageDecoderClient/Client.h b/Userland/Libraries/LibImageDecoderClient/Client.h index ed163252c5..a2a3ebaf54 100644 --- a/Userland/Libraries/LibImageDecoderClient/Client.h +++ b/Userland/Libraries/LibImageDecoderClient/Client.h @@ -20,6 +20,7 @@ struct Frame { struct DecodedImage { bool is_animated { false }; + Gfx::FloatPoint scale { 1, 1 }; u32 loop_count { 0 }; Vector frames; }; diff --git a/Userland/Services/ImageDecoder/ConnectionFromClient.cpp b/Userland/Services/ImageDecoder/ConnectionFromClient.cpp index 54ad58adcd..acb5d7f0b0 100644 --- a/Userland/Services/ImageDecoder/ConnectionFromClient.cpp +++ b/Userland/Services/ImageDecoder/ConnectionFromClient.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace ImageDecoder { @@ -37,7 +38,7 @@ static void decode_image_to_bitmaps_and_durations_with_decoder(Gfx::ImageDecoder } } -static void decode_image_to_details(Core::AnonymousBuffer const& encoded_buffer, Optional ideal_size, Optional const& known_mime_type, bool& is_animated, u32& loop_count, Vector& bitmaps, Vector& durations) +static void decode_image_to_details(Core::AnonymousBuffer const& encoded_buffer, Optional ideal_size, Optional const& known_mime_type, bool& is_animated, u32& loop_count, Vector& bitmaps, Vector& durations, Gfx::FloatPoint& scale) { VERIFY(bitmaps.size() == 0); VERIFY(durations.size() == 0); @@ -54,6 +55,19 @@ static void decode_image_to_details(Core::AnonymousBuffer const& encoded_buffer, } is_animated = decoder->is_animated(); loop_count = decoder->loop_count(); + + if (auto maybe_metadata = decoder->metadata(); maybe_metadata.has_value() && is(*maybe_metadata)) { + auto const& exif = static_cast(maybe_metadata.value()); + if (exif.x_resolution().has_value() && exif.y_resolution().has_value()) { + auto const x_resolution = exif.x_resolution()->as_double(); + auto const y_resolution = exif.y_resolution()->as_double(); + if (x_resolution < y_resolution) + scale.set_y(x_resolution / y_resolution); + else + scale.set_x(y_resolution / x_resolution); + } + } + decode_image_to_bitmaps_and_durations_with_decoder(*decoder, ideal_size, bitmaps, durations); } @@ -66,10 +80,11 @@ Messages::ImageDecoderServer::DecodeImageResponse ConnectionFromClient::decode_i bool is_animated = false; u32 loop_count = 0; + Gfx::FloatPoint scale { 1, 1 }; Vector bitmaps; Vector durations; - decode_image_to_details(encoded_buffer, ideal_size, mime_type, is_animated, loop_count, bitmaps, durations); - return { is_animated, loop_count, bitmaps, durations }; + decode_image_to_details(encoded_buffer, ideal_size, mime_type, is_animated, loop_count, bitmaps, durations, scale); + return { is_animated, loop_count, bitmaps, durations, scale }; } } diff --git a/Userland/Services/ImageDecoder/ImageDecoderServer.ipc b/Userland/Services/ImageDecoder/ImageDecoderServer.ipc index 10e92fe8cd..2776417df0 100644 --- a/Userland/Services/ImageDecoder/ImageDecoderServer.ipc +++ b/Userland/Services/ImageDecoder/ImageDecoderServer.ipc @@ -3,5 +3,5 @@ endpoint ImageDecoderServer { - decode_image(Core::AnonymousBuffer data, Optional ideal_size, Optional mime_type) => (bool is_animated, u32 loop_count, Vector bitmaps, Vector durations) + decode_image(Core::AnonymousBuffer data, Optional ideal_size, Optional mime_type) => (bool is_animated, u32 loop_count, Vector bitmaps, Vector durations, Gfx::FloatPoint scale) }