浏览代码

LibGfx/JPEG2000: Add parsing loop for main header

We don't interpret any of the marker data yet.
Nico Weber 1 年之前
父节点
当前提交
9eae6b3a90
共有 1 个文件被更改,包括 87 次插入0 次删除
  1. 87 0
      Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp

+ 87 - 0
Userland/Libraries/LibGfx/ImageFormats/JPEG2000Loader.cpp

@@ -66,6 +66,7 @@ struct JPEG2000LoadingContext {
     };
     State state { State::NotDecoded };
     ReadonlyBytes codestream_data;
+    size_t codestream_cursor { 0 };
     Optional<ReadonlyBytes> icc_data;
 
     IntSize size;
@@ -73,6 +74,90 @@ struct JPEG2000LoadingContext {
     ISOBMFF::BoxList boxes;
 };
 
+struct MarkerSegment {
+    u16 marker;
+
+    // OptionalNone for markers that don't have data.
+    // For markers that do have data, this does not include the marker length data. (`data.size() + 2` is the value of the marker length field.)
+    Optional<ReadonlyBytes> data;
+};
+
+static ErrorOr<u16> peek_marker(JPEG2000LoadingContext& context)
+{
+    if (context.codestream_cursor + 2 > context.codestream_data.size())
+        return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker");
+    return *reinterpret_cast<AK::BigEndian<u16> const*>(context.codestream_data.data() + context.codestream_cursor);
+}
+
+static ErrorOr<MarkerSegment> read_marker_at_cursor(JPEG2000LoadingContext& context)
+{
+    u16 marker = TRY(peek_marker(context));
+    // "All markers with the marker code between 0xFF30 and 0xFF3F have no marker segment parameters. They shall be skipped by the decoder."
+    // "The SOC, SOD and EOC are delimiting markers not marker segments, and have no explicit length information or other parameters."
+    bool is_marker_segment = !(marker >= 0xFF30 && marker <= 0xFF3F) && marker != J2K_SOC && marker != J2K_SOD && marker != J2K_EOC;
+
+    MarkerSegment marker_segment;
+    marker_segment.marker = marker;
+
+    if (is_marker_segment) {
+        if (context.codestream_cursor + 4 > context.codestream_data.size())
+            return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker segment length");
+        u16 marker_length = *reinterpret_cast<AK::BigEndian<u16> const*>(context.codestream_data.data() + context.codestream_cursor + 2);
+        if (marker_length < 2)
+            return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Marker segment length too small");
+        if (context.codestream_cursor + 2 + marker_length > context.codestream_data.size())
+            return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Not enough data for marker segment data");
+        marker_segment.data = ReadonlyBytes { context.codestream_data.data() + context.codestream_cursor + 4, marker_length - 2u };
+    }
+
+    context.codestream_cursor += 2;
+    if (is_marker_segment)
+        context.codestream_cursor += 2 + marker_segment.data->size();
+
+    return marker_segment;
+}
+
+static ErrorOr<void> parse_codestream_main_header(JPEG2000LoadingContext& context)
+{
+    // Figure A.3 – Construction of the main header
+    // "Required as the first marker"
+    auto marker = TRY(read_marker_at_cursor(context));
+    if (marker.marker != J2K_SOC)
+        return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected SOC marker");
+
+    // "Required as the second marker segment"
+    marker = TRY(read_marker_at_cursor(context));
+    if (marker.marker != J2K_SIZ)
+        return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Expected SIZ marker");
+    // FIXME: Parse SIZ marker.
+
+    while (true) {
+        u16 marker = TRY(peek_marker(context));
+        switch (marker) {
+        case J2K_COD:
+        case J2K_COC:
+        case J2K_QCD:
+        case J2K_QCC:
+        case J2K_RGN:
+        case J2K_POC:
+        case J2K_PPM:
+        case J2K_TLM:
+        case J2K_PLM:
+        case J2K_CRG:
+        case J2K_COM: {
+            // FIXME: These are valid main header markers. Parse contents.
+            auto marker = TRY(read_marker_at_cursor(context));
+            dbgln("JPEG2000ImageDecoderPlugin: marker {:#04x} not yet implemented", marker.marker);
+            break;
+        }
+        case J2K_SOT:
+            return {};
+        default:
+            return Error::from_string_literal("JPEG2000ImageDecoderPlugin: Unexpected marker in main header");
+        }
+    }
+}
+
 static ErrorOr<void> decode_jpeg2000_header(JPEG2000LoadingContext& context, ReadonlyBytes data)
 {
     if (!JPEG2000ImageDecoderPlugin::sniff(data))
@@ -163,6 +248,8 @@ static ErrorOr<void> decode_jpeg2000_header(JPEG2000LoadingContext& context, Rea
     if (color_header_box.method == 2 || color_header_box.method == 3)
         context.icc_data = color_header_box.icc_data.bytes();
 
+    TRY(parse_codestream_main_header(context));
+
     return {};
 }