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:
parent
753cf04be8
commit
57c81c1719
2 changed files with 78 additions and 19 deletions
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue