diff --git a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp index d5e819a092..19f2bee52f 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp @@ -329,7 +329,11 @@ private: ErrorOr> TinyVGDecodedImageData::decode(Stream& stream) { - auto header = TRY(decode_tinyvg_header(stream)); + return decode(stream, TRY(decode_tinyvg_header(stream))); +} + +ErrorOr> TinyVGDecodedImageData::decode(Stream& stream, TinyVGHeader const& header) +{ if (header.version != 1) 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 decoded_image {}; + RefPtr bitmap {}; + enum class State { + NotDecoded = 0, + HeaderDecoded, + ImageDecoded, + Error, + }; + State state { State::NotDecoded }; +}; + +static ErrorOr 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 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 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) - : m_context { bytes } + : m_context { make(FixedMemoryStream { bytes }) } { } @@ -486,29 +542,33 @@ bool TinyVGImageDecoderPlugin::sniff(ReadonlyBytes bytes) IntSize TinyVGImageDecoderPlugin::size() { - if (m_context.decoded_image) - return m_context.decoded_image->size(); - return {}; + if (m_context->state == TinyVGLoadingContext::State::NotDecoded) + (void)decode_header_and_update_context(*m_context); + + if (m_context->state == TinyVGLoadingContext::State::Error) + return {}; + + return { m_context->header.width, m_context->header.height }; } ErrorOr TinyVGImageDecoderPlugin::initialize() { - FixedMemoryStream stream { { m_context.data.data(), m_context.data.size() } }; - m_context.decoded_image = TRY(TinyVGDecodedImageData::decode(stream)); - return {}; + return decode_header_and_update_context(*m_context); } ErrorOr TinyVGImageDecoderPlugin::frame(size_t, Optional ideal_size) { - auto target_size = ideal_size.value_or(m_context.decoded_image->size()); - if (!m_context.bitmap || m_context.bitmap->size() != target_size) - m_context.bitmap = TRY(m_context.decoded_image->bitmap(target_size)); - return ImageFrameDescriptor { m_context.bitmap }; + TRY(ensure_fully_decoded(*m_context)); + auto target_size = ideal_size.value_or(m_context->decoded_image->size()); + if (!m_context->bitmap || m_context->bitmap->size() != target_size) + m_context->bitmap = TRY(m_context->decoded_image->bitmap(target_size)); + return ImageFrameDescriptor { m_context->bitmap }; } ErrorOr 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 }; } } diff --git a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h index de7f192103..e3c1c1b346 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h +++ b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h @@ -34,6 +34,8 @@ namespace Gfx { // Decoder from the "Tiny Vector Graphics" format (v1.0). // https://tinyvg.tech/download/specification.pdf +struct TinyVGHeader; + class TinyVGDecodedImageData final : public VectorGraphic { public: using Style = Variant>; @@ -58,6 +60,7 @@ public: } static ErrorOr> decode(Stream& stream); + static ErrorOr> decode(Stream& stream, TinyVGHeader const& header); private: TinyVGDecodedImageData(IntSize size, Vector draw_commands) @@ -70,11 +73,7 @@ private: Vector m_draw_commands; }; -struct TinyVGLoadingContext { - ReadonlyBytes data; - RefPtr decoded_image {}; - RefPtr bitmap {}; -}; +struct TinyVGLoadingContext; class TinyVGImageDecoderPlugin final : public ImageDecoderPlugin { public: @@ -98,7 +97,7 @@ public: private: TinyVGImageDecoderPlugin(ReadonlyBytes); - TinyVGLoadingContext m_context; + NonnullOwnPtr m_context; }; }