diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index b753abf1aa..360dc7f657 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -82,7 +82,7 @@ set(SOURCES ) 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) list(TRANSFORM generated_sources PREPEND "ImageFormats/") diff --git a/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp index 1b10f91978..3300ae649d 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp @@ -12,21 +12,11 @@ #include #include #include +#include namespace Gfx { -struct IFFHeader { - FourCC form; - BigEndian file_size; - FourCC format; -}; - -static_assert(AssertSize()); - -struct Chunk { - FourCC type; - ReadonlyBytes data; -}; +static constexpr size_t const ilbm_header_size = 12; enum class CompressionType : u8 { None = 0, @@ -56,11 +46,6 @@ enum class Format : u8 { AK_ENUM_BITWISE_OPERATORS(ViewportMode); -struct ChunkHeader { - FourCC chunk_type; - BigEndian chunk_size; -}; - struct BMHDHeader { BigEndian width; BigEndian height; @@ -113,27 +98,27 @@ static ErrorOr decode_iff_ilbm_header(ILBMLoadingContext& context) if (context.state >= ILBMLoadingContext::State::HeaderDecoded) return {}; - if (context.data.size() < sizeof(IFFHeader)) + if (context.data.size() < ilbm_header_size) return Error::from_string_literal("Missing IFF header"); - auto& header = *bit_cast(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"); - context.format = header.format == FourCC("ILBM") ? Format::ILBM : Format::PBM; + context.format = header.subformat == "ILBM" ? Format::ILBM : Format::PBM; return {}; } -static ErrorOr> decode_cmap_chunk(Chunk cmap_chunk) +static ErrorOr> 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_table; TRY(color_table.try_ensure_capacity(size)); 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; @@ -295,23 +280,23 @@ static ErrorOr reduce_ham_palette(ILBMLoadingContext& context) return {}; } -static ErrorOr decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& context) +static ErrorOr 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; 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) pixel_data = TRY(planar_to_chunky(plane_data, context)); else pixel_data = plane_data; } else { 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 - 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, @@ -329,37 +314,6 @@ static ErrorOr decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& con return {}; } -static ErrorOr 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(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 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 decode_iff_chunks(ILBMLoadingContext& context) { auto& chunks = context.chunks_cursor; @@ -367,14 +321,14 @@ static ErrorOr decode_iff_chunks(ILBMLoadingContext& context) dbgln_if(ILBM_DEBUG, "decode_iff_chunks"); 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 // why but we should not return an error in this case. context.color_table = TRY(decode_cmap_chunk(chunk)); 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) return Error::from_string_literal("Decoding indexed BODY chunk without a color map is not currently supported"); @@ -385,10 +339,10 @@ static ErrorOr decode_iff_chunks(ILBMLoadingContext& context) TRY(decode_body_chunk(chunk, context)); context.state = ILBMLoadingContext::State::BitmapDecoded; - } else if (chunk.type == FourCC("CRNG")) { + } else if (chunk.id() == "CRNG"sv) { dbgln_if(ILBM_DEBUG, "Chunk:CRNG"); - } else if (chunk.type == FourCC("CAMG")) { - context.viewport_mode = static_cast(AK::convert_between_host_and_big_endian(ByteReader::load32(chunk.data.data()))); + } else if (chunk.id() == "CAMG"sv) { + context.viewport_mode = static_cast(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)); } } @@ -401,16 +355,16 @@ static ErrorOr decode_iff_chunks(ILBMLoadingContext& context) static ErrorOr 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"); - 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"); - context.bm_header = *bit_cast(first_chunk.data.data()); + context.bm_header = *bit_cast(first_chunk.data().data()); if (context.bm_header.mask >= MaskType::__Count) return Error::from_string_literal("IFFImageDecoderPlugin: Unsupported mask type");