diff --git a/Base/res/apps/ImageViewer.af b/Base/res/apps/ImageViewer.af index fd99c575d5..60298be3f8 100644 --- a/Base/res/apps/ImageViewer.af +++ b/Base/res/apps/ImageViewer.af @@ -4,4 +4,4 @@ Executable=/bin/ImageViewer Category=Graphics [Launcher] -FileTypes=bmp,dds,gif,ico,iff,jpeg,jpg,jxl,pbm,pgm,png,ppm,qoi,tga,tiff,tif,tvg +FileTypes=bmp,dds,gif,ico,iff,jpeg,jpg,jxl,lbm,pbm,pgm,png,ppm,qoi,tga,tiff,tif,tvg diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 40479c0eb3..796f8ed592 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -149,6 +149,17 @@ TEST_CASE(test_ilbm_ham6) EXPECT_EQ(frame.image->get_pixel(77, 107), Gfx::Color(0xf0, 0x40, 0x40, 0xff)); } +TEST_CASE(test_ilbm_dos) +{ + auto file = MUST(Core::MappedFile::map(TEST_INPUT("ilbm/serenity.lbm"sv))); + EXPECT(Gfx::ILBMImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::ILBMImageDecoderPlugin::create(file->bytes())); + + auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 640, 480 })); + + EXPECT_EQ(frame.image->get_pixel(315, 134), Gfx::Color::NamedColor::Red); +} + TEST_CASE(test_ilbm_malformed_header) { Array test_inputs = { diff --git a/Tests/LibGfx/test-inputs/ilbm/serenity.lbm b/Tests/LibGfx/test-inputs/ilbm/serenity.lbm new file mode 100644 index 0000000000..b44a31d53b Binary files /dev/null and b/Tests/LibGfx/test-inputs/ilbm/serenity.lbm differ diff --git a/Userland/Libraries/LibCore/MimeData.cpp b/Userland/Libraries/LibCore/MimeData.cpp index 0dcccf426a..9a68f9f39c 100644 --- a/Userland/Libraries/LibCore/MimeData.cpp +++ b/Userland/Libraries/LibCore/MimeData.cpp @@ -123,7 +123,7 @@ static Array const s_registered_mime_type = { MimeType { .name = "image/vnd.ms-dds"sv, .common_extensions = { ".dds"sv }, .description = "DDS image data"sv, .magic_bytes = Vector { 'D', 'D', 'S', ' ' } }, MimeType { .name = "image/webp"sv, .common_extensions = { ".webp"sv }, .description = "WebP image data"sv, .magic_bytes = Vector { 'W', 'E', 'B', 'P' }, .offset = 8 }, MimeType { .name = "image/x-icon"sv, .common_extensions = { ".ico"sv }, .description = "ICO image data"sv }, - MimeType { .name = "image/x-ilbm"sv, .common_extensions = { ".iff"sv }, .description = "Interleaved bitmap image data"sv, .magic_bytes = Vector { 0x46, 0x4F, 0x52, 0x4F } }, + MimeType { .name = "image/x-ilbm"sv, .common_extensions = { ".iff"sv, ".lbm"sv }, .description = "Interleaved bitmap image data"sv, .magic_bytes = Vector { 0x46, 0x4F, 0x52, 0x4F } }, MimeType { .name = "image/x-portable-bitmap"sv, .common_extensions = { ".pbm"sv }, .description = "PBM image data"sv, .magic_bytes = Vector { 0x50, 0x31, 0x0A } }, MimeType { .name = "image/x-portable-graymap"sv, .common_extensions = { ".pgm"sv }, .description = "PGM image data"sv, .magic_bytes = Vector { 0x50, 0x32, 0x0A } }, MimeType { .name = "image/x-portable-pixmap"sv, .common_extensions = { ".ppm"sv }, .description = "PPM image data"sv, .magic_bytes = Vector { 0x50, 0x33, 0x0A } }, diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h index 75369b2dde..33afb3fe62 100644 --- a/Userland/Libraries/LibGfx/Bitmap.h +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -25,6 +25,7 @@ __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpeg") \ __ENUMERATE_IMAGE_FORMAT(jpeg, ".jpg") \ __ENUMERATE_IMAGE_FORMAT(jxl, ".jxl") \ + __ENUMERATE_IMAGE_FORMAT(iff, ".lbm") \ __ENUMERATE_IMAGE_FORMAT(pbm, ".pbm") \ __ENUMERATE_IMAGE_FORMAT(pgm, ".pgm") \ __ENUMERATE_IMAGE_FORMAT(png, ".png") \ diff --git a/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp index f00832c0b0..4eb005fe37 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ILBMLoader.cpp @@ -46,6 +46,13 @@ enum class ViewportMode : u32 { HAM = 0x800 }; +enum class Format : u8 { + // Amiga interleaved format + ILBM = 0, + // PC-DeluxePaint chunky format + PBM = 1 +}; + AK_ENUM_BITWISE_OPERATORS(ViewportMode); struct ChunkHeader { @@ -96,6 +103,8 @@ struct ILBMLoadingContext { RefPtr bitmap; BMHDHeader bm_header; + + Format format; }; static ErrorOr decode_iff_ilbm_header(ILBMLoadingContext& context) @@ -107,9 +116,12 @@ static ErrorOr decode_iff_ilbm_header(ILBMLoadingContext& context) return Error::from_string_literal("Missing IFF header"); auto& header = *bit_cast(context.data.data()); - if (header.form != FourCC("FORM") || header.format != FourCC("ILBM")) + + if (header.form != FourCC("FORM") || (header.format != FourCC("ILBM") && header.format != FourCC("PBM "))) return Error::from_string_literal("Invalid IFF-ILBM header"); + context.format = header.format == FourCC("ILBM") ? Format::ILBM : Format::PBM; + return {}; } @@ -292,9 +304,15 @@ static ErrorOr decode_body_chunk(Chunk body_chunk, ILBMLoadingContext& con if (context.bm_header.compression == CompressionType::ByteRun) { auto plane_data = TRY(uncompress_byte_run(body_chunk.data, context)); - pixel_data = TRY(planar_to_chunky(plane_data, context)); + if (context.format == Format::ILBM) + pixel_data = TRY(planar_to_chunky(plane_data, context)); + else + pixel_data = plane_data; } else { - pixel_data = TRY(planar_to_chunky(body_chunk.data, context)); + if (context.format == Format::ILBM) + pixel_data = TRY(planar_to_chunky(body_chunk.data, context)); + else + pixel_data = TRY(ByteBuffer::copy(body_chunk.data.data(), body_chunk.data.size())); } // Some files already have 64 colors defined in the palette,