1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 07:17:35 +00:00

LibGfx: Lazily load TinyVG image data

This matches the behavior of other decoders, and ensures just getting
the size (e.g. for the `file` command) does not read the whole file.
This commit is contained in:
MacDue 2023-07-08 03:29:51 +01:00 committed by Andreas Kling
parent 753cf04be8
commit 57c81c1719
2 changed files with 78 additions and 19 deletions

View file

@ -329,7 +329,11 @@ private:
ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(Stream& stream) ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(Stream& stream)
{ {
auto header = TRY(decode_tinyvg_header(stream)); return decode(stream, TRY(decode_tinyvg_header(stream)));
}
ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(Stream& stream, TinyVGHeader const& header)
{
if (header.version != 1) if (header.version != 1)
return Error::from_string_literal("Invalid TinyVG: Unsupported version"); return Error::from_string_literal("Invalid TinyVG: Unsupported version");
@ -468,8 +472,60 @@ void TinyVGDecodedImageData::draw_transformed(Painter& painter, AffineTransform
} }
} }
struct TinyVGLoadingContext {
FixedMemoryStream stream;
TinyVGHeader header {};
RefPtr<TinyVGDecodedImageData> decoded_image {};
RefPtr<Bitmap> bitmap {};
enum class State {
NotDecoded = 0,
HeaderDecoded,
ImageDecoded,
Error,
};
State state { State::NotDecoded };
};
static ErrorOr<void> decode_header_and_update_context(TinyVGLoadingContext& context)
{
VERIFY(context.state == TinyVGLoadingContext::State::NotDecoded);
auto header_or_error = decode_tinyvg_header(context.stream);
if (header_or_error.is_error()) {
context.state = TinyVGLoadingContext::State::Error;
return header_or_error.release_error();
}
context.state = TinyVGLoadingContext::State::HeaderDecoded;
context.header = header_or_error.release_value();
return {};
}
static ErrorOr<void> decode_image_data_and_update_context(TinyVGLoadingContext& context)
{
VERIFY(context.state == TinyVGLoadingContext::State::HeaderDecoded);
auto image_data_or_error = TinyVGDecodedImageData::decode(context.stream, context.header);
if (image_data_or_error.is_error()) {
context.state = TinyVGLoadingContext::State::Error;
return image_data_or_error.release_error();
}
context.state = TinyVGLoadingContext::State::ImageDecoded;
context.decoded_image = image_data_or_error.release_value();
return {};
}
static ErrorOr<void> ensure_fully_decoded(TinyVGLoadingContext& context)
{
if (context.state == TinyVGLoadingContext::State::Error)
return Error::from_string_literal("TinyVGImageDecoderPlugin: Decoding failed!");
if (context.state == TinyVGLoadingContext::State::NotDecoded)
TRY(decode_header_and_update_context(context));
if (context.state == TinyVGLoadingContext::State::HeaderDecoded)
TRY(decode_image_data_and_update_context(context));
VERIFY(context.state == TinyVGLoadingContext::State::ImageDecoded);
return {};
}
TinyVGImageDecoderPlugin::TinyVGImageDecoderPlugin(ReadonlyBytes bytes) TinyVGImageDecoderPlugin::TinyVGImageDecoderPlugin(ReadonlyBytes bytes)
: m_context { bytes } : m_context { make<TinyVGLoadingContext>(FixedMemoryStream { bytes }) }
{ {
} }
@ -486,29 +542,33 @@ bool TinyVGImageDecoderPlugin::sniff(ReadonlyBytes bytes)
IntSize TinyVGImageDecoderPlugin::size() IntSize TinyVGImageDecoderPlugin::size()
{ {
if (m_context.decoded_image) if (m_context->state == TinyVGLoadingContext::State::NotDecoded)
return m_context.decoded_image->size(); (void)decode_header_and_update_context(*m_context);
return {};
if (m_context->state == TinyVGLoadingContext::State::Error)
return {};
return { m_context->header.width, m_context->header.height };
} }
ErrorOr<void> TinyVGImageDecoderPlugin::initialize() ErrorOr<void> TinyVGImageDecoderPlugin::initialize()
{ {
FixedMemoryStream stream { { m_context.data.data(), m_context.data.size() } }; return decode_header_and_update_context(*m_context);
m_context.decoded_image = TRY(TinyVGDecodedImageData::decode(stream));
return {};
} }
ErrorOr<ImageFrameDescriptor> TinyVGImageDecoderPlugin::frame(size_t, Optional<IntSize> ideal_size) ErrorOr<ImageFrameDescriptor> TinyVGImageDecoderPlugin::frame(size_t, Optional<IntSize> ideal_size)
{ {
auto target_size = ideal_size.value_or(m_context.decoded_image->size()); TRY(ensure_fully_decoded(*m_context));
if (!m_context.bitmap || m_context.bitmap->size() != target_size) auto target_size = ideal_size.value_or(m_context->decoded_image->size());
m_context.bitmap = TRY(m_context.decoded_image->bitmap(target_size)); if (!m_context->bitmap || m_context->bitmap->size() != target_size)
return ImageFrameDescriptor { m_context.bitmap }; m_context->bitmap = TRY(m_context->decoded_image->bitmap(target_size));
return ImageFrameDescriptor { m_context->bitmap };
} }
ErrorOr<VectorImageFrameDescriptor> TinyVGImageDecoderPlugin::vector_frame(size_t) ErrorOr<VectorImageFrameDescriptor> TinyVGImageDecoderPlugin::vector_frame(size_t)
{ {
return VectorImageFrameDescriptor { m_context.decoded_image, 0 }; TRY(ensure_fully_decoded(*m_context));
return VectorImageFrameDescriptor { m_context->decoded_image, 0 };
} }
} }

View file

@ -34,6 +34,8 @@ namespace Gfx {
// Decoder from the "Tiny Vector Graphics" format (v1.0). // Decoder from the "Tiny Vector Graphics" format (v1.0).
// https://tinyvg.tech/download/specification.pdf // https://tinyvg.tech/download/specification.pdf
struct TinyVGHeader;
class TinyVGDecodedImageData final : public VectorGraphic { class TinyVGDecodedImageData final : public VectorGraphic {
public: public:
using Style = Variant<Color, NonnullRefPtr<SVGGradientPaintStyle>>; using Style = Variant<Color, NonnullRefPtr<SVGGradientPaintStyle>>;
@ -58,6 +60,7 @@ public:
} }
static ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> decode(Stream& stream); static ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> decode(Stream& stream);
static ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> decode(Stream& stream, TinyVGHeader const& header);
private: private:
TinyVGDecodedImageData(IntSize size, Vector<DrawCommand> draw_commands) TinyVGDecodedImageData(IntSize size, Vector<DrawCommand> draw_commands)
@ -70,11 +73,7 @@ private:
Vector<DrawCommand> m_draw_commands; Vector<DrawCommand> m_draw_commands;
}; };
struct TinyVGLoadingContext { struct TinyVGLoadingContext;
ReadonlyBytes data;
RefPtr<TinyVGDecodedImageData> decoded_image {};
RefPtr<Bitmap> bitmap {};
};
class TinyVGImageDecoderPlugin final : public ImageDecoderPlugin { class TinyVGImageDecoderPlugin final : public ImageDecoderPlugin {
public: public:
@ -98,7 +97,7 @@ public:
private: private:
TinyVGImageDecoderPlugin(ReadonlyBytes); TinyVGImageDecoderPlugin(ReadonlyBytes);
TinyVGLoadingContext m_context; NonnullOwnPtr<TinyVGLoadingContext> m_context;
}; };
} }