Explorar o código

LibGfx/JBIG2: Scan for the first PageInformation segment and decode it

This allows `file` to correctly print the dimensions of a .jbig2 file,
and it allows us to write a test that covers much of all the code
written so far.
Nico Weber hai 1 ano
pai
achega
8f4930f2df

+ 9 - 0
Tests/LibGfx/TestImageDecoder.cpp

@@ -13,6 +13,7 @@
 #include <LibGfx/ImageFormats/ICOLoader.h>
 #include <LibGfx/ImageFormats/ILBMLoader.h>
 #include <LibGfx/ImageFormats/ImageDecoder.h>
+#include <LibGfx/ImageFormats/JBIG2Loader.h>
 #include <LibGfx/ImageFormats/JPEGLoader.h>
 #include <LibGfx/ImageFormats/JPEGXLLoader.h>
 #include <LibGfx/ImageFormats/PAMLoader.h>
@@ -316,6 +317,14 @@ TEST_CASE(test_ilbm_malformed_frame)
     }
 }
 
+TEST_CASE(test_jbig2_size)
+{
+    auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jbig2/bitmap.jbig2"sv)));
+    EXPECT(Gfx::JBIG2ImageDecoderPlugin::sniff(file->bytes()));
+    auto plugin_decoder = TRY_OR_FAIL(Gfx::JBIG2ImageDecoderPlugin::create(file->bytes()));
+    EXPECT_EQ(plugin_decoder->size(), Gfx::IntSize(399, 400));
+}
+
 TEST_CASE(test_jpeg_sof0_one_scan)
 {
     auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("jpg/rgb24.jpg"sv)));

+ 36 - 0
Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp

@@ -266,6 +266,38 @@ static ErrorOr<void> decode_segment_headers(JBIG2LoadingContext& context, Readon
     return {};
 }
 
+// 7.4.8 Page information segment syntax
+struct [[gnu::packed]] PageInformationSegment {
+    BigEndian<u32> bitmap_width;
+    BigEndian<u32> bitmap_height;
+    BigEndian<u32> page_x_resolution; // In pixels/meter.
+    BigEndian<u32> page_y_resolution; // In pixels/meter.
+    u8 flags;
+    BigEndian<u16> striping_information;
+};
+static_assert(AssertSize<PageInformationSegment, 19>());
+
+static ErrorOr<PageInformationSegment> decode_page_information_segment(ReadonlyBytes data)
+{
+    // 7.4.8 Page information segment syntax
+    if (data.size() != sizeof(PageInformationSegment))
+        return Error::from_string_literal("JBIG2ImageDecoderPlugin: Invalid page information segment size");
+    return *(PageInformationSegment const*)data.data();
+}
+
+static ErrorOr<void> scan_for_page_size(JBIG2LoadingContext& context)
+{
+    // We only decode the first page at the moment.
+    for (auto const& segment : context.segments) {
+        if (segment.header.type != SegmentType::PageInformation)
+            continue;
+        auto page_information = TRY(decode_page_information_segment(segment.data));
+        context.size = { page_information.bitmap_width, page_information.bitmap_height };
+        return {};
+    }
+    return Error::from_string_literal("JBIG2ImageDecoderPlugin: No page information segment found");
+}
+
 JBIG2ImageDecoderPlugin::JBIG2ImageDecoderPlugin()
 {
     m_context = make<JBIG2LoadingContext>();
@@ -289,6 +321,8 @@ ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JBIG2ImageDecoderPlugin::create(Reado
     data = data.slice(sizeof(id_string) + sizeof(u8) + (plugin->m_context->number_of_pages.has_value() ? sizeof(u32) : 0));
     TRY(decode_segment_headers(*plugin->m_context, data));
 
+    TRY(scan_for_page_size(*plugin->m_context));
+
     return plugin;
 }
 
@@ -312,6 +346,8 @@ ErrorOr<ByteBuffer> JBIG2ImageDecoderPlugin::decode_embedded(Vector<ReadonlyByte
     for (auto const& segment_data : data)
         TRY(decode_segment_headers(*plugin->m_context, segment_data));
 
+    TRY(scan_for_page_size(*plugin->m_context));
+
     return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode embedded JBIG2 yet");
 }