mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 17:54:57 +00:00
LibGfx: Return ErrorOr<> in ICOLoader internal functions
This commit is contained in:
parent
3f932081ae
commit
492d5d7cb2
2 changed files with 41 additions and 55 deletions
|
@ -59,24 +59,22 @@ struct ICOLoadingContext {
|
||||||
size_t largest_index;
|
size_t largest_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Optional<size_t> decode_ico_header(DeprecatedInputMemoryStream& stream)
|
static ErrorOr<size_t> decode_ico_header(DeprecatedInputMemoryStream& stream)
|
||||||
{
|
{
|
||||||
ICONDIR header;
|
ICONDIR header;
|
||||||
stream >> Bytes { &header, sizeof(header) };
|
stream >> Bytes { &header, sizeof(header) };
|
||||||
if (stream.handle_any_error())
|
TRY(stream.try_handle_any_error());
|
||||||
return {};
|
|
||||||
|
|
||||||
if (header.must_be_0 != 0 || header.must_be_1 != 1)
|
if (header.must_be_0 != 0 || header.must_be_1 != 1)
|
||||||
return {};
|
return Error::from_string_literal("Invalid ICO header");
|
||||||
return { header.image_count };
|
return { header.image_count };
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<ICOImageDescriptor> decode_ico_direntry(DeprecatedInputMemoryStream& stream)
|
static ErrorOr<ICOImageDescriptor> decode_ico_direntry(DeprecatedInputMemoryStream& stream)
|
||||||
{
|
{
|
||||||
ICONDIRENTRY entry;
|
ICONDIRENTRY entry;
|
||||||
stream >> Bytes { &entry, sizeof(entry) };
|
stream >> Bytes { &entry, sizeof(entry) };
|
||||||
if (stream.handle_any_error())
|
TRY(stream.try_handle_any_error());
|
||||||
return {};
|
|
||||||
|
|
||||||
ICOImageDescriptor desc = { entry.width, entry.height, entry.bits_per_pixel, entry.offset, entry.size, nullptr };
|
ICOImageDescriptor desc = { entry.width, entry.height, entry.bits_per_pixel, entry.offset, entry.size, nullptr };
|
||||||
if (desc.width == 0)
|
if (desc.width == 0)
|
||||||
|
@ -106,89 +104,77 @@ static size_t find_largest_image(ICOLoadingContext const& context)
|
||||||
return largest_index;
|
return largest_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool load_ico_directory(ICOLoadingContext& context)
|
static ErrorOr<void> load_ico_directory(ICOLoadingContext& context)
|
||||||
{
|
{
|
||||||
DeprecatedInputMemoryStream stream { { context.data, context.data_size } };
|
DeprecatedInputMemoryStream stream { { context.data, context.data_size } };
|
||||||
|
|
||||||
auto image_count = decode_ico_header(stream);
|
auto image_count = TRY(decode_ico_header(stream));
|
||||||
if (!image_count.has_value() || image_count.value() == 0) {
|
if (image_count == 0)
|
||||||
return false;
|
return Error::from_string_literal("ICO file has no images");
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < image_count.value(); ++i) {
|
for (size_t i = 0; i < image_count; ++i) {
|
||||||
auto maybe_desc = decode_ico_direntry(stream);
|
auto desc = TRY(decode_ico_direntry(stream));
|
||||||
if (!maybe_desc.has_value()) {
|
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_directory: error loading entry: {}", i);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& desc = maybe_desc.value();
|
|
||||||
if (desc.offset + desc.size < desc.offset // detect integer overflow
|
if (desc.offset + desc.size < desc.offset // detect integer overflow
|
||||||
|| (desc.offset + desc.size) > context.data_size) {
|
|| (desc.offset + desc.size) > context.data_size) {
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_directory: offset: {} size: {} doesn't fit in ICO size: {}", desc.offset, desc.size, context.data_size);
|
dbgln_if(ICO_DEBUG, "load_ico_directory: offset: {} size: {} doesn't fit in ICO size: {}", desc.offset, desc.size, context.data_size);
|
||||||
return false;
|
return Error::from_string_literal("ICO size too large");
|
||||||
}
|
}
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_directory: index {} width: {} height: {} offset: {} size: {}", i, desc.width, desc.height, desc.offset, desc.size);
|
dbgln_if(ICO_DEBUG, "load_ico_directory: index {} width: {} height: {} offset: {} size: {}", i, desc.width, desc.height, desc.offset, desc.size);
|
||||||
context.images.append(desc);
|
TRY(context.images.try_append(desc));
|
||||||
}
|
}
|
||||||
context.largest_index = find_largest_image(context);
|
context.largest_index = find_largest_image(context);
|
||||||
context.state = ICOLoadingContext::State::DirectoryDecoded;
|
context.state = ICOLoadingContext::State::DirectoryDecoded;
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICOImageDecoderPlugin::load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
ErrorOr<void> ICOImageDecoderPlugin::load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index)
|
||||||
{
|
{
|
||||||
if (context.state < ICOLoadingContext::State::DirectoryDecoded) {
|
if (context.state < ICOLoadingContext::State::DirectoryDecoded)
|
||||||
if (!load_ico_directory(context)) {
|
TRY(load_ico_directory(context));
|
||||||
context.state = ICOLoadingContext::State::Error;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
context.state = ICOLoadingContext::State::DirectoryDecoded;
|
|
||||||
}
|
|
||||||
size_t real_index = context.largest_index;
|
size_t real_index = context.largest_index;
|
||||||
if (index.has_value())
|
if (index.has_value())
|
||||||
real_index = index.value();
|
real_index = index.value();
|
||||||
if (real_index >= context.images.size()) {
|
if (real_index >= context.images.size())
|
||||||
return false;
|
return Error::from_string_literal("Index out of bounds");
|
||||||
}
|
|
||||||
|
|
||||||
ICOImageDescriptor& desc = context.images[real_index];
|
ICOImageDescriptor& desc = context.images[real_index];
|
||||||
if (PNGImageDecoderPlugin::sniff({ context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors()) {
|
if (TRY(PNGImageDecoderPlugin::sniff({ context.data + desc.offset, desc.size }))) {
|
||||||
auto png_decoder = PNGImageDecoderPlugin::create({ context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors();
|
auto png_decoder = TRY(PNGImageDecoderPlugin::create({ context.data + desc.offset, desc.size }));
|
||||||
if (png_decoder->initialize()) {
|
if (png_decoder->initialize()) {
|
||||||
auto decoded_png_frame = png_decoder->frame(0);
|
auto decoded_png_frame = TRY(png_decoder->frame(0));
|
||||||
if (decoded_png_frame.is_error() || !decoded_png_frame.value().image) {
|
if (!decoded_png_frame.image) {
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load PNG encoded image index: {}", real_index);
|
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load PNG encoded image index: {}", real_index);
|
||||||
return false;
|
return Error::from_string_literal("Encoded image not null");
|
||||||
}
|
}
|
||||||
desc.bitmap = decoded_png_frame.value().image;
|
desc.bitmap = decoded_png_frame.image;
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
return false;
|
return Error::from_string_literal("Couldn't initialize PNG Decoder");
|
||||||
} else {
|
} else {
|
||||||
auto bmp_decoder = BMPImageDecoderPlugin::create_as_included_in_ico({}, { context.data + desc.offset, desc.size }).release_value_but_fixme_should_propagate_errors();
|
auto bmp_decoder = TRY(BMPImageDecoderPlugin::create_as_included_in_ico({}, { context.data + desc.offset, desc.size }));
|
||||||
// NOTE: We don't initialize a BMP decoder in the usual way, but rather
|
// 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
|
// we just create an object and try to sniff for a frame when it's included
|
||||||
// inside an ICO image.
|
// inside an ICO image.
|
||||||
if (bmp_decoder->sniff_dib()) {
|
if (bmp_decoder->sniff_dib()) {
|
||||||
auto decoded_bmp_frame = bmp_decoder->frame(0);
|
auto decoded_bmp_frame = TRY(bmp_decoder->frame(0));
|
||||||
if (decoded_bmp_frame.is_error() || !decoded_bmp_frame.value().image) {
|
if (!decoded_bmp_frame.image) {
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load BMP encoded image index: {}", real_index);
|
dbgln_if(ICO_DEBUG, "load_ico_bitmap: failed to load BMP encoded image index: {}", real_index);
|
||||||
return false;
|
return Error::from_string_literal("Encoded image not null");
|
||||||
}
|
}
|
||||||
desc.bitmap = decoded_bmp_frame.value().image;
|
desc.bitmap = decoded_bmp_frame.image;
|
||||||
} else {
|
} else {
|
||||||
dbgln_if(ICO_DEBUG, "load_ico_bitmap: encoded image not supported at index: {}", real_index);
|
dbgln_if(ICO_DEBUG, "load_ico_bitmap: encoded image not supported at index: {}", real_index);
|
||||||
return false;
|
return Error::from_string_literal("Encoded image not supported");
|
||||||
}
|
}
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<bool> ICOImageDecoderPlugin::sniff(ReadonlyBytes data)
|
ErrorOr<bool> ICOImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||||
{
|
{
|
||||||
DeprecatedInputMemoryStream stream { { data.data(), data.size() } };
|
DeprecatedInputMemoryStream stream { { data.data(), data.size() } };
|
||||||
return decode_ico_header(stream).has_value();
|
return !decode_ico_header(stream).is_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> ICOImageDecoderPlugin::create(ReadonlyBytes data)
|
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> ICOImageDecoderPlugin::create(ReadonlyBytes data)
|
||||||
|
@ -212,7 +198,7 @@ IntSize ICOImageDecoderPlugin::size()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_context->state < ICOLoadingContext::State::DirectoryDecoded) {
|
if (m_context->state < ICOLoadingContext::State::DirectoryDecoded) {
|
||||||
if (!load_ico_directory(*m_context)) {
|
if (!load_ico_directory(*m_context).is_error()) {
|
||||||
m_context->state = ICOLoadingContext::State::Error;
|
m_context->state = ICOLoadingContext::State::Error;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -238,7 +224,7 @@ bool ICOImageDecoderPlugin::set_nonvolatile(bool& was_purged)
|
||||||
bool ICOImageDecoderPlugin::initialize()
|
bool ICOImageDecoderPlugin::initialize()
|
||||||
{
|
{
|
||||||
DeprecatedInputMemoryStream stream { { m_context->data, m_context->data_size } };
|
DeprecatedInputMemoryStream stream { { m_context->data, m_context->data_size } };
|
||||||
return decode_ico_header(stream).has_value();
|
return !decode_ico_header(stream).is_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICOImageDecoderPlugin::is_animated()
|
bool ICOImageDecoderPlugin::is_animated()
|
||||||
|
@ -266,8 +252,8 @@ ErrorOr<ImageFrameDescriptor> ICOImageDecoderPlugin::frame(size_t index)
|
||||||
|
|
||||||
if (m_context->state < ICOLoadingContext::State::BitmapDecoded) {
|
if (m_context->state < ICOLoadingContext::State::BitmapDecoded) {
|
||||||
// NOTE: This forces the chunk decoding to happen.
|
// NOTE: This forces the chunk decoding to happen.
|
||||||
bool success = load_ico_bitmap(*m_context, {});
|
auto maybe_error = load_ico_bitmap(*m_context, {});
|
||||||
if (!success) {
|
if (maybe_error.is_error()) {
|
||||||
m_context->state = ICOLoadingContext::State::Error;
|
m_context->state = ICOLoadingContext::State::Error;
|
||||||
return Error::from_string_literal("ICOImageDecoderPlugin: Decoding failed");
|
return Error::from_string_literal("ICOImageDecoderPlugin: Decoding failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ICOImageDecoderPlugin(u8 const*, size_t);
|
ICOImageDecoderPlugin(u8 const*, size_t);
|
||||||
static bool load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index);
|
static ErrorOr<void> load_ico_bitmap(ICOLoadingContext& context, Optional<size_t> index);
|
||||||
|
|
||||||
OwnPtr<ICOLoadingContext> m_context;
|
OwnPtr<ICOLoadingContext> m_context;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue