Sfoglia il codice sorgente

LibGfx/JBIG2: Add an initial decode_segment_header()

With `#define JBIG2_DEBUG 1` at the top of the file:

    % Build/lagom/bin/image --no-output \
        .../JBIG2_ConformanceData-A20180829/F01_200_TT10.jb2
    JBIG2LoadingContext: Organization: 0 (Sequential)
    JBIG2LoadingContext: Number of pages: 1
    Segment number: 0
    Segment type: 48
    Referred to segment count: 0
    Segment page association: 1
    Segment data length: 19
    Runtime error: JBIG2ImageDecoderPlugin: Draw the rest of the owl
Nico Weber 1 anno fa
parent
commit
177664cfae
1 ha cambiato i file con 112 aggiunte e 0 eliminazioni
  1. 112 0
      Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp

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

@@ -9,12 +9,39 @@
 
 // Spec: ITU-T_T_88__08_2018.pdf in the zip file here:
 // https://www.itu.int/rec/T-REC-T.88-201808-I
+// Annex H has a datastream example.
 
 namespace Gfx {
 
 // JBIG2 spec, Annex D, D.4.1 ID string
 static constexpr u8 id_string[] = { 0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A };
 
+// 7.3 Segment types
+enum SegmentType {
+    SymbolDictionary = 0,
+    IntermediateTextRegion = 4,
+    ImmediateTextRegion = 6,
+    ImmediateLosslessTextRegion = 7,
+    PatternDictionary = 16,
+    IntermediateHalftoneRegion = 20,
+    ImmediateHalftoneRegion = 22,
+    ImmediateLosslessHalftoneRegion = 23,
+    IntermediateGenericRegion = 36,
+    ImmediateGenericRegion = 38,
+    ImmediateLosslessGenericRegion = 39,
+    IntermediateGenericRefinementRegion = 40,
+    ImmediateGenericRefinementRegion = 42,
+    ImmediateLosslessGenericRefinementRegion = 43,
+    PageInformation = 48,
+    EndOfPage = 49,
+    EndOfStripe = 50,
+    EndOfFile = 51,
+    Profiles = 52,
+    Tables = 53,
+    ColorPalette = 54,
+    Extension = 62,
+};
+
 // Annex D
 enum class Organization {
     // D.1 Sequential organization
@@ -27,6 +54,14 @@ enum class Organization {
     Embedded,
 };
 
+struct SegmentHeader {
+    u32 segment_number;
+    SegmentType type;
+    Vector<u32> referred_to_segment_numbers;
+    u32 page_association;
+    Optional<u32> data_length;
+};
+
 struct JBIG2LoadingContext {
     enum class State {
         NotDecoded = 0,
@@ -53,6 +88,7 @@ static ErrorOr<void> decode_jbig2_header(JBIG2LoadingContext& context)
     if (header_flags & 0b11110000)
         return Error::from_string_literal("JBIG2LoadingContext: Invalid header flags");
     context.organization = (header_flags & 1) ? Organization::Sequential : Organization::RandomAccess;
+    dbgln_if(JBIG2_DEBUG, "JBIG2LoadingContext: Organization: {} ({})", (int)context.organization, context.organization == Organization::Sequential ? "Sequential" : "Random-access");
     bool has_known_number_of_pages = (header_flags & 2) ? false : true;
     bool uses_templates_with_12_AT_pixels = (header_flags & 4) ? true : false;
     bool contains_colored_region_segments = (header_flags & 8) ? true : false;
@@ -70,6 +106,81 @@ static ErrorOr<void> decode_jbig2_header(JBIG2LoadingContext& context)
     return {};
 }
 
+static ErrorOr<SegmentHeader> decode_segment_header(JBIG2LoadingContext& context)
+{
+    ReadonlyBytes data = context.data.slice(sizeof(id_string) + sizeof(u8) + (context.number_of_pages.has_value() ? sizeof(u32) : 0));
+    FixedMemoryStream stream(data);
+
+    // 7.2.2 Segment number
+    u32 segment_number = TRY(stream.read_value<BigEndian<u32>>());
+    dbgln_if(JBIG2_DEBUG, "Segment number: {}", segment_number);
+
+    // 7.2.3 Segment header flags
+    u8 flags = TRY(stream.read_value<u8>());
+    SegmentType type = static_cast<SegmentType>(flags & 0b11'1111);
+    dbgln_if(JBIG2_DEBUG, "Segment type: {}", (int)type);
+    bool segment_page_association_size_is_32_bits = (flags & 0b100'0000) != 0;
+    bool segment_retained_only_by_itself_and_extension_segments = (flags & 0b1000'00000) != 0;
+
+    // FIXME: Do something with these.
+    (void)segment_page_association_size_is_32_bits;
+    (void)segment_retained_only_by_itself_and_extension_segments;
+
+    // 7.2.4 Referred-to segment count and retention flags
+    u8 referred_to_segment_count_and_retention_flags = TRY(stream.read_value<u8>());
+    u32 count_of_referred_to_segments = referred_to_segment_count_and_retention_flags >> 5;
+    if (count_of_referred_to_segments == 5 || count_of_referred_to_segments == 6)
+        return Error::from_string_literal("JBIG2ImageDecoderPlugin: Invalid count_of_referred_to_segments");
+    u32 extra_count = 0;
+    if (count_of_referred_to_segments == 7) {
+        TRY(stream.seek(-1, SeekMode::FromCurrentPosition));
+        count_of_referred_to_segments = TRY(stream.read_value<BigEndian<u32>>()) & 0x1FFF'FFFF;
+        extra_count = ceil_div(count_of_referred_to_segments + 1, 8);
+        TRY(stream.seek(extra_count, SeekMode::FromCurrentPosition));
+    }
+    dbgln_if(JBIG2_DEBUG, "Referred-to segment count: {}", count_of_referred_to_segments);
+
+    // 7.2.5 Referred-to segment numbers
+    Vector<u32> referred_to_segment_numbers;
+    for (u32 i = 0; i < count_of_referred_to_segments; ++i) {
+        u32 referred_to_segment_number;
+        if (segment_number <= 256)
+            referred_to_segment_number = TRY(stream.read_value<u8>());
+        else if (segment_number <= 65536)
+            referred_to_segment_number = TRY(stream.read_value<BigEndian<u16>>());
+        else
+            referred_to_segment_number = TRY(stream.read_value<BigEndian<u32>>());
+        referred_to_segment_numbers.append(referred_to_segment_number);
+        dbgln_if(JBIG2_DEBUG, "Referred-to segment number: {}", referred_to_segment_number);
+    }
+
+    // 7.2.6 Segment page association
+    u32 segment_page_association;
+    if (segment_page_association_size_is_32_bits) {
+        segment_page_association = TRY(stream.read_value<BigEndian<u32>>());
+    } else {
+        segment_page_association = TRY(stream.read_value<u8>());
+    }
+    dbgln_if(JBIG2_DEBUG, "Segment page association: {}", segment_page_association);
+
+    // 7.2.7 Segment data length
+    u32 data_length = TRY(stream.read_value<BigEndian<u32>>());
+    dbgln_if(JBIG2_DEBUG, "Segment data length: {}", data_length);
+
+    // FIXME: Add some validity checks:
+    // - data_length can only be 0xffff'ffff for type ImmediateGenericRegion
+    // - check type is valid
+    // - check referred_to_segment_numbers are smaller than segment_number
+    // - 7.3.1 Rules for segment references
+    // - 7.3.2 Rules for page associations
+
+    Optional<u32> opt_data_length;
+    if (data_length != 0xffff'ffff)
+        opt_data_length = data_length;
+
+    return SegmentHeader { segment_number, type, move(referred_to_segment_numbers), segment_page_association, opt_data_length };
+}
+
 JBIG2ImageDecoderPlugin::JBIG2ImageDecoderPlugin(ReadonlyBytes data)
 {
     m_context = make<JBIG2LoadingContext>();
@@ -90,6 +201,7 @@ ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JBIG2ImageDecoderPlugin::create(Reado
 {
     auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JBIG2ImageDecoderPlugin(data)));
     TRY(decode_jbig2_header(*plugin->m_context));
+    TRY(decode_segment_header(*plugin->m_context));
     return plugin;
 }