LibGfx: Apply Exif orientation for PNG images

Fixes wpt/png/exif-chunk.html.

At some point there should probably be some mechanism to handle this
outside of the individual decoder plugins. The TIFF decoder seems to
have its own version of this, and as far as I can tell, the JPEG decoder
doesn't handle this at all, even though that's probably the most common
use case for Exif orientations. :^)
This commit is contained in:
justus2510 2024-10-16 05:51:39 +02:00 committed by Tim Ledbetter
parent 120bc52f23
commit 144907f5bd
Notes: github-actions[bot] 2024-10-31 02:19:07 +00:00
2 changed files with 36 additions and 1 deletions

View file

@ -345,10 +345,13 @@ TEST_CASE(test_exif)
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 }));
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 200, 100 }));
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);
EXPECT_EQ(frame.image->get_pixel(65, 70), Gfx::Color(0, 255, 0));
EXPECT_EQ(frame.image->get_pixel(190, 10), Gfx::Color(255, 0, 0));
}
TEST_CASE(test_png_malformed_frame)

View file

@ -7,6 +7,7 @@
#include <AK/Vector.h>
#include <LibGfx/DeprecatedPainter.h>
#include <LibGfx/ImageFormats/ExifOrientedBitmap.h>
#include <LibGfx/ImageFormats/PNGLoader.h>
#include <LibGfx/ImageFormats/TIFFLoader.h>
#include <LibGfx/ImageFormats/TIFFMetadata.h>
@ -67,6 +68,7 @@ struct PNGLoadingContext {
RefPtr<Gfx::Bitmap> decoded_frame_bitmap;
ErrorOr<size_t> read_frames(png_structp, png_infop);
ErrorOr<void> apply_exif_orientation();
};
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> PNGImageDecoderPlugin::create(ReadonlyBytes bytes)
@ -196,10 +198,40 @@ ErrorOr<bool> PNGImageDecoderPlugin::initialize()
m_context->exif_metadata = TRY(TIFFImageDecoderPlugin::read_exif_metadata({ exif_data, exif_length }));
}
if (m_context->exif_metadata) {
if (auto result = m_context->apply_exif_orientation(); result.is_error())
dbgln("Could not apply eXIf chunk orientation for PNG: {}", result.error());
}
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return true;
}
ErrorOr<void> PNGLoadingContext::apply_exif_orientation()
{
auto orientation = exif_metadata->orientation().value_or(TIFF::Orientation::Default);
if (orientation == TIFF::Orientation::Default)
return {};
for (auto& img_frame_descriptor : frame_descriptors) {
auto& img = img_frame_descriptor.image;
auto oriented_bmp = TRY(ExifOrientedBitmap::create(orientation, img->size(), img->format()));
for (int y = 0; y < img->size().height(); ++y) {
for (int x = 0; x < img->size().width(); ++x) {
auto pixel = img->get_pixel(x, y);
oriented_bmp.set_pixel(x, y, pixel.value());
}
}
img_frame_descriptor.image = oriented_bmp.bitmap();
}
size = ExifOrientedBitmap::oriented_size(size, orientation);
return {};
}
static ErrorOr<NonnullRefPtr<Bitmap>> render_animation_frame(AnimationFrame const& prev_animation_frame, AnimationFrame const& animation_frame, Bitmap const& decoded_frame_bitmap)
{
auto rendered_bitmap = TRY(prev_animation_frame.bitmap->clone());