mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
LibGfx: Re-structure the whole initialization pattern for image decoders
When trying to figure out the correct implementation, we now have a very strong distinction on plugins that are well suited for sniffing, and plugins that need a MIME type to be chosen. Instead of having multiple calls to non-static virtual sniff methods for each Image decoding plugin, we have 2 static methods for each implementation: 1. The sniff method, which in contrast to the old method, gets a ReadonlyBytes parameter and ensures we can figure out the result with zero heap allocations for most implementations. 2. The create method, which just creates a new instance so we don't expose the constructor to everyone anymore. In addition to that, we have a new virtual method called initialize, which has a per-implementation initialization pattern to actually ensure each implementation can construct a decoder object, and then have a correct context being applied to it for the actual decoding.
This commit is contained in:
parent
6e6999ce57
commit
57e19a7e56
Notes:
sideshowbarker
2024-07-17 01:53:23 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/57e19a7e56 Pull-request: https://github.com/SerenityOS/serenity/pull/17101 Reviewed-by: https://github.com/linusg
33 changed files with 493 additions and 206 deletions
|
@ -9,7 +9,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::BMPImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::BMPImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -13,14 +13,18 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::GIFImageDecoderPlugin gif_decoder(data, size);
|
||||
auto bitmap_or_error = gif_decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::GIFImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
auto& gif_decoder = *decoder;
|
||||
auto bitmap_or_error = decoder->frame(0);
|
||||
if (!bitmap_or_error.is_error()) {
|
||||
auto const& bitmap = bitmap_or_error.value().image;
|
||||
// Looks like a valid GIF. Try to load the other frames:
|
||||
dbgln_if(GIF_DEBUG, "bitmap size: {}", bitmap->size());
|
||||
dbgln_if(GIF_DEBUG, "codec size: {}", gif_decoder.size());
|
||||
dbgln_if(GIF_DEBUG, "is_sniff: {}", gif_decoder.sniff());
|
||||
dbgln_if(GIF_DEBUG, "is_animated: {}", gif_decoder.is_animated());
|
||||
dbgln_if(GIF_DEBUG, "loop_count: {}", gif_decoder.loop_count());
|
||||
dbgln_if(GIF_DEBUG, "frame_count: {}", gif_decoder.frame_count());
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::ICOImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::ICOImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::JPGImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::JPGImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::PBMImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::PBMImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::PGMImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::PGMImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::PNGImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::PNGImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::PPMImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::PPMImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::QOIImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::QOIImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Gfx::TGAImageDecoderPlugin decoder(data, size);
|
||||
(void)decoder.frame(0);
|
||||
auto decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ data, size });
|
||||
if (decoder_or_error.is_error())
|
||||
return 0;
|
||||
auto decoder = decoder_or_error.release_value();
|
||||
decoder->initialize();
|
||||
(void)decoder->frame(0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,138 +24,180 @@
|
|||
TEST_CASE(test_bmp)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/bmpsuite_files/rgba32-1.bmp"sv).release_value();
|
||||
auto bmp = Gfx::BMPImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(bmp.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::BMPImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::BMPImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(bmp.sniff());
|
||||
EXPECT(!bmp.is_animated());
|
||||
EXPECT(!bmp.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = bmp.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_gif)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/graphics/download-animation.gif"sv).release_value();
|
||||
auto gif = Gfx::GIFImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(gif.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::GIFImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::GIFImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(gif.sniff());
|
||||
EXPECT(gif.is_animated());
|
||||
EXPECT(!gif.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = gif.frame(1).release_value_but_fixme_should_propagate_errors();
|
||||
auto frame = plugin_decoder->frame(1).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 400);
|
||||
}
|
||||
|
||||
TEST_CASE(test_not_ico)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/graphics/buggie.png"sv).release_value();
|
||||
auto ico = Gfx::ICOImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(ico.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::ICOImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::ICOImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(!ico.sniff());
|
||||
EXPECT(!ico.is_animated());
|
||||
EXPECT(!ico.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
EXPECT(ico.frame(0).is_error());
|
||||
EXPECT(plugin_decoder->frame(0).is_error());
|
||||
}
|
||||
|
||||
TEST_CASE(test_bmp_embedded_in_ico)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/icons/16x16/serenity.ico"sv).release_value();
|
||||
auto ico = Gfx::ICOImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(ico.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::ICOImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::ICOImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(ico.sniff());
|
||||
EXPECT(!ico.is_animated());
|
||||
EXPECT(!ico.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
EXPECT(!ico.frame(0).is_error());
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
}
|
||||
|
||||
TEST_CASE(test_jpg)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/bmpsuite_files/rgb24.jpg"sv).release_value();
|
||||
auto jpg = Gfx::JPGImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(jpg.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::JPGImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::JPGImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(jpg.sniff());
|
||||
EXPECT(!jpg.is_animated());
|
||||
EXPECT(!jpg.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = jpg.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_pbm)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/pbmsuite_files/buggie-raw.pbm"sv).release_value();
|
||||
auto pbm = Gfx::PBMImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(pbm.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::PBMImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::PBMImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(pbm.sniff());
|
||||
EXPECT(!pbm.is_animated());
|
||||
EXPECT(!pbm.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = pbm.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_pgm)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/pgmsuite_files/buggie-raw.pgm"sv).release_value();
|
||||
auto pgm = Gfx::PGMImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(pgm.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::PGMImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::PGMImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(pgm.sniff());
|
||||
EXPECT(!pgm.is_animated());
|
||||
EXPECT(!pgm.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = pgm.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_png)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/graphics/buggie.png"sv).release_value();
|
||||
auto png = Gfx::PNGImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(png.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::PNGImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::PNGImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(png.sniff());
|
||||
EXPECT(!png.is_animated());
|
||||
EXPECT(!png.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = png.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_ppm)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/ppmsuite_files/buggie-raw.ppm"sv).release_value();
|
||||
auto ppm = Gfx::PPMImageDecoderPlugin((u8 const*)file->data(), file->size());
|
||||
EXPECT(ppm.frame_count());
|
||||
EXPECT_EQ(MUST(Gfx::PPMImageDecoderPlugin::sniff({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::PPMImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(ppm.sniff());
|
||||
EXPECT(!ppm.is_animated());
|
||||
EXPECT(!ppm.loop_count());
|
||||
EXPECT(plugin_decoder->frame_count());
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame = ppm.frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame = plugin_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_targa_bottom_left)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/targasuite_files/buggie-bottom-left-uncompressed.tga"sv).release_value();
|
||||
auto tga = Gfx::TGAImageDecoderPlugin(reinterpret_cast<u8 const*>(file->data()), file->size());
|
||||
EXPECT_EQ(tga.frame_count(), 1u);
|
||||
EXPECT_EQ(MUST(Gfx::TGAImageDecoderPlugin::validate_before_create({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(tga.sniff());
|
||||
EXPECT(!tga.is_animated());
|
||||
EXPECT(!tga.loop_count());
|
||||
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame_or_error = tga.frame(0);
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame_or_error = plugin_decoder->frame(0);
|
||||
EXPECT(!frame_or_error.is_error());
|
||||
auto frame = frame_or_error.release_value();
|
||||
EXPECT(frame.duration == 0);
|
||||
|
@ -164,14 +206,19 @@ TEST_CASE(test_targa_bottom_left)
|
|||
TEST_CASE(test_targa_top_left)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/targasuite_files/buggie-top-left-uncompressed.tga"sv).release_value();
|
||||
auto tga = Gfx::TGAImageDecoderPlugin(reinterpret_cast<u8 const*>(file->data()), file->size());
|
||||
EXPECT_EQ(tga.frame_count(), 1u);
|
||||
EXPECT_EQ(MUST(Gfx::TGAImageDecoderPlugin::validate_before_create({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(tga.sniff());
|
||||
EXPECT(!tga.is_animated());
|
||||
EXPECT(!tga.loop_count());
|
||||
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame_or_error = tga.frame(0);
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame_or_error = plugin_decoder->frame(0);
|
||||
EXPECT(!frame_or_error.is_error());
|
||||
auto frame = frame_or_error.release_value();
|
||||
EXPECT(frame.duration == 0);
|
||||
|
@ -180,14 +227,19 @@ TEST_CASE(test_targa_top_left)
|
|||
TEST_CASE(test_targa_bottom_left_compressed)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/targasuite_files/buggie-bottom-left-compressed.tga"sv).release_value();
|
||||
auto tga = Gfx::TGAImageDecoderPlugin(reinterpret_cast<u8 const*>(file->data()), file->size());
|
||||
EXPECT_EQ(tga.frame_count(), 1u);
|
||||
EXPECT_EQ(MUST(Gfx::TGAImageDecoderPlugin::validate_before_create({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(tga.sniff());
|
||||
EXPECT(!tga.is_animated());
|
||||
EXPECT(!tga.loop_count());
|
||||
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame_or_error = tga.frame(0);
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame_or_error = plugin_decoder->frame(0);
|
||||
EXPECT(!frame_or_error.is_error());
|
||||
auto frame = frame_or_error.release_value();
|
||||
EXPECT(frame.duration == 0);
|
||||
|
@ -196,14 +248,19 @@ TEST_CASE(test_targa_bottom_left_compressed)
|
|||
TEST_CASE(test_targa_top_left_compressed)
|
||||
{
|
||||
auto file = Core::MappedFile::map("/res/html/misc/targasuite_files/buggie-top-left-compressed.tga"sv).release_value();
|
||||
auto tga = Gfx::TGAImageDecoderPlugin(reinterpret_cast<u8 const*>(file->data()), file->size());
|
||||
EXPECT_EQ(tga.frame_count(), 1u);
|
||||
EXPECT_EQ(MUST(Gfx::TGAImageDecoderPlugin::validate_before_create({ (u8 const*)file->data(), file->size() })), true);
|
||||
auto plugin_decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ (u8 const*)file->data(), file->size() });
|
||||
EXPECT(!plugin_decoder_or_error.is_error());
|
||||
auto plugin_decoder = plugin_decoder_or_error.release_value();
|
||||
EXPECT_EQ(plugin_decoder->initialize(), true);
|
||||
|
||||
EXPECT(tga.sniff());
|
||||
EXPECT(!tga.is_animated());
|
||||
EXPECT(!tga.loop_count());
|
||||
EXPECT_EQ(plugin_decoder->frame_count(), 1u);
|
||||
EXPECT(!plugin_decoder->is_animated());
|
||||
EXPECT(!plugin_decoder->loop_count());
|
||||
|
||||
auto frame_or_error = tga.frame(0);
|
||||
EXPECT(!plugin_decoder->frame(0).is_error());
|
||||
|
||||
auto frame_or_error = plugin_decoder->frame(0);
|
||||
EXPECT(!frame_or_error.is_error());
|
||||
auto frame = frame_or_error.release_value();
|
||||
EXPECT(frame.duration == 0);
|
||||
|
|
|
@ -210,9 +210,14 @@ Icon FileIconProvider::icon_for_executable(DeprecatedString const& path)
|
|||
bitmap = s_executable_icon.bitmap_for_size(icon_section.image_size);
|
||||
} else {
|
||||
// FIXME: Use the ImageDecoder service.
|
||||
auto frame_or_error = Gfx::PNGImageDecoderPlugin(reinterpret_cast<u8 const*>(section->raw_data()), section->size()).frame(0);
|
||||
if (!frame_or_error.is_error()) {
|
||||
bitmap = frame_or_error.value().image;
|
||||
if (Gfx::PNGImageDecoderPlugin::sniff({ section->raw_data(), section->size() }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto png_decoder = Gfx::PNGImageDecoderPlugin::create({ section->raw_data(), section->size() }).release_value_but_fixme_should_propagate_errors();
|
||||
if (png_decoder->initialize()) {
|
||||
auto frame_or_error = png_decoder->frame(0);
|
||||
if (!frame_or_error.is_error()) {
|
||||
bitmap = frame_or_error.value().image;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1400,12 +1400,12 @@ static ErrorOr<void> decode_bmp_pixel_data(BMPLoadingContext& context)
|
|||
return {};
|
||||
}
|
||||
|
||||
BMPImageDecoderPlugin::BMPImageDecoderPlugin(u8 const* data, size_t data_size, bool is_included_in_ico)
|
||||
BMPImageDecoderPlugin::BMPImageDecoderPlugin(u8 const* data, size_t data_size, IncludedInICO is_included_in_ico)
|
||||
{
|
||||
m_context = make<BMPLoadingContext>();
|
||||
m_context->file_bytes = data;
|
||||
m_context->file_size = data_size;
|
||||
m_context->is_included_in_ico = is_included_in_ico;
|
||||
m_context->is_included_in_ico = (is_included_in_ico == IncludedInICO::Yes);
|
||||
}
|
||||
|
||||
BMPImageDecoderPlugin::~BMPImageDecoderPlugin() = default;
|
||||
|
@ -1434,11 +1434,29 @@ bool BMPImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool BMPImageDecoderPlugin::sniff()
|
||||
bool BMPImageDecoderPlugin::initialize()
|
||||
{
|
||||
return !decode_bmp_header(*m_context).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<bool> BMPImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
BMPLoadingContext context;
|
||||
context.file_bytes = data.data();
|
||||
context.file_size = data.size();
|
||||
return !decode_bmp_header(context).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> BMPImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) BMPImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<BMPImageDecoderPlugin>> BMPImageDecoderPlugin::create_as_included_in_ico(Badge<ICOImageDecoderPlugin>, ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) BMPImageDecoderPlugin(data.data(), data.size(), IncludedInICO::Yes));
|
||||
}
|
||||
|
||||
bool BMPImageDecoderPlugin::sniff_dib()
|
||||
{
|
||||
return !decode_bmp_dib(*m_context).is_error();
|
||||
|
|
|
@ -6,21 +6,31 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibGfx/ICOLoader.h>
|
||||
#include <LibGfx/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
struct BMPLoadingContext;
|
||||
class ICOImageDecoderPlugin;
|
||||
|
||||
class BMPImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<BMPImageDecoderPlugin>> create_as_included_in_ico(Badge<ICOImageDecoderPlugin>, ReadonlyBytes);
|
||||
|
||||
enum class IncludedInICO {
|
||||
Yes,
|
||||
No,
|
||||
};
|
||||
|
||||
virtual ~BMPImageDecoderPlugin() override;
|
||||
BMPImageDecoderPlugin(u8 const*, size_t, bool is_included_in_ico = false);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
bool sniff_dib();
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
|
@ -28,6 +38,8 @@ public:
|
|||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
BMPImageDecoderPlugin(u8 const*, size_t, IncludedInICO included_in_ico = IncludedInICO::No);
|
||||
|
||||
OwnPtr<BMPLoadingContext> m_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -968,7 +968,7 @@ bool DDSImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool DDSImageDecoderPlugin::sniff()
|
||||
bool DDSImageDecoderPlugin::initialize()
|
||||
{
|
||||
// The header is always at least 128 bytes, so if the file is smaller, it can't be a DDS.
|
||||
return m_context->data_size > 128
|
||||
|
@ -978,6 +978,21 @@ bool DDSImageDecoderPlugin::sniff()
|
|||
&& m_context->data[3] == 0x20;
|
||||
}
|
||||
|
||||
ErrorOr<bool> DDSImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
// The header is always at least 128 bytes, so if the file is smaller, it can't be a DDS.
|
||||
return data.size() > 128
|
||||
&& data.data()[0] == 0x44
|
||||
&& data.data()[1] == 0x44
|
||||
&& data.data()[2] == 0x53
|
||||
&& data.data()[3] == 0x20;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> DDSImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) DDSImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
bool DDSImageDecoderPlugin::is_animated()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -235,19 +235,23 @@ struct DDSLoadingContext;
|
|||
|
||||
class DDSImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~DDSImageDecoderPlugin() override;
|
||||
DDSImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
DDSImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
OwnPtr<DDSLoadingContext> m_context;
|
||||
void dump_debug();
|
||||
};
|
||||
|
|
|
@ -614,12 +614,23 @@ bool GIFImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->frame_buffer->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool GIFImageDecoderPlugin::sniff()
|
||||
bool GIFImageDecoderPlugin::initialize()
|
||||
{
|
||||
InputMemoryStream stream { { m_context->data, m_context->data_size } };
|
||||
return !decode_gif_header(stream).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<bool> GIFImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
InputMemoryStream stream { { data.data(), data.size() } };
|
||||
return !decode_gif_header(stream).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> GIFImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) GIFImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
bool GIFImageDecoderPlugin::is_animated()
|
||||
{
|
||||
if (m_context->error_state != GIFLoadingContext::ErrorState::NoError) {
|
||||
|
|
|
@ -15,19 +15,23 @@ struct GIFLoadingContext;
|
|||
|
||||
class GIFImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~GIFImageDecoderPlugin() override;
|
||||
GIFImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
GIFImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
OwnPtr<GIFLoadingContext> m_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ static bool load_ico_directory(ICOLoadingContext& context)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
||||
bool ICOImageDecoderPlugin::load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
||||
{
|
||||
if (context.state < ICOLoadingContext::State::DirectoryDecoded) {
|
||||
if (!load_ico_directory(context)) {
|
||||
|
@ -153,20 +153,25 @@ static bool load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
|||
}
|
||||
|
||||
ICOImageDescriptor& desc = context.images[real_index];
|
||||
|
||||
PNGImageDecoderPlugin png_decoder(context.data + desc.offset, desc.size);
|
||||
if (png_decoder.sniff()) {
|
||||
auto decoded_png_frame = png_decoder.frame(0);
|
||||
if (decoded_png_frame.is_error() || !decoded_png_frame.value().image) {
|
||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load PNG encoded image index: {}", real_index);
|
||||
return false;
|
||||
if (PNGImageDecoderPlugin::sniff({ context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto png_decoder = PNGImageDecoderPlugin::create({ context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors();
|
||||
if (png_decoder->initialize()) {
|
||||
auto decoded_png_frame = png_decoder->frame(0);
|
||||
if (decoded_png_frame.is_error() || !decoded_png_frame.value().image) {
|
||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load PNG encoded image index: {}", real_index);
|
||||
return false;
|
||||
}
|
||||
desc.bitmap = decoded_png_frame.value().image;
|
||||
return true;
|
||||
}
|
||||
desc.bitmap = decoded_png_frame.value().image;
|
||||
return true;
|
||||
return false;
|
||||
} else {
|
||||
BMPImageDecoderPlugin bmp_decoder(context.data + desc.offset, desc.size, true);
|
||||
if (bmp_decoder.sniff_dib()) {
|
||||
auto decoded_bmp_frame = bmp_decoder.frame(0);
|
||||
auto bmp_decoder = BMPImageDecoderPlugin::create_as_included_in_ico({}, { context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors();
|
||||
// NOTE: We don't initialize a BMP decoder in the usual way, but rather
|
||||
// we just create an object and try to sniff for a frame when it's included
|
||||
// inside an ICO image.
|
||||
if (bmp_decoder->sniff_dib()) {
|
||||
auto decoded_bmp_frame = bmp_decoder->frame(0);
|
||||
if (decoded_bmp_frame.is_error() || !decoded_bmp_frame.value().image) {
|
||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load BMP encoded image index: {}", real_index);
|
||||
return false;
|
||||
|
@ -180,6 +185,17 @@ static bool load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
|||
}
|
||||
}
|
||||
|
||||
ErrorOr<bool> ICOImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
InputMemoryStream stream { { data.data(), data.size() } };
|
||||
return decode_ico_header(stream).has_value();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> ICOImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) ICOImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
ICOImageDecoderPlugin::ICOImageDecoderPlugin(u8 const* data, size_t size)
|
||||
{
|
||||
m_context = make<ICOLoadingContext>();
|
||||
|
@ -219,7 +235,7 @@ bool ICOImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->images[0].bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool ICOImageDecoderPlugin::sniff()
|
||||
bool ICOImageDecoderPlugin::initialize()
|
||||
{
|
||||
InputMemoryStream stream { { m_context->data, m_context->data_size } };
|
||||
return decode_ico_header(stream).has_value();
|
||||
|
|
|
@ -14,19 +14,24 @@ struct ICOLoadingContext;
|
|||
|
||||
class ICOImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~ICOImageDecoderPlugin() override;
|
||||
ICOImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
ICOImageDecoderPlugin(u8 const*, size_t);
|
||||
static bool load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index);
|
||||
|
||||
OwnPtr<ICOLoadingContext> m_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,64 +20,58 @@
|
|||
|
||||
namespace Gfx {
|
||||
|
||||
struct ImagePluginInitializer {
|
||||
ErrorOr<bool> (*sniff)(ReadonlyBytes) = nullptr;
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> (*create)(ReadonlyBytes) = nullptr;
|
||||
};
|
||||
|
||||
static constexpr ImagePluginInitializer s_initializers[] = {
|
||||
{ PNGImageDecoderPlugin::sniff, PNGImageDecoderPlugin::create },
|
||||
{ GIFImageDecoderPlugin::sniff, GIFImageDecoderPlugin::create },
|
||||
{ BMPImageDecoderPlugin::sniff, BMPImageDecoderPlugin::create },
|
||||
{ PBMImageDecoderPlugin::sniff, PBMImageDecoderPlugin::create },
|
||||
{ PGMImageDecoderPlugin::sniff, PGMImageDecoderPlugin::create },
|
||||
{ PPMImageDecoderPlugin::sniff, PPMImageDecoderPlugin::create },
|
||||
{ ICOImageDecoderPlugin::sniff, ICOImageDecoderPlugin::create },
|
||||
{ JPGImageDecoderPlugin::sniff, JPGImageDecoderPlugin::create },
|
||||
{ DDSImageDecoderPlugin::sniff, DDSImageDecoderPlugin::create },
|
||||
{ QOIImageDecoderPlugin::sniff, QOIImageDecoderPlugin::create },
|
||||
};
|
||||
|
||||
struct ImagePluginWithMIMETypeInitializer {
|
||||
ErrorOr<bool> (*validate_before_create)(ReadonlyBytes) = nullptr;
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> (*create)(ReadonlyBytes) = nullptr;
|
||||
StringView mime_type;
|
||||
};
|
||||
|
||||
static constexpr ImagePluginWithMIMETypeInitializer s_initializers_with_mime_type[] = {
|
||||
{ TGAImageDecoderPlugin::validate_before_create, TGAImageDecoderPlugin::create, "image/x-targa"sv },
|
||||
};
|
||||
|
||||
static OwnPtr<ImageDecoderPlugin> probe_and_sniff_for_appropriate_plugin(ReadonlyBytes bytes)
|
||||
{
|
||||
auto* data = bytes.data();
|
||||
auto size = bytes.size();
|
||||
OwnPtr<ImageDecoderPlugin> plugin;
|
||||
|
||||
plugin = make<PNGImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<GIFImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<BMPImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<PBMImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<PGMImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<PPMImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<ICOImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<JPGImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<DDSImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
plugin = make<QOIImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
|
||||
for (auto& plugin : s_initializers) {
|
||||
auto sniff_result = plugin.sniff(bytes).release_value_but_fixme_should_propagate_errors();
|
||||
if (!sniff_result)
|
||||
continue;
|
||||
auto plugin_decoder = plugin.create(bytes).release_value_but_fixme_should_propagate_errors();
|
||||
if (plugin_decoder->initialize())
|
||||
return plugin_decoder;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static OwnPtr<ImageDecoderPlugin> probe_and_sniff_for_appropriate_plugin_with_known_mime_type(StringView mime_type, ReadonlyBytes bytes)
|
||||
{
|
||||
auto* data = bytes.data();
|
||||
auto size = bytes.size();
|
||||
OwnPtr<ImageDecoderPlugin> plugin;
|
||||
if (mime_type == "image/x-targa"sv) {
|
||||
plugin = make<TGAImageDecoderPlugin>(data, size);
|
||||
if (plugin->sniff())
|
||||
return plugin;
|
||||
for (auto& plugin : s_initializers_with_mime_type) {
|
||||
if (plugin.mime_type != mime_type)
|
||||
continue;
|
||||
auto validation_result = plugin.validate_before_create(bytes).release_value_but_fixme_should_propagate_errors();
|
||||
if (!validation_result)
|
||||
continue;
|
||||
auto plugin_decoder = plugin.create(bytes).release_value_but_fixme_should_propagate_errors();
|
||||
if (plugin_decoder->initialize())
|
||||
return plugin_decoder;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
virtual void set_volatile() = 0;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) = 0;
|
||||
|
||||
virtual bool sniff() = 0;
|
||||
virtual bool initialize() = 0;
|
||||
|
||||
virtual bool is_animated() = 0;
|
||||
virtual size_t loop_count() = 0;
|
||||
|
@ -55,7 +55,6 @@ public:
|
|||
int height() const { return size().height(); }
|
||||
void set_volatile() { m_plugin->set_volatile(); }
|
||||
[[nodiscard]] bool set_nonvolatile(bool& was_purged) { return m_plugin->set_nonvolatile(was_purged); }
|
||||
bool sniff() const { return m_plugin->sniff(); }
|
||||
bool is_animated() const { return m_plugin->is_animated(); }
|
||||
size_t loop_count() const { return m_plugin->loop_count(); }
|
||||
size_t frame_count() const { return m_plugin->frame_count(); }
|
||||
|
|
|
@ -1179,12 +1179,22 @@ bool JPGImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool JPGImageDecoderPlugin::sniff()
|
||||
bool JPGImageDecoderPlugin::initialize()
|
||||
{
|
||||
return m_context->data_size > 3
|
||||
&& m_context->data[0] == 0xFF
|
||||
&& m_context->data[1] == 0xD8
|
||||
&& m_context->data[2] == 0xFF;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<bool> JPGImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
return data.size() > 3
|
||||
&& data.data()[0] == 0xFF
|
||||
&& data.data()[1] == 0xD8
|
||||
&& data.data()[2] == 0xFF;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JPGImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) JPGImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
bool JPGImageDecoderPlugin::is_animated()
|
||||
|
|
|
@ -14,18 +14,22 @@ struct JPGLoadingContext;
|
|||
|
||||
class JPGImageDecoderPlugin : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~JPGImageDecoderPlugin() override;
|
||||
JPGImageDecoderPlugin(u8 const*, size_t);
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
JPGImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
OwnPtr<JPGLoadingContext> m_context;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -913,11 +913,24 @@ bool PNGImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool PNGImageDecoderPlugin::sniff()
|
||||
bool PNGImageDecoderPlugin::initialize()
|
||||
{
|
||||
return decode_png_header(*m_context);
|
||||
}
|
||||
|
||||
ErrorOr<bool> PNGImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
PNGLoadingContext context;
|
||||
context.data = data.data();
|
||||
context.data_size = data.size();
|
||||
return decode_png_header(context);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> PNGImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) PNGImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
bool PNGImageDecoderPlugin::is_animated()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -14,19 +14,23 @@ struct PNGLoadingContext;
|
|||
|
||||
class PNGImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~PNGImageDecoderPlugin() override;
|
||||
PNGImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index) override;
|
||||
|
||||
private:
|
||||
PNGImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
OwnPtr<PNGLoadingContext> m_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -49,6 +49,9 @@ struct PortableImageMapLoadingContext {
|
|||
template<typename TContext>
|
||||
class PortableImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
PortableImageDecoderPlugin(u8 const*, size_t);
|
||||
virtual ~PortableImageDecoderPlugin() override = default;
|
||||
|
||||
|
@ -57,8 +60,7 @@ public:
|
|||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
|
||||
virtual bool sniff() override;
|
||||
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
|
@ -108,7 +110,7 @@ bool PortableImageDecoderPlugin<TContext>::set_nonvolatile(bool& was_purged)
|
|||
}
|
||||
|
||||
template<typename TContext>
|
||||
bool PortableImageDecoderPlugin<TContext>::sniff()
|
||||
bool PortableImageDecoderPlugin<TContext>::initialize()
|
||||
{
|
||||
using Context = TContext;
|
||||
if (m_context->data_size < 2)
|
||||
|
@ -123,6 +125,28 @@ bool PortableImageDecoderPlugin<TContext>::sniff()
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> PortableImageDecoderPlugin<TContext>::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) PortableImageDecoderPlugin<TContext>(data.data(), data.size()));
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
ErrorOr<bool> PortableImageDecoderPlugin<TContext>::sniff(ReadonlyBytes data)
|
||||
{
|
||||
using Context = TContext;
|
||||
if (data.size() < 2)
|
||||
return false;
|
||||
|
||||
if (data.data()[0] == 'P' && data.data()[1] == Context::FormatDetails::ascii_magic_number)
|
||||
return true;
|
||||
|
||||
if (data.data()[0] == 'P' && data.data()[1] == Context::FormatDetails::binary_magic_number)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
bool PortableImageDecoderPlugin<TContext>::is_animated()
|
||||
{
|
||||
|
|
|
@ -223,12 +223,23 @@ bool QOIImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
|||
return m_context->bitmap->set_nonvolatile(was_purged);
|
||||
}
|
||||
|
||||
bool QOIImageDecoderPlugin::sniff()
|
||||
bool QOIImageDecoderPlugin::initialize()
|
||||
{
|
||||
InputMemoryStream stream { { m_context->data, m_context->data_size } };
|
||||
return !decode_qoi_header(stream).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<bool> QOIImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
{
|
||||
InputMemoryStream stream { { data.data(), data.size() } };
|
||||
return !decode_qoi_header(stream).is_error();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> QOIImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) QOIImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
ErrorOr<ImageFrameDescriptor> QOIImageDecoderPlugin::frame(size_t index)
|
||||
{
|
||||
if (index > 0)
|
||||
|
|
|
@ -40,13 +40,15 @@ struct QOILoadingContext {
|
|||
|
||||
class QOIImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> sniff(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~QOIImageDecoderPlugin() override = default;
|
||||
QOIImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override { return false; }
|
||||
virtual size_t loop_count() override { return 0; }
|
||||
virtual size_t frame_count() override { return 1; }
|
||||
|
@ -56,6 +58,8 @@ private:
|
|||
ErrorOr<void> decode_header_and_update_context(InputMemoryStream&);
|
||||
ErrorOr<void> decode_image_and_update_context(InputMemoryStream&);
|
||||
|
||||
QOIImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
OwnPtr<QOILoadingContext> m_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -212,11 +212,28 @@ bool TGAImageDecoderPlugin::decode_tga_header()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TGAImageDecoderPlugin::sniff()
|
||||
bool TGAImageDecoderPlugin::initialize()
|
||||
{
|
||||
return decode_tga_header();
|
||||
}
|
||||
|
||||
ErrorOr<bool> TGAImageDecoderPlugin::validate_before_create(ReadonlyBytes data)
|
||||
{
|
||||
if (data.size() < sizeof(TGAHeader))
|
||||
return false;
|
||||
TGAHeader const& header = *reinterpret_cast<TGAHeader const*>(data.data());
|
||||
if (header.data_type_code == TGADataType::UncompressedRGB && data.size() < (header.width * header.height * (header.bits_per_pixel / 8)))
|
||||
return false;
|
||||
if (header.bits_per_pixel < 8 || header.bits_per_pixel > 32)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> TGAImageDecoderPlugin::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) TGAImageDecoderPlugin(data.data(), data.size()));
|
||||
}
|
||||
|
||||
bool TGAImageDecoderPlugin::is_animated()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -14,13 +14,16 @@ struct TGALoadingContext;
|
|||
|
||||
class TGAImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||
public:
|
||||
static ErrorOr<bool> validate_before_create(ReadonlyBytes);
|
||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||
|
||||
virtual ~TGAImageDecoderPlugin() override;
|
||||
TGAImageDecoderPlugin(u8 const*, size_t);
|
||||
|
||||
virtual IntSize size() override;
|
||||
virtual void set_volatile() override;
|
||||
[[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override;
|
||||
virtual bool sniff() override;
|
||||
virtual bool initialize() override;
|
||||
virtual bool is_animated() override;
|
||||
virtual size_t loop_count() override;
|
||||
virtual size_t frame_count() override;
|
||||
|
|
|
@ -269,9 +269,14 @@ ErrorOr<ByteBuffer> Filter::decode_jbig2(ReadonlyBytes)
|
|||
|
||||
ErrorOr<ByteBuffer> Filter::decode_dct(ReadonlyBytes bytes)
|
||||
{
|
||||
Gfx::JPGImageDecoderPlugin decoder(bytes.data(), bytes.size());
|
||||
auto frame = TRY(decoder.frame(0));
|
||||
return frame.image->serialize_to_byte_buffer();
|
||||
if (Gfx::JPGImageDecoderPlugin::sniff({ bytes.data(), bytes.size() }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto decoder = Gfx::JPGImageDecoderPlugin::create({ bytes.data(), bytes.size() }).release_value_but_fixme_should_propagate_errors();
|
||||
if (decoder->initialize()) {
|
||||
auto frame = TRY(decoder->frame(0));
|
||||
return frame.image->serialize_to_byte_buffer();
|
||||
}
|
||||
}
|
||||
return AK::Error::from_string_literal("Not a JPG image!");
|
||||
};
|
||||
|
||||
ErrorOr<ByteBuffer> Filter::decode_jpx(ReadonlyBytes)
|
||||
|
|
|
@ -142,11 +142,23 @@ void SpiceAgent::on_message_received()
|
|||
} else {
|
||||
ErrorOr<Gfx::ImageFrameDescriptor> frame_or_error = Gfx::ImageFrameDescriptor {};
|
||||
if (type == ClipboardType::PNG) {
|
||||
frame_or_error = Gfx::PNGImageDecoderPlugin(data_buffer.data(), data_buffer.size()).frame(0);
|
||||
if (Gfx::PNGImageDecoderPlugin::sniff({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto png_decoder = Gfx::PNGImageDecoderPlugin::create({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors();
|
||||
if (png_decoder->initialize())
|
||||
frame_or_error = png_decoder->frame(0);
|
||||
}
|
||||
} else if (type == ClipboardType::BMP) {
|
||||
frame_or_error = Gfx::BMPImageDecoderPlugin(data_buffer.data(), data_buffer.size()).frame(0);
|
||||
if (Gfx::BMPImageDecoderPlugin::sniff({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto bmp_decoder = Gfx::BMPImageDecoderPlugin::create({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors();
|
||||
if (bmp_decoder->initialize())
|
||||
frame_or_error = bmp_decoder->frame(0);
|
||||
}
|
||||
} else if (type == ClipboardType::JPG) {
|
||||
frame_or_error = Gfx::JPGImageDecoderPlugin(data_buffer.data(), data_buffer.size()).frame(0);
|
||||
if (Gfx::JPGImageDecoderPlugin::sniff({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors()) {
|
||||
auto jpg_decoder = Gfx::JPGImageDecoderPlugin::create({ data_buffer.data(), data_buffer.size() }).release_value_but_fixme_should_propagate_errors();
|
||||
if (jpg_decoder->initialize())
|
||||
frame_or_error = jpg_decoder->frame(0);
|
||||
}
|
||||
} else {
|
||||
dbgln("Unknown clipboard type: {}", (u32)type);
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue