diff --git a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.h b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.h index e9498e4924..daefbbb00b 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.h +++ b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace Gfx { @@ -25,6 +26,11 @@ struct ImageFrameDescriptor { int duration { 0 }; }; +struct VectorImageFrameDescriptor { + RefPtr image; + int duration { 0 }; +}; + class ImageDecoderPlugin { public: virtual ~ImageDecoderPlugin() = default; @@ -34,12 +40,16 @@ public: virtual ErrorOr initialize() { return {}; } virtual bool is_animated() = 0; + virtual size_t loop_count() = 0; virtual size_t frame_count() = 0; virtual size_t first_animated_frame_index() = 0; virtual ErrorOr frame(size_t index, Optional ideal_size = {}) = 0; virtual ErrorOr> icc_data() = 0; + virtual bool is_vector() { return false; } + virtual ErrorOr vector_frame(size_t) { VERIFY_NOT_REACHED(); } + protected: ImageDecoderPlugin() = default; }; @@ -59,6 +69,9 @@ public: ErrorOr frame(size_t index, Optional ideal_size = {}) const { return m_plugin->frame(index, ideal_size); } ErrorOr> icc_data() const { return m_plugin->icc_data(); } + bool is_vector() { return m_plugin->is_vector(); } + ErrorOr vector_frame(size_t index) { return m_plugin->vector_frame(index); } + private: explicit ImageDecoder(NonnullOwnPtr); diff --git a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp index 25ea74f29a..d5e819a092 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp @@ -327,7 +327,7 @@ private: ReadonlySpan m_color_table; }; -ErrorOr TinyVGDecodedImageData::decode(Stream& stream) +ErrorOr> TinyVGDecodedImageData::decode(Stream& stream) { auto header = TRY(decode_tinyvg_header(stream)); if (header.version != 1) @@ -437,48 +437,35 @@ ErrorOr TinyVGDecodedImageData::decode(Stream& stream) } } - return TinyVGDecodedImageData { { header.width, header.height }, move(draw_commands) }; + return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) TinyVGDecodedImageData({ header.width, header.height }, move(draw_commands)))); } -ErrorOr> TinyVGDecodedImageData::bitmap(IntSize size) const +void TinyVGDecodedImageData::draw_transformed(Painter& painter, AffineTransform transform) const { - auto scale_x = float(size.width()) / m_size.width(); - auto scale_y = float(size.height()) / m_size.height(); - auto transform = Gfx::AffineTransform {}.scale(scale_x, scale_y); - auto bitmap = TRY(Bitmap::create(Gfx::BitmapFormat::BGRA8888, size)); - Painter base_painter { *bitmap }; - AntiAliasingPainter painter { base_painter }; + // FIXME: Correctly handle non-uniform scales. + auto scale = max(transform.x_scale(), transform.y_scale()); + AntiAliasingPainter aa_painter { painter }; for (auto const& command : draw_commands()) { auto draw_path = command.path.copy_transformed(transform); if (command.fill.has_value()) { auto fill_path = draw_path; fill_path.close_all_subpaths(); command.fill->visit( - [&](Color color) { painter.fill_path(fill_path, color, Painter::WindingRule::EvenOdd); }, + [&](Color color) { aa_painter.fill_path(fill_path, color, Painter::WindingRule::EvenOdd); }, [&](NonnullRefPtr style) { const_cast(*style).set_gradient_transform(transform); - painter.fill_path(fill_path, style, 1.0f, Painter::WindingRule::EvenOdd); + aa_painter.fill_path(fill_path, style, 1.0f, Painter::WindingRule::EvenOdd); }); } if (command.stroke.has_value()) { - // FIXME: A more correct way to non-uniformly scale strokes would be: - // 1. Scale the path uniformly by the largest of scale_x/y - // 2. Convert that to a fill with .stroke_to_fill() - // 3. - // If scale_x > scale_y - // Scale by: (1, scale_y/scale_x) - // else - // Scale by: (scale_x/scale_y, 1) - auto stroke_scale = max(scale_x, scale_y); command.stroke->visit( - [&](Color color) { painter.stroke_path(draw_path, color, command.stroke_width * stroke_scale); }, + [&](Color color) { aa_painter.stroke_path(draw_path, color, command.stroke_width * scale); }, [&](NonnullRefPtr style) { const_cast(*style).set_gradient_transform(transform); - painter.stroke_path(draw_path, style, command.stroke_width * stroke_scale); + aa_painter.stroke_path(draw_path, style, command.stroke_width * scale); }); } } - return bitmap; } TinyVGImageDecoderPlugin::TinyVGImageDecoderPlugin(ReadonlyBytes bytes) @@ -507,7 +494,7 @@ IntSize TinyVGImageDecoderPlugin::size() ErrorOr TinyVGImageDecoderPlugin::initialize() { FixedMemoryStream stream { { m_context.data.data(), m_context.data.size() } }; - m_context.decoded_image = make(TRY(TinyVGDecodedImageData::decode(stream))); + m_context.decoded_image = TRY(TinyVGDecodedImageData::decode(stream)); return {}; } @@ -519,4 +506,9 @@ ErrorOr TinyVGImageDecoderPlugin::frame(size_t, Optional TinyVGImageDecoderPlugin::vector_frame(size_t) +{ + return VectorImageFrameDescriptor { m_context.decoded_image, 0 }; +} + } diff --git a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h index 6baa3bac64..de7f192103 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h +++ b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace Gfx { @@ -33,7 +34,7 @@ namespace Gfx { // Decoder from the "Tiny Vector Graphics" format (v1.0). // https://tinyvg.tech/download/specification.pdf -class TinyVGDecodedImageData { +class TinyVGDecodedImageData final : public VectorGraphic { public: using Style = Variant>; @@ -44,19 +45,19 @@ public: float stroke_width { 0.0f }; }; - ErrorOr> bitmap(IntSize size) const; - - IntSize size() const + virtual IntSize intrinsic_size() const override { return m_size; } + virtual void draw_transformed(Painter&, AffineTransform) const override; + ReadonlySpan draw_commands() const { return m_draw_commands; } - static ErrorOr decode(Stream& stream); + static ErrorOr> decode(Stream& stream); private: TinyVGDecodedImageData(IntSize size, Vector draw_commands) @@ -71,7 +72,7 @@ private: struct TinyVGLoadingContext { ReadonlyBytes data; - OwnPtr decoded_image {}; + RefPtr decoded_image {}; RefPtr bitmap {}; }; @@ -89,6 +90,9 @@ public: virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; virtual ErrorOr> icc_data() override { return OptionalNone {}; } + virtual bool is_vector() override { return true; } + virtual ErrorOr vector_frame(size_t index) override; + virtual ~TinyVGImageDecoderPlugin() override = default; private: