diff options
author | Andreas Kling <kling@serenityos.org> | 2021-01-29 22:30:48 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-29 22:38:22 +0100 |
commit | 7449c1b27f08305a30e63e3f16694c85306fba08 (patch) | |
tree | 128a45164cf86aff2e7895173745b5da5ce2bc50 /Userland/Libraries | |
parent | 449d56ef746cde5563cd42d33b1cff0b79d2e10c (diff) | |
download | serenity-7449c1b27f08305a30e63e3f16694c85306fba08.zip |
ImageDecoder+LibImageDecoder+LibWeb: Support animations in ImageDecoder
The ImageDecoder service now returns a list of image frames, each with
a duration value.
The code for in-process image decoding is removed from LibWeb, an all
image decode requests are sent out-of-process to ImageDecoder. :^)
This won't scale super well to very long and/or large animations, but
we can work on improving that separately. The main goal here is simply
to stop doing any image decoding inside LibWeb.
Fixes #5165.
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibImageDecoderClient/Client.cpp | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibImageDecoderClient/Client.h | 13 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/ImageBox.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Loader/ImageLoader.cpp | 43 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Loader/ImageLoader.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Loader/ImageResource.cpp | 75 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Loader/ImageResource.h | 33 |
8 files changed, 121 insertions, 68 deletions
diff --git a/Userland/Libraries/LibImageDecoderClient/Client.cpp b/Userland/Libraries/LibImageDecoderClient/Client.cpp index 4b6a7325c9..e9c2a25350 100644 --- a/Userland/Libraries/LibImageDecoderClient/Client.cpp +++ b/Userland/Libraries/LibImageDecoderClient/Client.cpp @@ -46,20 +46,30 @@ void Client::handle(const Messages::ImageDecoderClient::Dummy&) { } -RefPtr<Gfx::Bitmap> Client::decode_image(const ByteBuffer& encoded_data) +Optional<DecodedImage> Client::decode_image(const ByteBuffer& encoded_data) { if (encoded_data.is_empty()) - return nullptr; + return {}; auto encoded_buffer = Core::AnonymousBuffer::create_with_size(encoded_data.size()); if (!encoded_buffer.is_valid()) { dbgln("Could not allocate encoded buffer"); - return nullptr; + return {}; } memcpy(encoded_buffer.data<void>(), encoded_data.data(), encoded_data.size()); auto response = send_sync<Messages::ImageDecoderServer::DecodeImage>(move(encoded_buffer)); - return response->bitmap().bitmap(); + + DecodedImage image; + image.is_animated = response->is_animated(); + image.loop_count = response->loop_count(); + image.frames.resize(response->bitmaps().size()); + for (size_t i = 0; i < image.frames.size(); ++i) { + auto& frame = image.frames[i]; + frame.bitmap = response->bitmaps()[i].bitmap(); + frame.duration = response->durations()[i]; + } + return move(image); } } diff --git a/Userland/Libraries/LibImageDecoderClient/Client.h b/Userland/Libraries/LibImageDecoderClient/Client.h index 7eeace6b22..11d0b77545 100644 --- a/Userland/Libraries/LibImageDecoderClient/Client.h +++ b/Userland/Libraries/LibImageDecoderClient/Client.h @@ -33,6 +33,17 @@ namespace ImageDecoderClient { +struct Frame { + RefPtr<Gfx::Bitmap> bitmap; + u32 duration { 0 }; +}; + +struct DecodedImage { + bool is_animated { false }; + u32 loop_count { 0 }; + Vector<Frame> frames; +}; + class Client : public IPC::ServerConnection<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint> , public ImageDecoderClientEndpoint { @@ -41,7 +52,7 @@ class Client public: virtual void handshake() override; - RefPtr<Gfx::Bitmap> decode_image(const ByteBuffer&); + Optional<DecodedImage> decode_image(const ByteBuffer&); private: Client(); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp index 61b99f0ffa..c4a4890160 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -94,7 +94,7 @@ RefPtr<Layout::Node> HTMLImageElement::create_layout_node() const Gfx::Bitmap* HTMLImageElement::bitmap() const { - return m_image_loader.bitmap(); + return m_image_loader.bitmap(m_image_loader.current_frame_index()); } } diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp index a00c551c7a..aefd465eda 100644 --- a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp @@ -114,7 +114,7 @@ void ImageBox::paint(PaintContext& context, PaintPhase phase) if (alt.is_empty()) alt = image_element.src(); context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right); - } else if (auto bitmap = m_image_loader.bitmap()) { + } else if (auto bitmap = m_image_loader.bitmap(m_image_loader.current_frame_index())) { context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *bitmap, bitmap->rect()); } } diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp index 72b3bb3eb6..cd4cfc0a6a 100644 --- a/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp +++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp @@ -80,15 +80,10 @@ void ImageLoader::resource_did_load() } } - if (resource()->should_decode_in_process()) { - auto& decoder = resource()->ensure_decoder(); - - if (decoder.is_animated() && decoder.frame_count() > 1) { - const auto& first_frame = decoder.frame(0); - m_timer->set_interval(first_frame.duration); - m_timer->on_timeout = [this] { animate(); }; - m_timer->start(); - } + if (resource()->is_animated() && resource()->frame_count() > 1) { + m_timer->set_interval(resource()->frame_duration(0)); + m_timer->on_timeout = [this] { animate(); }; + m_timer->start(); } if (on_load) @@ -100,18 +95,16 @@ void ImageLoader::animate() if (!m_visible_in_viewport) return; - auto& decoder = resource()->ensure_decoder(); - - m_current_frame_index = (m_current_frame_index + 1) % decoder.frame_count(); - const auto& current_frame = decoder.frame(m_current_frame_index); + m_current_frame_index = (m_current_frame_index + 1) % resource()->frame_count(); + auto current_frame_duration = resource()->frame_duration(m_current_frame_index); - if (current_frame.duration != m_timer->interval()) { - m_timer->restart(current_frame.duration); + if (current_frame_duration != m_timer->interval()) { + m_timer->restart(current_frame_duration); } - if (m_current_frame_index == decoder.frame_count() - 1) { + if (m_current_frame_index == resource()->frame_count() - 1) { ++m_loops_completed; - if (m_loops_completed > 0 && m_loops_completed == decoder.loop_count()) { + if (m_loops_completed > 0 && m_loops_completed == resource()->loop_count()) { m_timer->stop(); } } @@ -132,34 +125,28 @@ bool ImageLoader::has_image() const { if (!resource()) return false; - if (resource()->should_decode_in_process()) - return const_cast<ImageResource*>(resource())->ensure_decoder().bitmap(); - return true; + return bitmap(0); } unsigned ImageLoader::width() const { if (!resource()) return 0; - if (resource()->should_decode_in_process()) - return const_cast<ImageResource*>(resource())->ensure_decoder().width(); - return bitmap() ? bitmap()->width() : 0; + return bitmap(0) ? bitmap(0)->width() : 0; } unsigned ImageLoader::height() const { if (!resource()) return 0; - if (resource()->should_decode_in_process()) - return const_cast<ImageResource*>(resource())->ensure_decoder().height(); - return bitmap() ? bitmap()->height() : 0; + return bitmap(0) ? bitmap(0)->height() : 0; } -const Gfx::Bitmap* ImageLoader::bitmap() const +const Gfx::Bitmap* ImageLoader::bitmap(size_t frame_index) const { if (!resource()) return nullptr; - return resource()->bitmap(m_current_frame_index); + return resource()->bitmap(frame_index); } } diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.h b/Userland/Libraries/LibWeb/Loader/ImageLoader.h index f07b6e3055..1297958366 100644 --- a/Userland/Libraries/LibWeb/Loader/ImageLoader.h +++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.h @@ -38,7 +38,8 @@ public: void load(const URL&); - const Gfx::Bitmap* bitmap() const; + const Gfx::Bitmap* bitmap(size_t index) const; + size_t current_frame_index() const { return m_current_frame_index; } bool has_image() const; diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.cpp b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp index 3daa284deb..73f9b93850 100644 --- a/Userland/Libraries/LibWeb/Loader/ImageResource.cpp +++ b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp @@ -41,43 +41,51 @@ ImageResource::~ImageResource() { } -bool ImageResource::should_decode_in_process() const +int ImageResource::frame_duration(size_t frame_index) const { - return mime_type() == "image/gif"; + decode_if_needed(); + if (frame_index >= m_decoded_frames.size()) + return 0; + return m_decoded_frames[frame_index].duration; } -Gfx::ImageDecoder& ImageResource::ensure_decoder() +void ImageResource::decode_if_needed() const { - if (!m_decoder) - m_decoder = Gfx::ImageDecoder::create(encoded_data()); - return *m_decoder; + if (!has_encoded_data()) + return; + + if (m_has_attempted_decode) + return; + + if (!m_decoded_frames.is_empty()) + return; + + auto image_decoder_client = ImageDecoderClient::Client::construct(); + auto image = image_decoder_client->decode_image(encoded_data()); + if (image.has_value()) { + m_loop_count = image.value().loop_count; + m_animated = image.value().is_animated; + m_decoded_frames.resize(image.value().frames.size()); + for (size_t i = 0; i < m_decoded_frames.size(); ++i) { + auto& frame = m_decoded_frames[i]; + frame.bitmap = image.value().frames[i].bitmap; + frame.duration = image.value().frames[i].duration; + } + } + + m_has_attempted_decode = true; } const Gfx::Bitmap* ImageResource::bitmap(size_t frame_index) const { - if (!has_encoded_data()) + decode_if_needed(); + if (frame_index >= m_decoded_frames.size()) return nullptr; - - if (should_decode_in_process()) { - if (!m_decoder) - return nullptr; - if (m_decoder->is_animated()) - m_decoded_image = m_decoder->frame(frame_index).image; - else - m_decoded_image = m_decoder->bitmap(); - } else if (!m_decoded_image && !m_has_attempted_decode) { - auto image_decoder_client = ImageDecoderClient::Client::construct(); - m_decoded_image = image_decoder_client->decode_image(encoded_data()); - m_has_attempted_decode = true; - } - return m_decoded_image; + return m_decoded_frames[frame_index].bitmap; } void ImageResource::update_volatility() { - if (!m_decoder) - return; - bool visible_in_viewport = false; for_each_client([&](auto& client) { if (static_cast<const ImageResourceClient&>(client).is_visible_in_viewport()) @@ -85,15 +93,28 @@ void ImageResource::update_volatility() }); if (!visible_in_viewport) { - m_decoder->set_volatile(); + for (auto& frame : m_decoded_frames) { + if (frame.bitmap) + frame.bitmap->set_volatile(); + } return; } - bool still_has_decoded_image = m_decoder->set_nonvolatile(); + bool still_has_decoded_image = true; + for (auto& frame : m_decoded_frames) { + if (!frame.bitmap) { + still_has_decoded_image = false; + } else { + bool still_has_frame = frame.bitmap->set_nonvolatile(); + if (!still_has_frame) + still_has_decoded_image = false; + } + } if (still_has_decoded_image) return; - m_decoder = nullptr; + m_decoded_frames.clear(); + m_has_attempted_decode = false; } ImageResourceClient::~ImageResourceClient() diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.h b/Userland/Libraries/LibWeb/Loader/ImageResource.h index 0e66785a70..4f3f2fe866 100644 --- a/Userland/Libraries/LibWeb/Loader/ImageResource.h +++ b/Userland/Libraries/LibWeb/Loader/ImageResource.h @@ -35,17 +35,40 @@ class ImageResource final : public Resource { public: virtual ~ImageResource() override; - Gfx::ImageDecoder& ensure_decoder(); - const Gfx::Bitmap* bitmap(size_t frame_index = 0) const; - bool should_decode_in_process() const; + struct Frame { + RefPtr<Gfx::Bitmap> bitmap; + size_t duration { 0 }; + }; + + const Gfx::Bitmap* bitmap(size_t frame_index = 0) const; + int frame_duration(size_t frame_index) const; + size_t frame_count() const + { + decode_if_needed(); + return m_decoded_frames.size(); + } + bool is_animated() const + { + decode_if_needed(); + return m_animated; + } + size_t loop_count() const + { + decode_if_needed(); + return m_loop_count; + } void update_volatility(); private: explicit ImageResource(const LoadRequest&); - RefPtr<Gfx::ImageDecoder> m_decoder; - mutable RefPtr<Gfx::Bitmap> m_decoded_image; + + void decode_if_needed() const; + + mutable bool m_animated { false }; + mutable int m_loop_count { 0 }; + mutable Vector<Frame> m_decoded_frames; mutable bool m_has_attempted_decode { false }; }; |