From 680fc3f90a6cfa0abcbc63ef203ad0e51e920211 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 11 Jun 2023 15:37:36 +0200 Subject: [PATCH] LibWeb: Piggyback on HTML::ImageRequest in CSS ImageStyleValue This is all ad-hoc since no spec currently exists for this behavior. Basically, ImageStyleValue now uses ImageRequest for fetching and decoding of images. This already leads to visible improvements on many websites. --- .../CSS/StyleValues/ImageStyleValue.cpp | 100 ++++++++++++------ .../LibWeb/CSS/StyleValues/ImageStyleValue.h | 12 +-- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index 872f09c31c7..f7c08d40efe 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2023, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * Copyright (c) 2021-2023, Sam Atkins * Copyright (c) 2022-2023, MacDue @@ -11,7 +11,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -25,41 +27,59 @@ ImageStyleValue::ImageStyleValue(AK::URL const& url) void ImageStyleValue::load_any_resources(DOM::Document& document) { - if (resource()) + if (m_image_request) return; - m_document = &document; - auto request = LoadRequest::create_for_url_on_page(m_url, document.page()); - set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request)); -} -void ImageStyleValue::resource_did_load() -{ - if (!m_document) + m_image_request = HTML::ImageRequest::get_shareable_or_create(*document.page(), m_url).release_value_but_fixme_should_propagate_errors(); + m_image_request->add_callbacks( + [this, weak_this = make_weak_ptr()] { + if (!weak_this) + return; + + if (!m_document) + return; + + // FIXME: Do less than a full repaint if possible? + if (auto* browsing_context = m_document->browsing_context()) + browsing_context->set_needs_display(); + + auto image_data = m_image_request->image_data(); + if (image_data->is_animated() && image_data->frame_count() > 1) { + m_timer = Platform::Timer::create(); + m_timer->set_interval(image_data->frame_duration(0)); + m_timer->on_timeout = [this] { animate(); }; + m_timer->start(); + } + }, + nullptr); + + // If the image request is already available or fetching, no need to start another fetch. + if (m_image_request->is_available() || m_image_request->fetch_controller()) return; - // FIXME: Do less than a full repaint if possible? - if (m_document && m_document->browsing_context()) - m_document->browsing_context()->set_needs_display(); - if (resource()->is_animated() && resource()->frame_count() > 1) { - m_timer = Platform::Timer::create(); - m_timer->set_interval(resource()->frame_duration(0)); - m_timer->on_timeout = [this] { animate(); }; - m_timer->start(); - } + auto request = HTML::create_potential_CORS_request(document.vm(), m_url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS); + request->set_client(&document.relevant_settings_object()); + m_image_request->fetch_image(document.realm(), request); } void ImageStyleValue::animate() { - m_current_frame_index = (m_current_frame_index + 1) % resource()->frame_count(); - auto current_frame_duration = resource()->frame_duration(m_current_frame_index); + if (!m_image_request) + return; + auto image_data = m_image_request->image_data(); + if (!image_data) + return; + + m_current_frame_index = (m_current_frame_index + 1) % image_data->frame_count(); + auto current_frame_duration = image_data->frame_duration(m_current_frame_index); if (current_frame_duration != m_timer->interval()) m_timer->restart(current_frame_duration); - if (m_current_frame_index == resource()->frame_count() - 1) { + if (m_current_frame_index == image_data->frame_count() - 1) { ++m_loops_completed; - if (m_loops_completed > 0 && m_loops_completed == resource()->loop_count()) + if (m_loops_completed > 0 && m_loops_completed == image_data->loop_count()) m_timer->stop(); } @@ -67,11 +87,16 @@ void ImageStyleValue::animate() on_animate(); } -Gfx::Bitmap const* ImageStyleValue::bitmap(size_t frame_index) const +bool ImageStyleValue::is_paintable() const { - if (!resource()) - return nullptr; - return resource()->bitmap(frame_index); + return image_data(); +} + +Gfx::Bitmap const* ImageStyleValue::bitmap(size_t frame_index, Gfx::IntSize size) const +{ + if (auto image_data = this->image_data()) + return image_data->bitmap(frame_index, size); + return nullptr; } ErrorOr ImageStyleValue::to_string() const @@ -88,24 +113,31 @@ bool ImageStyleValue::equals(StyleValue const& other) const Optional ImageStyleValue::natural_width() const { - if (auto* b = bitmap(0); b != nullptr) - return b->width(); + if (auto image_data = this->image_data()) + return image_data->intrinsic_width(); return {}; } Optional ImageStyleValue::natural_height() const { - if (auto* b = bitmap(0); b != nullptr) - return b->height(); + if (auto image_data = this->image_data()) + return image_data->intrinsic_height(); return {}; } void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const { - if (auto* b = bitmap(m_current_frame_index); b != nullptr) { - auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap(0)->rect(), dest_rect.to_type()); - context.painter().draw_scaled_bitmap(dest_rect.to_type(), *b, bitmap(0)->rect(), 1.f, scaling_mode); + if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type()); b != nullptr) { + auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type()); + context.painter().draw_scaled_bitmap(dest_rect.to_type(), *b, b->rect(), 1.f, scaling_mode); } } +RefPtr ImageStyleValue::image_data() const +{ + if (!m_image_request) + return nullptr; + return m_image_request->image_data(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 53c4d894849..b5fb47b25f0 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -12,13 +12,12 @@ #include #include #include -#include namespace Web::CSS { class ImageStyleValue final : public AbstractImageStyleValue - , public ImageResourceClient { + , public Weakable { public: static ErrorOr> create(AK::URL const& url) { @@ -34,19 +33,20 @@ public: Optional natural_width() const override; Optional natural_height() const override; - bool is_paintable() const override { return bitmap(0) != nullptr; } + virtual bool is_paintable() const override; void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; Function on_animate; + RefPtr image_data() const; + private: ImageStyleValue(AK::URL const&); - // ^ResourceClient - virtual void resource_did_load() override; + RefPtr m_image_request; void animate(); - Gfx::Bitmap const* bitmap(size_t index) const; + Gfx::Bitmap const* bitmap(size_t frame_index, Gfx::IntSize = {}) const; AK::URL m_url; WeakPtr m_document;