ladybird/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp
Shannon Booth a342370dfb LibWeb: Rename SharedImageRequest to SharedResourceRequest
For the SVG <use> element, we want to support loading HTML documents
that have a SVG element inside of it pointed to by the URL fragment.

In this situation we would need to fetch and parse the entire document
in SharedImageRequest (so that we can still cache the SVGs). Rename
SharedImageRequest to SharedResourceRequest to make the class a little
more generic for future usecases.
2024-08-05 11:26:41 +02:00

165 lines
5.1 KiB
C++

/*
* Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ImageStyleValue.h"
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/ImageRequest.h>
#include <LibWeb/HTML/PotentialCORSRequest.h>
#include <LibWeb/Painting/DisplayListRecorder.h>
#include <LibWeb/Painting/PaintContext.h>
#include <LibWeb/Platform/Timer.h>
namespace Web::CSS {
ImageStyleValue::ImageStyleValue(URL::URL const& url)
: AbstractImageStyleValue(Type::Image)
, m_url(url)
{
}
ImageStyleValue::~ImageStyleValue() = default;
void ImageStyleValue::load_any_resources(DOM::Document& document)
{
if (m_resource_request)
return;
m_document = &document;
m_resource_request = HTML::SharedResourceRequest::get_or_create(document.realm(), document.page(), m_url);
m_resource_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 navigable = m_document->navigable())
navigable->set_needs_display();
auto image_data = m_resource_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 (m_resource_request->needs_fetching()) {
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_resource_request->fetch_resource(document.realm(), request);
}
}
void ImageStyleValue::animate()
{
if (!m_resource_request)
return;
auto image_data = m_resource_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 == image_data->frame_count() - 1) {
++m_loops_completed;
if (m_loops_completed > 0 && m_loops_completed == image_data->loop_count())
m_timer->stop();
}
if (on_animate)
on_animate();
}
bool ImageStyleValue::is_paintable() const
{
return image_data();
}
Gfx::ImmutableBitmap 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;
}
String ImageStyleValue::to_string() const
{
return serialize_a_url(MUST(m_url.to_string()));
}
bool ImageStyleValue::equals(StyleValue const& other) const
{
if (type() != other.type())
return false;
return m_url == other.as_image().m_url;
}
Optional<CSSPixels> ImageStyleValue::natural_width() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_width();
return {};
}
Optional<CSSPixels> ImageStyleValue::natural_height() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_height();
return {};
}
Optional<CSSPixelFraction> ImageStyleValue::natural_aspect_ratio() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_aspect_ratio();
return {};
}
void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, RefPtr<Painting::DisplayList> text_clip) const
{
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.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, text_clip);
}
}
Gfx::ImmutableBitmap const* ImageStyleValue::current_frame_bitmap(DevicePixelRect const& dest_rect) const
{
return bitmap(m_current_frame_index, dest_rect.size().to_type<int>());
}
JS::GCPtr<HTML::DecodedImageData> ImageStyleValue::image_data() const
{
if (!m_resource_request)
return nullptr;
return m_resource_request->image_data();
}
Optional<Gfx::Color> ImageStyleValue::color_if_single_pixel_bitmap() const
{
if (auto const* b = bitmap(m_current_frame_index)) {
if (b->width() == 1 && b->height() == 1)
return b->bitmap().get_pixel(0, 0);
}
return {};
}
}