Explorar el Código

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.
Andreas Kling hace 2 años
padre
commit
680fc3f90a

+ 67 - 35
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
  * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
@@ -11,7 +11,9 @@
 #include <LibWeb/CSS/ComputedValues.h>
 #include <LibWeb/CSS/ComputedValues.h>
 #include <LibWeb/CSS/Serialize.h>
 #include <LibWeb/CSS/Serialize.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Document.h>
-#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/HTML/DecodedImageData.h>
+#include <LibWeb/HTML/ImageRequest.h>
+#include <LibWeb/HTML/PotentialCORSRequest.h>
 #include <LibWeb/Painting/PaintContext.h>
 #include <LibWeb/Painting/PaintContext.h>
 #include <LibWeb/Platform/Timer.h>
 #include <LibWeb/Platform/Timer.h>
 
 
@@ -25,41 +27,59 @@ ImageStyleValue::ImageStyleValue(AK::URL const& url)
 
 
 void ImageStyleValue::load_any_resources(DOM::Document& document)
 void ImageStyleValue::load_any_resources(DOM::Document& document)
 {
 {
-    if (resource())
+    if (m_image_request)
         return;
         return;
-
     m_document = &document;
     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;
         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()
 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())
     if (current_frame_duration != m_timer->interval())
         m_timer->restart(current_frame_duration);
         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;
         ++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();
             m_timer->stop();
     }
     }
 
 
@@ -67,11 +87,16 @@ void ImageStyleValue::animate()
         on_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<String> ImageStyleValue::to_string() const
 ErrorOr<String> ImageStyleValue::to_string() const
@@ -88,24 +113,31 @@ bool ImageStyleValue::equals(StyleValue const& other) const
 
 
 Optional<CSSPixels> ImageStyleValue::natural_width() const
 Optional<CSSPixels> 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 {};
     return {};
 }
 }
 
 
 Optional<CSSPixels> ImageStyleValue::natural_height() const
 Optional<CSSPixels> 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 {};
     return {};
 }
 }
 
 
 void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const
 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<int>());
-        context.painter().draw_scaled_bitmap(dest_rect.to_type<int>(), *b, bitmap(0)->rect(), 1.f, scaling_mode);
+    if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) {
+        auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>());
+        context.painter().draw_scaled_bitmap(dest_rect.to_type<int>(), *b, b->rect(), 1.f, scaling_mode);
     }
     }
 }
 }
 
 
+RefPtr<HTML::DecodedImageData const> ImageStyleValue::image_data() const
+{
+    if (!m_image_request)
+        return nullptr;
+    return m_image_request->image_data();
+}
+
 }
 }

+ 6 - 6
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h

@@ -12,13 +12,12 @@
 #include <AK/URL.h>
 #include <AK/URL.h>
 #include <LibWeb/CSS/Enums.h>
 #include <LibWeb/CSS/Enums.h>
 #include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
 #include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
-#include <LibWeb/Loader/ImageResource.h>
 
 
 namespace Web::CSS {
 namespace Web::CSS {
 
 
 class ImageStyleValue final
 class ImageStyleValue final
     : public AbstractImageStyleValue
     : public AbstractImageStyleValue
-    , public ImageResourceClient {
+    , public Weakable<ImageStyleValue> {
 public:
 public:
     static ErrorOr<ValueComparingNonnullRefPtr<ImageStyleValue>> create(AK::URL const& url)
     static ErrorOr<ValueComparingNonnullRefPtr<ImageStyleValue>> create(AK::URL const& url)
     {
     {
@@ -34,19 +33,20 @@ public:
     Optional<CSSPixels> natural_width() const override;
     Optional<CSSPixels> natural_width() const override;
     Optional<CSSPixels> natural_height() const override;
     Optional<CSSPixels> 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;
     void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
 
 
     Function<void()> on_animate;
     Function<void()> on_animate;
 
 
+    RefPtr<HTML::DecodedImageData const> image_data() const;
+
 private:
 private:
     ImageStyleValue(AK::URL const&);
     ImageStyleValue(AK::URL const&);
 
 
-    // ^ResourceClient
-    virtual void resource_did_load() override;
+    RefPtr<HTML::ImageRequest> m_image_request;
 
 
     void animate();
     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;
     AK::URL m_url;
     WeakPtr<DOM::Document> m_document;
     WeakPtr<DOM::Document> m_document;