Parcourir la source

LibGfx: Use LibRIFF for IFF parsing

kleines Filmröllchen il y a 1 an
Parent
commit
60c3a1a77b

+ 1 - 1
Userland/Libraries/LibGfx/CMakeLists.txt

@@ -82,7 +82,7 @@ set(SOURCES
 )
 )
 
 
 serenity_lib(LibGfx gfx)
 serenity_lib(LibGfx gfx)
-target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibTextCodec LibIPC LibUnicode)
+target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibRIFF LibTextCodec LibIPC LibUnicode)
 
 
 set(generated_sources TIFFMetadata.h TIFFTagHandler.cpp)
 set(generated_sources TIFFMetadata.h TIFFTagHandler.cpp)
 list(TRANSFORM generated_sources PREPEND "ImageFormats/")
 list(TRANSFORM generated_sources PREPEND "ImageFormats/")

+ 26 - 72
Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp

@@ -12,21 +12,11 @@
 #include <LibCompress/PackBitsDecoder.h>
 #include <LibCompress/PackBitsDecoder.h>
 #include <LibGfx/FourCC.h>
 #include <LibGfx/FourCC.h>
 #include <LibGfx/ImageFormats/ILBMLoader.h>
 #include <LibGfx/ImageFormats/ILBMLoader.h>
+#include <LibRIFF/IFF.h>
 
 
 namespace Gfx {
 namespace Gfx {
 
 
-struct IFFHeader {
-    FourCC form;
-    BigEndian<u32> file_size;
-    FourCC format;
-};
-
-static_assert(AssertSize<IFFHeader, 12>());
-
-struct Chunk {
-    FourCC type;
-    ReadonlyBytes data;
-};
+static constexpr size_t const ilbm_header_size = 12;
 
 
 enum class CompressionType : u8 {
 enum class CompressionType : u8 {
     None = 0,
     None = 0,
@@ -56,11 +46,6 @@ enum class Format : u8 {
 
 
 AK_ENUM_BITWISE_OPERATORS(ViewportMode);
 AK_ENUM_BITWISE_OPERATORS(ViewportMode);
 
 
-struct ChunkHeader {
-    FourCC chunk_type;
-    BigEndian<u32> chunk_size;
-};
-
 struct BMHDHeader {
 struct BMHDHeader {
     BigEndian<u16> width;
     BigEndian<u16> width;
     BigEndian<u16> height;
     BigEndian<u16> height;
@@ -113,27 +98,27 @@ static ErrorOr<void> decode_iff_ilbm_header(ILBMLoadingContext& context)
     if (context.state >= ILBMLoadingContext::State::HeaderDecoded)
     if (context.state >= ILBMLoadingContext::State::HeaderDecoded)
         return {};
         return {};
 
 
-    if (context.data.size() < sizeof(IFFHeader))
+    if (context.data.size() < ilbm_header_size)
         return Error::from_string_literal("Missing IFF header");
         return Error::from_string_literal("Missing IFF header");
 
 
-    auto& header = *bit_cast<IFFHeader const*>(context.data.data());
-
-    if (header.form != FourCC("FORM") || (header.format != FourCC("ILBM") && header.format != FourCC("PBM ")))
+    auto header_stream = FixedMemoryStream { context.data };
+    auto header = TRY(IFF::FileHeader::read_from_stream(header_stream));
+    if (header.magic() != "FORM"sv || (header.subformat != "ILBM"sv && header.subformat != "PBM "sv))
         return Error::from_string_literal("Invalid IFF-ILBM header");
         return Error::from_string_literal("Invalid IFF-ILBM header");
 
 
-    context.format = header.format == FourCC("ILBM") ? Format::ILBM : Format::PBM;
+    context.format = header.subformat == "ILBM" ? Format::ILBM : Format::PBM;
 
 
     return {};
     return {};
 }
 }
 
 
-static ErrorOr<Vector<Color>> decode_cmap_chunk(Chunk cmap_chunk)
+static ErrorOr<Vector<Color>> decode_cmap_chunk(IFF::Chunk cmap_chunk)
 {
 {
-    size_t const size = cmap_chunk.data.size() / 3;
+    size_t const size = cmap_chunk.size() / 3;
     Vector<Color> color_table;
     Vector<Color> color_table;
     TRY(color_table.try_ensure_capacity(size));
     TRY(color_table.try_ensure_capacity(size));
 
 
     for (size_t i = 0; i < size; ++i) {
     for (size_t i = 0; i < size; ++i) {
-        color_table.unchecked_append(Color(cmap_chunk.data[i * 3], cmap_chunk.data[(i * 3) + 1], cmap_chunk.data[(i * 3) + 2]));
+        color_table.unchecked_append(Color(cmap_chunk[i * 3], cmap_chunk[(i * 3) + 1], cmap_chunk[(i * 3) + 2]));
     }
     }
 
 
     return color_table;
     return color_table;
@@ -295,23 +280,23 @@ static ErrorOr<void> reduce_ham_palette(ILBMLoadingContext& context)
     return {};
     return {};
 }
 }
 
 
-static ErrorOr<void> decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& context)
+static ErrorOr<void> decode_body_chunk(IFF::Chunk body_chunk, ILBMLoadingContext& context)
 {
 {
-    dbgln_if(ILBM_DEBUG, "decode_body_chunk {}", body_chunk.data.size());
+    dbgln_if(ILBM_DEBUG, "decode_body_chunk {}", body_chunk.size());
 
 
     ByteBuffer pixel_data;
     ByteBuffer pixel_data;
 
 
     if (context.bm_header.compression == CompressionType::ByteRun) {
     if (context.bm_header.compression == CompressionType::ByteRun) {
-        auto plane_data = TRY(uncompress_byte_run(body_chunk.data, context));
+        auto plane_data = TRY(uncompress_byte_run(body_chunk.data(), context));
         if (context.format == Format::ILBM)
         if (context.format == Format::ILBM)
             pixel_data = TRY(planar_to_chunky(plane_data, context));
             pixel_data = TRY(planar_to_chunky(plane_data, context));
         else
         else
             pixel_data = plane_data;
             pixel_data = plane_data;
     } else {
     } else {
         if (context.format == Format::ILBM)
         if (context.format == Format::ILBM)
-            pixel_data = TRY(planar_to_chunky(body_chunk.data, context));
+            pixel_data = TRY(planar_to_chunky(body_chunk.data(), context));
         else
         else
-            pixel_data = TRY(ByteBuffer::copy(body_chunk.data.data(), body_chunk.data.size()));
+            pixel_data = TRY(ByteBuffer::copy(body_chunk.data().data(), body_chunk.size()));
     }
     }
 
 
     // Some files already have 64 colors defined in the palette,
     // Some files already have 64 colors defined in the palette,
@@ -329,37 +314,6 @@ static ErrorOr<void> decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& con
     return {};
     return {};
 }
 }
 
 
-static ErrorOr<Chunk> decode_iff_chunk_header(ReadonlyBytes chunks)
-{
-    if (chunks.size() < sizeof(ChunkHeader))
-        return Error::from_string_literal("Not enough data for IFF chunk header");
-
-    auto const& header = *bit_cast<ChunkHeader const*>(chunks.data());
-
-    if (chunks.size() < sizeof(ChunkHeader) + header.chunk_size)
-        return Error::from_string_literal("Not enough data for IFF chunk");
-
-    return Chunk { header.chunk_type, { chunks.data() + sizeof(ChunkHeader), header.chunk_size } };
-}
-
-static ErrorOr<Chunk> decode_iff_advance_chunk(ReadonlyBytes& chunks)
-{
-    auto chunk = TRY(decode_iff_chunk_header(chunks));
-
-    chunks = chunks.slice(sizeof(ChunkHeader) + chunk.data.size());
-
-    // add padding if needed
-    if (chunk.data.size() % 2 != 0) {
-        if (chunks.is_empty())
-            return Error::from_string_literal("Missing data for padding byte");
-        if (*chunks.data() != 0)
-            return Error::from_string_literal("Padding byte is not 0");
-        chunks = chunks.slice(1);
-    }
-
-    return chunk;
-}
-
 static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context)
 static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context)
 {
 {
     auto& chunks = context.chunks_cursor;
     auto& chunks = context.chunks_cursor;
@@ -367,14 +321,14 @@ static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context)
     dbgln_if(ILBM_DEBUG, "decode_iff_chunks");
     dbgln_if(ILBM_DEBUG, "decode_iff_chunks");
 
 
     while (!chunks.is_empty()) {
     while (!chunks.is_empty()) {
-        auto chunk = TRY(decode_iff_advance_chunk(chunks));
-        if (chunk.type == FourCC("CMAP")) {
+        auto chunk = TRY(IFF::Chunk::decode_and_advance(chunks));
+        if (chunk.id() == "CMAP"sv) {
             // Some files (HAM mainly) have CMAP chunks larger than the planes they advertise: I'm not sure
             // Some files (HAM mainly) have CMAP chunks larger than the planes they advertise: I'm not sure
             // why but we should not return an error in this case.
             // why but we should not return an error in this case.
 
 
             context.color_table = TRY(decode_cmap_chunk(chunk));
             context.color_table = TRY(decode_cmap_chunk(chunk));
             context.cmap_bits = AK::ceil_log2(context.color_table.size());
             context.cmap_bits = AK::ceil_log2(context.color_table.size());
-        } else if (chunk.type == FourCC("BODY")) {
+        } else if (chunk.id() == "BODY"sv) {
             if (context.color_table.is_empty() && context.bm_header.planes != 24)
             if (context.color_table.is_empty() && context.bm_header.planes != 24)
                 return Error::from_string_literal("Decoding indexed BODY chunk without a color map is not currently supported");
                 return Error::from_string_literal("Decoding indexed BODY chunk without a color map is not currently supported");
 
 
@@ -385,10 +339,10 @@ static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context)
 
 
             TRY(decode_body_chunk(chunk, context));
             TRY(decode_body_chunk(chunk, context));
             context.state = ILBMLoadingContext::State::BitmapDecoded;
             context.state = ILBMLoadingContext::State::BitmapDecoded;
-        } else if (chunk.type == FourCC("CRNG")) {
+        } else if (chunk.id() == "CRNG"sv) {
             dbgln_if(ILBM_DEBUG, "Chunk:CRNG");
             dbgln_if(ILBM_DEBUG, "Chunk:CRNG");
-        } else if (chunk.type == FourCC("CAMG")) {
-            context.viewport_mode = static_cast<ViewportMode>(AK::convert_between_host_and_big_endian(ByteReader::load32(chunk.data.data())));
+        } else if (chunk.id() == "CAMG"sv) {
+            context.viewport_mode = static_cast<ViewportMode>(AK::convert_between_host_and_big_endian(ByteReader::load32(chunk.data().data())));
             dbgln_if(ILBM_DEBUG, "Chunk:CAMG, Viewport={}, EHB={}, HAM={}", (u32)context.viewport_mode, has_flag(context.viewport_mode, ViewportMode::EHB), has_flag(context.viewport_mode, ViewportMode::HAM));
             dbgln_if(ILBM_DEBUG, "Chunk:CAMG, Viewport={}, EHB={}, HAM={}", (u32)context.viewport_mode, has_flag(context.viewport_mode, ViewportMode::EHB), has_flag(context.viewport_mode, ViewportMode::HAM));
         }
         }
     }
     }
@@ -401,16 +355,16 @@ static ErrorOr<void> decode_iff_chunks(ILBMLoadingContext& context)
 
 
 static ErrorOr<void> decode_bmhd_chunk(ILBMLoadingContext& context)
 static ErrorOr<void> decode_bmhd_chunk(ILBMLoadingContext& context)
 {
 {
-    context.chunks_cursor = context.data.slice(sizeof(IFFHeader));
-    auto first_chunk = TRY(decode_iff_advance_chunk(context.chunks_cursor));
+    context.chunks_cursor = context.data.slice(sizeof(IFF::FileHeader));
+    auto first_chunk = TRY(IFF::Chunk::decode_and_advance(context.chunks_cursor));
 
 
-    if (first_chunk.type != FourCC("BMHD"))
+    if (first_chunk.id() != "BMHD"sv)
         return Error::from_string_literal("IFFImageDecoderPlugin: Invalid chunk type, expected BMHD");
         return Error::from_string_literal("IFFImageDecoderPlugin: Invalid chunk type, expected BMHD");
 
 
-    if (first_chunk.data.size() < sizeof(BMHDHeader))
+    if (first_chunk.size() < sizeof(BMHDHeader))
         return Error::from_string_literal("IFFImageDecoderPlugin: Not enough data for header chunk");
         return Error::from_string_literal("IFFImageDecoderPlugin: Not enough data for header chunk");
 
 
-    context.bm_header = *bit_cast<BMHDHeader const*>(first_chunk.data.data());
+    context.bm_header = *bit_cast<BMHDHeader const*>(first_chunk.data().data());
 
 
     if (context.bm_header.mask >= MaskType::__Count)
     if (context.bm_header.mask >= MaskType::__Count)
         return Error::from_string_literal("IFFImageDecoderPlugin: Unsupported mask type");
         return Error::from_string_literal("IFFImageDecoderPlugin: Unsupported mask type");